Skip to content

Commit 1364bd5

Browse files
authored
Merge pull request #1 from CaptureContext/return-values
feat: Interception improvements
2 parents 91589ff + f8c0a04 commit 1364bd5

14 files changed

+1012
-906
lines changed

.spi.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: 1
2+
builder:
3+
configs:
4+
- documentation_targets: [CombineInterception]
5+
swift_version: 5.9

Package.swift

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,37 @@
1-
// swift-tools-version:5.8
1+
// swift-tools-version:5.9
22

33
import PackageDescription
44

55
let package = Package(
6-
name: "combine-interception",
7-
platforms: [
8-
.iOS(.v13),
9-
.macOS(.v10_15),
10-
.tvOS(.v13),
11-
.watchOS(.v6)
12-
],
13-
products: [
14-
.library(
15-
name: "CombineInterception",
16-
type: .static,
17-
targets: ["CombineInterception"]
18-
)
19-
],
20-
targets: [
21-
.target(
22-
name: "CombineInterception",
23-
dependencies: [
24-
.target(name: "CombineRuntime")
25-
]
26-
),
27-
.target(name: "CombineRuntime")
28-
]
6+
name: "combine-interception",
7+
platforms: [
8+
.iOS(.v13),
9+
.macOS(.v10_15),
10+
.tvOS(.v13),
11+
.macCatalyst(.v13),
12+
.watchOS(.v6)
13+
],
14+
products: [
15+
.library(
16+
name: "CombineInterception",
17+
type: .static,
18+
targets: ["CombineInterception"]
19+
)
20+
],
21+
targets: [
22+
.target(
23+
name: "CombineInterception",
24+
dependencies: [
25+
.target(name: "CombineRuntime")
26+
]
27+
),
28+
.target(name: "CombineRuntime"),
29+
30+
.testTarget(
31+
name: "CombineInterceptionTests",
32+
dependencies: [
33+
.target(name: "CombineInterception")
34+
]
35+
)
36+
]
2937
)

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# combine-interception
22

3-
[![SwiftPM 5.8](https://img.shields.io/badge/swiftpm-5.8-ED523F.svg?style=flat)](https://swift.org/download/) ![Platforms](https://img.shields.io/badge/Platforms-iOS_13_|_macOS_10.15_|_tvOS_14_|_watchOS_7-ED523F.svg?style=flat) [![@maximkrouk](https://img.shields.io/badge/contact-@capturecontext-1DA1F2.svg?style=flat&logo=twitter)](https://twitter.com/capture_context)
3+
[![SwiftPM 5.9](https://img.shields.io/badge/swiftpm-5.9-ED523F.svg?style=flat)](https://swift.org/download/) ![Platforms](https://img.shields.io/badge/Platforms-iOS_13_|_macOS_10.15_|_Catalyst_13_|_tvOS_14_|_watchOS_7-ED523F.svg?style=flat) [![@capturecontext](https://img.shields.io/badge/contact-@capturecontext-1DA1F2.svg?style=flat&logo=twitter)](https://twitter.com/capture_context)
44

55
Package extending Apple `Combine` framework for interception of objc selectors.
66

@@ -13,6 +13,9 @@ Observe any selectors on NSObject instances
1313
```swift
1414
navigationController
1515
.publisher(for: #selector(UINavigationController.popViewController))
16+
.sink { (args, output) in
17+
// ...
18+
}
1619
```
1720

1821
### Library
@@ -41,7 +44,7 @@ If you use SwiftPM for your project, you can add CombineInterception to your pac
4144
```swift
4245
.package(
4346
url: "https://github.com/capturecontext/combine-interception.git",
44-
.upToNextMinor(from: "0.0.1")
47+
.upToNextMinor(from: "0.1.0")
4548
)
4649
```
4750

Lines changed: 130 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,142 @@
11
import Foundation
22

33
#if canImport(CombineRuntime)
4-
import CombineRuntime
4+
import CombineRuntime
55
#endif
66

77
internal struct AssociationKey<Value> {
8-
fileprivate let address: UnsafeRawPointer
9-
fileprivate let `default`: Value!
10-
11-
/// Create an ObjC association key.
12-
///
13-
/// - warning: The key must be uniqued.
14-
///
15-
/// - parameters:
16-
/// - default: The default value, or `nil` to trap on undefined value. It is
17-
/// ignored if `Value` is an optional.
18-
init(default: Value? = nil) {
19-
self.address = UnsafeRawPointer(
20-
UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
21-
)
22-
self.default = `default`
23-
}
24-
25-
/// Create an ObjC association key from a `StaticString`.
26-
///
27-
/// - precondition: `key` has a pointer representation.
28-
///
29-
/// - parameters:
30-
/// - default: The default value, or `nil` to trap on undefined value. It is
31-
/// ignored if `Value` is an optional.
32-
init(_ key: StaticString, default: Value? = nil) {
33-
assert(key.hasPointerRepresentation)
34-
self.address = UnsafeRawPointer(key.utf8Start)
35-
self.default = `default`
36-
}
37-
38-
/// Create an ObjC association key from a `Selector`.
39-
///
40-
/// - parameters:
41-
/// - default: The default value, or `nil` to trap on undefined value. It is
42-
/// ignored if `Value` is an optional.
43-
init(_ key: Selector, default: Value? = nil) {
44-
self.address = UnsafeRawPointer(key.utf8Start)
45-
self.default = `default`
46-
}
8+
fileprivate let address: UnsafeRawPointer
9+
fileprivate let `default`: Value!
10+
11+
/// Create an ObjC association key.
12+
///
13+
/// - warning: The key must be uniqued.
14+
///
15+
/// - parameters:
16+
/// - default: The default value, or `nil` to trap on undefined value. It is
17+
/// ignored if `Value` is an optional.
18+
init(default: Value? = nil) {
19+
self.address = UnsafeRawPointer(
20+
UnsafeMutablePointer<UInt8>.allocate(capacity: 1)
21+
)
22+
self.default = `default`
23+
}
24+
25+
/// Create an ObjC association key from a `StaticString`.
26+
///
27+
/// - precondition: `key` has a pointer representation.
28+
///
29+
/// - parameters:
30+
/// - default: The default value, or `nil` to trap on undefined value. It is
31+
/// ignored if `Value` is an optional.
32+
init(_ key: StaticString, default: Value? = nil) {
33+
assert(key.hasPointerRepresentation)
34+
self.address = UnsafeRawPointer(key.utf8Start)
35+
self.default = `default`
36+
}
37+
38+
/// Create an ObjC association key from a `Selector`.
39+
///
40+
/// - parameters:
41+
/// - default: The default value, or `nil` to trap on undefined value. It is
42+
/// ignored if `Value` is an optional.
43+
init(_ key: Selector, default: Value? = nil) {
44+
self.address = UnsafeRawPointer(key.utf8Start)
45+
self.default = `default`
46+
}
4747
}
4848

4949
internal struct Associations<Base: AnyObject> {
50-
fileprivate let base: Base
51-
52-
init(_ base: Base) {
53-
self.base = base
54-
}
50+
fileprivate let base: Base
51+
52+
init(_ base: Base) {
53+
self.base = base
54+
}
5555
}
5656

5757
extension NSObjectProtocol {
58-
/// Retrieve the associated value for the specified key. If the value does not
59-
/// exist, `initial` would be called and the returned value would be
60-
/// associated subsequently.
61-
///
62-
/// - parameters:
63-
/// - key: An optional key to differentiate different values.
64-
/// - initial: The action that supples an initial value.
65-
///
66-
/// - returns: The associated value for the specified key.
67-
internal func associatedValue<T>(
68-
forKey key: StaticString = #function,
69-
initial: (Self) -> T
70-
) -> T {
71-
let key = AssociationKey<T?>(key)
72-
73-
if let value = associations.value(forKey: key) {
74-
return value
75-
}
76-
77-
let value = initial(self)
78-
associations.setValue(value, forKey: key)
79-
80-
return value
81-
}
58+
/// Retrieve the associated value for the specified key. If the value does not
59+
/// exist, `initial` would be called and the returned value would be
60+
/// associated subsequently.
61+
///
62+
/// - parameters:
63+
/// - key: An optional key to differentiate different values.
64+
/// - initial: The action that supples an initial value.
65+
///
66+
/// - returns: The associated value for the specified key.
67+
internal func associatedValue<T>(
68+
forKey key: StaticString = #function,
69+
initial: (Self) -> T
70+
) -> T {
71+
let key = AssociationKey<T?>(key)
72+
73+
if let value = associations.value(forKey: key) {
74+
return value
75+
}
76+
77+
let value = initial(self)
78+
associations.setValue(value, forKey: key)
79+
80+
return value
81+
}
8282
}
8383

8484
extension NSObjectProtocol {
85-
@nonobjc internal var associations: Associations<Self> {
86-
return Associations(self)
87-
}
85+
@nonobjc internal var associations: Associations<Self> {
86+
return Associations(self)
87+
}
8888
}
8989

9090
extension Associations {
91-
/// Retrieve the associated value for the specified key.
92-
///
93-
/// - parameters:
94-
/// - key: The key.
95-
///
96-
/// - returns: The associated value, or the default value if no value has been
97-
/// associated with the key.
98-
internal func value<Value>(
99-
forKey key: AssociationKey<Value>
100-
) -> Value {
101-
return (objc_getAssociatedObject(base, key.address) as! Value?) ?? key.default
102-
}
103-
104-
/// Retrieve the associated value for the specified key.
105-
///
106-
/// - parameters:
107-
/// - key: The key.
108-
///
109-
/// - returns: The associated value, or `nil` if no value is associated with
110-
/// the key.
111-
internal func value<Value>(
112-
forKey key: AssociationKey<Value?>
113-
) -> Value? {
114-
return objc_getAssociatedObject(base, key.address) as! Value?
115-
}
116-
117-
/// Set the associated value for the specified key.
118-
///
119-
/// - parameters:
120-
/// - value: The value to be associated.
121-
/// - key: The key.
122-
internal func setValue<Value>(
123-
_ value: Value,
124-
forKey key: AssociationKey<Value>
125-
) {
126-
objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
127-
}
128-
129-
/// Set the associated value for the specified key.
130-
///
131-
/// - parameters:
132-
/// - value: The value to be associated.
133-
/// - key: The key.
134-
internal func setValue<Value>(
135-
_ value: Value?,
136-
forKey key: AssociationKey<Value?>
137-
) {
138-
objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
139-
}
91+
/// Retrieve the associated value for the specified key.
92+
///
93+
/// - parameters:
94+
/// - key: The key.
95+
///
96+
/// - returns: The associated value, or the default value if no value has been
97+
/// associated with the key.
98+
internal func value<Value>(
99+
forKey key: AssociationKey<Value>
100+
) -> Value {
101+
return (objc_getAssociatedObject(base, key.address) as! Value?) ?? key.default
102+
}
103+
104+
/// Retrieve the associated value for the specified key.
105+
///
106+
/// - parameters:
107+
/// - key: The key.
108+
///
109+
/// - returns: The associated value, or `nil` if no value is associated with
110+
/// the key.
111+
internal func value<Value>(
112+
forKey key: AssociationKey<Value?>
113+
) -> Value? {
114+
return objc_getAssociatedObject(base, key.address) as! Value?
115+
}
116+
117+
/// Set the associated value for the specified key.
118+
///
119+
/// - parameters:
120+
/// - value: The value to be associated.
121+
/// - key: The key.
122+
internal func setValue<Value>(
123+
_ value: Value,
124+
forKey key: AssociationKey<Value>
125+
) {
126+
objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
127+
}
128+
129+
/// Set the associated value for the specified key.
130+
///
131+
/// - parameters:
132+
/// - value: The value to be associated.
133+
/// - key: The key.
134+
internal func setValue<Value>(
135+
_ value: Value?,
136+
forKey key: AssociationKey<Value?>
137+
) {
138+
objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
139+
}
140140
}
141141

142142
/// Set the associated value for the specified key.
@@ -146,14 +146,14 @@ extension Associations {
146146
/// - key: The key.
147147
/// - address: The address of the object.
148148
internal func unsafeSetAssociatedValue<Value>(
149-
_ value: Value?,
150-
forKey key: AssociationKey<Value>,
151-
forObjectAt address: UnsafeRawPointer
149+
_ value: Value?,
150+
forKey key: AssociationKey<Value>,
151+
forObjectAt address: UnsafeRawPointer
152152
) {
153-
_combineRuntimeSetAssociatedObject(
154-
address,
155-
key.address,
156-
value,
157-
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
158-
)
153+
_combineRuntimeSetAssociatedObject(
154+
address,
155+
key.address,
156+
value,
157+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
158+
)
159159
}

0 commit comments

Comments
 (0)