Skip to content

Commit f7724f3

Browse files
authored
Merge pull request #10 from Infomaniak/sendable-property-wrappers
fix: Property wrappers are now thread safe
2 parents 7d127ea + ef0650d commit f7724f3

File tree

2 files changed

+23
-18
lines changed

2 files changed

+23
-18
lines changed

Sources/InfomaniakDI/InjectService.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import Foundation
1616
// MARK: - InjectService<Service>
1717

1818
/// A property wrapper that resolves shared objects when the host type is initialized.
19-
@propertyWrapper public struct InjectService<Service>: CustomDebugStringConvertible, Equatable, Identifiable {
19+
@propertyWrapper public struct InjectService<Service>: CustomDebugStringConvertible, Equatable, Identifiable,
20+
@unchecked Sendable {
2021
/// Identifiable
2122
///
2223
/// Something to link the identity of this property wrapper to the underlying Service type.
@@ -40,12 +41,11 @@ import Foundation
4041
"""
4142
}
4243

43-
/// Store the resolved service
44-
var service: Service!
44+
let service: Service
4545

46-
public var container: SimpleResolvable
47-
public var customTypeIdentifier: String?
48-
public var factoryParameters: [String: Any]?
46+
public let container: SimpleResolvable
47+
public let customTypeIdentifier: String?
48+
public let factoryParameters: [String: Any]?
4949

5050
public init(customTypeIdentifier: String? = nil,
5151
factoryParameters: [String: Any]? = nil,

Sources/InfomaniakDI/LazyInjectService.swift

+17-12
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
import Foundation
1515

1616
/// Inject a service at the first use of the property
17-
@propertyWrapper public final class LazyInjectService<Service>: Equatable, Identifiable {
17+
@propertyWrapper public final class LazyInjectService<Service>: Equatable, Identifiable, @unchecked Sendable {
18+
private let semaphore = DispatchSemaphore(value: 1)
19+
20+
var service: Service?
21+
1822
/// Identifiable
1923
///
2024
/// Something to link the identity of this property wrapper to the underlying Service type.
@@ -38,12 +42,9 @@ import Foundation
3842
"""
3943
}
4044

41-
/// Store the resolved service
42-
var service: Service?
43-
44-
public var container: SimpleResolvable
45-
public var customTypeIdentifier: String?
46-
public var factoryParameters: [String: Any]?
45+
public let container: SimpleResolvable
46+
public let customTypeIdentifier: String?
47+
public let factoryParameters: [String: Any]?
4748

4849
public init(customTypeIdentifier: String? = nil,
4950
factoryParameters: [String: Any]? = nil,
@@ -55,16 +56,20 @@ import Foundation
5556

5657
public var wrappedValue: Service {
5758
get {
59+
semaphore.wait()
60+
defer { semaphore.signal() }
61+
5862
if let service {
5963
return service
6064
}
6165

6266
do {
63-
service = try container.resolve(type: Service.self,
64-
forCustomTypeIdentifier: customTypeIdentifier,
65-
factoryParameters: factoryParameters,
66-
resolver: container)
67-
return service!
67+
let resolvedService = try container.resolve(type: Service.self,
68+
forCustomTypeIdentifier: customTypeIdentifier,
69+
factoryParameters: factoryParameters,
70+
resolver: container)
71+
service = resolvedService
72+
return resolvedService
6873
} catch {
6974
fatalError("DI fatal error :\(error)")
7075
}

0 commit comments

Comments
 (0)