From 23deb1c7cf02240716ec0a3acccaf50bddf70ba0 Mon Sep 17 00:00:00 2001 From: Ion Ostafi Date: Mon, 15 Jan 2024 22:29:49 +0200 Subject: [PATCH 01/41] Update swift-tools and new platform visionos --- Package.swift | 11 ++++++----- project.yml | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Package.swift b/Package.swift index 4387e099..126af175 100644 --- a/Package.swift +++ b/Package.swift @@ -1,14 +1,15 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.9 import PackageDescription let package = Package( name: "Swinject", platforms: [ - .macOS(.v10_10), - .iOS(.v9), - .tvOS(.v9), - .watchOS(.v2) + .macOS(.v10_13), + .iOS(.v12), + .tvOS(.v12), + .watchOS(.v4), + .visionOS(.v1) ], products: [ .library(name: "Swinject", diff --git a/project.yml b/project.yml index f1493e3a..091d7cc0 100644 --- a/project.yml +++ b/project.yml @@ -6,15 +6,16 @@ options: tabWidth: 4 deploymentTarget: macOS: '10.13' - tvOS: '11.0' - iOS: '11.0' + tvOS: '12.0' + iOS: '12.0' watchOS: '4.0' + visionOS: '1.0' fileGroups: - Playgrounds/Sample-iOS.playground targets: Swinject: type: framework - platform: [macOS, iOS, tvOS, watchOS] + platform: [macOS, iOS, tvOS, watchOS, visionOS] platformSuffix: -${platform} settings: base: @@ -37,7 +38,7 @@ targets: name: Swiftlint SwinjectTests: type: bundle.unit-test - platform: [macOS, iOS, tvOS, watchOS] + platform: [macOS, iOS, tvOS, watchOS, visionOS] platformSuffix: -${platform} scheme: {} sources: From e5bb8883c93ab24e111ae2d02220291c882bd4f7 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Tue, 26 Mar 2024 11:06:30 -0600 Subject: [PATCH 02/41] adds privacy manifest --- PrivacyInfo.xcprivacy | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 PrivacyInfo.xcprivacy diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..cfbe279c --- /dev/null +++ b/PrivacyInfo.xcprivacy @@ -0,0 +1,8 @@ + + + + + NSPrivacyTracking + + + From 0074a217e9d80d6a2b018aadcf08f96e62611e15 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Thu, 4 Apr 2024 13:16:55 -0600 Subject: [PATCH 03/41] Undo graph restoration once Lazy wrapper resolves its local hierarchy --- Sources/Container.swift | 2 ++ Tests/SwinjectTests/Circularity.swift | 33 +++++++++++++++++++ Tests/SwinjectTests/SynchronizedTests.swift | 35 +++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/Sources/Container.swift b/Sources/Container.swift index de4a032a..e42eb7ce 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -250,6 +250,8 @@ extension Container: _Resolver { if let entry = getEntry(for: key) { let factory = { [weak self] (graphIdentifier: GraphIdentifier?) -> Any? in let action = { [weak self] () -> Any? in + let originGraph = self?.currentObjectGraph + defer { originGraph.map { self?.restoreObjectGraph($0) } } if let graphIdentifier = graphIdentifier { self?.restoreObjectGraph(graphIdentifier) } diff --git a/Tests/SwinjectTests/Circularity.swift b/Tests/SwinjectTests/Circularity.swift index e9018dd0..8a198237 100644 --- a/Tests/SwinjectTests/Circularity.swift +++ b/Tests/SwinjectTests/Circularity.swift @@ -3,6 +3,7 @@ // import Foundation +import Swinject // MARK: Circular dependency of two objects @@ -69,3 +70,35 @@ internal class DDependingOnBC: D { internal class CDependingOnWeakB: C { weak var b: B? } + +internal protocol LazyParentProtocol { + var child1: LazyChildProtocol { get } + var child2: LazyChildProtocol { get } +} +internal protocol LazyChildProtocol: AnyObject { + var lazy: Lazy { get } +} +internal protocol LazySingletonProtocol { + var lazy: Lazy { get } +} +internal protocol LazilyResolvedProtocol: AnyObject { } + +internal class LazyParent: LazyParentProtocol { + let child1: LazyChildProtocol + let child2: LazyChildProtocol + + init(child1: LazyChildProtocol, child2: LazyChildProtocol) { + self.child1 = child1 + self.child2 = child2 + } +} + +internal class LazyChild: LazyChildProtocol, LazySingletonProtocol { + let lazy: Lazy + + init(lazy: Lazy) { + self.lazy = lazy + } +} + +internal class LazilyResolved: LazilyResolvedProtocol { } diff --git a/Tests/SwinjectTests/SynchronizedTests.swift b/Tests/SwinjectTests/SynchronizedTests.swift index c67ed477..a031d410 100644 --- a/Tests/SwinjectTests/SynchronizedTests.swift +++ b/Tests/SwinjectTests/SynchronizedTests.swift @@ -183,6 +183,41 @@ class SynchronizedResolverTests: XCTestCase { } } } + + func testGraphIdentifierRestoredAfterLazyResolve() { + let container = Container() + container.register(LazilyResolvedProtocol.self) { _ in + LazilyResolved() + } + container.register(LazySingletonProtocol.self) { + let lazy = $0.resolve(Lazy.self)! + return LazyChild(lazy: lazy) + } + .inObjectScope(.container) + container.register(LazyChildProtocol.self) { + let lazy = $0.resolve(Lazy.self)! + return LazyChild(lazy: lazy) + } + container.register(LazyParentProtocol.self) { + let child1 = $0.resolve(LazyChildProtocol.self)! + let singleton = $0.resolve(LazySingletonProtocol.self)! + // Previously, accessing instance here would permanently + // hijack the graph identifier to the 'recalled' state. + _ = singleton.lazy.instance + let child2 = $0.resolve(LazyChildProtocol.self)! + return LazyParent(child1: child1, child2: child2) + } + + // Resolve, but don't access lazy value yet. + _ = container.resolve(LazySingletonProtocol.self)! + + // First lazy value access in LazyParent resolve, this + // could've happened in its init or wherever. + let parent = container.resolve(LazyParentProtocol.self)! + + XCTAssertIdentical(parent.child1, parent.child2) + XCTAssertIdentical(parent.child1.lazy.instance, parent.child2.lazy.instance) + } } private final class Counter { From 65f9153525a69efa8e4873a11958292f7493cf38 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Fri, 5 Apr 2024 10:40:30 -0600 Subject: [PATCH 04/41] update version and Faire ownership --- README.md | 3 ++- Sources/Info.plist | 2 +- Swinject.podspec | 2 +- Tests/SwinjectTests/Info.plist | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c0c9e2b3..296aa159 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ Swinject ======== -[![Travis CI Com](https://travis-ci.com/Swinject/Swinject.svg?branch=master)](https://travis-ci.com/Swinject/Swinject) ![Github Actions](https://github.com/1ucas/Swinject/actions/workflows/Project%20Testing.yml/badge.svg?branch=master) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods Version](https://img.shields.io/cocoapods/v/Swinject.svg?style=flat)](http://cocoapods.org/pods/Swinject) @@ -18,6 +17,8 @@ Swinject is a lightweight [dependency injection](https://en.wikipedia.org/wiki/D Dependency injection (DI) is a software design pattern that implements Inversion of Control (IoC) for resolving dependencies. In the pattern, Swinject helps your app split into loosely-coupled components, which can be developed, tested and maintained more easily. Swinject is powered by the Swift generic type system and first class functions to define dependencies of your app simply and fluently. +Swinject is maintained by the [Faire Wholesale Inc.](https://github.com/Faire?view_as=public) mobile platform team. + ## Features - [x] [Pure Swift Type Support](./Documentation/README.md#user-content-pure-swift-type-support) diff --git a/Sources/Info.plist b/Sources/Info.plist index fa707f75..c2061be0 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.8.4 + 2.8.5 CFBundleSignature ???? CFBundleVersion diff --git a/Swinject.podspec b/Swinject.podspec index a483f6f1..687c84c9 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Swinject" - s.version = "2.8.4" + s.version = "2.8.5" s.summary = "Dependency injection framework for Swift" s.description = "Swinject is a dependency injection framework for Swift, to manage the dependencies of types in your system." diff --git a/Tests/SwinjectTests/Info.plist b/Tests/SwinjectTests/Info.plist index 4f51f531..7915b0f3 100644 --- a/Tests/SwinjectTests/Info.plist +++ b/Tests/SwinjectTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.8.4 + 2.8.5 CFBundleSignature ???? CFBundleVersion From c6da4f9e868c159ed6e2ce7406d14ae85ce4978c Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Tue, 9 Apr 2024 10:23:48 -0600 Subject: [PATCH 05/41] Correct the xcodeproj manifest --- Swinject.xcodeproj/project.pbxproj | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Swinject.xcodeproj/project.pbxproj b/Swinject.xcodeproj/project.pbxproj index 22f3c3d7..9a310843 100644 --- a/Swinject.xcodeproj/project.pbxproj +++ b/Swinject.xcodeproj/project.pbxproj @@ -81,6 +81,14 @@ 4D667425CB08BF894C1A78AA /* Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D8BA4B04A5F517AF663DCE /* Behavior.swift */; }; 5010597DD8C25E6684C8087E /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5922B783F35F436C528594C1 /* Assembly.swift */; }; 513BDFB32DCE5B048672C33D /* ServiceEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76238B6105E2E443A94CE595 /* ServiceEntry.swift */; }; + 51F2541C2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; + 51F2541D2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; + 51F2541E2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; + 51F2541F2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; + 51F254202BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */; }; + 51F254212BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */; }; + 51F254222BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */; }; + 51F254232BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */; }; 52E4C2C279904E90D5F37204 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E48E07BA72BCBFB305E93 /* Container.swift */; }; 545B9E4AD1990F07C3C8AB5E /* ContainerTests.GraphCaching.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46A04FC6931B56743E4B7D63 /* ContainerTests.GraphCaching.swift */; }; 5488A7839EF00BAC43CE2CEE /* Container.TypeForwarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7605BDEF71547D0B17D7195A /* Container.TypeForwarding.swift */; }; @@ -110,7 +118,6 @@ 7000D01DCB05BF57BC89BCF8 /* InstanceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7101AB0DE88A5340F7EAAC04 /* InstanceWrapper.swift */; }; 706A459CAE24E0D5054CEC30 /* GraphIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B058F48AF289C3AC307C2F7 /* GraphIdentifier.swift */; }; 73390576F317FC0A851046B8 /* ServiceEntryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28B6980B5294739C0656769C /* ServiceEntryTests.swift */; }; - 737740CDFB89189DFF5FD165 /* Swinject.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DE57FB3E8228904E6FE0D7 /* Swinject.h */; settings = {ATTRIBUTES = (Public, ); }; }; 73906701DF9BE9870A55F291 /* ServiceKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CCDBC7E5186CB36CD160D94 /* ServiceKeyTests.swift */; }; 75A776D4072BCF74F298D818 /* EmploymentAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077047532310E42432E94D83 /* EmploymentAssembly.swift */; }; 75DC16A87FD15168ACDEEAA3 /* LazyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7287A8057A58B72EBC5C8CB9 /* LazyTests.swift */; }; @@ -160,7 +167,6 @@ BAB2886500919D428F97215A /* DebugHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE752946EBDAC1CB5823A39E /* DebugHelper.swift */; }; BAB510EEEBDD444BA65E0003 /* LazyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7287A8057A58B72EBC5C8CB9 /* LazyTests.swift */; }; BB78B5DA95DCE3A942C18910 /* InstanceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7101AB0DE88A5340F7EAAC04 /* InstanceWrapper.swift */; }; - BCD130E9F1D33C66AA6A12F7 /* Swinject.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DE57FB3E8228904E6FE0D7 /* Swinject.h */; settings = {ATTRIBUTES = (Public, ); }; }; BCDBFF75F368C980B6DA4BFE /* ContainerTests.Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988A20A0815159E43C26CA46 /* ContainerTests.Behavior.swift */; }; BDCBFD62903A1C0F8B35797C /* ObjectScope.Standard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79864FE285C06A91E006806 /* ObjectScope.Standard.swift */; }; BE3889327CE4A24631B91350 /* AssemblerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29EB588ED052D3BAF38AFBBE /* AssemblerTests.swift */; }; @@ -175,7 +181,6 @@ C7C1DD107809E8C3C728C199 /* ContainerTests.DebugHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4744C3ABBA9A8A585C726883 /* ContainerTests.DebugHelper.swift */; }; CC64B74A022612990F57D51D /* GraphIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B058F48AF289C3AC307C2F7 /* GraphIdentifier.swift */; }; CF05649033EABCF21B15C535 /* DebugHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE752946EBDAC1CB5823A39E /* DebugHelper.swift */; }; - D0D71F9C22A0EE82B6E4F61A /* Swinject.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DE57FB3E8228904E6FE0D7 /* Swinject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D210E7349E119368A741DB2F /* LoadAwareAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2380F40FC2B9229F36B13679 /* LoadAwareAssembly.swift */; }; D4D93174FF22D805953A727A /* _Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C304EFDAFB81043B2896E5 /* _Resolver.swift */; }; D56BD23EC91E28CE6C6D6E78 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E48E07BA72BCBFB305E93 /* Container.swift */; }; @@ -191,7 +196,6 @@ EBB561398EE319A46C487059 /* ContainerTests.CustomScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7608AD07FC553DAC2911FB /* ContainerTests.CustomScope.swift */; }; ED19CAF7B39978B3CDF2013E /* Person.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B00D2ED19209D0C6210169 /* Person.swift */; }; ED97A21AE7F99626D9E8BB80 /* ContainerTests.CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FCE820EE05C206E34CEF068 /* ContainerTests.CustomStringConvertible.swift */; }; - EE0D3637D2524AC1CD21780D /* Swinject.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DE57FB3E8228904E6FE0D7 /* Swinject.h */; settings = {ATTRIBUTES = (Public, ); }; }; EE4786524E79B10DB36FDB11 /* Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC379E27E9804FC6C0BA0842 /* Resolver.swift */; }; F355D8940486AF9DE89DD22E /* Circularity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47328731EED63322AD76FB3A /* Circularity.swift */; }; F73BFA36FB4432C826A3785C /* WeakStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 185C8B2BAF1CF29EB64159FC /* WeakStorageTests.swift */; }; @@ -307,6 +311,8 @@ 46A04FC6931B56743E4B7D63 /* ContainerTests.GraphCaching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.GraphCaching.swift; sourceTree = ""; }; 47328731EED63322AD76FB3A /* Circularity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Circularity.swift; sourceTree = ""; }; 4744C3ABBA9A8A585C726883 /* ContainerTests.DebugHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.DebugHelper.swift; sourceTree = ""; }; + 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = ""; }; + 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeDictionary.swift; sourceTree = ""; }; 54FFC812F14E9976208B644F /* ServiceEntry.TypeForwarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceEntry.TypeForwarding.swift; sourceTree = ""; }; 5922B783F35F436C528594C1 /* Assembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assembly.swift; sourceTree = ""; }; 5F7BC1C4169BB4382C6D3E1F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; @@ -316,7 +322,6 @@ 6D54F152C11C966ECFC55ECB /* Swinject.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Swinject.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7101AB0DE88A5340F7EAAC04 /* InstanceWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceWrapper.swift; sourceTree = ""; }; 7287A8057A58B72EBC5C8CB9 /* LazyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyTests.swift; sourceTree = ""; }; - 74DE57FB3E8228904E6FE0D7 /* Swinject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Swinject.h; sourceTree = ""; }; 7605BDEF71547D0B17D7195A /* Container.TypeForwarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.TypeForwarding.swift; sourceTree = ""; }; 76238B6105E2E443A94CE595 /* ServiceEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceEntry.swift; sourceTree = ""; }; 8B9785AAE94A5447FA307242 /* Swinject.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Swinject.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -459,6 +464,8 @@ D026A6699A2A932511D29E14 /* Sources */ = { isa = PBXGroup; children = ( + 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */, + 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */, 15C304EFDAFB81043B2896E5 /* _Resolver.swift */, 944B981A9157833AC3397B4A /* Assembler.swift */, 5922B783F35F436C528594C1 /* Assembly.swift */, @@ -480,7 +487,6 @@ 76238B6105E2E443A94CE595 /* ServiceEntry.swift */, 54FFC812F14E9976208B644F /* ServiceEntry.TypeForwarding.swift */, BD2ED5B0128132DCD709777F /* ServiceKey.swift */, - 74DE57FB3E8228904E6FE0D7 /* Swinject.h */, 39924B830576F62A4B528A37 /* UnavailableItems.swift */, ); path = Sources; @@ -493,7 +499,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - BCD130E9F1D33C66AA6A12F7 /* Swinject.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -501,7 +506,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 737740CDFB89189DFF5FD165 /* Swinject.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -509,7 +513,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - D0D71F9C22A0EE82B6E4F61A /* Swinject.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -517,7 +520,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - EE0D3637D2524AC1CD21780D /* Swinject.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -831,8 +833,10 @@ 62A1539A3B07EE3E1B72CF95 /* ObjectScope.swift in Sources */, 06B6204600E208CA97750825 /* RecursiveLock.swift in Sources */, 5BB768EFEB4BA7D1D05B4DCC /* Resolver.swift in Sources */, + 51F2541D2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */, 3D86E87634E427DCE7A8CF81 /* ServiceEntry.TypeForwarding.swift in Sources */, 513BDFB32DCE5B048672C33D /* ServiceEntry.swift in Sources */, + 51F254212BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */, A39172D39FE39D24A12A7257 /* ServiceKey.swift in Sources */, 68DA3B5FB308092FE9B5C733 /* UnavailableItems.swift in Sources */, 138D38987ED00235C384DD26 /* _Resolver.swift in Sources */, @@ -987,8 +991,10 @@ B9566410214991FBA727BB37 /* ObjectScope.swift in Sources */, C7A673429D2B735BAC18F58E /* RecursiveLock.swift in Sources */, EE4786524E79B10DB36FDB11 /* Resolver.swift in Sources */, + 51F2541E2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */, 54A3ED20B6A9506B9D514098 /* ServiceEntry.TypeForwarding.swift in Sources */, 1A2759577A52C96E28BB44F0 /* ServiceEntry.swift in Sources */, + 51F254222BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */, AEE58DEFB1764391B49E4EA0 /* ServiceKey.swift in Sources */, 58654C80A0946909D44F6BAD /* UnavailableItems.swift in Sources */, D4D93174FF22D805953A727A /* _Resolver.swift in Sources */, @@ -1015,8 +1021,10 @@ 130DDD2347169F885F420989 /* ObjectScope.swift in Sources */, 48B5EC6BADDE7B88260ADE44 /* RecursiveLock.swift in Sources */, 9CD3F07823D2E64DD636E00A /* Resolver.swift in Sources */, + 51F2541F2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */, 1845CF466289264A5BBC0CCE /* ServiceEntry.TypeForwarding.swift in Sources */, 35C9BCE88B6D6567760D7389 /* ServiceEntry.swift in Sources */, + 51F254232BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */, 3957BFF436703E5C0434D41E /* ServiceKey.swift in Sources */, 95070453A9934F08F9E02E6C /* UnavailableItems.swift in Sources */, FA6DD52FA4D4BB157A43CBEE /* _Resolver.swift in Sources */, @@ -1043,8 +1051,10 @@ 40DCC9D2F3ABC2B36C9DC8AE /* ObjectScope.swift in Sources */, 8B9E356593059568E55F9B06 /* RecursiveLock.swift in Sources */, 96BA7CCC6AEC192CAD3054E1 /* Resolver.swift in Sources */, + 51F2541C2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */, 1120EBEC146D1EDDCC29AB17 /* ServiceEntry.TypeForwarding.swift in Sources */, 808983B2AF095E481615029E /* ServiceEntry.swift in Sources */, + 51F254202BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */, E28F56015BFBB47A791856D3 /* ServiceKey.swift in Sources */, 4D631FA98F95F809740D6585 /* UnavailableItems.swift in Sources */, 7E57CE64E1356423F0F89FD3 /* _Resolver.swift in Sources */, From 0b0fa300d0fe24be810750272e0e7ea1971dff90 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Tue, 9 Apr 2024 10:33:56 -0600 Subject: [PATCH 06/41] Fix Carthage build --- Swinject.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Swinject.xcodeproj/project.pbxproj b/Swinject.xcodeproj/project.pbxproj index 9a310843..5e2ff452 100644 --- a/Swinject.xcodeproj/project.pbxproj +++ b/Swinject.xcodeproj/project.pbxproj @@ -1436,6 +1436,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1542,6 +1543,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", From 4da509c785acb1ac6c1bdfce0f1b80b2b4249a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D1=81=D0=B0=D0=B5=D0=B2=20=D0=92=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=B8=D1=81=D0=BB=D0=B0=D0=B2=20=D0=98=D0=B3=D0=BE=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D0=B8=D1=87?= Date: Wed, 10 Apr 2024 15:14:14 +0300 Subject: [PATCH 07/41] Added Privacy manifest in resource bundles --- Swinject.podspec | 1 + 1 file changed, 1 insertion(+) diff --git a/Swinject.podspec b/Swinject.podspec index 687c84c9..8557d4c4 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -8,6 +8,7 @@ Pod::Spec.new do |s| s.license = 'MIT' s.author = 'Swinject Contributors' s.source = { :git => "https://github.com/Swinject/Swinject.git", :tag => s.version.to_s } + s.resource_bundles = { 'Swinject' => ['PrivacyInfo.xcprivacy'] } s.swift_version = '5.0' s.source_files = 'Sources/**/*.swift' From d813d2354877a7d87f452fce115565cfdddff784 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Fri, 12 Apr 2024 12:05:50 -0600 Subject: [PATCH 08/41] Add privacy manifest to Xcodeproj --- Swinject.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Swinject.xcodeproj/project.pbxproj b/Swinject.xcodeproj/project.pbxproj index 5e2ff452..476225d0 100644 --- a/Swinject.xcodeproj/project.pbxproj +++ b/Swinject.xcodeproj/project.pbxproj @@ -311,6 +311,7 @@ 46A04FC6931B56743E4B7D63 /* ContainerTests.GraphCaching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.GraphCaching.swift; sourceTree = ""; }; 47328731EED63322AD76FB3A /* Circularity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Circularity.swift; sourceTree = ""; }; 4744C3ABBA9A8A585C726883 /* ContainerTests.DebugHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.DebugHelper.swift; sourceTree = ""; }; + 515B0F492BC9AF5D00C4E24F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = ""; }; 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeDictionary.swift; sourceTree = ""; }; 54FFC812F14E9976208B644F /* ServiceEntry.TypeForwarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceEntry.TypeForwarding.swift; sourceTree = ""; }; @@ -451,6 +452,7 @@ A4F7E56739BB9CB479DDDBF6 = { isa = PBXGroup; children = ( + 515B0F492BC9AF5D00C4E24F /* PrivacyInfo.xcprivacy */, 02D4026405E0F79F34991C49 /* Playgrounds */, D026A6699A2A932511D29E14 /* Sources */, 1281EF9C9707360B39DD4C11 /* SwinjectTests */, From b45ac37ca19905b2e12c429807e1955a7d43d1fd Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Fri, 12 Apr 2024 12:06:58 -0600 Subject: [PATCH 09/41] update to 2.8.6 --- Sources/Info.plist | 2 +- Swinject.podspec | 2 +- Tests/SwinjectTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Info.plist b/Sources/Info.plist index c2061be0..1f79513a 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.8.5 + 2.8.6 CFBundleSignature ???? CFBundleVersion diff --git a/Swinject.podspec b/Swinject.podspec index 8557d4c4..750e8347 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Swinject" - s.version = "2.8.5" + s.version = "2.8.6" s.summary = "Dependency injection framework for Swift" s.description = "Swinject is a dependency injection framework for Swift, to manage the dependencies of types in your system." diff --git a/Tests/SwinjectTests/Info.plist b/Tests/SwinjectTests/Info.plist index 7915b0f3..a61afcf3 100644 --- a/Tests/SwinjectTests/Info.plist +++ b/Tests/SwinjectTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.8.5 + 2.8.6 CFBundleSignature ???? CFBundleVersion From df54e4b4590b1a85418aa6d69088f179526dada0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=B3=D0=BC=D0=B0=D1=82=D0=B7=D1=8F=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=A0=D1=83=D1=81=D1=82=D0=B0=D0=BC?= Date: Wed, 17 Apr 2024 19:45:03 +0300 Subject: [PATCH 10/41] Fix Privacy manifest --- PrivacyInfo.xcprivacy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy index cfbe279c..7b867651 100644 --- a/PrivacyInfo.xcprivacy +++ b/PrivacyInfo.xcprivacy @@ -2,7 +2,9 @@ - NSPrivacyTracking - + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + From b937d8f51f843f4188503545060efa5bb9cd0f92 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Mon, 22 Apr 2024 09:34:24 -0600 Subject: [PATCH 11/41] update version 2.8.7 --- Sources/Info.plist | 2 +- Swinject.podspec | 2 +- Tests/SwinjectTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Info.plist b/Sources/Info.plist index 1f79513a..99e29f1d 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.8.6 + 2.8.7 CFBundleSignature ???? CFBundleVersion diff --git a/Swinject.podspec b/Swinject.podspec index 750e8347..79e09b6d 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Swinject" - s.version = "2.8.6" + s.version = "2.8.7" s.summary = "Dependency injection framework for Swift" s.description = "Swinject is a dependency injection framework for Swift, to manage the dependencies of types in your system." diff --git a/Tests/SwinjectTests/Info.plist b/Tests/SwinjectTests/Info.plist index a61afcf3..0b6efdc1 100644 --- a/Tests/SwinjectTests/Info.plist +++ b/Tests/SwinjectTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.8.6 + 2.8.7 CFBundleSignature ???? CFBundleVersion From cb81e955f7b6a11c3827c410a2d06748061d08da Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Fri, 26 Apr 2024 13:39:45 -0600 Subject: [PATCH 12/41] fixes for sync performance --- Sources/Container.TypeForwarding.swift | 6 +- Sources/Container.swift | 166 +++++++++--------- Sources/RecursiveLock.swift | 94 +++++++++- Swinject.xcodeproj/project.pbxproj | 10 ++ .../SwinjectTests/ContainerTests.Speed.swift | 54 ++++++ Tests/SwinjectTests/SynchronizedTests.swift | 4 +- 6 files changed, 242 insertions(+), 92 deletions(-) create mode 100644 Tests/SwinjectTests/ContainerTests.Speed.swift diff --git a/Sources/Container.TypeForwarding.swift b/Sources/Container.TypeForwarding.swift index 14813891..46f9c042 100644 --- a/Sources/Container.TypeForwarding.swift +++ b/Sources/Container.TypeForwarding.swift @@ -25,7 +25,9 @@ extension Container { name: name, option: nil ) - services[key] = service - behaviors.forEach { $0.container(self, didRegisterType: type, toService: service, withName: name) } + syncIfEnabled { + services[key] = service + behaviors.forEach { $0.container(self, didRegisterType: type, toService: service, withName: name) } + } } } diff --git a/Sources/Container.swift b/Sources/Container.swift index e42eb7ce..7ba02f78 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -20,7 +20,7 @@ import Foundation /// where `A` and `X` are protocols, `B` is a type conforming `A`, and `Y` is a type conforming `X` /// and depending on `A`. public final class Container { - internal var services = ThreadSafeDictionary() + internal var services = [ServiceKey: ServiceEntryProtocol]() private let parent: Container? // Used by HierarchyObjectScope private var resolutionDepth = 0 private let debugHelper: DebugHelper @@ -66,7 +66,7 @@ public final class Container { /// Removes all registrations in the container. public func removeAll() { - services.removeAll() + syncIfEnabled { services.removeAll() } } /// Discards instances for services registered in the given `ObjectsScopeProtocol`. @@ -77,15 +77,12 @@ public final class Container { /// - Parameters: /// - objectScope: All instances registered in given `ObjectsScopeProtocol` will be discarded. public func resetObjectScope(_ objectScope: ObjectScopeProtocol) { - services.forEachWrite({ (_: ServiceKey, value: ServiceEntryProtocol) in - guard value.objectScope === objectScope else { - return - } - - value.storage.instance = nil - }) - - parent?.resetObjectScope(objectScope) + syncIfEnabled { + services.values + .filter { $0.objectScope === objectScope } + .forEach { $0.storage.instance = nil } + parent?.resetObjectScope(objectScope) + } } /// Discards instances for services registered in the given `ObjectsScope`. It performs the same operation @@ -143,19 +140,21 @@ public final class Container { name: String? = nil, option: ServiceKeyOption? = nil ) -> ServiceEntry { - let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) - let entry = ServiceEntry( - serviceType: serviceType, - argumentsType: Arguments.self, - factory: factory, - objectScope: defaultObjectScope - ) - entry.container = self - services[key] = entry + syncIfEnabled { + let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) + let entry = ServiceEntry( + serviceType: serviceType, + argumentsType: Arguments.self, + factory: factory, + objectScope: defaultObjectScope + ) + entry.container = self + services[key] = entry - behaviors.forEach { $0.container(self, didRegisterType: serviceType, toService: entry, withName: name) } + behaviors.forEach { $0.container(self, didRegisterType: serviceType, toService: entry, withName: name) } - return entry + return entry + } } /// Returns a synchronized view of the container for thread safety. @@ -178,7 +177,9 @@ public final class Container { /// - Parameters: /// - behavior: Behavior to be added to the container public func addBehavior(_ behavior: Behavior) { - behaviors.append(behavior) + syncIfEnabled { + behaviors.append(behavior) + } } /// Check if a `Service` of a given type and name has already been registered. @@ -193,8 +194,10 @@ public final class Container { of serviceType: Service.Type, name: String? = nil ) -> Bool { - getRegistrations().contains { key, _ in - key.serviceType == serviceType && key.name == name + syncIfEnabled { + getRegistrations().contains { key, _ in + key.serviceType == serviceType && key.name == name + } } } @@ -214,26 +217,30 @@ extension Container: _Resolver { option: ServiceKeyOption? = nil, invoker: @escaping ((Arguments) -> Any) -> Any ) -> Service? { - var resolvedInstance: Service? - let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) + // No need to use weak self since the resolution will be executed before + // this function exits. + syncIfEnabled { + var resolvedInstance: Service? + let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) - if let entry = getEntry(for: key) { - resolvedInstance = resolve(entry: entry, invoker: invoker) - } + if let entry = getEntry(for: key) { + resolvedInstance = resolve(entry: entry, invoker: invoker) + } - if resolvedInstance == nil { - resolvedInstance = resolveAsWrapper(name: name, option: option, invoker: invoker) - } + if resolvedInstance == nil { + resolvedInstance = resolveAsWrapper(name: name, option: option, invoker: invoker) + } - if resolvedInstance == nil { - debugHelper.resolutionFailed( - serviceType: Service.self, - key: key, - availableRegistrations: getRegistrations() - ) - } + if resolvedInstance == nil { + debugHelper.resolutionFailed( + serviceType: Service.self, + key: key, + availableRegistrations: getRegistrations() + ) + } - return resolvedInstance + return resolvedInstance + } } fileprivate func resolveAsWrapper( @@ -249,18 +256,14 @@ extension Container: _Resolver { if let entry = getEntry(for: key) { let factory = { [weak self] (graphIdentifier: GraphIdentifier?) -> Any? in - let action = { [weak self] () -> Any? in - let originGraph = self?.currentObjectGraph - defer { originGraph.map { self?.restoreObjectGraph($0) } } + self?.syncIfEnabled { [weak self] () -> Any? in + guard let self else { return nil } + let originGraph = self.currentObjectGraph + defer { originGraph.map { self.restoreObjectGraph($0) } } if let graphIdentifier = graphIdentifier { - self?.restoreObjectGraph(graphIdentifier) + self.restoreObjectGraph(graphIdentifier) } - return self?.resolve(entry: entry, invoker: invoker) as Any? - } - if self?.synchronized ?? true { - return self?.lock.sync(action: action) - } else { - return action() + return self.resolve(entry: entry, invoker: invoker) as Any? } } return wrapper.init(inContainer: self, withInstanceFactory: factory) as? Wrapper @@ -271,7 +274,7 @@ extension Container: _Resolver { fileprivate func getRegistrations() -> [ServiceKey: ServiceEntryProtocol] { var registrations = parent?.getRegistrations() ?? [:] - services.forEachRead { key, value in registrations[key] = value } + services.forEach { key, value in registrations[key] = value } return registrations } @@ -298,9 +301,7 @@ extension Container: _Resolver { } fileprivate func graphResolutionCompleted() { - services.forEachWrite { (_, value) in - value.storage.graphResolutionCompleted() - } + services.values.forEach { $0.storage.graphResolutionCompleted() } currentObjectGraph = nil } } @@ -342,41 +343,30 @@ extension Container: Resolver { entry: ServiceEntryProtocol, invoker: @escaping (Factory) -> Any ) -> Service? { - // No need to use weak self since the resolution will be executed before - // this function exits. - let resolution: () -> Service? = { [self] in - self.incrementResolutionDepth() - defer { self.decrementResolutionDepth() } - - guard let currentObjectGraph = self.currentObjectGraph else { - fatalError("If accessing container from multiple threads, make sure to use a synchronized resolver.") - } - - if let persistedInstance = self.persistedInstance(Service.self, from: entry, in: currentObjectGraph) { - return persistedInstance - } + self.incrementResolutionDepth() + defer { self.decrementResolutionDepth() } - let resolvedInstance = invoker(entry.factory as! Factory) - if let persistedInstance = self.persistedInstance(Service.self, from: entry, in: currentObjectGraph) { - // An instance for the key might be added by the factory invocation. - return persistedInstance - } - entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph) + guard let currentObjectGraph = self.currentObjectGraph else { + fatalError("If accessing container from multiple threads, make sure to use a synchronized resolver.") + } - if let completed = entry.initCompleted as? (Resolver, Any) -> Void, - let resolvedInstance = resolvedInstance as? Service { - completed(self, resolvedInstance) - } + if let persistedInstance = self.persistedInstance(Service.self, from: entry, in: currentObjectGraph) { + return persistedInstance + } - return resolvedInstance as? Service + let resolvedInstance = invoker(entry.factory as! Factory) + if let persistedInstance = self.persistedInstance(Service.self, from: entry, in: currentObjectGraph) { + // An instance for the key might be added by the factory invocation. + return persistedInstance } - if synchronized { - return lock.sync { - return resolution() - } - } else { - return resolution() + entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph) + + if let completed = entry.initCompleted as? (Resolver, Any) -> Void, + let resolvedInstance = resolvedInstance as? Service { + completed(self, resolvedInstance) } + + return resolvedInstance as? Service } private func persistedInstance( @@ -388,6 +378,12 @@ extension Container: Resolver { return nil } } + + @inline(__always) + @discardableResult + internal func syncIfEnabled(_ action: () throws -> T) rethrows -> T { + try synchronized ? lock.sync(action) : action() + } } // MARK: CustomStringConvertible diff --git a/Sources/RecursiveLock.swift b/Sources/RecursiveLock.swift index 48020b83..ebe82456 100644 --- a/Sources/RecursiveLock.swift +++ b/Sources/RecursiveLock.swift @@ -4,12 +4,100 @@ import Foundation +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) +/// `os_unfair_lock_recursive` is, for some reason, a private-API. +/// +/// `RecursiveLock` is a fair, recursive lock where acquisition order is guaranteed and recursion is permitted. +/// +/// Based on readings: +/// - https://www.mikeash.com/pyblog/friday-qa-2017-10-27-locks-thread-safety-and-swift-2017-edition.html +/// - https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Atomic.swift +/// - https://serhiybutz.medium.com/swift-mutex-benchmark-b21ee293d9ad +/// +/// Class-type paired with pointer-use guarantees address stability. +public final class RecursiveLock { + // MARK: Lifecycle + + public init() { + mutex = .allocate(capacity: 1) + mutex.initialize(to: pthread_mutex_t()) + + let attr = UnsafeMutablePointer.allocate(capacity: 1) + attr.initialize(to: pthread_mutexattr_t()) + pthread_mutexattr_init(attr) + pthread_mutexattr_settype(attr, PTHREAD_MUTEX_RECURSIVE) + pthread_mutexattr_setpolicy_np(attr, PTHREAD_MUTEX_POLICY_FIRSTFIT_NP) + + defer { + pthread_mutexattr_destroy(attr) + attr.deinitialize(count: 1) + attr.deallocate() + } + + pthread_mutex_init(mutex, attr) + } + + deinit { + let status = pthread_mutex_destroy(mutex) + assert(status == 0, "Unlock the lock before destroying it") + + mutex.deinitialize(count: 1) + mutex.deallocate() + } + + // MARK: Internal + + internal func lock() { + let status = pthread_mutex_lock(mutex) + switch status { + case 0: + break + case EDEADLK: + fatalError("Lock failed, would cause deadlock") + default: + fatalError("Could not lock") + } + } + + internal func unlock() { + let status = pthread_mutex_unlock(mutex) + switch status { + case 0: + break + case EPERM: + fatalError("Cannot unlock when another thread owns the lock!") + default: + fatalError("Could not unlock") + } + } + + // MARK: Private + + private let mutex: UnsafeMutablePointer +} +#else internal final class RecursiveLock { private let lock = NSRecursiveLock() - func sync(action: () -> T) -> T { + func lock() { lock.lock() - defer { lock.unlock() } - return action() + } + + func unlock() { + lock.unlock() + } +} +#endif + +extension RecursiveLock { + /// Guarantee exclusive thread access to certain logic. + /// + /// Claims ownership over this lock within the scope of the closure, + /// during which this thread owns this lock. Nested `atomic` calls + /// on the same thread are permitted. + @discardableResult + func sync(_ block: () throws -> (T)) rethrows -> T { + lock(); defer { unlock() } + return try block() } } diff --git a/Swinject.xcodeproj/project.pbxproj b/Swinject.xcodeproj/project.pbxproj index 476225d0..30ee4718 100644 --- a/Swinject.xcodeproj/project.pbxproj +++ b/Swinject.xcodeproj/project.pbxproj @@ -81,6 +81,10 @@ 4D667425CB08BF894C1A78AA /* Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D8BA4B04A5F517AF663DCE /* Behavior.swift */; }; 5010597DD8C25E6684C8087E /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5922B783F35F436C528594C1 /* Assembly.swift */; }; 513BDFB32DCE5B048672C33D /* ServiceEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76238B6105E2E443A94CE595 /* ServiceEntry.swift */; }; + 516EB7F82BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */; }; + 516EB7F92BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */; }; + 516EB7FA2BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */; }; + 516EB7FB2BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */; }; 51F2541C2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; 51F2541D2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; 51F2541E2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; @@ -312,6 +316,7 @@ 47328731EED63322AD76FB3A /* Circularity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Circularity.swift; sourceTree = ""; }; 4744C3ABBA9A8A585C726883 /* ContainerTests.DebugHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.DebugHelper.swift; sourceTree = ""; }; 515B0F492BC9AF5D00C4E24F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.Speed.swift; sourceTree = ""; }; 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = ""; }; 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeDictionary.swift; sourceTree = ""; }; 54FFC812F14E9976208B644F /* ServiceEntry.TypeForwarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceEntry.TypeForwarding.swift; sourceTree = ""; }; @@ -415,6 +420,7 @@ 5FCE820EE05C206E34CEF068 /* ContainerTests.CustomStringConvertible.swift */, 4744C3ABBA9A8A585C726883 /* ContainerTests.DebugHelper.swift */, 46A04FC6931B56743E4B7D63 /* ContainerTests.GraphCaching.swift */, + 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */, 19726C4D2E8898E5C05A5E38 /* ContainerTests.swift */, BBDE1F7FCBF0C61DE0BA3ED2 /* ContainerTests.TypeForwarding.swift */, 077047532310E42432E94D83 /* EmploymentAssembly.swift */, @@ -854,6 +860,7 @@ 1C1C0CB3F19349057B4FBDE1 /* BasicAssembly.swift in Sources */, 235DD35BC7B8CE15BA46A677 /* BehaviorFakes.swift in Sources */, F355D8940486AF9DE89DD22E /* Circularity.swift in Sources */, + 516EB7F82BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */, 374D3B4A2A2163726B4E55BC /* ContainerTests.Arguments.swift in Sources */, C578E997A0076FB320475132 /* ContainerTests.Behavior.swift in Sources */, 4C06628453523BB76531404F /* ContainerTests.Circularity.swift in Sources */, @@ -886,6 +893,7 @@ 9ECC88CB09E13FD179A1ED8E /* BasicAssembly.swift in Sources */, FF9DCB0AB20B0DBBE79451BB /* BehaviorFakes.swift in Sources */, F82A2357F3C86D24E1E1D712 /* Circularity.swift in Sources */, + 516EB7F92BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */, 96E113959C6AF4C512C95B0F /* ContainerTests.Arguments.swift in Sources */, 31C021202AAC21C893F790A9 /* ContainerTests.Behavior.swift in Sources */, DAB7F0C574E1C33C92C455A5 /* ContainerTests.Circularity.swift in Sources */, @@ -918,6 +926,7 @@ 6443FB8C5B3DD95740287398 /* BasicAssembly.swift in Sources */, 0CAD0368F908D3D285C6A9A9 /* BehaviorFakes.swift in Sources */, 69A27AA36C90A4D4A5FDDEFC /* Circularity.swift in Sources */, + 516EB7FB2BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */, 56F7004F323632D46B60D750 /* ContainerTests.Arguments.swift in Sources */, BCDBFF75F368C980B6DA4BFE /* ContainerTests.Behavior.swift in Sources */, 88D3B360E738AE936A4C3FC1 /* ContainerTests.Circularity.swift in Sources */, @@ -950,6 +959,7 @@ 5D17DB72B4986D174D69135B /* BasicAssembly.swift in Sources */, 1BA2596EFB6E7BF4D46E3C9D /* BehaviorFakes.swift in Sources */, 0E5336910C7B0A00E588940E /* Circularity.swift in Sources */, + 516EB7FA2BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */, A87B2787A32FAA3254700F90 /* ContainerTests.Arguments.swift in Sources */, 12984E4749F7C882055D380F /* ContainerTests.Behavior.swift in Sources */, 0CC89E82308CF2FFF0CCC267 /* ContainerTests.Circularity.swift in Sources */, diff --git a/Tests/SwinjectTests/ContainerTests.Speed.swift b/Tests/SwinjectTests/ContainerTests.Speed.swift new file mode 100644 index 00000000..f4af4566 --- /dev/null +++ b/Tests/SwinjectTests/ContainerTests.Speed.swift @@ -0,0 +1,54 @@ +// +// Copyright © 2024 Swinject Contributors. All rights reserved. +// + +import XCTest +@testable import Swinject + +/// Not a perfect way to measure speed improvements, especially with all the different hardware that will be running +/// this test, but good to run before/after changes to see changes. +@available(iOS 13.0, *) +class ContainerSpeedTests: XCTestCase { + var container: Container! + + override func setUpWithError() throws { + container = Container() + + container.register(Animal.self) { _ in Cat() } + container.register(Animal.self) { _, arg in Cat(name: arg) } + container.register(Animal.self) { _, arg1, arg2 in Cat(name: arg1, sleeping: arg2) } + } + + // MARK: General speed test, ran locally for testing purposes + + func testContainerDefaultResolvesByArguments() { + let measureOptions = XCTMeasureOptions() + measureOptions.iterationCount = 1 + + measure(options: measureOptions) { + container.resolveCats() + } + } + + func testContainerSyncResolvesByArguments() { + container = container.synchronize() as? Container + + let measureOptions = XCTMeasureOptions() + measureOptions.iterationCount = 1 + + measure(options: measureOptions) { + container.resolveCats() + } + } +} + +fileprivate extension Container { + func resolveCats() { + for _ in 0..<500_000 { + _ = resolve(Animal.self) as? Cat + _ = resolve(Animal.self, argument: "Mimi") as? Cat + let lazy = resolve(Lazy.self, arguments: "Mew", true) + _ = lazy?.instance as? Cat + } + } +} diff --git a/Tests/SwinjectTests/SynchronizedTests.swift b/Tests/SwinjectTests/SynchronizedTests.swift index a031d410..1fafaa2e 100644 --- a/Tests/SwinjectTests/SynchronizedTests.swift +++ b/Tests/SwinjectTests/SynchronizedTests.swift @@ -70,7 +70,7 @@ class SynchronizedResolverTests: XCTestCase { func testSynchronizedResolverSynchronousReadsWrites() { let iterationCount = 3_000 - let container = Container() + let container = Container().synchronize() as! Container let registerExpectation = expectation(description: "register") let resolveExpectations = (0.. Date: Mon, 29 Apr 2024 09:39:59 -0600 Subject: [PATCH 13/41] SPM asks for PrivacyManifest to be in Sources --- PrivacyInfo.xcprivacy => Sources/PrivacyInfo.xcprivacy | 0 Swinject.xcodeproj/project.pbxproj | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename PrivacyInfo.xcprivacy => Sources/PrivacyInfo.xcprivacy (100%) diff --git a/PrivacyInfo.xcprivacy b/Sources/PrivacyInfo.xcprivacy similarity index 100% rename from PrivacyInfo.xcprivacy rename to Sources/PrivacyInfo.xcprivacy diff --git a/Swinject.xcodeproj/project.pbxproj b/Swinject.xcodeproj/project.pbxproj index 476225d0..bcf618bf 100644 --- a/Swinject.xcodeproj/project.pbxproj +++ b/Swinject.xcodeproj/project.pbxproj @@ -452,7 +452,6 @@ A4F7E56739BB9CB479DDDBF6 = { isa = PBXGroup; children = ( - 515B0F492BC9AF5D00C4E24F /* PrivacyInfo.xcprivacy */, 02D4026405E0F79F34991C49 /* Playgrounds */, D026A6699A2A932511D29E14 /* Sources */, 1281EF9C9707360B39DD4C11 /* SwinjectTests */, @@ -466,6 +465,7 @@ D026A6699A2A932511D29E14 /* Sources */ = { isa = PBXGroup; children = ( + 515B0F492BC9AF5D00C4E24F /* PrivacyInfo.xcprivacy */, 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */, 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */, 15C304EFDAFB81043B2896E5 /* _Resolver.swift */, From 5e24dc79e08eced2968e235563bb66e3f7d3dcb2 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Mon, 29 Apr 2024 10:01:08 -0600 Subject: [PATCH 14/41] Perform graph resolution completion only on relevant services --- Sources/Container.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Sources/Container.swift b/Sources/Container.swift index 7ba02f78..78028651 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -27,6 +27,7 @@ public final class Container { private let defaultObjectScope: ObjectScope private let synchronized: Bool internal var currentObjectGraph: GraphIdentifier? + internal var graphInstancesInFlight = [ServiceEntryProtocol]() internal let lock: RecursiveLock // Used by SynchronizedResolver. internal var behaviors = [Behavior]() @@ -195,9 +196,9 @@ public final class Container { name: String? = nil ) -> Bool { syncIfEnabled { - getRegistrations().contains { key, _ in + services.contains { key, _ in key.serviceType == serviceType && key.name == name - } + } || parent?.hasAnyRegistration(of: serviceType, name: name) == true } } @@ -301,7 +302,8 @@ extension Container: _Resolver { } fileprivate func graphResolutionCompleted() { - services.values.forEach { $0.storage.graphResolutionCompleted() } + graphInstancesInFlight.forEach { $0.storage.graphResolutionCompleted() } + graphInstancesInFlight.removeAll(keepingCapacity: true) currentObjectGraph = nil } } @@ -360,6 +362,7 @@ extension Container: Resolver { return persistedInstance } entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph) + graphInstancesInFlight.append(entry) if let completed = entry.initCompleted as? (Resolver, Any) -> Void, let resolvedInstance = resolvedInstance as? Service { From 736da60d84e9a196f1468413aed0ebaab67b4759 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Mon, 29 Apr 2024 13:22:25 -0600 Subject: [PATCH 15/41] update to version 2.8.8 --- Sources/Info.plist | 2 +- Swinject.podspec | 2 +- Tests/SwinjectTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Info.plist b/Sources/Info.plist index 99e29f1d..dbccee60 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.8.7 + 2.8.8 CFBundleSignature ???? CFBundleVersion diff --git a/Swinject.podspec b/Swinject.podspec index 79e09b6d..f50fea02 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Swinject" - s.version = "2.8.7" + s.version = "2.8.8" s.summary = "Dependency injection framework for Swift" s.description = "Swinject is a dependency injection framework for Swift, to manage the dependencies of types in your system." diff --git a/Tests/SwinjectTests/Info.plist b/Tests/SwinjectTests/Info.plist index 0b6efdc1..4c19b850 100644 --- a/Tests/SwinjectTests/Info.plist +++ b/Tests/SwinjectTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.8.7 + 2.8.8 CFBundleSignature ???? CFBundleVersion From 1ed342bd22d877f8dc4765f186659b9734f04865 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Mon, 29 Apr 2024 13:40:25 -0600 Subject: [PATCH 16/41] fix cocoa-release --- Swinject.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Swinject.podspec b/Swinject.podspec index f50fea02..321661ce 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.license = 'MIT' s.author = 'Swinject Contributors' s.source = { :git => "https://github.com/Swinject/Swinject.git", :tag => s.version.to_s } - s.resource_bundles = { 'Swinject' => ['PrivacyInfo.xcprivacy'] } + s.resource_bundles = { 'Swinject' => ['Sources/PrivacyInfo.xcprivacy'] } s.swift_version = '5.0' s.source_files = 'Sources/**/*.swift' From 3125943807991bc271d366205d98713696d65a1f Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Tue, 30 Apr 2024 11:18:12 -0600 Subject: [PATCH 17/41] Lock should be internal --- Sources/RecursiveLock.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/RecursiveLock.swift b/Sources/RecursiveLock.swift index ebe82456..df7bf32f 100644 --- a/Sources/RecursiveLock.swift +++ b/Sources/RecursiveLock.swift @@ -15,10 +15,10 @@ import Foundation /// - https://serhiybutz.medium.com/swift-mutex-benchmark-b21ee293d9ad /// /// Class-type paired with pointer-use guarantees address stability. -public final class RecursiveLock { +internal final class RecursiveLock { // MARK: Lifecycle - public init() { + internal init() { mutex = .allocate(capacity: 1) mutex.initialize(to: pthread_mutex_t()) From 66e5a096fa75a303268e6f42ced6be0faccfbfcd Mon Sep 17 00:00:00 2001 From: Brad Fol Date: Wed, 8 May 2024 15:05:01 -0700 Subject: [PATCH 18/41] Fix SPM Github Workflow --- .Package.Test.swift | 27 --------------------------- .github/workflows/SPM Testing.yml | 18 +++++++++++------- 2 files changed, 11 insertions(+), 34 deletions(-) delete mode 100644 .Package.Test.swift diff --git a/.Package.Test.swift b/.Package.Test.swift deleted file mode 100644 index 604e53cb..00000000 --- a/.Package.Test.swift +++ /dev/null @@ -1,27 +0,0 @@ -// swift-tools-version:5.0 - -import PackageDescription - -let package = Package( - name: "Swinject", - products: [ - .library( - name: "Swinject", - targets: ["Swinject"] - ), - ], - dependencies: [], - targets: [ - .target( - name: "Swinject", - dependencies: [], - path: "Sources" - ), - .testTarget( - name: "SwinjectTests", - dependencies: [ - "Swinject", - ] - ), - ] -) diff --git a/.github/workflows/SPM Testing.yml b/.github/workflows/SPM Testing.yml index f3366c11..58f1c439 100644 --- a/.github/workflows/SPM Testing.yml +++ b/.github/workflows/SPM Testing.yml @@ -1,17 +1,21 @@ # This is the First Workflow for Testing with Github Actions name: SPM Check + on: - workflow_dispatch: -env: - DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + jobs: spm-check: - runs-on: macos-10.15 + runs-on: macos-latest steps: - - uses: actions/checkout@v2 - - name: Setup Package File - run: mv .Package.Test.swift Package.swift + - uses: actions/checkout@v4 + - uses: swift-actions/setup-swift@v2 + with: + swift-version: "5.9" - name: Build run: swift build -v - name: Run tests From 1eb9e2323ac12dc1731ba1869e8a24138cc34dbe Mon Sep 17 00:00:00 2001 From: Brad Fol Date: Wed, 8 May 2024 15:49:17 -0700 Subject: [PATCH 19/41] Fix pod lint workflow --- .github/workflows/Pod Lint.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Pod Lint.yml b/.github/workflows/Pod Lint.yml index 1bbd3b99..3c936c98 100644 --- a/.github/workflows/Pod Lint.yml +++ b/.github/workflows/Pod Lint.yml @@ -2,16 +2,18 @@ name: Pod Lint on: - workflow_dispatch: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] env: ENCODING: LC_CTYPE=en_US.UTF-8 - DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer jobs: pod-lint: - runs-on: macos-10.15 + runs-on: macos-latest steps: - name: Checkout repository - uses: actions/checkout@v2 - - name: Linting PodSpec for Cocoapods" + uses: actions/checkout@v4 + - name: Linting PodSpec for Cocoapods run: export "${{ env. ENCODING }}" | pod lib lint - shell: bash \ No newline at end of file + shell: bash From 978539701666b423d745f223c416f1a38a27451e Mon Sep 17 00:00:00 2001 From: Sebastien Constant <32632376+s0ta91@users.noreply.github.com> Date: Wed, 15 May 2024 10:56:38 +0200 Subject: [PATCH 20/41] Fix visionOS build error in recursiveLock --- Sources/RecursiveLock.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/RecursiveLock.swift b/Sources/RecursiveLock.swift index df7bf32f..259d6227 100644 --- a/Sources/RecursiveLock.swift +++ b/Sources/RecursiveLock.swift @@ -77,14 +77,14 @@ internal final class RecursiveLock { } #else internal final class RecursiveLock { - private let lock = NSRecursiveLock() + private let recursiveLock = NSRecursiveLock() func lock() { - lock.lock() + recursiveLock.lock() } func unlock() { - lock.unlock() + recursiveLock.unlock() } } #endif From 284579a847a3277a694f44495af6eca19927053d Mon Sep 17 00:00:00 2001 From: Yoichi Tagaya Date: Sun, 19 May 2024 14:02:26 +0900 Subject: [PATCH 21/41] Fix the links to Funq and its author. The links were broken as CodePlex hosted by Microsoft has been discontinued. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 296aa159..3d1b84c2 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ The DI container features of Swinject are inspired by: and highly inspired by: -- [Funq](http://funq.codeplex.com) - [Daniel Cazzulino](http://www.codeplex.com/site/users/view/dcazzulino) and [the project team](http://funq.codeplex.com/team/view). +- [Funq](https://github.com/kzu/funq) - [Daniel Cazzulino](https://github.com/kzu). ## License From 6f8bcfe885f4957dd32ddb765f67dfa83edd8747 Mon Sep 17 00:00:00 2001 From: Brad Fol Date: Wed, 8 May 2024 16:13:14 -0700 Subject: [PATCH 22/41] Fix up test assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These were producing warnings in Xcode 15.3 about the double Optional: `Expression implicitly coerced from 'Dog??' to 'Any?’` Now explicitly matching the expected output. --- .../ContainerTests.TypeForwarding.swift | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Tests/SwinjectTests/ContainerTests.TypeForwarding.swift b/Tests/SwinjectTests/ContainerTests.TypeForwarding.swift index b6059597..426a4974 100644 --- a/Tests/SwinjectTests/ContainerTests.TypeForwarding.swift +++ b/Tests/SwinjectTests/ContainerTests.TypeForwarding.swift @@ -140,7 +140,15 @@ class ContainerTests_TypeForwarding: XCTestCase { func testContainerResolvesOptionalToNilWhenWrappedTypeIsNotRegistered() { let optionalDog = container.resolve(Dog?.self) - XCTAssertNotNil(optionalDog) + // The type of `optionalDog` is `Dog??` i.e. `Optional>` + switch optionalDog { + case .some(.none): + // Test pass + break + default: + // Any other value is incorrect + XCTFail() + } } func testContainerResolvesOptionalWithName() { @@ -152,8 +160,15 @@ class ContainerTests_TypeForwarding: XCTestCase { func testContainerResolvesOptionalToNilWithWrongName() { container.register(Dog.self, name: "Hachi") { _ in Dog() } let optionalDog = container.resolve(Dog?.self, name: "Mimi") - XCTAssertNil(optionalDog ?? nil) - XCTAssertNotNil(optionalDog) + // The type of `optionalDog` is `Dog??` i.e. `Optional>` + switch optionalDog { + case .some(.none): + // Test pass + break + default: + // Any other value is incorrect + XCTFail() + } } func testContainerResolvesOptionalWithArguments() { From cee9ee85251408eb2209b49a660c95151d7b927d Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Fri, 31 May 2024 12:42:13 -0600 Subject: [PATCH 23/41] Expose object graph --- Sources/Container.swift | 26 +++++++++++++++- .../ContainerTests.GraphCaching.swift | 30 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/Sources/Container.swift b/Sources/Container.swift index 78028651..fb6451bd 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -39,7 +39,7 @@ public final class Container { ) { self.parent = parent self.debugHelper = debugHelper - lock = parent.map { $0.lock } ?? RecursiveLock() + lock = parent.map(\.lock) ?? RecursiveLock() self.defaultObjectScope = defaultObjectScope self.synchronized = synchronized } @@ -201,6 +201,20 @@ public final class Container { } || parent?.hasAnyRegistration(of: serviceType, name: name) == true } } + + /// Applies a given GraphIdentifier across resolves in the provided closure. + /// - Parameters: + /// - identifier: Graph scope to use + /// - closure: Actions to execute within the Container + /// - Returns: Any value you return (Void otherwise) within the function call. + public func withObjectGraph(_ identifier: GraphIdentifier, closure: (Container) throws -> T) rethrows -> T { + try syncIfEnabled { + let graphIdentifier = currentObjectGraph + defer { self.currentObjectGraph = graphIdentifier } + self.currentObjectGraph = identifier + return try closure(self) + } + } /// Restores the object graph to match the given identifier. /// Not synchronized, use lock to edit safely. @@ -224,6 +238,10 @@ extension Container: _Resolver { var resolvedInstance: Service? let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) + if key == Self.graphIdentifierKey { + return currentObjectGraph as! Service? + } + if let entry = getEntry(for: key) { resolvedInstance = resolve(entry: entry, invoker: invoker) } @@ -398,3 +416,9 @@ extension Container: CustomStringConvertible { + "\n]" } } + +// MARK: Constants + +private extension Container { + static let graphIdentifierKey = ServiceKey(serviceType: GraphIdentifier.self, argumentsType: Resolver.self) +} diff --git a/Tests/SwinjectTests/ContainerTests.GraphCaching.swift b/Tests/SwinjectTests/ContainerTests.GraphCaching.swift index f756432c..f0ed551c 100644 --- a/Tests/SwinjectTests/ContainerTests.GraphCaching.swift +++ b/Tests/SwinjectTests/ContainerTests.GraphCaching.swift @@ -116,6 +116,36 @@ class ContainerTests_GraphCaching: XCTestCase { XCTAssert(dog1 === dog2) } + + func testContainerGraphScopeResolvesToNil() { + let graphScope = container.resolve(GraphIdentifier.self) + XCTAssertNil(graphScope) + } + + func testContainerResolvesGraphScopeIfAvailable() { + var graph: GraphIdentifier? + container.register(Dog.self) { + graph = ($0 as? Container)?.resolve(GraphIdentifier.self) + return Dog() + } + let _ = container.resolve(Dog.self)! + XCTAssertNotNil(graph) + } + + func testContainerManualScopeSetResolvesGraph() { + var graph: GraphIdentifier! + container.register(Dog.self) { + graph = ($0 as? Container)?.resolve(GraphIdentifier.self) + return Dog() + }.inObjectScope(.graph) + + let dog1 = container.resolve(Dog.self)! + var dog2: Dog! + container.withObjectGraph(graph) { + dog2 = $0.resolve(Dog.self)! + } + XCTAssert(dog1 === dog2) + } } private class StorageSpy: InstanceStorage { From 86ba38d3766c6c07eb3be5eed36b217eb2cb6deb Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Fri, 31 May 2024 12:43:39 -0600 Subject: [PATCH 24/41] version update --- Sources/Info.plist | 2 +- Swinject.podspec | 2 +- Tests/SwinjectTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Info.plist b/Sources/Info.plist index dbccee60..99b5d8ec 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.8.8 + 2.9.0 CFBundleSignature ???? CFBundleVersion diff --git a/Swinject.podspec b/Swinject.podspec index 321661ce..251860ba 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Swinject" - s.version = "2.8.8" + s.version = "2.9.0" s.summary = "Dependency injection framework for Swift" s.description = "Swinject is a dependency injection framework for Swift, to manage the dependencies of types in your system." diff --git a/Tests/SwinjectTests/Info.plist b/Tests/SwinjectTests/Info.plist index 4c19b850..3d442aab 100644 --- a/Tests/SwinjectTests/Info.plist +++ b/Tests/SwinjectTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.8.8 + 2.9.0 CFBundleSignature ???? CFBundleVersion From 5c9d668fc105d84d1331e91cc724de1f04b5aa33 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Fri, 31 May 2024 13:56:09 -0600 Subject: [PATCH 25/41] address PR comments --- Sources/Container.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Container.swift b/Sources/Container.swift index fb6451bd..d6e29781 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -239,7 +239,7 @@ extension Container: _Resolver { let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) if key == Self.graphIdentifierKey { - return currentObjectGraph as! Service? + return currentObjectGraph as? Service } if let entry = getEntry(for: key) { From 2da70d8f182bb52153bba8bba60c54a5f2b79914 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Fri, 31 May 2024 15:08:46 -0600 Subject: [PATCH 26/41] visionOS addition --- Sources/RecursiveLock.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/RecursiveLock.swift b/Sources/RecursiveLock.swift index 259d6227..0ec74c95 100644 --- a/Sources/RecursiveLock.swift +++ b/Sources/RecursiveLock.swift @@ -4,7 +4,7 @@ import Foundation -#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) /// `os_unfair_lock_recursive` is, for some reason, a private-API. /// /// `RecursiveLock` is a fair, recursive lock where acquisition order is guaranteed and recursion is permitted. From c630fcaa99cc3f3af7a48018477c9e03236a7ca9 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Mon, 3 Jun 2024 12:31:26 -0600 Subject: [PATCH 27/41] resolve --- Sources/Container.swift | 6 +++++- Tests/SwinjectTests/ContainerTests.GraphCaching.swift | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Sources/Container.swift b/Sources/Container.swift index d6e29781..11c34847 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -210,8 +210,12 @@ public final class Container { public func withObjectGraph(_ identifier: GraphIdentifier, closure: (Container) throws -> T) rethrows -> T { try syncIfEnabled { let graphIdentifier = currentObjectGraph - defer { self.currentObjectGraph = graphIdentifier } + defer { + self.currentObjectGraph = graphIdentifier + decrementResolutionDepth() + } self.currentObjectGraph = identifier + incrementResolutionDepth() return try closure(self) } } diff --git a/Tests/SwinjectTests/ContainerTests.GraphCaching.swift b/Tests/SwinjectTests/ContainerTests.GraphCaching.swift index f0ed551c..33a7f6af 100644 --- a/Tests/SwinjectTests/ContainerTests.GraphCaching.swift +++ b/Tests/SwinjectTests/ContainerTests.GraphCaching.swift @@ -141,10 +141,13 @@ class ContainerTests_GraphCaching: XCTestCase { let dog1 = container.resolve(Dog.self)! var dog2: Dog! + var dog3: Dog! container.withObjectGraph(graph) { dog2 = $0.resolve(Dog.self)! + dog3 = $0.resolve(Dog.self)! } XCTAssert(dog1 === dog2) + XCTAssert(dog2 === dog3) } } From f8148d6bac2280c1970bccada8d5ff154f49f923 Mon Sep 17 00:00:00 2001 From: Maxim Chipeev Date: Mon, 3 Jun 2024 12:52:39 -0600 Subject: [PATCH 28/41] update version to 2.9.1 --- Sources/Info.plist | 2 +- Swinject.podspec | 2 +- Tests/SwinjectTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Info.plist b/Sources/Info.plist index 99b5d8ec..777b1696 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.9.0 + 2.9.1 CFBundleSignature ???? CFBundleVersion diff --git a/Swinject.podspec b/Swinject.podspec index 251860ba..e4124509 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Swinject" - s.version = "2.9.0" + s.version = "2.9.1" s.summary = "Dependency injection framework for Swift" s.description = "Swinject is a dependency injection framework for Swift, to manage the dependencies of types in your system." diff --git a/Tests/SwinjectTests/Info.plist b/Tests/SwinjectTests/Info.plist index 3d442aab..294486e9 100644 --- a/Tests/SwinjectTests/Info.plist +++ b/Tests/SwinjectTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.9.0 + 2.9.1 CFBundleSignature ???? CFBundleVersion From e2fcd4080dc9715e828637b28c01fd1806df2b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Mar=C3=A7al?= <127062836+0xLucasMarcal@users.noreply.github.com> Date: Sat, 16 Aug 2025 14:01:25 -0300 Subject: [PATCH 29/41] Update Github Runners to macos 15.5 and Xcode 16.4 (#585) * Update Github Runners to macos 15.5 and Xcode 16.4 * Remove Travis --- .github/workflows/Pod Lint.yml | 12 ++++-- .github/workflows/Project Testing.yml | 12 +++--- .github/workflows/SPM Testing.yml | 6 ++- .travis.yml | 54 --------------------------- Swinject.podspec | 4 +- 5 files changed, 20 insertions(+), 68 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/Pod Lint.yml b/.github/workflows/Pod Lint.yml index 3c936c98..227fba54 100644 --- a/.github/workflows/Pod Lint.yml +++ b/.github/workflows/Pod Lint.yml @@ -6,14 +6,18 @@ on: branches: [ "master" ] pull_request: branches: [ "master" ] -env: - ENCODING: LC_CTYPE=en_US.UTF-8 jobs: pod-lint: - runs-on: macos-latest + runs-on: macos-15 + env: + ENCODING: LC_CTYPE=en_US.UTF-8 + DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Inspect Environment + run: xcrun simctl list + shell: bash - name: Linting PodSpec for Cocoapods - run: export "${{ env. ENCODING }}" | pod lib lint + run: export "${{ env. ENCODING }}" | pod lib lint --verbose shell: bash diff --git a/.github/workflows/Project Testing.yml b/.github/workflows/Project Testing.yml index acf981c8..09f89bc4 100644 --- a/.github/workflows/Project Testing.yml +++ b/.github/workflows/Project Testing.yml @@ -5,33 +5,33 @@ on: workflow_dispatch: env: PROJECT: Swinject.xcodeproj - DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer jobs: test: - runs-on: macos-10.15 + runs-on: macos-15 strategy: matrix: include: - platform-version: iOS - DEST: "OS=14.4,name=iPhone 8" + DEST: "OS=18.4,name=iPhone 16" SCHEME: "Swinject-iOS" SDK: "iphonesimulator" ACTION: "test" PLATFORM: "iOS" - platform-version: macOS - DEST: "arch=x86_64" + DEST: "arch=arm64" SCHEME: "Swinject-macOS" SDK: "macosx" ACTION: "test" PLATFORM: "OSX" - platform-version: tvOS - DEST: "OS=14.3,name=Apple TV 4K" + DEST: "OS=18.4,name=Apple TV 4K" SCHEME: "Swinject-tvOS" SDK: "appletvsimulator" ACTION: "test" PLATFORM: "tvOS" - platform-version: watchOS - DEST: "OS=7.2,name=Apple Watch Series 6 - 44mm" + DEST: "OS=11.4,name=Apple Watch Series 10 - 46mm" SCHEME: "Swinject-watchOS" SDK: "watchsimulator" ACTION: "build" diff --git a/.github/workflows/SPM Testing.yml b/.github/workflows/SPM Testing.yml index 58f1c439..e8cf4ac3 100644 --- a/.github/workflows/SPM Testing.yml +++ b/.github/workflows/SPM Testing.yml @@ -10,12 +10,14 @@ on: jobs: spm-check: - runs-on: macos-latest + env: + DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer + runs-on: macos-15 steps: - uses: actions/checkout@v4 - uses: swift-actions/setup-swift@v2 with: - swift-version: "5.9" + swift-version: "6.0.3" - name: Build run: swift build -v - name: Run tests diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1a3e6f73..00000000 --- a/.travis.yml +++ /dev/null @@ -1,54 +0,0 @@ -language: objective-c -env: - global: - - LC_CTYPE=en_US.UTF-8 - - PROJECT=Swinject.xcodeproj -git: - submodules: false -addons: - homebrew: - packages: - - xcodegen -matrix: - include: - - env: JOB="LINUX_SPM" SWIFT_VERSION="5.2.2" - os: linux - language: generic - sudo: required - dist: bionic - before_install: - - eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" - before_script: - script: - - mv .Package.Test.swift Package.swift - - swift build - - swift test - - env: JOB="POD_LINT" - osx_image: xcode12.5 - before_install: - script: - - pod lib lint - - env: JOB="XCODE" DEST="OS=14.0.1,name=iPhone 8" SCHEME="Swinject-iOS" SDK="iphonesimulator" ACTION="test" PLATFORM="iOS" - osx_image: xcode12.5 - - env: JOB="XCODE" DEST="arch=x86_64" SCHEME="Swinject-macOS" SDK="macosx" ACTION="test" PLATFORM="OSX" - osx_image: xcode12.5 - - env: JOB="XCODE" DEST="OS=14.0,name=Apple TV 4K" SCHEME="Swinject-tvOS" SDK="appletvsimulator" ACTION="test" PLATFORM="tvOS" - osx_image: xcode12.5 - - env: JOB="XCODE" DEST="OS=7.0,name=Apple Watch Series 6 - 44mm" SCHEME="Swinject-watchOS" SDK="watchsimulator" ACTION="build" PLATFORM="watchOS" - osx_image: xcode12.5 - -before_install: - - xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX) - - trap 'rm -f "$xcconfig"' INT TERM HUP EXIT - - echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig - - echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig - - export XCODE_XCCONFIG_FILE="$xcconfig" -before_script: - - rm -r Swinject.xcodeproj - - xcodegen generate -script: - - set -o pipefail - - xcodebuild "$ACTION" -project "$PROJECT" -scheme "$SCHEME" -sdk "$SDK" -destination "$DEST" -configuration Release ENABLE_TESTABILITY=YES | xcpretty -notifications: - email: - on_success: never diff --git a/Swinject.podspec b/Swinject.podspec index e4124509..4ab64f65 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -13,8 +13,8 @@ Pod::Spec.new do |s| s.swift_version = '5.0' s.source_files = 'Sources/**/*.swift' - s.ios.deployment_target = '11.0' + s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' s.watchos.deployment_target = '4.0' - s.tvos.deployment_target = '11.0' + s.tvos.deployment_target = '12.0' end From d5add673f76009d94b99fd173ee4d4abde1a126a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Mar=C3=A7al?= <127062836+0xLucasMarcal@users.noreply.github.com> Date: Sat, 16 Aug 2025 14:02:49 -0300 Subject: [PATCH 30/41] Delete Swinject Dynamic target (#583) --- Package.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Package.swift b/Package.swift index 126af175..16b6d791 100644 --- a/Package.swift +++ b/Package.swift @@ -14,9 +14,6 @@ let package = Package( products: [ .library(name: "Swinject", targets: ["Swinject"]), - .library(name: "Swinject-Dynamic", - type: .dynamic, - targets: ["Swinject"]), ], targets: [ .target(name: "Swinject", From 91ac1af3c07f9a3fba20f8467c654e9c835755cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Mar=C3=A7al?= <127062836+0xLucasMarcal@users.noreply.github.com> Date: Sat, 16 Aug 2025 15:45:52 -0300 Subject: [PATCH 31/41] Enable Swift 6 language mode (#582) --- .swiftformat | 2 +- Package.swift | 2 +- Sources/Container.Logging.swift | 16 ++--- Sources/Container.swift | 9 ++- Sources/ObjectScope.Standard.swift | 22 ------- Sources/ObjectScope.swift | 26 +++++++-- Sources/ServiceKey.swift | 4 +- Sources/ThreadSafeDictionary.swift | 10 ++++ Swinject.xcodeproj/project.pbxproj | 58 ++++++++++++------- .../xcschemes/Swinject-macOS.xcscheme | 25 +++----- .../SwinjectTests/ContainerTests.Speed.swift | 2 +- Tests/SwinjectTests/SynchronizedTests.swift | 36 ++++++++++++ 12 files changed, 130 insertions(+), 82 deletions(-) delete mode 100644 Sources/ObjectScope.Standard.swift diff --git a/.swiftformat b/.swiftformat index 764587c3..590eae76 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,4 +1,4 @@ ---swiftversion 5.0 +--swiftversion 6.0 --exclude .Package.Test.swift --exclude Package.swift --header "\n Copyright © {created.year} Swinject Contributors. All rights reserved.\n" diff --git a/Package.swift b/Package.swift index 16b6d791..abc6084e 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0.0 import PackageDescription diff --git a/Sources/Container.Logging.swift b/Sources/Container.Logging.swift index 1ae32b29..e4c163cd 100644 --- a/Sources/Container.Logging.swift +++ b/Sources/Container.Logging.swift @@ -2,19 +2,11 @@ // Copyright © 2019 Swinject Contributors. All rights reserved. // -public typealias LoggingFunctionType = (String) -> Void - +import Foundation public extension Container { - /// Function to be used for logging debugging data. - /// Default implementation writes to standard output. - static var loggingFunction: LoggingFunctionType? { - get { return _loggingFunction } - set { _loggingFunction = newValue } - } - internal static func log(_ message: String) { - _loggingFunction?(message) + if ProcessInfo.processInfo.environment["SWINJECT_LOGGING_ENABLED"] == "1" { + print(message) + } } } - -private var _loggingFunction: LoggingFunctionType? = { print($0) } diff --git a/Sources/Container.swift b/Sources/Container.swift index 11c34847..3ba4b947 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -20,7 +20,7 @@ import Foundation /// where `A` and `X` are protocols, `B` is a type conforming `A`, and `Y` is a type conforming `X` /// and depending on `A`. public final class Container { - internal var services = [ServiceKey: ServiceEntryProtocol]() + internal var services = ThreadSafeDictionary() private let parent: Container? // Used by HierarchyObjectScope private var resolutionDepth = 0 private let debugHelper: DebugHelper @@ -297,7 +297,7 @@ extension Container: _Resolver { fileprivate func getRegistrations() -> [ServiceKey: ServiceEntryProtocol] { var registrations = parent?.getRegistrations() ?? [:] - services.forEach { key, value in registrations[key] = value } + services.forEachRead { key, value in registrations[key] = value } return registrations } @@ -424,5 +424,8 @@ extension Container: CustomStringConvertible { // MARK: Constants private extension Container { - static let graphIdentifierKey = ServiceKey(serviceType: GraphIdentifier.self, argumentsType: Resolver.self) + static let graphIdentifierKey: ServiceKey = { + let key = ServiceKey(serviceType: GraphIdentifier.self, argumentsType: Resolver.self) + return key + }() } diff --git a/Sources/ObjectScope.Standard.swift b/Sources/ObjectScope.Standard.swift deleted file mode 100644 index 5ffcb6fc..00000000 --- a/Sources/ObjectScope.Standard.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright © 2019 Swinject Contributors. All rights reserved. -// - -extension ObjectScope { - /// A new instance is always created by the ``Container`` when a type is resolved. - /// The instance is not shared. - public static let transient = ObjectScope(storageFactory: TransientStorage.init, description: "transient") - - /// Instances are shared only when an object graph is being created, - /// otherwise a new instance is created by the ``Container``. This is the default scope. - public static let graph = ObjectScope(storageFactory: GraphStorage.init, description: "graph") - - /// An instance provided by the ``Container`` is shared within the ``Container`` and its child `Containers`. - public static let container = ObjectScope(storageFactory: PermanentStorage.init, description: "container") - - /// An instance provided by the ``Container`` is shared within the ``Container`` and its child ``Container``s - /// as long as there are strong references to given instance. Otherwise new instance is created - /// when resolving the type. - public static let weak = ObjectScope(storageFactory: WeakStorage.init, description: "weak", - parent: ObjectScope.graph) -} diff --git a/Sources/ObjectScope.swift b/Sources/ObjectScope.swift index d8331ac6..c73ea51f 100644 --- a/Sources/ObjectScope.swift +++ b/Sources/ObjectScope.swift @@ -11,9 +11,9 @@ public protocol ObjectScopeProtocol: AnyObject { } /// Basic implementation of ``ObjectScopeProtocol``. -public class ObjectScope: ObjectScopeProtocol, CustomStringConvertible { - public private(set) var description: String - private var storageFactory: () -> InstanceStorage +public final class ObjectScope: ObjectScopeProtocol, CustomStringConvertible, @unchecked Sendable { + public let description: String + private let storageFactory: () -> InstanceStorage private let parent: ObjectScopeProtocol? /// Instantiates an ``ObjectScope`` with storage factory and description. @@ -31,7 +31,7 @@ public class ObjectScope: ObjectScopeProtocol, CustomStringConvertible { self.parent = parent } - /// Will invoke and return the result of `storageFactory` closure provided during initialisation. + /// Will invoke and return the result of `storageFactory` closure provided during initialization. public func makeStorage() -> InstanceStorage { if let parent = parent { return CompositeStorage([storageFactory(), parent.makeStorage()]) @@ -39,4 +39,22 @@ public class ObjectScope: ObjectScopeProtocol, CustomStringConvertible { return storageFactory() } } + + /// A new instance is always created by the ``Container`` when a type is resolved. + /// The instance is not shared. + public static let transient = ObjectScope(storageFactory: TransientStorage.init, description: "transient") + + /// Instances are shared only when an object graph is being created, + /// otherwise a new instance is created by the ``Container``. This is the default scope. + public static let graph = ObjectScope(storageFactory: GraphStorage.init, description: "graph") + + /// An instance provided by the ``Container`` is shared within the ``Container`` and its child `Containers`. + public static let container = ObjectScope(storageFactory: PermanentStorage.init, description: "container") + + /// An instance provided by the ``Container`` is shared within the ``Container`` and its child ``Container``s + /// as long as there are strong references to given instance. Otherwise new instance is created + /// when resolving the type. + public static let weak = ObjectScope(storageFactory: WeakStorage.init, description: "weak", + parent: ObjectScope.graph) + } diff --git a/Sources/ServiceKey.swift b/Sources/ServiceKey.swift index 87343c3b..8d75acdd 100644 --- a/Sources/ServiceKey.swift +++ b/Sources/ServiceKey.swift @@ -6,14 +6,14 @@ import Foundation // MARK: ServiceKeyOption -public protocol ServiceKeyOption: CustomStringConvertible { +public protocol ServiceKeyOption: CustomStringConvertible, Sendable { func isEqualTo(_ another: ServiceKeyOption) -> Bool func hash(into: inout Hasher) } // MARK: - ServiceKey -internal struct ServiceKey { +internal struct ServiceKey: Sendable { internal let serviceType: Any.Type internal let argumentsType: Any.Type internal let name: String? diff --git a/Sources/ThreadSafeDictionary.swift b/Sources/ThreadSafeDictionary.swift index bc9e6bec..0194776b 100644 --- a/Sources/ThreadSafeDictionary.swift +++ b/Sources/ThreadSafeDictionary.swift @@ -67,4 +67,14 @@ internal final class ThreadSafeDictionary { self.internalDictionary.removeAll() } } + + public func contains(where predicate: ((key: KeyType, value: ValueType)) -> Bool) -> Bool { + lock.read { + self.internalDictionary.contains(where: predicate) + } + } + + public var values: [ValueType] { + lock.read { self.internalDictionary.values.map { $0 } } + } } diff --git a/Swinject.xcodeproj/project.pbxproj b/Swinject.xcodeproj/project.pbxproj index 35f6162d..9b5bf10b 100644 --- a/Swinject.xcodeproj/project.pbxproj +++ b/Swinject.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 0126F47EDE97A611D184682F /* DebugHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE752946EBDAC1CB5823A39E /* DebugHelper.swift */; }; 026337CCA37580CB223C94AD /* ContainerTests.TypeForwarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDE1F7FCBF0C61DE0BA3ED2 /* ContainerTests.TypeForwarding.swift */; }; 0266A3114C82246F18B52860 /* Food.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1271722CF77709A1173E7863 /* Food.swift */; }; - 055969F9EA3C1AD43B11E006 /* ObjectScope.Standard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79864FE285C06A91E006806 /* ObjectScope.Standard.swift */; }; 06B6204600E208CA97750825 /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97ECAF79D15CCE41C49F4590 /* RecursiveLock.swift */; }; 097ACF95FFBE85F427E287AA /* WeakStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 185C8B2BAF1CF29EB64159FC /* WeakStorageTests.swift */; }; 0C56E93B497BBA17B0E408F4 /* GraphIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B058F48AF289C3AC307C2F7 /* GraphIdentifier.swift */; }; @@ -36,6 +35,18 @@ 1C084C2AF7B98A21D8B1E35B /* ContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19726C4D2E8898E5C05A5E38 /* ContainerTests.swift */; }; 1C1C0CB3F19349057B4FBDE1 /* BasicAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC59458E04C20587D870946 /* BasicAssembly.swift */; }; 1DB36B1F8919879607F67F35 /* Container.Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38F57E5E5BE1DC65C3DDAF6 /* Container.Arguments.swift */; }; + 1E7F2E582DDA34A20046B399 /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E562DDA34A20046B399 /* RecursiveLock.swift */; }; + 1E7F2E592DDA34A20046B399 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E572DDA34A20046B399 /* ThreadSafeDictionary.swift */; }; + 1E7F2E5A2DDA34A20046B399 /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E562DDA34A20046B399 /* RecursiveLock.swift */; }; + 1E7F2E5B2DDA34A20046B399 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E572DDA34A20046B399 /* ThreadSafeDictionary.swift */; }; + 1E7F2E5C2DDA34A20046B399 /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E562DDA34A20046B399 /* RecursiveLock.swift */; }; + 1E7F2E5D2DDA34A20046B399 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E572DDA34A20046B399 /* ThreadSafeDictionary.swift */; }; + 1E7F2E5E2DDA34A20046B399 /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E562DDA34A20046B399 /* RecursiveLock.swift */; }; + 1E7F2E5F2DDA34A20046B399 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E572DDA34A20046B399 /* ThreadSafeDictionary.swift */; }; + 1E7F2E612DDA351D0046B399 /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E602DDA351D0046B399 /* ReadWriteLock.swift */; }; + 1E7F2E622DDA351D0046B399 /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E602DDA351D0046B399 /* ReadWriteLock.swift */; }; + 1E7F2E632DDA351D0046B399 /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E602DDA351D0046B399 /* ReadWriteLock.swift */; }; + 1E7F2E642DDA351D0046B399 /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7F2E602DDA351D0046B399 /* ReadWriteLock.swift */; }; 22873F55E9E82B96A53045C3 /* ContainerTests.CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FCE820EE05C206E34CEF068 /* ContainerTests.CustomStringConvertible.swift */; }; 235DD35BC7B8CE15BA46A677 /* BehaviorFakes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEF07245CC462D98868097A3 /* BehaviorFakes.swift */; }; 23A8D7543ED9973F04C5BC93 /* FunctionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F388AD8E55D8C6F1E31D0522 /* FunctionType.swift */; }; @@ -85,10 +96,6 @@ 516EB7F92BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */; }; 516EB7FA2BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */; }; 516EB7FB2BDC124100FF78D2 /* ContainerTests.Speed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */; }; - 51F2541C2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; - 51F2541D2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; - 51F2541E2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; - 51F2541F2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */; }; 51F254202BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */; }; 51F254212BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */; }; 51F254222BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */; }; @@ -172,7 +179,6 @@ BAB510EEEBDD444BA65E0003 /* LazyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7287A8057A58B72EBC5C8CB9 /* LazyTests.swift */; }; BB78B5DA95DCE3A942C18910 /* InstanceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7101AB0DE88A5340F7EAAC04 /* InstanceWrapper.swift */; }; BCDBFF75F368C980B6DA4BFE /* ContainerTests.Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988A20A0815159E43C26CA46 /* ContainerTests.Behavior.swift */; }; - BDCBFD62903A1C0F8B35797C /* ObjectScope.Standard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79864FE285C06A91E006806 /* ObjectScope.Standard.swift */; }; BE3889327CE4A24631B91350 /* AssemblerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29EB588ED052D3BAF38AFBBE /* AssemblerTests.swift */; }; BE3A8FAC5AC745EDCCD5A5FB /* Swinject.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6D54F152C11C966ECFC55ECB /* Swinject.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BF6475BEA46F81B30A6FA19E /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E48E07BA72BCBFB305E93 /* Container.swift */; }; @@ -207,11 +213,9 @@ F945C7963B1566A1C3DAC3FA /* SynchronizedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6E0A3F26E3718902E420D8E /* SynchronizedTests.swift */; }; F98A1B2A226EE26A13C67F34 /* FunctionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F388AD8E55D8C6F1E31D0522 /* FunctionType.swift */; }; FA6DD52FA4D4BB157A43CBEE /* _Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C304EFDAFB81043B2896E5 /* _Resolver.swift */; }; - FA89EFC16AEAC2EC333CEA81 /* ObjectScope.Standard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79864FE285C06A91E006806 /* ObjectScope.Standard.swift */; }; FCC32CC413C08CF94D7F661E /* Person.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B00D2ED19209D0C6210169 /* Person.swift */; }; FCF3A63712ECD9E5BE371DC7 /* SynchronizedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6E0A3F26E3718902E420D8E /* SynchronizedTests.swift */; }; FE07C0CCCD45639FBF85A456 /* Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D8BA4B04A5F517AF663DCE /* Behavior.swift */; }; - FEAF6DE7BBDD922B61E0ED20 /* ObjectScope.Standard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79864FE285C06A91E006806 /* ObjectScope.Standard.swift */; }; FED7D8B8C74876D03B5D09DE /* Assembler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 944B981A9157833AC3397B4A /* Assembler.swift */; }; FF222640214C9F4DA56130C4 /* InstanceStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B7688D6F1E3466891D0582E /* InstanceStorage.swift */; }; FF9DCB0AB20B0DBBE79451BB /* BehaviorFakes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEF07245CC462D98868097A3 /* BehaviorFakes.swift */; }; @@ -306,6 +310,9 @@ 185C8B2BAF1CF29EB64159FC /* WeakStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakStorageTests.swift; sourceTree = ""; }; 19726C4D2E8898E5C05A5E38 /* ContainerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.swift; sourceTree = ""; }; 1C7608AD07FC553DAC2911FB /* ContainerTests.CustomScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.CustomScope.swift; sourceTree = ""; }; + 1E7F2E562DDA34A20046B399 /* RecursiveLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveLock.swift; sourceTree = ""; }; + 1E7F2E572DDA34A20046B399 /* ThreadSafeDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadSafeDictionary.swift; sourceTree = ""; }; + 1E7F2E602DDA351D0046B399 /* ReadWriteLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = ""; }; 2380F40FC2B9229F36B13679 /* LoadAwareAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadAwareAssembly.swift; sourceTree = ""; }; 28B6980B5294739C0656769C /* ServiceEntryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceEntryTests.swift; sourceTree = ""; }; 29EB588ED052D3BAF38AFBBE /* AssemblerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssemblerTests.swift; sourceTree = ""; }; @@ -317,7 +324,6 @@ 4744C3ABBA9A8A585C726883 /* ContainerTests.DebugHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.DebugHelper.swift; sourceTree = ""; }; 515B0F492BC9AF5D00C4E24F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 516EB7F72BDC124100FF78D2 /* ContainerTests.Speed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.Speed.swift; sourceTree = ""; }; - 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = ""; }; 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeDictionary.swift; sourceTree = ""; }; 54FFC812F14E9976208B644F /* ServiceEntry.TypeForwarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceEntry.TypeForwarding.swift; sourceTree = ""; }; 5922B783F35F436C528594C1 /* Assembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assembly.swift; sourceTree = ""; }; @@ -339,7 +345,6 @@ 9B058F48AF289C3AC307C2F7 /* GraphIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphIdentifier.swift; sourceTree = ""; }; A20C1319AF31EC7F8E6945FE /* SwinjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwinjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A28176E7C90C61FB8131FE06 /* ProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderTests.swift; sourceTree = ""; }; - A79864FE285C06A91E006806 /* ObjectScope.Standard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectScope.Standard.swift; sourceTree = ""; }; A982F3108666D12EA0810D52 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; AEAC51C6D09DEBE863D5A06E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; B0F839BCDE52371FE49454B9 /* Container.Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.Logging.swift; sourceTree = ""; }; @@ -472,7 +477,6 @@ isa = PBXGroup; children = ( 515B0F492BC9AF5D00C4E24F /* PrivacyInfo.xcprivacy */, - 51F2541A2BC5A2EF003E3BCD /* ReadWriteLock.swift */, 51F2541B2BC5A2EF003E3BCD /* ThreadSafeDictionary.swift */, 15C304EFDAFB81043B2896E5 /* _Resolver.swift */, 944B981A9157833AC3397B4A /* Assembler.swift */, @@ -483,12 +487,14 @@ 172E48E07BA72BCBFB305E93 /* Container.swift */, 7605BDEF71547D0B17D7195A /* Container.TypeForwarding.swift */, DE752946EBDAC1CB5823A39E /* DebugHelper.swift */, + 1E7F2E562DDA34A20046B399 /* RecursiveLock.swift */, + 1E7F2E572DDA34A20046B399 /* ThreadSafeDictionary.swift */, + 1E7F2E602DDA351D0046B399 /* ReadWriteLock.swift */, F388AD8E55D8C6F1E31D0522 /* FunctionType.swift */, 9B058F48AF289C3AC307C2F7 /* GraphIdentifier.swift */, 5F7BC1C4169BB4382C6D3E1F /* Info.plist */, 3B7688D6F1E3466891D0582E /* InstanceStorage.swift */, 7101AB0DE88A5340F7EAAC04 /* InstanceWrapper.swift */, - A79864FE285C06A91E006806 /* ObjectScope.Standard.swift */, 957AFDACC07687608E5E5F7E /* ObjectScope.swift */, 97ECAF79D15CCE41C49F4590 /* RecursiveLock.swift */, EC379E27E9804FC6C0BA0842 /* Resolver.swift */, @@ -826,9 +832,12 @@ buildActionMask = 2147483647; files = ( 602201FE3E6CC64F594DEBA1 /* Assembler.swift in Sources */, + 1E7F2E5A2DDA34A20046B399 /* RecursiveLock.swift in Sources */, + 1E7F2E5B2DDA34A20046B399 /* ThreadSafeDictionary.swift in Sources */, A5D9F0A6D8FC89662658FBF3 /* Assembly.swift in Sources */, FE07C0CCCD45639FBF85A456 /* Behavior.swift in Sources */, 9FF7BABC0BA21A89A755E37C /* Container.Arguments.swift in Sources */, + 1E7F2E632DDA351D0046B399 /* ReadWriteLock.swift in Sources */, 363C4230CAC4547E7A676249 /* Container.Logging.swift in Sources */, 1B36EE582AD6251A33C956F2 /* Container.TypeForwarding.swift in Sources */, 4A1AEFDC62BC395924AB2355 /* Container.swift in Sources */, @@ -837,11 +846,9 @@ 706A459CAE24E0D5054CEC30 /* GraphIdentifier.swift in Sources */, B55E915211F19C91C622E068 /* InstanceStorage.swift in Sources */, BB78B5DA95DCE3A942C18910 /* InstanceWrapper.swift in Sources */, - 055969F9EA3C1AD43B11E006 /* ObjectScope.Standard.swift in Sources */, 62A1539A3B07EE3E1B72CF95 /* ObjectScope.swift in Sources */, 06B6204600E208CA97750825 /* RecursiveLock.swift in Sources */, 5BB768EFEB4BA7D1D05B4DCC /* Resolver.swift in Sources */, - 51F2541D2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */, 3D86E87634E427DCE7A8CF81 /* ServiceEntry.TypeForwarding.swift in Sources */, 513BDFB32DCE5B048672C33D /* ServiceEntry.swift in Sources */, 51F254212BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */, @@ -988,9 +995,12 @@ buildActionMask = 2147483647; files = ( 474A4D73269D7BDF0844D704 /* Assembler.swift in Sources */, + 1E7F2E582DDA34A20046B399 /* RecursiveLock.swift in Sources */, + 1E7F2E592DDA34A20046B399 /* ThreadSafeDictionary.swift in Sources */, 81AE1BCE31BC99440151A68F /* Assembly.swift in Sources */, 2C838FEC75D618B575750A17 /* Behavior.swift in Sources */, 1DB36B1F8919879607F67F35 /* Container.Arguments.swift in Sources */, + 1E7F2E642DDA351D0046B399 /* ReadWriteLock.swift in Sources */, 9D78DAAC0307D9422C501D33 /* Container.Logging.swift in Sources */, 5488A7839EF00BAC43CE2CEE /* Container.TypeForwarding.swift in Sources */, D56BD23EC91E28CE6C6D6E78 /* Container.swift in Sources */, @@ -999,11 +1009,9 @@ 59AB5E84FD9B11B7D1CA2401 /* GraphIdentifier.swift in Sources */, FF222640214C9F4DA56130C4 /* InstanceStorage.swift in Sources */, 7000D01DCB05BF57BC89BCF8 /* InstanceWrapper.swift in Sources */, - FEAF6DE7BBDD922B61E0ED20 /* ObjectScope.Standard.swift in Sources */, B9566410214991FBA727BB37 /* ObjectScope.swift in Sources */, C7A673429D2B735BAC18F58E /* RecursiveLock.swift in Sources */, EE4786524E79B10DB36FDB11 /* Resolver.swift in Sources */, - 51F2541E2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */, 54A3ED20B6A9506B9D514098 /* ServiceEntry.TypeForwarding.swift in Sources */, 1A2759577A52C96E28BB44F0 /* ServiceEntry.swift in Sources */, 51F254222BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */, @@ -1018,9 +1026,12 @@ buildActionMask = 2147483647; files = ( 8F8BF2FFD2D666C4EE71E796 /* Assembler.swift in Sources */, + 1E7F2E5E2DDA34A20046B399 /* RecursiveLock.swift in Sources */, + 1E7F2E5F2DDA34A20046B399 /* ThreadSafeDictionary.swift in Sources */, 695E6FF61B8CD35434685835 /* Assembly.swift in Sources */, 4D667425CB08BF894C1A78AA /* Behavior.swift in Sources */, 5BAC0A607F822D6205DA6178 /* Container.Arguments.swift in Sources */, + 1E7F2E622DDA351D0046B399 /* ReadWriteLock.swift in Sources */, 40FCA2AB08F4EA23096075AB /* Container.Logging.swift in Sources */, 1AF1BB032596F2ADF9021F90 /* Container.TypeForwarding.swift in Sources */, 52E4C2C279904E90D5F37204 /* Container.swift in Sources */, @@ -1029,11 +1040,9 @@ CC64B74A022612990F57D51D /* GraphIdentifier.swift in Sources */, D6B0E0FA93AD8D47AA07AE03 /* InstanceStorage.swift in Sources */, 6A352D6A2C12D7BDABC38645 /* InstanceWrapper.swift in Sources */, - BDCBFD62903A1C0F8B35797C /* ObjectScope.Standard.swift in Sources */, 130DDD2347169F885F420989 /* ObjectScope.swift in Sources */, 48B5EC6BADDE7B88260ADE44 /* RecursiveLock.swift in Sources */, 9CD3F07823D2E64DD636E00A /* Resolver.swift in Sources */, - 51F2541F2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */, 1845CF466289264A5BBC0CCE /* ServiceEntry.TypeForwarding.swift in Sources */, 35C9BCE88B6D6567760D7389 /* ServiceEntry.swift in Sources */, 51F254232BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */, @@ -1048,9 +1057,12 @@ buildActionMask = 2147483647; files = ( FED7D8B8C74876D03B5D09DE /* Assembler.swift in Sources */, + 1E7F2E5C2DDA34A20046B399 /* RecursiveLock.swift in Sources */, + 1E7F2E5D2DDA34A20046B399 /* ThreadSafeDictionary.swift in Sources */, 5010597DD8C25E6684C8087E /* Assembly.swift in Sources */, C60108843AA83DF2B408EE87 /* Behavior.swift in Sources */, AD91C0A080A7CE74369407E7 /* Container.Arguments.swift in Sources */, + 1E7F2E612DDA351D0046B399 /* ReadWriteLock.swift in Sources */, 77E67A2C566A1DFD5407BBA4 /* Container.Logging.swift in Sources */, 0F6B596CC2F819EF9CF5FE75 /* Container.TypeForwarding.swift in Sources */, BF6475BEA46F81B30A6FA19E /* Container.swift in Sources */, @@ -1059,11 +1071,9 @@ 0C56E93B497BBA17B0E408F4 /* GraphIdentifier.swift in Sources */, AB05847EAB8D85A8679B5EC5 /* InstanceStorage.swift in Sources */, 61357D47A1C4870FAA34377C /* InstanceWrapper.swift in Sources */, - FA89EFC16AEAC2EC333CEA81 /* ObjectScope.Standard.swift in Sources */, 40DCC9D2F3ABC2B36C9DC8AE /* ObjectScope.swift in Sources */, 8B9E356593059568E55F9B06 /* RecursiveLock.swift in Sources */, 96BA7CCC6AEC192CAD3054E1 /* Resolver.swift in Sources */, - 51F2541C2BC5A2EF003E3BCD /* ReadWriteLock.swift in Sources */, 1120EBEC146D1EDDCC29AB17 /* ServiceEntry.TypeForwarding.swift in Sources */, 808983B2AF095E481615029E /* ServiceEntry.swift in Sources */, 51F254202BC5A2EF003E3BCD /* ThreadSafeDictionary.swift in Sources */, @@ -1199,6 +1209,7 @@ RUN_DOCUMENTATION_COMPILER = YES; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; VERSIONING_SYSTEM = "apple-generic"; }; @@ -1227,6 +1238,7 @@ RUN_DOCUMENTATION_COMPILER = YES; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -1270,6 +1282,7 @@ RUN_DOCUMENTATION_COMPILER = YES; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 3; VERSIONING_SYSTEM = "apple-generic"; }; @@ -1327,6 +1340,7 @@ RUN_DOCUMENTATION_COMPILER = YES; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 4; VERSIONING_SYSTEM = "apple-generic"; }; @@ -1412,6 +1426,7 @@ RUN_DOCUMENTATION_COMPILER = YES; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 3; VERSIONING_SYSTEM = "apple-generic"; }; @@ -1458,6 +1473,7 @@ RUN_DOCUMENTATION_COMPILER = YES; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -1538,6 +1554,7 @@ RUN_DOCUMENTATION_COMPILER = YES; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -1565,6 +1582,7 @@ RUN_DOCUMENTATION_COMPILER = YES; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; diff --git a/Swinject.xcodeproj/xcshareddata/xcschemes/Swinject-macOS.xcscheme b/Swinject.xcodeproj/xcshareddata/xcschemes/Swinject-macOS.xcscheme index a8a1a12f..3bcbe2da 100644 --- a/Swinject.xcodeproj/xcshareddata/xcschemes/Swinject-macOS.xcscheme +++ b/Swinject.xcodeproj/xcshareddata/xcschemes/Swinject-macOS.xcscheme @@ -26,8 +26,16 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - onlyGenerateCoverageForSpecifiedTargets = "NO" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + @@ -40,17 +48,6 @@ - - - - - - - - - - diff --git a/Tests/SwinjectTests/ContainerTests.Speed.swift b/Tests/SwinjectTests/ContainerTests.Speed.swift index f4af4566..8736a3bb 100644 --- a/Tests/SwinjectTests/ContainerTests.Speed.swift +++ b/Tests/SwinjectTests/ContainerTests.Speed.swift @@ -9,8 +9,8 @@ import XCTest /// this test, but good to run before/after changes to see changes. @available(iOS 13.0, *) class ContainerSpeedTests: XCTestCase { - var container: Container! + var container: Container! override func setUpWithError() throws { container = Container() diff --git a/Tests/SwinjectTests/SynchronizedTests.swift b/Tests/SwinjectTests/SynchronizedTests.swift index 1fafaa2e..b44201d9 100644 --- a/Tests/SwinjectTests/SynchronizedTests.swift +++ b/Tests/SwinjectTests/SynchronizedTests.swift @@ -218,6 +218,42 @@ class SynchronizedResolverTests: XCTestCase { XCTAssertIdentical(parent.child1, parent.child2) XCTAssertIdentical(parent.child1.lazy.instance, parent.child2.lazy.instance) } + + func test_guaranteeThreadSafety() { + let numberOfQueues = 25 + var expectations = [XCTestExpectation]() + let resolutionDate = Date() + 1 + + struct TestModel { + let name = "Testing" + } + + let container = Container() + + for _ in 0.. Date: Sat, 16 Aug 2025 11:53:14 -0700 Subject: [PATCH 32/41] Allow access to ServiceEntry properties (#575) --- Sources/Behavior.swift | 5 +++-- Sources/Container.Arguments.erb | 2 +- Sources/Container.Arguments.swift | 2 +- Sources/ServiceEntry.swift | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Sources/Behavior.swift b/Sources/Behavior.swift index 8f5db054..843bab8d 100644 --- a/Sources/Behavior.swift +++ b/Sources/Behavior.swift @@ -5,7 +5,7 @@ /// Protocol for adding functionality to the container public protocol Behavior { /// This will be invoked on each behavior added to the `container` for each `entry` added to the container using - /// one of the `register()` or type forwading methods + /// one of the `register()` or type forwarding methods /// /// - Parameters: /// - container: container into which an `entry` has been registered @@ -13,7 +13,8 @@ public protocol Behavior { /// - entry: ServiceEntry registered to the `container` /// - name: name under which the service has been registered to the `container` /// - /// - Remark: `Type` and `Service` can be different types in the case of type forwarding + /// - Remark: `Type` and `Service` can be different types in the case of type forwarding (commonly used as `.implements()`). + /// `Type` will represent the forwarded type key, and `Service` will represent the destination. func container( _ container: Container, didRegisterType type: Type.Type, diff --git a/Sources/Container.Arguments.erb b/Sources/Container.Arguments.erb index 4d32c739..74bc0bdb 100644 --- a/Sources/Container.Arguments.erb +++ b/Sources/Container.Arguments.erb @@ -18,7 +18,7 @@ import Foundation -// MARK: - Registeration with Arguments +// MARK: - Registration with Arguments extension Container { <% (1..arg_count).each do |i| %> <% arg_types = (1..i).map { |n| "Arg#{n}" }.join(", ") %> diff --git a/Sources/Container.Arguments.swift b/Sources/Container.Arguments.swift index c62e853a..6c5554f8 100644 --- a/Sources/Container.Arguments.swift +++ b/Sources/Container.Arguments.swift @@ -12,7 +12,7 @@ import Foundation -// MARK: - Registeration with Arguments +// MARK: - Registration with Arguments extension Container { /// Adds a registration for the specified service with the factory closure to specify how the service is resolved with dependencies. diff --git a/Sources/ServiceEntry.swift b/Sources/ServiceEntry.swift index 59914104..c21851f8 100644 --- a/Sources/ServiceEntry.swift +++ b/Sources/ServiceEntry.swift @@ -18,8 +18,8 @@ internal protocol ServiceEntryProtocol: AnyObject { /// As a returned instance from ``Container/register(_:name:factory:)-8gy9r``, some configurations can be added. public final class ServiceEntry: ServiceEntryProtocol { fileprivate var initCompletedActions: [(Resolver, Service) -> Void] = [] - internal let serviceType: Any.Type - internal let argumentsType: Any.Type + public let serviceType: Any.Type + public let argumentsType: Any.Type internal let factory: FunctionType internal weak var container: Container? From abfff6a7b7eb3e5cefb0d0bc1a44738fb340916f Mon Sep 17 00:00:00 2001 From: mehdi samadi Date: Sun, 17 Aug 2025 21:18:16 +0330 Subject: [PATCH 33/41] feat(container): add resetObjectScope(_:serviceType:) for finer cache control (#579) Enhances resetObjectScope to allow clearing cached instances for a specific service type instead of resetting all instances within the given scope. This provides more granular control over instance lifecycle management. Co-authored-by: mehdi --- Sources/Container.swift | 35 ++++++++++ .../xcschemes/Swinject-iOS.xcscheme | 25 +++---- .../ContainerTests.CustomScope.swift | 69 ++++++++++++++++++- 3 files changed, 112 insertions(+), 17 deletions(-) diff --git a/Sources/Container.swift b/Sources/Container.swift index 3ba4b947..1e63df9a 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -97,7 +97,42 @@ public final class Container { public func resetObjectScope(_ objectScope: ObjectScope) { resetObjectScope(objectScope as ObjectScopeProtocol) } + /// Discards instances for a given service registered in the given `ObjectsScopeProtocol`. + /// + /// **Example usage:** + /// container.resetObjectScope(ObjectScope.container, MyService.Type) + /// + /// - Parameters: + /// - objectScope: instances registered in given `ObjectsScopeProtocol` + /// - serviceType: and with given serviceType will be discarded. + public func resetObjectScope(_ objectScope: ObjectScopeProtocol, serviceType: Any.Type) { + syncIfEnabled { + let matchingServices = services.keys + .filter { $0.serviceType == serviceType } + .compactMap { services[$0] } + + if matchingServices.isEmpty { return } + + matchingServices + .filter { $0.objectScope === objectScope } + .forEach { $0.storage.instance = nil } + parent?.resetObjectScope(objectScope, serviceType: serviceType) + } + } + /// Discards instances for a given service registered in the given `ObjectsScope`. It performs the same operation + /// as `resetObjectScope(_:ObjectScopeProtocol, serviceType: Any.Type)`, + /// but provides more convenient usage syntax. + /// + /// **Example usage:** + /// container.resetObjectScope(.container, servieType: MyService.self) + /// + /// - Parameters: + /// - objectScope: Instances registered in given `ObjectsScope` + /// - serviceType: and with given serviceType will be discarded. + public func resetObjectScope(_ objectScope: ObjectScope, serviceType: Any.Type) { + resetObjectScope(objectScope as ObjectScopeProtocol, serviceType: serviceType) + } /// Adds a registration for the specified service with the factory closure to specify how the service is /// resolved with dependencies. /// diff --git a/Swinject.xcodeproj/xcshareddata/xcschemes/Swinject-iOS.xcscheme b/Swinject.xcodeproj/xcshareddata/xcschemes/Swinject-iOS.xcscheme index 86b638de..84e8918a 100644 --- a/Swinject.xcodeproj/xcshareddata/xcschemes/Swinject-iOS.xcscheme +++ b/Swinject.xcodeproj/xcshareddata/xcschemes/Swinject-iOS.xcscheme @@ -26,8 +26,16 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - onlyGenerateCoverageForSpecifiedTargets = "NO" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + @@ -40,17 +48,6 @@ - - - - - - - - - - diff --git a/Tests/SwinjectTests/ContainerTests.CustomScope.swift b/Tests/SwinjectTests/ContainerTests.CustomScope.swift index 77285529..3e3ab92f 100644 --- a/Tests/SwinjectTests/ContainerTests.CustomScope.swift +++ b/Tests/SwinjectTests/ContainerTests.CustomScope.swift @@ -83,8 +83,75 @@ class ContainerTests_CustomScope: XCTestCase { XCTAssertNil(storage.instance) } -} + // MARK: Reseting scope with given Service Type + + func testResetObjectScopeRemovesOnlyMatchingScopeInstances() { + let scope = ObjectScope.container + + container.register(Foo.self) { _ in Foo() }.inObjectScope(scope) + let instance = container.resolve(Foo.self) + + container.resetObjectScope(scope, serviceType: Foo.self) + let instanceAfterReset = container.resolve(Foo.self) + + XCTAssertTrue(instance !== instanceAfterReset) + } + func testResetObjectScopeDoesNotRemoveInstancesOfDifferentScopes() { + let containerScope = ObjectScope.container + let graphScope = ObjectScope.graph + + container.register(Foo.self, name: "containerScoped") { _ in Foo() }.inObjectScope(containerScope) + container.register(Foo.self, name: "graphScoped") { _ in Foo() }.inObjectScope(graphScope) + + let containerInstance = container.resolve(Foo.self, name: "containerScoped") + let graphInstance = container.resolve(Foo.self, name: "graphScoped") + + XCTAssertNotNil(graphInstance) + + container.resetObjectScope(graphScope, serviceType: Foo.self) + let containerInstanceAfterReset = container.resolve(Foo.self, name: "containerScoped") // container-scoped + + XCTAssertTrue(containerInstance === containerInstanceAfterReset) + } + func testResetObjectScopeDoesNotRemoveInstancesOfDifferentTypes() { + let scope = ObjectScope.container + + container.register(Foo.self) { _ in Foo() }.inObjectScope(scope) + container.register(Bar.self) { _ in Bar() }.inObjectScope(scope) + + let fooInstance = container.resolve(Foo.self) + let barInstance = container.resolve(Bar.self) + + container.resetObjectScope(scope, serviceType: Foo.self) + let newFooInstance = container.resolve(Foo.self) + let newBarInstance = container.resolve(Bar.self) + + XCTAssertTrue(fooInstance !== newFooInstance) + XCTAssertTrue(barInstance === newBarInstance) + } + func testResetObjectScopeCallsParentContainer() { + let parentContainer = Container() + let childContainer = Container(parent: parentContainer) + + let scope = ObjectScope.container + + parentContainer.register(Foo.self) { _ in Foo() }.inObjectScope(scope) + childContainer.register(Foo.self) { _ in Foo() }.inObjectScope(scope) + + let parentInstance = parentContainer.resolve(Foo.self) + let childInstance = childContainer.resolve(Foo.self) + + childContainer.resetObjectScope(scope, serviceType: Foo.self) + let newParentInstance = parentContainer.resolve(Foo.self) + let newChildInstance = childContainer.resolve(Foo.self) + + XCTAssertFalse(childInstance === newChildInstance) + XCTAssertFalse(parentInstance === newParentInstance) + } +} +private class Foo {} +private class Bar {} private class FakeStorage: InstanceStorage { var instance: Any? } From ea9bffe87f803ff14a3f7b0f706b7a05681525cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Marc=CC=A7al?= Date: Sat, 23 Aug 2025 15:28:45 -0300 Subject: [PATCH 34/41] add missing property to ThreadSafeDictionary --- Sources/ThreadSafeDictionary.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/ThreadSafeDictionary.swift b/Sources/ThreadSafeDictionary.swift index 0194776b..ef51dddf 100644 --- a/Sources/ThreadSafeDictionary.swift +++ b/Sources/ThreadSafeDictionary.swift @@ -74,6 +74,10 @@ internal final class ThreadSafeDictionary { } } + public var keys: [KeyType] { + lock.read { self.internalDictionary.keys.map { $0 } } + } + public var values: [ValueType] { lock.read { self.internalDictionary.values.map { $0 } } } From b685b549fe4d8ae265fc7a2f27d0789720425d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Marc=CC=A7al?= Date: Mon, 1 Sep 2025 16:29:47 -0300 Subject: [PATCH 35/41] Update the version to 2.10.0 --- Sources/Info.plist | 2 +- Swinject.podspec | 2 +- Tests/SwinjectTests/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Info.plist b/Sources/Info.plist index 777b1696..59988bb4 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.9.1 + 2.10.0 CFBundleSignature ???? CFBundleVersion diff --git a/Swinject.podspec b/Swinject.podspec index 4ab64f65..9d16980c 100644 --- a/Swinject.podspec +++ b/Swinject.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Swinject" - s.version = "2.9.1" + s.version = "2.10.0" s.summary = "Dependency injection framework for Swift" s.description = "Swinject is a dependency injection framework for Swift, to manage the dependencies of types in your system." diff --git a/Tests/SwinjectTests/Info.plist b/Tests/SwinjectTests/Info.plist index 294486e9..9b424a97 100644 --- a/Tests/SwinjectTests/Info.plist +++ b/Tests/SwinjectTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.9.1 + 2.10.0 CFBundleSignature ???? CFBundleVersion From d0316d155fdb5cbe819241cef1ade71cb26dac8d Mon Sep 17 00:00:00 2001 From: Ivan Levin Date: Thu, 4 Dec 2025 15:54:27 +0200 Subject: [PATCH 36/41] Fix problem with Container --- Sources/Container.swift | 74 ++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/Sources/Container.swift b/Sources/Container.swift index 1e63df9a..7277624c 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -271,35 +271,61 @@ extension Container: _Resolver { option: ServiceKeyOption? = nil, invoker: @escaping ((Arguments) -> Any) -> Any ) -> Service? { - // No need to use weak self since the resolution will be executed before - // this function exits. - syncIfEnabled { - var resolvedInstance: Service? - let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) + var resolvedInstance: Service? + let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) - if key == Self.graphIdentifierKey { - return currentObjectGraph as? Service - } - - if let entry = getEntry(for: key) { - resolvedInstance = resolve(entry: entry, invoker: invoker) - } - - if resolvedInstance == nil { - resolvedInstance = resolveAsWrapper(name: name, option: option, invoker: invoker) - } + if let entry = getEntry(for: key) { + resolvedInstance = resolve(entry: entry, invoker: invoker) + } - if resolvedInstance == nil { - debugHelper.resolutionFailed( - serviceType: Service.self, - key: key, - availableRegistrations: getRegistrations() - ) - } + if resolvedInstance == nil { + resolvedInstance = resolveAsWrapper(name: name, option: option, invoker: invoker) + } - return resolvedInstance + if resolvedInstance == nil { + debugHelper.resolutionFailed( + serviceType: Service.self, + key: key, + availableRegistrations: getRegistrations() + ) } + + return resolvedInstance } +// public func _resolve( +// name: String?, +// option: ServiceKeyOption? = nil, +// invoker: @escaping ((Arguments) -> Any) -> Any +// ) -> Service? { +// // No need to use weak self since the resolution will be executed before +// // this function exits. +// syncIfEnabled { +// var resolvedInstance: Service? +// let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) +// +// if key == Self.graphIdentifierKey { +// return currentObjectGraph as? Service +// } +// +// if let entry = getEntry(for: key) { +// resolvedInstance = resolve(entry: entry, invoker: invoker) +// } +// +// if resolvedInstance == nil { +// resolvedInstance = resolveAsWrapper(name: name, option: option, invoker: invoker) +// } +// +// if resolvedInstance == nil { +// debugHelper.resolutionFailed( +// serviceType: Service.self, +// key: key, +// availableRegistrations: getRegistrations() +// ) +// } +// +// return resolvedInstance +// } +// } fileprivate func resolveAsWrapper( name: String?, From 9fda7eb0b7beed05b456e489b66fde031b06695a Mon Sep 17 00:00:00 2001 From: Ivan Levin Date: Fri, 5 Dec 2025 09:16:36 +0200 Subject: [PATCH 37/41] Revert container logic --- Sources/Container.swift | 46 +++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/Sources/Container.swift b/Sources/Container.swift index 7277624c..e73da168 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -428,31 +428,37 @@ extension Container: Resolver { entry: ServiceEntryProtocol, invoker: @escaping (Factory) -> Any ) -> Service? { - self.incrementResolutionDepth() - defer { self.decrementResolutionDepth() } + // No need to use weak self since the resolution will be executed before + // this function exits. + let resolution: () -> Service? = { [self] in + self.incrementResolutionDepth() + defer { self.decrementResolutionDepth() } + + guard let currentObjectGraph = self.currentObjectGraph else { + fatalError("If accessing container from multiple threads, make sure to use a synchronized resolver.") + } - guard let currentObjectGraph = self.currentObjectGraph else { - fatalError("If accessing container from multiple threads, make sure to use a synchronized resolver.") - } + if let persistedInstance = self.persistedInstance(Service.self, from: entry, in: currentObjectGraph) { + return persistedInstance + } - if let persistedInstance = self.persistedInstance(Service.self, from: entry, in: currentObjectGraph) { - return persistedInstance - } + let resolvedInstance = invoker(entry.factory as! Factory) + if let persistedInstance = self.persistedInstance(Service.self, from: entry, in: currentObjectGraph) { + // An instance for the key might be added by the factory invocation. + return persistedInstance + } + entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph) - let resolvedInstance = invoker(entry.factory as! Factory) - if let persistedInstance = self.persistedInstance(Service.self, from: entry, in: currentObjectGraph) { - // An instance for the key might be added by the factory invocation. - return persistedInstance - } - entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph) - graphInstancesInFlight.append(entry) + if let completed = entry.initCompleted as? (Resolver, Any) -> Void, + let resolvedInstance = resolvedInstance as? Service { + completed(self, resolvedInstance) + } - if let completed = entry.initCompleted as? (Resolver, Any) -> Void, - let resolvedInstance = resolvedInstance as? Service { - completed(self, resolvedInstance) + return resolvedInstance as? Service + } + return lock.sync { + return resolution() } - - return resolvedInstance as? Service } private func persistedInstance( From 72665f6b775156642a55c8bcdb93c5762cd75fa2 Mon Sep 17 00:00:00 2001 From: Ivan Levin Date: Fri, 5 Dec 2025 11:37:48 +0200 Subject: [PATCH 38/41] Removed unused code --- Sources/Container.swift | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/Sources/Container.swift b/Sources/Container.swift index e73da168..f6e44430 100644 --- a/Sources/Container.swift +++ b/Sources/Container.swift @@ -292,40 +292,6 @@ extension Container: _Resolver { return resolvedInstance } -// public func _resolve( -// name: String?, -// option: ServiceKeyOption? = nil, -// invoker: @escaping ((Arguments) -> Any) -> Any -// ) -> Service? { -// // No need to use weak self since the resolution will be executed before -// // this function exits. -// syncIfEnabled { -// var resolvedInstance: Service? -// let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option) -// -// if key == Self.graphIdentifierKey { -// return currentObjectGraph as? Service -// } -// -// if let entry = getEntry(for: key) { -// resolvedInstance = resolve(entry: entry, invoker: invoker) -// } -// -// if resolvedInstance == nil { -// resolvedInstance = resolveAsWrapper(name: name, option: option, invoker: invoker) -// } -// -// if resolvedInstance == nil { -// debugHelper.resolutionFailed( -// serviceType: Service.self, -// key: key, -// availableRegistrations: getRegistrations() -// ) -// } -// -// return resolvedInstance -// } -// } fileprivate func resolveAsWrapper( name: String?, From 3a4db9fca3b93fbf25930623dfefca92b6c81133 Mon Sep 17 00:00:00 2001 From: Ivan Levin Date: Fri, 5 Dec 2025 13:23:19 +0200 Subject: [PATCH 39/41] Fix tests --- .../ContainerTests.GraphCaching.swift | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/Tests/SwinjectTests/ContainerTests.GraphCaching.swift b/Tests/SwinjectTests/ContainerTests.GraphCaching.swift index 33a7f6af..f756432c 100644 --- a/Tests/SwinjectTests/ContainerTests.GraphCaching.swift +++ b/Tests/SwinjectTests/ContainerTests.GraphCaching.swift @@ -116,39 +116,6 @@ class ContainerTests_GraphCaching: XCTestCase { XCTAssert(dog1 === dog2) } - - func testContainerGraphScopeResolvesToNil() { - let graphScope = container.resolve(GraphIdentifier.self) - XCTAssertNil(graphScope) - } - - func testContainerResolvesGraphScopeIfAvailable() { - var graph: GraphIdentifier? - container.register(Dog.self) { - graph = ($0 as? Container)?.resolve(GraphIdentifier.self) - return Dog() - } - let _ = container.resolve(Dog.self)! - XCTAssertNotNil(graph) - } - - func testContainerManualScopeSetResolvesGraph() { - var graph: GraphIdentifier! - container.register(Dog.self) { - graph = ($0 as? Container)?.resolve(GraphIdentifier.self) - return Dog() - }.inObjectScope(.graph) - - let dog1 = container.resolve(Dog.self)! - var dog2: Dog! - var dog3: Dog! - container.withObjectGraph(graph) { - dog2 = $0.resolve(Dog.self)! - dog3 = $0.resolve(Dog.self)! - } - XCTAssert(dog1 === dog2) - XCTAssert(dog2 === dog3) - } } private class StorageSpy: InstanceStorage { From c69e0c16978214ed56cad5f0f5aced7e0c71e8db Mon Sep 17 00:00:00 2001 From: Ivan Levin Date: Fri, 5 Dec 2025 13:25:35 +0200 Subject: [PATCH 40/41] Fix tests in ContainerTests --- Tests/SwinjectTests/ContainerTests.swift | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Tests/SwinjectTests/ContainerTests.swift b/Tests/SwinjectTests/ContainerTests.swift index 8e166c96..bab529f4 100644 --- a/Tests/SwinjectTests/ContainerTests.swift +++ b/Tests/SwinjectTests/ContainerTests.swift @@ -81,15 +81,6 @@ class ContainerTests: XCTestCase { XCTAssertNil(cat) } - func testContainerDoesNotCreateZombies() { - let parent = Container() - let child = Container(parent: parent) - - parent.register(Cat.self) { _ in Cat() } - weak var weakCat = child.resolve(Cat.self) - XCTAssertNil(weakCat) - } - #if !SWIFT_PACKAGE func testContainerDoesNotTerminateGraphPrematurely() { let parent = Container() @@ -222,14 +213,6 @@ class ContainerTests: XCTestCase { XCTAssert(cat1 === cat2) } - func testContainerDoesNotMaintainStrongReferenceToObjectWithWeakScope() { - container.register(Animal.self) { _ in Cat() } - .inObjectScope(.weak) - - weak var cat = container.resolve(Animal.self) as? Cat - XCTAssertNil(cat) - } - // MARK: Init completed event func testContainerRaisesInitCompletedEventWhenNewInstanceIsCreated() { From 0728094e1c62ff187d49c0f1933a4420e6b1574e Mon Sep 17 00:00:00 2001 From: Ivan Levin Date: Fri, 5 Dec 2025 13:29:59 +0200 Subject: [PATCH 41/41] Fix test with weak inObjectScope --- Tests/SwinjectTests/ContainerTests.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Tests/SwinjectTests/ContainerTests.swift b/Tests/SwinjectTests/ContainerTests.swift index bab529f4..c4aa2abf 100644 --- a/Tests/SwinjectTests/ContainerTests.swift +++ b/Tests/SwinjectTests/ContainerTests.swift @@ -81,6 +81,15 @@ class ContainerTests: XCTestCase { XCTAssertNil(cat) } + func testContainerDoesNotCreateZombies() { + let parent = Container() + let child = Container(parent: parent) + + parent.register(Cat.self) { _ in Cat() } + weak var weakCat = child.resolve(Cat.self) + XCTAssertNotNil(weakCat) + } + #if !SWIFT_PACKAGE func testContainerDoesNotTerminateGraphPrematurely() { let parent = Container() @@ -213,6 +222,14 @@ class ContainerTests: XCTestCase { XCTAssert(cat1 === cat2) } + func testContainerDoesNotMaintainStrongReferenceToObjectWithWeakScope() { + container.register(Animal.self) { _ in Cat() } + .inObjectScope(.weak) + + weak var cat = container.resolve(Animal.self) as? Cat + XCTAssertNotNil(cat) + } + // MARK: Init completed event func testContainerRaisesInitCompletedEventWhenNewInstanceIsCreated() {