Skip to content

Commit

Permalink
Copy code from Introspect for SwiftUI
Browse files Browse the repository at this point in the history
This library currently depends on Introspect for SwiftUI, hosted at
https://github.com/siteline/SwiftUI-Introspect.
There is an issue that seems to be caused by Introspect for SwiftUI,
and fixing it may have unexpected side effects.
I will copy the code and fix it locally avoiding the impact to the
original one.
  • Loading branch information
hugehoge committed Feb 19, 2022
1 parent 3047f04 commit a0f46a8
Show file tree
Hide file tree
Showing 8 changed files with 815 additions and 0 deletions.
15 changes: 15 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,18 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


This library includes the copied code from Introspect for SwiftUI, hosted at
https://github.com/siteline/SwiftUI-Introspect.

Here is the original license of Introspect for SwiftUI:


Copyright 2019 Timber Software

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
68 changes: 68 additions & 0 deletions Sources/Introspect/AppKitIntrospectionView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
import SwiftUI
import AppKit

/// Introspection NSView that is inserted alongside the target view.
@available(macOS 10.15.0, *)
public class IntrospectionNSView: NSView {

required init() {
super.init(frame: .zero)
isHidden = true
}

public override func hitTest(_ point: NSPoint) -> NSView? {
return nil
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

/// Introspection View that is injected into the UIKit hierarchy alongside the target view.
/// After `updateNSView` is called, it calls `selector` to find the target view, then `customize` when the target view is found.
@available(macOS 10.15.0, *)
public struct AppKitIntrospectionView<TargetViewType: NSView>: NSViewRepresentable {

/// Method that introspects the view hierarchy to find the target view.
/// First argument is the introspection view itself, which is contained in a view host alongside the target view.
let selector: (IntrospectionNSView) -> TargetViewType?

/// User-provided customization method for the target view.
let customize: (TargetViewType) -> Void

public init(
selector: @escaping (IntrospectionNSView) -> TargetViewType?,
customize: @escaping (TargetViewType) -> Void
) {
self.selector = selector
self.customize = customize
}

public func makeNSView(context: NSViewRepresentableContext<AppKitIntrospectionView>) -> IntrospectionNSView {
let view = IntrospectionNSView()
view.setAccessibilityLabel("IntrospectionNSView<\(TargetViewType.self)>")
return view
}

/// When `updateNSView` is called after creating the Introspection view, it is not yet in the AppKit hierarchy.
/// At this point, `introspectionView.superview.superview` is nil and we can't access the target AppKit view.
/// To workaround this, we wait until the runloop is done inserting the introspection view in the hierarchy, then run the selector.
/// Finding the target view fails silently if the selector yield no result. This happens when `updateNSView`
/// gets called when the introspection view gets removed from the hierarchy.
public func updateNSView(
_ nsView: IntrospectionNSView,
context: NSViewRepresentableContext<AppKitIntrospectionView>
) {
DispatchQueue.main.async {
guard let targetView = self.selector(nsView) else {
return
}
self.customize(targetView)
}
}
}
#endif

22 changes: 22 additions & 0 deletions Sources/Introspect/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
7 changes: 7 additions & 0 deletions Sources/Introspect/Introspect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>

//! Project version number for Introspect.
FOUNDATION_EXPORT double IntrospectVersionNumber;

//! Project version string for Introspect.
FOUNDATION_EXPORT const unsigned char IntrospectVersionString[];
Loading

0 comments on commit a0f46a8

Please sign in to comment.