Skip to content

Commit fe0548c

Browse files
committed
WIP
1 parent 446c0ab commit fe0548c

File tree

1 file changed

+18
-22
lines changed

1 file changed

+18
-22
lines changed

README.md

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ SwiftUI Introspect
55
[![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)
66
[![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)
77

8-
SwiftUI Introspect allows you to get the underlying UIKit or AppKit element of a SwiftUI view.
9-
10-
For instance, with SwiftUI Introspect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar.
8+
SwiftUI Introspect lets you access the underlying UIKit or AppKit view for a SwiftUI view.
119

1210
- [How it works](#how-it-works)
1311
- [Install](#install)
@@ -26,7 +24,7 @@ For instance, with SwiftUI Introspect you can access `UITableView` to modify sep
2624
How it works
2725
------------
2826

29-
SwiftUI 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.
27+
SwiftUI Introspect adds an invisible `IntrospectionView` above the selected view and an invisible anchor below it, then searches the UIKit/AppKit view hierarchy between them to find the relevant view.
3028

3129
For instance, when introspecting a `ScrollView`...
3230

@@ -41,13 +39,13 @@ ScrollView {
4139

4240
... it will:
4341

44-
1. Add marker views in front and behind `ScrollView`.
42+
1. Add marker views before and after `ScrollView`.
4543
2. Traverse through all subviews between both marker views until a `UIScrollView` instance (if any) is found.
4644

4745
> [!IMPORTANT]
48-
> 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.
46+
> Although this method is solid and unlikely to break on its own, future OS releases require explicit opt in for introspection (`.iOS(.vXYZ)`) because underlying UIKit/AppKit types can change between major versions.
4947
50-
By 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`:
48+
By default, `.introspect` acts on its receiver. Calling `.introspect` from inside the view you want to introspect has no effect. If you need to introspect an ancestor instead, set `scope: .ancestor`:
5149

5250
```swift
5351
ScrollView {
@@ -60,7 +58,7 @@ ScrollView {
6058

6159
### Usage in production
6260

63-
SwiftUI 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.
61+
SwiftUI Introspect is suitable for production. It does not use private APIs. It inspects the view hierarchy using public methods and takes a defensive approach: it makes no hard layout assumptions, performs no forced casts to UIKit/AppKit classes, and ignores `.introspect` when the expected UIKit/AppKit view cannot be found.
6462

6563
Install
6664
-------
@@ -225,18 +223,18 @@ General Guidelines
225223

226224
Here are some guidelines to keep in mind when using SwiftUI Introspect:
227225

228-
- **Use sparingly**: introspection should be a last resort when you need to access underlying UIKit/AppKit components that SwiftUI does not expose. Overusing it can lead to fragile code that may break with future SwiftUI updates. As Apple introduces new modifiers to SwiftUI, consider replacing introspection with native SwiftUI solutions.
226+
- **Use sparingly**: prefer native SwiftUI modifiers when available. Use introspection only when you need underlying UIKit/AppKit APIs that SwiftUI does not expose.
229227
- **Program defensively**: the introspection closure may be called multiple times during the view's lifecycle, such as during view updates or re-renders. Ensure that your customization code can handle being executed multiple times without causing unintended side effects.
230-
- **Do not modify state directly**: avoid changing SwiftUI state directly from within the introspection closure. If you need to update state, enclose it within a `DispatchQueue.main.async` block to ensure it happens within safe SwiftUI update cycles.
231-
- **Test on all target OS versions**: since SwiftUI Introspect relies on the underlying view hierarchy, it's crucial to test your app on all the OS versions you intend to support. Different OS versions may have different underlying implementations, which can affect introspection.
228+
- **Avoid direct state changes**: do not change SwiftUI state from inside the introspection closure. If you must update state, wrap it in `DispatchQueue.main.async`.
229+
- **Test across OS versions**: underlying implementations can differ by OS, which can affect customization.
232230
- **Avoid retain cycles**: be cautious about capturing `self` or other strong references within the introspection closure, as this can lead to memory leaks. Use `[weak self]` or `[unowned self]` capture lists as appropriate.
233-
- **Use the correct scope**: by default, the `.introspect` modifier acts on its receiver. If you need to introspect an ancestor view, make sure to set the `scope` parameter to `.ancestor`. In general, you won't need to worry about this as each view type has sensible, predictable scope defaults.
231+
- **Scope**: `.introspect` targets its receiver by default. Use `scope: .ancestor` only when you need to introspect an ancestor. In general, you shouldn't worry about this as each view type has sensible, predictable default scopes.
234232

235233
Advanced usage
236234
--------------
237235

238236
> [!NOTE]
239-
> The following features are considered advanced and are not necessary for most use cases. They are provided for users who need more control or flexibility when using SwiftUI Introspect.
237+
> These features are advanced and unnecessary for most use cases. Use them when you need extra control or flexibility.
240238
241239
> [!IMPORTANT]
242240
> To access these features, import SwiftUI Introspect using `@_spi(Advanced)` (see examples below).
@@ -245,7 +243,7 @@ Advanced usage
245243

246244
**Missing an element?** Please [start a discussion](https://github.com/siteline/swiftui-introspect/discussions/new?category=ideas).
247245

248-
In case SwiftUI Introspect (unlikely) doesn't support the SwiftUI element that you're looking for, you can implement your own introspectable type.
246+
In the unlikely event SwiftUI Introspect does not support the element you need, you can implement your own introspectable type.
249247

250248
For example, here's how the library implements the introspectable `TextField` type:
251249

@@ -300,9 +298,7 @@ extension macOSViewVersion<TextFieldType, NSTextField> {
300298

301299
### Introspect on future platform versions
302300

303-
By 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.
304-
305-
For such cases, SwiftUI Introspect offers range-based platform version predicates behind the Advanced SPI:
301+
By default, introspection targets specific platform versions. This is an intentional design decision to maintain maximum predictability in actively maintained apps. However library authors may prefer to cover future versions to limit their commitment to regular maintenance without breaking client apps. For that, SwiftUI Introspect provides range-based version predicates via the Advanced SPI:
306302

307303
```swift
308304
import SwiftUI
@@ -320,11 +316,11 @@ struct ContentView: View {
320316
}
321317
```
322318

323-
Bear 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.
319+
Use this cautiously. Future OS versions may change underlying types, in which case the customization closure will not run unless support is explicitly declared.
324320

325321
### Keep instances outside the customize closure
326322

327-
Sometimes, 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:
323+
Sometimes you need to keep an introspected instance beyond the customization closure. `@State` is not appropriate for this, as it can create retain cycles. Instead, SwiftUI Introspect offers a `@Weak` property wrapper behind the Advanced SPI:
328324

329325
```swift
330326
import SwiftUI
@@ -347,18 +343,18 @@ struct ContentView: View {
347343
Note for library maintainers
348344
----------------------------
349345

350-
If your library depends on SwiftUI Introspect, declare your dependency with a version range that spans at least the **last two major versions** rather than bumping straight to the latest one. This avoids conflicts when apps depend on SwiftUI Introspect directly or through multiple libraries at once. For example:
346+
If your library depends on SwiftUI Introspect, declare a version range that spans at least the **last two major versions** instead of jumping straight to the latest. This avoids conflicts when apps pull the library directly or through multiple dependencies. For example:
351347

352348
```swift
353349
.package(url: "https://github.com/siteline/swiftui-introspect", "1.3.0"..<"27.0.0"),
354350
```
355351

356-
Supporting a wider range is safe because SwiftUI Introspect is essentially a “finished” library: no new features will be added, only support for newer platform versions. Thanks to [`@_spi(Advanced)` imports](https://github.com/siteline/swiftui-introspect#introspect-on-future-platform-versions), it’s already future-proofed without requiring frequent version bumps.
352+
A wider range is safe because SwiftUI Introspect is essentially “finished”: no new features will be added, only newer platform versions and view types. Thanks to [`@_spi(Advanced)` imports](https://github.com/siteline/swiftui-introspect#introspect-on-future-platform-versions), it is already future proof without frequent version bumps.
357353

358354
Community projects
359355
------------------
360356

361-
Here's a list of open source libraries powered by the SwiftUI Introspect library:
357+
Here are some open source libraries powered by SwiftUI Introspect:
362358

363359
<a href="https://github.com/paescebu/CustomKeyboardKit">
364360
<img src="https://github-readme-stats.vercel.app/api/pin/?username=paescebu&repo=CustomKeyboardKit" />

0 commit comments

Comments
 (0)