Skip to content

fix: Property wrappers are now thread safe #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions Sources/InfomaniakDI/InjectService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Foundation
// MARK: - InjectService<Service>

/// A property wrapper that resolves shared objects when the host type is initialized.
@propertyWrapper public struct InjectService<Service>: CustomDebugStringConvertible, Equatable, Identifiable {
@propertyWrapper public struct InjectService<Service: Sendable>: CustomDebugStringConvertible, Equatable, Identifiable, Sendable {
/// Identifiable
///
/// Something to link the identity of this property wrapper to the underlying Service type.
Expand All @@ -40,15 +40,14 @@ import Foundation
"""
}

/// Store the resolved service
var service: Service!
let service: Service

public var container: SimpleResolvable
public var customTypeIdentifier: String?
public var factoryParameters: [String: Any]?
public let container: SimpleResolvable
public let customTypeIdentifier: String?
public let factoryParameters: [String: Sendable]?

public init(customTypeIdentifier: String? = nil,
factoryParameters: [String: Any]? = nil,
factoryParameters: [String: Sendable]? = nil,
container: SimpleResolvable = SimpleResolver.sharedResolver) {
self.customTypeIdentifier = customTypeIdentifier
self.factoryParameters = factoryParameters
Expand Down
31 changes: 18 additions & 13 deletions Sources/InfomaniakDI/LazyInjectService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
import Foundation

/// Inject a service at the first use of the property
@propertyWrapper public final class LazyInjectService<Service>: Equatable, Identifiable {
@propertyWrapper public final class LazyInjectService<Service: Sendable>: Equatable, Identifiable, @unchecked Sendable {
private let semaphore = DispatchSemaphore(value: 1)

var service: Service?

/// Identifiable
///
/// Something to link the identity of this property wrapper to the underlying Service type.
Expand All @@ -38,15 +42,12 @@ import Foundation
"""
}

/// Store the resolved service
var service: Service?

public var container: SimpleResolvable
public var customTypeIdentifier: String?
public var factoryParameters: [String: Any]?
public let container: SimpleResolvable
public let customTypeIdentifier: String?
public let factoryParameters: [String: Sendable]?

public init(customTypeIdentifier: String? = nil,
factoryParameters: [String: Any]? = nil,
factoryParameters: [String: Sendable]? = nil,
container: SimpleResolvable = SimpleResolver.sharedResolver) {
self.customTypeIdentifier = customTypeIdentifier
self.factoryParameters = factoryParameters
Expand All @@ -55,16 +56,20 @@ import Foundation

public var wrappedValue: Service {
get {
semaphore.wait()
defer { semaphore.signal() }

if let service {
return service
}

do {
service = try container.resolve(type: Service.self,
forCustomTypeIdentifier: customTypeIdentifier,
factoryParameters: factoryParameters,
resolver: container)
return service!
let resolvedService = try container.resolve(type: Service.self,
forCustomTypeIdentifier: customTypeIdentifier,
factoryParameters: factoryParameters,
resolver: container)
service = resolvedService
return resolvedService
} catch {
fatalError("DI fatal error :\(error)")
}
Expand Down
Loading