{"id":13536894,"url":"https://github.com/siteline/SwiftUI-Introspect","last_synced_at":"2025-04-02T03:31:16.787Z","repository":{"id":37727963,"uuid":"224306658","full_name":"siteline/swiftui-introspect","owner":"siteline","description":"Introspect underlying UIKit/AppKit components from SwiftUI","archived":false,"fork":false,"pushed_at":"2024-07-19T18:17:44.000Z","size":650,"stargazers_count":5950,"open_issues_count":18,"forks_count":363,"subscribers_count":55,"default_branch":"main","last_synced_at":"2025-03-28T16:45:46.572Z","etag":null,"topics":["appkit","inspect","inspection","introspection","nsview","nsviewcontroller","swiftui","uikit","uiview","uiviewcontroller"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/siteline.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-26T23:46:53.000Z","updated_at":"2025-03-28T08:35:35.000Z","dependencies_parsed_at":"2023-09-24T04:07:02.309Z","dependency_job_id":"258b2f54-9cb7-4ec6-a6b1-96d282639020","html_url":"https://github.com/siteline/swiftui-introspect","commit_stats":{"total_commits":241,"total_committers":36,"mean_commits":6.694444444444445,"dds":0.4730290456431535,"last_synced_commit":"c561e0d08b7b801abdfe0b5d251866eac0eb12ad"},"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siteline%2Fswiftui-introspect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siteline%2Fswiftui-introspect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siteline%2Fswiftui-introspect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siteline%2Fswiftui-introspect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/siteline","download_url":"https://codeload.github.com/siteline/swiftui-introspect/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246718449,"owners_count":20822569,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appkit","inspect","inspection","introspection","nsview","nsviewcontroller","swiftui","uikit","uiview","uiviewcontroller"],"created_at":"2024-08-01T09:00:51.393Z","updated_at":"2025-04-02T03:31:16.764Z","avatar_url":"https://github.com/siteline.png","language":"Swift","readme":"SwiftUI Introspect\n=================\n\n[![CI Status Badge](https://github.com/siteline/swiftui-introspect/actions/workflows/ci.yml/badge.svg)](https://github.com/siteline/swiftui-introspect/actions/workflows/ci.yml)\n[![Swift Version Compatibility Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsiteline%2Fswiftui-introspect%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/siteline/swiftui-introspect)\n[![Platform Compatibility Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsiteline%2Fswiftui-introspect%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/siteline/swiftui-introspect)\n\nSwiftUI Introspect allows you to get the underlying UIKit or AppKit element of a SwiftUI view.\n\nFor instance, with SwiftUI Introspect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar.\n\nHow it works\n------------\n\nSwiftUI Introspect works by adding an invisible `IntrospectionView` on top of the selected view, and an invisible \"anchor\" view underneath it, then looking through the UIKit/AppKit view hierarchy between the two to find the relevant view.\n\nFor instance, when introspecting a `ScrollView`...\n\n```swift\nScrollView {\n    Text(\"Item 1\")\n}\n.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) { scrollView in\n    // do something with UIScrollView\n}\n```\n\n... it will:\n\n1. Add marker views in front and behind `ScrollView`.\n2. Traverse through all subviews between both marker views until a `UIScrollView` instance (if any) is found.\n\n\u003e [!IMPORTANT]\n\u003e Although this introspection method is very solid and unlikely to break in itself, future OS releases require explicit opt-in for introspection (`.iOS(.vXYZ)`), given potential differences in underlying UIKit/AppKit view types between major OS versions.\n\nBy default, the `.introspect` modifier acts directly on its _receiver_. This means calling `.introspect` from inside the view you're trying to introspect won't have any effect. However, there are times when this is not possible or simply too inflexible, in which case you **can** introspect an _ancestor_, but you must opt into this explicitly by overriding the introspection `scope`:\n\n```swift\nScrollView {\n    Text(\"Item 1\")\n        .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), scope: .ancestor) { scrollView in\n            // do something with UIScrollView\n        }\n}\n```\n\n### Usage in production\n\nSwiftUI Introspect is meant to be used in production. It does not use any private API. It only inspects the view hierarchy using publicly available methods. The library takes a defensive approach to inspecting the view hierarchy: there is no hard assumption that elements are laid out a certain way, there is no force-cast to UIKit/AppKit classes, and the `.introspect` modifier is simply ignored if UIKit/AppKit views cannot be found.\n\nInstall\n-------\n\n### Swift Package Manager\n\n#### Xcode\n\n\u003cimg width=\"656\" src=\"https://github.com/siteline/swiftui-introspect/assets/2538074/d19c1dd3-9aa4-4e4f-a5a5-b2d6a5b9b927\"\u003e\n\n#### Package.swift\n\n```swift\nlet package = Package(\n    dependencies: [\n        .package(url: \"https://github.com/siteline/swiftui-introspect\", from: \"1.0.0\"),\n    ],\n    targets: [\n        .target(name: \u003c#Target Name#\u003e, dependencies: [\n            .product(name: \"SwiftUIIntrospect\", package: \"swiftui-introspect\"),\n        ]),\n    ]\n)\n```\n\n### CocoaPods\n\n```ruby\npod 'SwiftUIIntrospect', '~\u003e 1.0'\n```\n\nIntrospection\n-------------\n\n### Implemented\n\n- [`Button`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/buttontype)\n- [`ColorPicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/colorpickertype)\n- [`DatePicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/datepickertype)\n- [`DatePicker` with `.compact` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/datepickerwithcompactstyletype)\n- [`DatePicker` with `.field` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/datepickerwithfieldstyletype)\n- [`DatePicker` with `.graphical` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/datepickerwithgraphicalstyletype)\n- [`DatePicker` with `.stepperField` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/datepickerwithstepperfieldstyletype)\n- [`DatePicker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/datepickerwithwheelstyletype)\n- [`Form`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/formtype)\n- [`Form` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/formwithgroupedstyletype)\n- [`.fullScreenCover`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/fullScreenCovertype)\n- [`List`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/listtype)\n- [`List` with `.bordered` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/listwithborderedstyletype)\n- [`List` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/listwithgroupedstyletype)\n- [`List` with `.insetGrouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/listwithinsetgroupedstyletype)\n- [`List` with `.inset` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/listwithinsetstyletype)\n- [`List` with `.sidebar` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/listwithsidebarstyletype)\n- [`ListCell`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/listcelltype)\n- [`Map`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/maptype)\n- [`NavigationSplitView`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/navigationsplitviewtype)\n- [`NavigationStack`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/navigationstacktype)\n- [`NavigationView` with `.columns` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/NavigationViewWithColumnsStyleType)\n- [`NavigationView` with `.stack` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/NavigationViewWithStackStyleType)\n- [`PageControl`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/pagecontroltype)\n- [`Picker` with `.menu` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/pickerwithmenustyletype)\n- [`Picker` with `.segmented` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/pickerwithsegmentedstyletype)\n- [`Picker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/pickerwithwheelstyletype)\n- [`.popover`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/popovertype)\n- [`ProgressView` with `.circular` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/progressviewwithcircularstyletype)\n- [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/progressviewwithlinearstyletype)\n- [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/scrollviewtype)\n- [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/searchfieldtype)\n- [`SecureField`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/securefieldtype)\n- [`.sheet`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/sheettype)\n- [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/slidertype)\n- [`Stepper`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/steppertype)\n- [`Table`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/tabletype)\n- [`TabView`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/tabviewtype)\n- [`TabView` with `.page` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/TabViewWithPageStyleType)\n- [`TextEditor`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/texteditortype)\n- [`TextField`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/textfieldtype)\n- [`TextField` with `.vertical` axis](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/TextFieldWithVerticalAxisType)\n- [`Toggle`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/toggletype)\n- [`Toggle` with `button` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/togglewithbuttonstyletype)\n- [`Toggle` with `checkbox` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/togglewithcheckboxstyletype)\n- [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/togglewithswitchstyletype)\n- [`VideoPlayer`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/videoplayertype)\n- [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/viewtype)\n- [`ViewController`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/viewcontrollertype)\n- [`Window`](https://swiftpackageindex.com/siteline/swiftui-introspect/main/documentation/swiftuiintrospect/windowtype)\n\n**Missing an element?** Please [start a discussion](https://github.com/siteline/swiftui-introspect/discussions/new?category=ideas). As a temporary solution, you can [implement your own introspectable view type](#implement-your-own-introspectable-type).\n\n### Cannot implement\n\nSwiftUI | Affected Frameworks | Why\n--- | --- | ---\nText | UIKit, AppKit | Not a UILabel / NSLabel\nImage | UIKit, AppKit | Not a UIImageView / NSImageView\nButton | UIKit | Not a UIButton\n\nExamples\n--------\n\n### List\n\n```swift\nList {\n    Text(\"Item\")\n}\n.introspect(.list, on: .iOS(.v13, .v14, .v15)) { tableView in\n    tableView.backgroundView = UIView()\n    tableView.backgroundColor = .cyan\n}\n.introspect(.list, on: .iOS(.v16, .v17, .v18)) { collectionView in\n    collectionView.backgroundView = UIView()\n    collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan\n}\n```\n\n### ScrollView\n\n```swift\nScrollView {\n    Text(\"Item\")\n}\n.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) { scrollView in\n    scrollView.backgroundColor = .red\n}\n```\n\n### NavigationView\n\n```swift\nNavigationView {\n    Text(\"Item\")\n}\n.navigationViewStyle(.stack)\n.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) { navigationController in\n    navigationController.navigationBar.backgroundColor = .cyan\n}\n```\n\n### TextField\n\n```swift\nTextField(\"Text Field\", text: \u003c#Binding\u003cString\u003e#\u003e)\n    .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) { textField in\n        textField.backgroundColor = .red\n    }\n```\n\nAdvanced usage\n--------------\n\n### Implement your own introspectable type\n\n**Missing an element?** Please [start a discussion](https://github.com/siteline/swiftui-introspect/discussions/new?category=ideas).\n\nIn case SwiftUI Introspect (unlikely) doesn't support the SwiftUI element that you're looking for, you can implement your own introspectable type.\n\nFor example, here's how the library implements the introspectable `TextField` type:\n\n```swift\nimport SwiftUI\n@_spi(Advanced) import SwiftUIIntrospect\n\npublic struct TextFieldType: IntrospectableViewType {}\n\nextension IntrospectableViewType where Self == TextFieldType {\n    public static var textField: Self { .init() }\n}\n\n#if canImport(UIKit)\nextension iOSViewVersion\u003cTextFieldType, UITextField\u003e {\n    public static let v13 = Self(for: .v13)\n    public static let v14 = Self(for: .v14)\n    public static let v15 = Self(for: .v15)\n    public static let v16 = Self(for: .v16)\n    public static let v17 = Self(for: .v17)\n    public static let v18 = Self(for: .v18)\n}\n\nextension tvOSViewVersion\u003cTextFieldType, UITextField\u003e {\n    public static let v13 = Self(for: .v13)\n    public static let v14 = Self(for: .v14)\n    public static let v15 = Self(for: .v15)\n    public static let v16 = Self(for: .v16)\n    public static let v17 = Self(for: .v17)\n    public static let v18 = Self(for: .v18)\n}\n\nextension visionOSViewVersion\u003cTextFieldType, UITextField\u003e {\n    public static let v1 = Self(for: .v1)\n    public static let v2 = Self(for: .v2)\n}\n#elseif canImport(AppKit)\nextension macOSViewVersion\u003cTextFieldType, NSTextField\u003e {\n    public static let v10_15 = Self(for: .v10_15)\n    public static let v11 = Self(for: .v11)\n    public static let v12 = Self(for: .v12)\n    public static let v13 = Self(for: .v13)\n    public static let v14 = Self(for: .v14)\n    public static let v15 = Self(for: .v15)\n}\n#endif\n```\n\n### Introspect on future platform versions\n\nBy default, introspection applies per specific platform version. This is a sensible default for maximum predictability in regularly maintained codebases, but it's not always a good fit for e.g. library developers who may want to cover as many future platform versions as possible in order to provide the best chance for long-term future functionality of their library without regular maintenance.\n\nFor such cases, SwiftUI Introspect offers range-based platform version predicates behind the Advanced SPI:\n\n```swift\nimport SwiftUI\n@_spi(Advanced) import SwiftUIIntrospect\n\nstruct ContentView: View {\n    var body: some View {\n        ScrollView {\n            // ...\n        }\n        .introspect(.scrollView, on: .iOS(.v13...)) { scrollView in\n            // ...\n        }\n    }\n}\n```\n\nBear in mind this should be used cautiously, and with full knowledge that any future OS version might break the expected introspection types unless explicitly available. For instance, if in the example above hypothetically iOS 19 stops using UIScrollView under the hood, the customization closure will never be called on said platform.\n\n### Keep instances outside the customize closure\n\nSometimes, you might need to keep your introspected instance around for longer than the customization closure lifetime. In such cases, `@State` is not a good option because it produces retain cycles. Instead, SwiftUI Introspect offers a `@Weak` property wrapper behind the Advanced SPI:\n\n```swift\nimport SwiftUI\n@_spi(Advanced) import SwiftUIIntrospect\n\nstruct ContentView: View {\n    @Weak var scrollView: UIScrollView?\n\n    var body: some View {\n        ScrollView {\n            // ...\n        }\n        .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) { scrollView in\n            self.scrollView = scrollView\n        }\n    }\n}\n```\n\nCommunity projects\n------------------\n\nHere's a list of open source libraries powered by the SwiftUI Introspect library:\n\n\u003ca href=\"https://github.com/paescebu/CustomKeyboardKit\"\u003e\n  \u003cimg src=\"https://github-readme-stats.vercel.app/api/pin/?username=paescebu\u0026repo=CustomKeyboardKit\" /\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://github.com/davdroman/swiftui-navigation-transitions\"\u003e\n  \u003cimg src=\"https://github-readme-stats.vercel.app/api/pin/?username=davdroman\u0026repo=swiftui-navigation-transitions\" /\u003e\n\u003c/a\u003e\n\nIf you're working on a library built on SwiftUI Introspect or know of one, feel free to submit a PR adding it to the list.\n","funding_links":[],"categories":["Samples","Swift","etc","🌎 by the community","Libraries"],"sub_categories":["Inspection"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsiteline%2FSwiftUI-Introspect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsiteline%2FSwiftUI-Introspect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsiteline%2FSwiftUI-Introspect/lists"}