Skip to content

Commit 654b88c

Browse files
authored
Merge pull request #32 from p-x9/feature/non-apple-platform
Support for non-apple platform
2 parents 8977c1f + 10ee736 commit 654b88c

12 files changed

+452
-303
lines changed

.github/workflows/ci.yml

+14
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,17 @@ jobs:
3535

3636
- name: Test
3737
run: swift test
38+
39+
linux-test:
40+
name: Linux Test
41+
runs-on: ubuntu-latest
42+
steps:
43+
- name: Install Swift
44+
uses: slashmo/[email protected]
45+
with:
46+
version: swift-5.9-RELEASE
47+
48+
- uses: actions/checkout@v4
49+
50+
- name: Test
51+
run: swift test

AssociatedObject.podspec

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ Pod::Spec.new do |s|
2121

2222
s.prepare_command = 'swift build -c release && cp -f .build/release/AssociatedObjectPlugin ./Binary'
2323

24-
s.source_files = "Sources/AssociatedObject/AssociatedObject.swift", 'Sources/AssociatedObjectC/**/*.{c,h,m,swift}'
24+
s.source_files = "Sources/AssociatedObject/**/*.{c,h,m,swift}", 'Sources/AssociatedObjectC/**/*.{c,h,m,swift}'
2525
s.swift_versions = "5.9"
2626

27+
# CocoaPods do not support Linux
28+
# s.dependency "ObjectAssociation", "0.5.0"
29+
2730
s.preserve_paths = ["Binary/AssociatedObjectPlugin"]
2831
s.pod_target_xcconfig = {
2932
'OTHER_SWIFT_FLAGS' => [

Package.resolved

+9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
"version" : "0.2.2"
1919
}
2020
},
21+
{
22+
"identity" : "swift-object-association",
23+
"kind" : "remoteSourceControl",
24+
"location" : "https://github.com/p-x9/swift-object-association.git",
25+
"state" : {
26+
"revision" : "93806cfecae1f198c894ed5585b93aff0e2d1f5a",
27+
"version" : "0.5.0"
28+
}
29+
},
2130
{
2231
"identity" : "swift-snapshot-testing",
2332
"kind" : "remoteSourceControl",

Package.swift

+14-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ let package = Package(
2424
url: "https://github.com/p-x9/swift-literal-type-inference.git",
2525
from: "0.1.0"
2626
),
27+
.package(
28+
url: "https://github.com/p-x9/swift-object-association.git",
29+
from: "0.5.0"
30+
),
2731
.package(
2832
url: "https://github.com/pointfreeco/swift-macro-testing.git",
2933
from: "0.2.2"
@@ -35,7 +39,16 @@ let package = Package(
3539
name: "AssociatedObject",
3640
dependencies: [
3741
"AssociatedObjectC",
38-
"AssociatedObjectPlugin"
42+
"AssociatedObjectPlugin",
43+
.product(
44+
name: "ObjectAssociation",
45+
package: "swift-object-association",
46+
condition: .when(
47+
platforms: [
48+
.linux, .openbsd, .windows, .android
49+
]
50+
)
51+
)
3952
]
4053
),
4154
.target(

Sources/AssociatedObject/AssociatedObject.swift

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1-
@_exported import ObjectiveC
2-
31
#if canImport(AssociatedObjectC)
42
@_exported import AssociatedObjectC
53
#endif
64

5+
6+
#if canImport(ObjectiveC)
7+
8+
@_exported import ObjectiveC
9+
public typealias Policy = objc_AssociationPolicy
10+
11+
#elseif canImport(ObjectAssociation)
12+
13+
@_exported import ObjectAssociation
14+
public typealias Policy = swift_AssociationPolicy
15+
16+
#endif
17+
18+
719
@attached(peer, names: arbitrary)
820
@attached(accessor)
921
public macro AssociatedObject(
10-
_ policy: objc_AssociationPolicy
22+
_ policy: Policy
1123
) = #externalMacro(
1224
module: "AssociatedObjectPlugin",
1325
type: "AssociatedObjectMacro"
@@ -16,7 +28,7 @@ public macro AssociatedObject(
1628
@attached(peer, names: arbitrary)
1729
@attached(accessor)
1830
public macro AssociatedObject(
19-
_ policy: objc_AssociationPolicy,
31+
_ policy: Policy,
2032
key: Any
2133
) = #externalMacro(
2234
module: "AssociatedObjectPlugin",
@@ -25,7 +37,7 @@ public macro AssociatedObject(
2537

2638
@attached(accessor)
2739
public macro _AssociatedObject(
28-
_ policy: objc_AssociationPolicy
40+
_ policy: Policy
2941
) = #externalMacro(
3042
module: "AssociatedObjectPlugin",
3143
type: "AssociatedObjectMacro"

Sources/AssociatedObject/Extension/objc_AssociationPolicy+.swift

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
//
77
//
88

9+
#if canImport(ObjectiveC)
10+
911
import ObjectiveC
1012

1113
/// Extension for objc_AssociationPolicy to provide a more Swift-friendly interface.
@@ -47,3 +49,5 @@ extension objc_AssociationPolicy {
4749
}
4850
}
4951
}
52+
53+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// swift_AssociationPolicy+.swift
3+
//
4+
//
5+
// Created by p-x9 on 2024/01/29.
6+
//
7+
//
8+
9+
#if canImport(ObjectAssociation)
10+
11+
import ObjectAssociation
12+
13+
/// Extension for swift_AssociationPolicy to provide a more Swift-friendly interface.
14+
extension swift_AssociationPolicy {
15+
/// Represents the atomicity options for associated objects.
16+
public enum Atomicity {
17+
/// Indicates that the associated object should be stored atomically.
18+
case atomic
19+
/// Indicates that the associated object should be stored non-atomically.
20+
case nonatomic
21+
}
22+
23+
/// A property wrapper that corresponds to `.SWIFT_ASSOCIATION_ASSIGN` policy.
24+
public static var assign: Self { .SWIFT_ASSOCIATION_ASSIGN }
25+
26+
/// A property wrapper that corresponds to `.SWIFT_ASSOCIATION_WEAK` policy.
27+
public static var weak: Self { .SWIFT_ASSOCIATION_WEAK }
28+
29+
/// Create an association policy for retaining an associated object with the specified atomicity.
30+
///
31+
/// - Parameter atomicity: The desired atomicity for the associated object.
32+
/// - Returns: The appropriate association policy for retaining with the specified atomicity.
33+
public static func retain(_ atomicity: Atomicity) -> Self {
34+
switch atomicity {
35+
case .atomic:
36+
return .SWIFT_ASSOCIATION_RETAIN
37+
case .nonatomic:
38+
return .SWIFT_ASSOCIATION_RETAIN_NONATOMIC
39+
}
40+
}
41+
}
42+
43+
#endif
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// functions.swift
3+
//
4+
//
5+
// Created by p-x9 on 2024/01/28.
6+
//
7+
//
8+
9+
#if canImport(ObjectiveC)
10+
import ObjectiveC
11+
#elseif canImport(ObjectAssociation)
12+
import ObjectAssociation
13+
#else
14+
#warning("Current platform is not supported")
15+
#endif
16+
17+
18+
public func getAssociatedObject(
19+
_ object: AnyObject,
20+
_ key: UnsafeRawPointer
21+
) -> Any? {
22+
#if canImport(ObjectiveC)
23+
objc_getAssociatedObject(object, key)
24+
#elseif canImport(ObjectAssociation)
25+
ObjectAssociation.getAssociatedObject(object, key)
26+
#endif
27+
}
28+
29+
30+
public func setAssociatedObject(
31+
_ object: AnyObject,
32+
_ key: UnsafeRawPointer,
33+
_ value: Any?,
34+
_ policy: Policy = .retain(.nonatomic)
35+
) {
36+
#if canImport(ObjectiveC)
37+
objc_setAssociatedObject(
38+
object,
39+
key,
40+
value,
41+
policy
42+
)
43+
#elseif canImport(ObjectAssociation)
44+
ObjectAssociation.setAssociatedObject(
45+
object,
46+
key,
47+
value
48+
)
49+
#endif
50+
}

Sources/AssociatedObjectPlugin/AssociatedObjectMacro.swift

+46-45
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extension AssociatedObjectMacro: PeerMacro {
7878
let flagName = "__associated_\(identifier.trimmed)IsSet"
7979
let flagDecl = VariableDeclSyntax(
8080
attributes: [
81-
.attribute("@_AssociatedObject(.OBJC_ASSOCIATION_ASSIGN)")
81+
.attribute("@_AssociatedObject(.retain(.nonatomic))")
8282
],
8383
bindingSpecifier: .identifier("var"),
8484
bindings: PatternBindingListSyntax {
@@ -176,15 +176,18 @@ extension AssociatedObjectMacro: AccessorMacro {
176176
return []
177177
}
178178

179-
guard case let .argumentList(arguments) = node.arguments,
180-
let firstElement = arguments.first?.expression,
181-
let policy = firstElement.as(ExprSyntax.self) else {
179+
guard case let .argumentList(arguments) = node.arguments else {
182180
return []
183181
}
184182

183+
var policy: ExprSyntax = ".retain(.nonatomic)"
184+
if let firstElement = arguments.first?.expression,
185+
let specifiedPolicy = firstElement.as(ExprSyntax.self) {
186+
policy = specifiedPolicy
187+
}
188+
185189
var associatedKey: ExprSyntax = "Self.__associated_\(identifier.trimmed)Key"
186-
if case let .argumentList(arguments) = node.arguments,
187-
let element = arguments.first(where: { $0.label?.text == "key" }),
190+
if let element = arguments.first(where: { $0.label?.text == "key" }),
188191
let customKey = element.expression.as(ExprSyntax.self) {
189192
// Provide store key from outside the macro
190193
associatedKey = "&\(customKey)"
@@ -225,7 +228,7 @@ extension AssociatedObjectMacro {
225228
policy: ExprSyntax,
226229
defaultValue: ExprSyntax?
227230
) -> AccessorDeclSyntax {
228-
let varTypeWithoutOptional = if let type = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) {
231+
let typeWithoutOptional = if let type = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) {
229232
type.wrappedType
230233
} else {
231234
type
@@ -234,51 +237,49 @@ extension AssociatedObjectMacro {
234237
return AccessorDeclSyntax(
235238
accessorSpecifier: .keyword(.get),
236239
body: CodeBlockSyntax {
237-
if let defaultValue {
238-
if type.isOptional {
239-
"""
240-
if !self.__associated_\(identifier.trimmed)IsSet {
241-
let value: \(type.trimmed) = \(defaultValue.trimmed)
242-
objc_setAssociatedObject(
243-
self,
244-
\(associatedKey),
245-
value,
246-
\(policy.trimmed)
247-
)
248-
self.__associated_\(identifier.trimmed)IsSet = true
249-
return value
250-
} else {
251-
return objc_getAssociatedObject(
252-
self,
253-
\(associatedKey)
254-
) as! \(varTypeWithoutOptional.trimmed)
255-
}
256-
"""
240+
if let defaultValue, type.isOptional {
241+
"""
242+
if !self.__associated_\(identifier.trimmed)IsSet {
243+
let value: \(type.trimmed) = \(defaultValue.trimmed)
244+
setAssociatedObject(
245+
self,
246+
\(associatedKey),
247+
value,
248+
\(policy.trimmed)
249+
)
250+
self.__associated_\(identifier.trimmed)IsSet = true
251+
return value
257252
} else {
258-
"""
259-
if let value = objc_getAssociatedObject(
253+
return getAssociatedObject(
260254
self,
261255
\(associatedKey)
262-
) as? \(varTypeWithoutOptional.trimmed) {
263-
return value
264-
} else {
265-
let value: \(type.trimmed) = \(defaultValue.trimmed)
266-
objc_setAssociatedObject(
267-
self,
268-
\(associatedKey),
269-
value,
270-
\(policy.trimmed)
271-
)
272-
return value
273-
}
274-
"""
256+
) as! \(typeWithoutOptional.trimmed)
275257
}
258+
"""
259+
} else if let defaultValue {
260+
"""
261+
if let value = getAssociatedObject(
262+
self,
263+
\(associatedKey)
264+
) as? \(typeWithoutOptional.trimmed) {
265+
return value
266+
} else {
267+
let value: \(type.trimmed) = \(defaultValue.trimmed)
268+
setAssociatedObject(
269+
self,
270+
\(associatedKey),
271+
value,
272+
\(policy.trimmed)
273+
)
274+
return value
275+
}
276+
"""
276277
} else {
277278
"""
278-
objc_getAssociatedObject(
279+
getAssociatedObject(
279280
self,
280281
\(associatedKey)
281-
) as? \(varTypeWithoutOptional.trimmed)
282+
) as? \(typeWithoutOptional.trimmed)
282283
?? \(defaultValue ?? "nil")
283284
"""
284285
}
@@ -324,7 +325,7 @@ extension AssociatedObjectMacro {
324325
}
325326

326327
"""
327-
objc_setAssociatedObject(
328+
setAssociatedObject(
328329
self,
329330
\(associatedKey),
330331
newValue,

0 commit comments

Comments
 (0)