diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..312d1f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output diff --git a/SwiftInjector/SwiftInjector.xcodeproj/project.pbxproj b/SwiftInjector/SwiftInjector.xcodeproj/project.pbxproj new file mode 100644 index 0000000..fedc077 --- /dev/null +++ b/SwiftInjector/SwiftInjector.xcodeproj/project.pbxproj @@ -0,0 +1,538 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + E676F70622C94F2B00DC1282 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F70522C94F2B00DC1282 /* AppDelegate.swift */; }; + E676F70822C94F2B00DC1282 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F70722C94F2B00DC1282 /* ViewController.swift */; }; + E676F70B22C94F2B00DC1282 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E676F70922C94F2B00DC1282 /* Main.storyboard */; }; + E676F70D22C94F2E00DC1282 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E676F70C22C94F2E00DC1282 /* Assets.xcassets */; }; + E676F71022C94F2E00DC1282 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E676F70E22C94F2E00DC1282 /* LaunchScreen.storyboard */; }; + E676F71B22C94F2E00DC1282 /* SwiftInjectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F71A22C94F2E00DC1282 /* SwiftInjectorTests.swift */; }; + E676F72722C94F4700DC1282 /* RootContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F72622C94F4700DC1282 /* RootContainer.swift */; }; + E676F72922C94F6000DC1282 /* Containerable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F72822C94F6000DC1282 /* Containerable.swift */; }; + E676F72B22C9511200DC1282 /* ContainerableMainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F72A22C9511200DC1282 /* ContainerableMainTests.swift */; }; + E676F72F22C9548B00DC1282 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F72E22C9548B00DC1282 /* Container.swift */; }; + E676F73122C954BB00DC1282 /* TestContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F73022C954BB00DC1282 /* TestContainer.swift */; }; + E676F73322C955B200DC1282 /* DIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F73222C955B200DC1282 /* DIContainer.swift */; }; + E676F73522C9688F00DC1282 /* TestClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E676F73422C9688F00DC1282 /* TestClass.swift */; }; + E6BF5C6322CD2A32009A9331 /* SILogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6BF5C6222CD2A32009A9331 /* SILogger.swift */; }; + E6BF5C6522CD45C0009A9331 /* RecursiveResolvingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6BF5C6422CD45C0009A9331 /* RecursiveResolvingTests.swift */; }; + E6F3F57B22D11056003D86FA /* ContainerablePropertyNameTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F3F57A22D11056003D86FA /* ContainerablePropertyNameTests.swift */; }; + E6F3F57D22D111CB003D86FA /* ContainerableRegisterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F3F57C22D111CB003D86FA /* ContainerableRegisterTests.swift */; }; + E6F3F57F22D11646003D86FA /* ContainerablePropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F3F57E22D11646003D86FA /* ContainerablePropertyTests.swift */; }; + E6F3F58122D117EE003D86FA /* ContainerableFormattedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F3F58022D117EE003D86FA /* ContainerableFormattedStringTests.swift */; }; + E6F3F59622D3516D003D86FA /* ContainerableRecordRelationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F3F59522D3516D003D86FA /* ContainerableRecordRelationsTests.swift */; }; + E6F3F59A22D3783E003D86FA /* ContrainerableManualRegistrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F3F59922D3783E003D86FA /* ContrainerableManualRegistrationTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E676F71722C94F2E00DC1282 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E676F6FA22C94F2B00DC1282 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E676F70122C94F2B00DC1282; + remoteInfo = SwiftInjector; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + E676F70222C94F2B00DC1282 /* SwiftInjector.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftInjector.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E676F70522C94F2B00DC1282 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + E676F70722C94F2B00DC1282 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + E676F70A22C94F2B00DC1282 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + E676F70C22C94F2E00DC1282 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E676F70F22C94F2E00DC1282 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + E676F71122C94F2E00DC1282 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E676F71622C94F2E00DC1282 /* SwiftInjectorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftInjectorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + E676F71A22C94F2E00DC1282 /* SwiftInjectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftInjectorTests.swift; sourceTree = ""; }; + E676F71C22C94F2E00DC1282 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E676F72622C94F4700DC1282 /* RootContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootContainer.swift; sourceTree = ""; }; + E676F72822C94F6000DC1282 /* Containerable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Containerable.swift; sourceTree = ""; }; + E676F72A22C9511200DC1282 /* ContainerableMainTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerableMainTests.swift; sourceTree = ""; }; + E676F72E22C9548B00DC1282 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; + E676F73022C954BB00DC1282 /* TestContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestContainer.swift; sourceTree = ""; }; + E676F73222C955B200DC1282 /* DIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DIContainer.swift; sourceTree = ""; }; + E676F73422C9688F00DC1282 /* TestClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestClass.swift; sourceTree = ""; }; + E6BF5C6222CD2A32009A9331 /* SILogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILogger.swift; sourceTree = ""; }; + E6BF5C6422CD45C0009A9331 /* RecursiveResolvingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveResolvingTests.swift; sourceTree = ""; }; + E6F3F57A22D11056003D86FA /* ContainerablePropertyNameTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerablePropertyNameTests.swift; sourceTree = ""; }; + E6F3F57C22D111CB003D86FA /* ContainerableRegisterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerableRegisterTests.swift; sourceTree = ""; }; + E6F3F57E22D11646003D86FA /* ContainerablePropertyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerablePropertyTests.swift; sourceTree = ""; }; + E6F3F58022D117EE003D86FA /* ContainerableFormattedStringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerableFormattedStringTests.swift; sourceTree = ""; }; + E6F3F59522D3516D003D86FA /* ContainerableRecordRelationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerableRecordRelationsTests.swift; sourceTree = ""; }; + E6F3F59922D3783E003D86FA /* ContrainerableManualRegistrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContrainerableManualRegistrationTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E676F6FF22C94F2B00DC1282 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E676F71322C94F2E00DC1282 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E676F6F922C94F2B00DC1282 = { + isa = PBXGroup; + children = ( + E676F70422C94F2B00DC1282 /* SwiftInjector */, + E676F71922C94F2E00DC1282 /* SwiftInjectorTests */, + E676F70322C94F2B00DC1282 /* Products */, + ); + sourceTree = ""; + }; + E676F70322C94F2B00DC1282 /* Products */ = { + isa = PBXGroup; + children = ( + E676F70222C94F2B00DC1282 /* SwiftInjector.app */, + E676F71622C94F2E00DC1282 /* SwiftInjectorTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + E676F70422C94F2B00DC1282 /* SwiftInjector */ = { + isa = PBXGroup; + children = ( + E676F70522C94F2B00DC1282 /* AppDelegate.swift */, + E676F72522C94F3800DC1282 /* Source */, + E676F70722C94F2B00DC1282 /* ViewController.swift */, + E676F73422C9688F00DC1282 /* TestClass.swift */, + E676F70922C94F2B00DC1282 /* Main.storyboard */, + E676F70C22C94F2E00DC1282 /* Assets.xcassets */, + E676F70E22C94F2E00DC1282 /* LaunchScreen.storyboard */, + E676F71122C94F2E00DC1282 /* Info.plist */, + ); + path = SwiftInjector; + sourceTree = ""; + }; + E676F71922C94F2E00DC1282 /* SwiftInjectorTests */ = { + isa = PBXGroup; + children = ( + E676F71A22C94F2E00DC1282 /* SwiftInjectorTests.swift */, + E6F3F59922D3783E003D86FA /* ContrainerableManualRegistrationTests.swift */, + E6F3F59522D3516D003D86FA /* ContainerableRecordRelationsTests.swift */, + E676F72A22C9511200DC1282 /* ContainerableMainTests.swift */, + E6F3F57C22D111CB003D86FA /* ContainerableRegisterTests.swift */, + E6F3F58022D117EE003D86FA /* ContainerableFormattedStringTests.swift */, + E6F3F57E22D11646003D86FA /* ContainerablePropertyTests.swift */, + E6F3F57A22D11056003D86FA /* ContainerablePropertyNameTests.swift */, + E6BF5C6422CD45C0009A9331 /* RecursiveResolvingTests.swift */, + E676F71C22C94F2E00DC1282 /* Info.plist */, + ); + path = SwiftInjectorTests; + sourceTree = ""; + }; + E676F72522C94F3800DC1282 /* Source */ = { + isa = PBXGroup; + children = ( + E676F72622C94F4700DC1282 /* RootContainer.swift */, + E6BF5C6222CD2A32009A9331 /* SILogger.swift */, + E676F73022C954BB00DC1282 /* TestContainer.swift */, + E676F73222C955B200DC1282 /* DIContainer.swift */, + E676F72E22C9548B00DC1282 /* Container.swift */, + E676F72822C94F6000DC1282 /* Containerable.swift */, + ); + path = Source; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E676F70122C94F2B00DC1282 /* SwiftInjector */ = { + isa = PBXNativeTarget; + buildConfigurationList = E676F71F22C94F2E00DC1282 /* Build configuration list for PBXNativeTarget "SwiftInjector" */; + buildPhases = ( + E676F6FE22C94F2B00DC1282 /* Sources */, + E676F6FF22C94F2B00DC1282 /* Frameworks */, + E676F70022C94F2B00DC1282 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftInjector; + productName = SwiftInjector; + productReference = E676F70222C94F2B00DC1282 /* SwiftInjector.app */; + productType = "com.apple.product-type.application"; + }; + E676F71522C94F2E00DC1282 /* SwiftInjectorTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E676F72222C94F2E00DC1282 /* Build configuration list for PBXNativeTarget "SwiftInjectorTests" */; + buildPhases = ( + E676F71222C94F2E00DC1282 /* Sources */, + E676F71322C94F2E00DC1282 /* Frameworks */, + E676F71422C94F2E00DC1282 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E676F71822C94F2E00DC1282 /* PBXTargetDependency */, + ); + name = SwiftInjectorTests; + productName = SwiftInjectorTests; + productReference = E676F71622C94F2E00DC1282 /* SwiftInjectorTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E676F6FA22C94F2B00DC1282 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1020; + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = Ghost; + TargetAttributes = { + E676F70122C94F2B00DC1282 = { + CreatedOnToolsVersion = 10.2.1; + }; + E676F71522C94F2E00DC1282 = { + CreatedOnToolsVersion = 10.2.1; + TestTargetID = E676F70122C94F2B00DC1282; + }; + }; + }; + buildConfigurationList = E676F6FD22C94F2B00DC1282 /* Build configuration list for PBXProject "SwiftInjector" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E676F6F922C94F2B00DC1282; + productRefGroup = E676F70322C94F2B00DC1282 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E676F70122C94F2B00DC1282 /* SwiftInjector */, + E676F71522C94F2E00DC1282 /* SwiftInjectorTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E676F70022C94F2B00DC1282 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E676F71022C94F2E00DC1282 /* LaunchScreen.storyboard in Resources */, + E676F70D22C94F2E00DC1282 /* Assets.xcassets in Resources */, + E676F70B22C94F2B00DC1282 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E676F71422C94F2E00DC1282 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E676F6FE22C94F2B00DC1282 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E676F73522C9688F00DC1282 /* TestClass.swift in Sources */, + E676F73322C955B200DC1282 /* DIContainer.swift in Sources */, + E676F70822C94F2B00DC1282 /* ViewController.swift in Sources */, + E6BF5C6322CD2A32009A9331 /* SILogger.swift in Sources */, + E676F72F22C9548B00DC1282 /* Container.swift in Sources */, + E676F72922C94F6000DC1282 /* Containerable.swift in Sources */, + E676F73122C954BB00DC1282 /* TestContainer.swift in Sources */, + E676F72722C94F4700DC1282 /* RootContainer.swift in Sources */, + E676F70622C94F2B00DC1282 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E676F71222C94F2E00DC1282 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E6F3F57F22D11646003D86FA /* ContainerablePropertyTests.swift in Sources */, + E6BF5C6522CD45C0009A9331 /* RecursiveResolvingTests.swift in Sources */, + E676F72B22C9511200DC1282 /* ContainerableMainTests.swift in Sources */, + E6F3F59A22D3783E003D86FA /* ContrainerableManualRegistrationTests.swift in Sources */, + E6F3F58122D117EE003D86FA /* ContainerableFormattedStringTests.swift in Sources */, + E6F3F59622D3516D003D86FA /* ContainerableRecordRelationsTests.swift in Sources */, + E6F3F57D22D111CB003D86FA /* ContainerableRegisterTests.swift in Sources */, + E676F71B22C94F2E00DC1282 /* SwiftInjectorTests.swift in Sources */, + E6F3F57B22D11056003D86FA /* ContainerablePropertyNameTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E676F71822C94F2E00DC1282 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E676F70122C94F2B00DC1282 /* SwiftInjector */; + targetProxy = E676F71722C94F2E00DC1282 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + E676F70922C94F2B00DC1282 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E676F70A22C94F2B00DC1282 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + E676F70E22C94F2E00DC1282 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E676F70F22C94F2E00DC1282 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E676F71D22C94F2E00DC1282 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E676F71E22C94F2E00DC1282 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E676F72022C94F2E00DC1282 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7SE34FE232; + INFOPLIST_FILE = SwiftInjector/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = AI.SwiftInjector; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E676F72122C94F2E00DC1282 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7SE34FE232; + INFOPLIST_FILE = SwiftInjector/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = AI.SwiftInjector; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + E676F72322C94F2E00DC1282 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7SE34FE232; + INFOPLIST_FILE = SwiftInjectorTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = AI.SwiftInjectorTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftInjector.app/SwiftInjector"; + }; + name = Debug; + }; + E676F72422C94F2E00DC1282 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7SE34FE232; + INFOPLIST_FILE = SwiftInjectorTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = AI.SwiftInjectorTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftInjector.app/SwiftInjector"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E676F6FD22C94F2B00DC1282 /* Build configuration list for PBXProject "SwiftInjector" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E676F71D22C94F2E00DC1282 /* Debug */, + E676F71E22C94F2E00DC1282 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E676F71F22C94F2E00DC1282 /* Build configuration list for PBXNativeTarget "SwiftInjector" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E676F72022C94F2E00DC1282 /* Debug */, + E676F72122C94F2E00DC1282 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E676F72222C94F2E00DC1282 /* Build configuration list for PBXNativeTarget "SwiftInjectorTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E676F72322C94F2E00DC1282 /* Debug */, + E676F72422C94F2E00DC1282 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = E676F6FA22C94F2B00DC1282 /* Project object */; +} diff --git a/SwiftInjector/SwiftInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SwiftInjector/SwiftInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..44182ee --- /dev/null +++ b/SwiftInjector/SwiftInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftInjector/SwiftInjector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftInjector/SwiftInjector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/SwiftInjector/SwiftInjector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftInjector/SwiftInjector/AppDelegate.swift b/SwiftInjector/SwiftInjector/AppDelegate.swift new file mode 100644 index 0000000..991ab0c --- /dev/null +++ b/SwiftInjector/SwiftInjector/AppDelegate.swift @@ -0,0 +1,50 @@ +// +// AppDelegate.swift +// SwiftInjector +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + let container = TestContainer() + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + let newVc: ViewController? = self.container.resolve(name: "123") + let testClass = newVc?.testClass +// #error("NOT FINISHED RECURSIVE RESOLVING. BECAUSE AFTER RESOLVING I DONT PASS NAME VARIABLE TO RESOLVE.") + print(testClass) + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/SwiftInjector/SwiftInjector/Assets.xcassets/AppIcon.appiconset/Contents.json b/SwiftInjector/SwiftInjector/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/SwiftInjector/SwiftInjector/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SwiftInjector/SwiftInjector/Assets.xcassets/Contents.json b/SwiftInjector/SwiftInjector/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/SwiftInjector/SwiftInjector/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SwiftInjector/SwiftInjector/Base.lproj/LaunchScreen.storyboard b/SwiftInjector/SwiftInjector/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..bfa3612 --- /dev/null +++ b/SwiftInjector/SwiftInjector/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftInjector/SwiftInjector/Base.lproj/Main.storyboard b/SwiftInjector/SwiftInjector/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f1bcf38 --- /dev/null +++ b/SwiftInjector/SwiftInjector/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftInjector/SwiftInjector/Info.plist b/SwiftInjector/SwiftInjector/Info.plist new file mode 100644 index 0000000..16be3b6 --- /dev/null +++ b/SwiftInjector/SwiftInjector/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/SwiftInjector/SwiftInjector/Source/Container.swift b/SwiftInjector/SwiftInjector/Source/Container.swift new file mode 100644 index 0000000..27aa32a --- /dev/null +++ b/SwiftInjector/SwiftInjector/Source/Container.swift @@ -0,0 +1,20 @@ +// +// Container.swift +// SwiftInjector +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import Foundation + +protocol Container: class { + var parentContainer: Containerable { get set } + func register() +} + +extension Container { + func resolve() -> T? { + return parentContainer.resolve() + } +} diff --git a/SwiftInjector/SwiftInjector/Source/Containerable.swift b/SwiftInjector/SwiftInjector/Source/Containerable.swift new file mode 100644 index 0000000..f13613c --- /dev/null +++ b/SwiftInjector/SwiftInjector/Source/Containerable.swift @@ -0,0 +1,186 @@ +// +// Containerable.swift +// SwiftInjector +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import Foundation + +protocol Containerable: class { + typealias Object = AnyObject + typealias ServiceName = String + typealias Service = (() -> Object) + typealias RelationIn = String + typealias RelationOut = String + + var dispatchRegistrationGroup: DispatchGroup { get } + + var services: [ServiceName: [ContainerObject]] { get set } + var relations: [RelationIn: [RelationOut]] { get set } + var recursiveNotResolvedObjects: [Object] { get set } +} + +struct ContainerObject { + enum RegistrationType { + case manual + case automatic + } + var name: String? = nil + var registrationType: RegistrationType + var registration: Containerable.Service + var object: Containerable.Object? = nil + init(_ registration: @escaping Containerable.Service, + name: String? = nil, + registrationType: RegistrationType) { + self.registration = registration + self.name = name + self.registrationType = registrationType + } +} + + +//MARK: TODO: MOVE IT TO DIContainer.swift +extension Containerable { + private func resolveAny(typeString: String, name: String? = nil) -> Object? { + let array: [ContainerObject]? = { + if let filterName = name { + return services[typeString]?.filter { $0.name == filterName } + } else { + return services[typeString] + } + }() + + if (array?.count ?? 0) > 1 { + SILogger("Warning in \(typeString) resolving. You registered two different objects for this type. Try to provide \"name\" argument while registering your object. But you will receive first object of all services you registered.").log(priority: .high) + } + + if let object = array?.first?.object { + print(1) + return object + } else if let object = array?.first?.registration() { + + var objects = services[typeString] + if objects?.count ?? 0 > 0 { + objects?[0].object = object + } + + services[typeString] = objects ?? [] + if array?.first?.registrationType == .automatic { + autoresolve(on: object) + finishRegistrations() + } + + return object + } else { + return nil + } + } + + func resolve(name: String? = nil) -> T? { + let key = String(describing: T.self) + let object = resolveAny(typeString: key, name: name) as? T + + return object + } + + func finishRegistrations() { + relations = [:] + for object in recursiveNotResolvedObjects { + autoresolve(on: object) + } + } + + private func autoresolve(on object: T) { + let mirror = Mirror(reflecting: object) + + for attr in mirror.children { + if let property_name = attr.label { + let objectType = type(of: attr.value) + let typeString = String(describing: objectType).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "") + + // MARK: MUTATE VARIABLE (DANGER OBJC RUNTIME ZONE) + if let ivar = class_getInstanceVariable(type(of: object), property_name) { + let typeStringOfMyObject = formattedString(of: object) + recordRelations(relationIn: typeString, relationOut: typeStringOfMyObject) + if relations[typeStringOfMyObject]?.contains(typeString) ?? false { + relations[typeString]?.removeAll(where: { (string) -> Bool in + return string == typeStringOfMyObject + }) + recursiveNotResolvedObjects.append(object) + return + } + if let value = resolveAny(typeString: typeString) { + object_setIvar(object, ivar, value) + } + } + } + } + } + + private func memmoryAddress(_ object: Object) -> String { + return "\(Unmanaged.passUnretained(object).toOpaque())" + } + + + // MARK: *TODO: Unable to test* + private func recordRelations(relationIn: RelationIn, relationOut: RelationOut) { + if let relationsOut = relations[relationIn], !relationsOut.contains(relationOut) { + var newRelationsOut = relationsOut + newRelationsOut.append(relationOut) + relations[relationIn] = newRelationsOut + } else { + relations[relationIn] = [relationOut] + } + } + + // MARK: *TESTED* + func formattedString(of object: T) -> String { + return String(describing: type(of: object)).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "") + } + + // MARK: *TESTED* + func property(object: Object, propertyName: String) -> T? { + let mirror = Mirror(reflecting: object) + for attr in mirror.children { + if attr.label == propertyName { + return attr.value as? T + } + } + + return nil + } + + // MARK: *TESTED* + // Returns only first variable of type + func propertyName(by typeString: String, in object: Object) -> String? { + let mirror = Mirror(reflecting: object) + for attr in mirror.children { + let propertyType = type(of: attr.value) + let typeStringInside = String(describing: propertyType).replacingOccurrences(of: "Optional<", with: "").replacingOccurrences(of: ">", with: "") + if typeStringInside == typeString { + return attr.label + } + } + + return nil + } + + // MARK: *TESTED* + func register(_ registration: @escaping (() -> T), + name: String? = nil, + registrationType: ContainerObject.RegistrationType = .automatic) { + dispatchRegistrationGroup.enter() + let object = registration() + let key = String(describing: type(of: object)) + if let array = services[key] { + var newArray = array + newArray.append(ContainerObject(registration, name: name, registrationType: registrationType)) + services[key] = newArray + } else { + services[key] = [ContainerObject(registration, name: name, registrationType: registrationType)] + } + dispatchRegistrationGroup.leave() + } +} diff --git a/SwiftInjector/SwiftInjector/Source/DIContainer.swift b/SwiftInjector/SwiftInjector/Source/DIContainer.swift new file mode 100644 index 0000000..4d77886 --- /dev/null +++ b/SwiftInjector/SwiftInjector/Source/DIContainer.swift @@ -0,0 +1,32 @@ +// +// DIContainer.swift +// SwiftInjector +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import Foundation + +class DIContainer: Container { + var parentContainer: Containerable + + init(parentContainer: Containerable? = nil) { + self.parentContainer = parentContainer ?? RootContainer() + self.register() + } + + final func register(registrationType: ContainerObject.RegistrationType = .automatic, + name: String? = nil, + _ registration: @escaping (() -> T)) { + parentContainer.register(registration, name: name, registrationType: registrationType) + } + + final func resolve(name: String? = nil) -> T? { + return parentContainer.resolve(name: name) + } + + func register() { + // Mark: leave empty only in this class + } +} diff --git a/SwiftInjector/SwiftInjector/Source/RootContainer.swift b/SwiftInjector/SwiftInjector/Source/RootContainer.swift new file mode 100644 index 0000000..2e6c486 --- /dev/null +++ b/SwiftInjector/SwiftInjector/Source/RootContainer.swift @@ -0,0 +1,29 @@ +// +// Container.swift +// SwiftInjector +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import Foundation + +class RootContainer: Containerable { + var recursiveNotResolvedObjects: [Object] = [] + + var relations: [RelationIn: [RelationOut]] = [:] + var services: [ServiceName: [ContainerObject]] = [:] + + var dispatchRegistrationGroup: DispatchGroup = DispatchGroup() + + + init() { + let date = Date() + dispatchRegistrationGroup.notify(queue: .main) { + + self.finishRegistrations() + let seconds = Date().timeIntervalSince(date) + print("All properties have been successfully injected for \(seconds) seconds.") + } + } +} diff --git a/SwiftInjector/SwiftInjector/Source/SILogger.swift b/SwiftInjector/SwiftInjector/Source/SILogger.swift new file mode 100644 index 0000000..da8e6aa --- /dev/null +++ b/SwiftInjector/SwiftInjector/Source/SILogger.swift @@ -0,0 +1,36 @@ +// +// SILogger.swift +// SwiftInjector +// +// Created by Ghost on 03.07.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import Foundation + +struct SILogger { + private let prefix: String = "SwiftInjector: " + enum Priority { + case low + case medium + case high + + var prefix: String { + switch self { + case .low, .medium: return "" + case .high: return "[!IMPORTANT!]:" + } + } + } + + private let text: String + + init(_ text: String) { + self.text = text + } + + func log(priority: Priority = .medium) { + let result = prefix + priority.prefix + " " + text + print(result) + } +} diff --git a/SwiftInjector/SwiftInjector/Source/TestContainer.swift b/SwiftInjector/SwiftInjector/Source/TestContainer.swift new file mode 100644 index 0000000..e08c086 --- /dev/null +++ b/SwiftInjector/SwiftInjector/Source/TestContainer.swift @@ -0,0 +1,20 @@ +// +// TestContainer.swift +// SwiftInjector +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import Foundation + +class TestContainer: DIContainer { + let vc = ViewController() + let vc2 = ViewController() + + override func register() { + register { TestClass() } + register(name: "123") { self.vc } + register { self.vc2 } + } +} diff --git a/SwiftInjector/SwiftInjector/TestClass.swift b/SwiftInjector/SwiftInjector/TestClass.swift new file mode 100644 index 0000000..c76207f --- /dev/null +++ b/SwiftInjector/SwiftInjector/TestClass.swift @@ -0,0 +1,18 @@ +// +// TestClass.swift +// SwiftInjector +// +// Created by Ghost on 01.07.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import Foundation + +class TestClass: Equatable { + var name: String = "123" + weak var viewController: ViewController? + + static func == (lhs: TestClass, rhs: TestClass) -> Bool { + return "\(Unmanaged.passUnretained(lhs).toOpaque())" == "\(Unmanaged.passUnretained(rhs).toOpaque())" + } +} diff --git a/SwiftInjector/SwiftInjector/ViewController.swift b/SwiftInjector/SwiftInjector/ViewController.swift new file mode 100644 index 0000000..7521b14 --- /dev/null +++ b/SwiftInjector/SwiftInjector/ViewController.swift @@ -0,0 +1,19 @@ +// +// ViewController.swift +// SwiftInjector +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import UIKit + +class ViewController: UIViewController { + var testClass: TestClass? + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } +} + diff --git a/SwiftInjector/SwiftInjectorTests/ContainerableFormattedStringTests.swift b/SwiftInjector/SwiftInjectorTests/ContainerableFormattedStringTests.swift new file mode 100644 index 0000000..aa40397 --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/ContainerableFormattedStringTests.swift @@ -0,0 +1,36 @@ +// +// ContainerableFormattedStringTests.swift +// SwiftInjectorTests +// +// Created by Ghost on 06.07.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import XCTest +@testable import SwiftInjector + +class ContainerableFormattedStringTests: XCTestCase { + let container = TestContainer() + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExampleRegister() { + let containerable: Containerable = RootContainer() + let object = TestClass() + let objectTypeString = containerable.formattedString(of: object) + XCTAssertEqual(objectTypeString, "TestClass") + } + + func testExampleRegisterInvalid() { + let containerable: Containerable = RootContainer() + let object = TestClass() + let objectTypeString = containerable.formattedString(of: object) + XCTAssertNotEqual(objectTypeString, "TestClass1") + } +} + diff --git a/SwiftInjector/SwiftInjectorTests/ContainerableMainTests.swift b/SwiftInjector/SwiftInjectorTests/ContainerableMainTests.swift new file mode 100644 index 0000000..461bac7 --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/ContainerableMainTests.swift @@ -0,0 +1,33 @@ +// +// SwiftInjectorTests.swift +// SwiftInjectorTests +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import XCTest +@testable import SwiftInjector + +class ContainerableMainTests: XCTestCase { + let container = TestContainer() + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExampleRegister() { + let newVc: ViewController? = container.resolve() + XCTAssertEqual(container.vc, newVc) + } + + func testExampleRegister_autoResolving() { + let newVc: ViewController? = container.resolve() + let testClass = newVc?.testClass + print(testClass) + XCTAssertTrue(true) + } +} diff --git a/SwiftInjector/SwiftInjectorTests/ContainerablePropertyNameTests.swift b/SwiftInjector/SwiftInjectorTests/ContainerablePropertyNameTests.swift new file mode 100644 index 0000000..33ec919 --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/ContainerablePropertyNameTests.swift @@ -0,0 +1,52 @@ +// +// ContainerablePropertyName.swift +// SwiftInjectorTests +// +// Created by Ghost on 06.07.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + + +import XCTest +@testable import SwiftInjector + +class ContainerablePropertyNameTests: XCTestCase { + let container: Containerable = RootContainer() + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testPropertyNameWithOneString() { + class Test { + var name: String = "123" + } + let typeString = "String" + let name = container.propertyName(by: typeString, in: Test()) + XCTAssertEqual(name, "name") + } + + func testPropertyNameWithTwoStrings() { + class Test { + var name: String = "123" + var test: String = "456" + } + let typeString = "String" + let name = container.propertyName(by: typeString, in: Test()) + XCTAssertEqual(name, "name") + } + + func testPropertyNameWithTest() { + class Test { + var test: Test? + } + let typeString = "Test" + let name = container.propertyName(by: typeString, in: Test()) + XCTAssertEqual(name, "test") + } +} + diff --git a/SwiftInjector/SwiftInjectorTests/ContainerablePropertyTests.swift b/SwiftInjector/SwiftInjectorTests/ContainerablePropertyTests.swift new file mode 100644 index 0000000..cfc46af --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/ContainerablePropertyTests.swift @@ -0,0 +1,36 @@ +// +// ContainerablePropertyTests.swift +// SwiftInjectorTests +// +// Created by Ghost on 06.07.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import XCTest +@testable import SwiftInjector + +class ContainerablePropertyTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testPropertyNil() { + let container: Containerable = RootContainer() + let object = TestClass() + let property: String? = container.property(object: object, propertyName: "name1") + XCTAssertNil(property) + } + + func testPropertyNotNil() { + let container: Containerable = RootContainer() + let object = TestClass() + let property: String? = container.property(object: object, propertyName: "name") + XCTAssertNotNil(property) + XCTAssertEqual(property, "123") + } +} diff --git a/SwiftInjector/SwiftInjectorTests/ContainerableRecordRelationsTests.swift b/SwiftInjector/SwiftInjectorTests/ContainerableRecordRelationsTests.swift new file mode 100644 index 0000000..6f25f2b --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/ContainerableRecordRelationsTests.swift @@ -0,0 +1,36 @@ +// +// ContainerableRecordRelationsTests.swift +// SwiftInjectorTests +// +// Created by Ghost on 08.07.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import XCTest +@testable import SwiftInjector + +import XCTest +@testable import SwiftInjector + +class ContainerableRecordRelationsTests: XCTestCase { + override func setUp() { + + } + + func testUsualRelation() { +// let containerable: Containerable = RootContainer() +// class FirstClass { +// var secondClass: SecondClass? +// } +// +// class SecondClass { +// } +// +// containerable.register ({ FirstClass() }) +// containerable.register ({ SecondClass() }) +// let _: FirstClass? = containerable.resolve() +// let records = containerable.relations +// XCTAssertNotNil(records["FirstClass"]) + } +} + diff --git a/SwiftInjector/SwiftInjectorTests/ContainerableRegisterTests.swift b/SwiftInjector/SwiftInjectorTests/ContainerableRegisterTests.swift new file mode 100644 index 0000000..e5c9f5e --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/ContainerableRegisterTests.swift @@ -0,0 +1,57 @@ +// +// ContainerableRegister.swift +// SwiftInjectorTests +// +// Created by Ghost on 06.07.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import XCTest +@testable import SwiftInjector + +class ContainerableRegisterTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExampleRegisterWithoutResolving() { + let container: Containerable = RootContainer() + let object = TestClass() + container.register({ () -> TestClass in + return object + }, name: nil) + + let key = String(describing: type(of: object)) + XCTAssertNotNil(container.services[key]) + XCTAssert(container.services[key]?.count ?? 0 == 1) + + let containerObject = container.services[key]?.first + XCTAssertNil(containerObject?.object) + // it cannot be exists, because it has been not resolved yet + } + + func testExampleRegisterWithResolving() { + let container: Containerable = RootContainer() + let object = TestClass() + container.register({ () -> TestClass in + return object + }, name: nil) + + let key = String(describing: type(of: object)) + XCTAssertNotNil(container.services[key]) + XCTAssert(container.services[key]?.count ?? 0 == 1) + + let obj: TestClass? = container.resolve() + XCTAssertNotNil(obj) + let containerObject = container.services[key]?.first + XCTAssertNotNil(containerObject?.object) + XCTAssert(containerObject?.object is TestClass) + XCTAssertEqual(containerObject?.object as? TestClass, object) + } +} + diff --git a/SwiftInjector/SwiftInjectorTests/ContrainerableManualRegistrationTests.swift b/SwiftInjector/SwiftInjectorTests/ContrainerableManualRegistrationTests.swift new file mode 100644 index 0000000..ade335a --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/ContrainerableManualRegistrationTests.swift @@ -0,0 +1,34 @@ +// +// ContrainerableManualRegistrationTests.swift +// SwiftInjectorTests +// +// Created by Ghost on 08.07.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + + + +import XCTest +@testable import SwiftInjector + +class ContainerableManualRegistrationTests: XCTestCase { + override func setUp() { + + } + + func testManualRegistrationSample() { + let contaienrable: Containerable = RootContainer() + contaienrable.register({ () -> ViewController in + let vc = ViewController() + vc.testClass = TestClass() + return vc + }, registrationType: .manual) + + let vc: ViewController? = contaienrable.resolve() + XCTAssertNotNil(vc) + XCTAssertNotNil(vc?.testClass) + let testClass: TestClass? = contaienrable.resolve() + XCTAssertNil(testClass) + } +} + diff --git a/SwiftInjector/SwiftInjectorTests/Info.plist b/SwiftInjector/SwiftInjectorTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/SwiftInjector/SwiftInjectorTests/RecursiveResolvingTests.swift b/SwiftInjector/SwiftInjectorTests/RecursiveResolvingTests.swift new file mode 100644 index 0000000..3754693 --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/RecursiveResolvingTests.swift @@ -0,0 +1,55 @@ +// +// SwiftInjectorTests.swift +// SwiftInjectorTests +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import XCTest +@testable import SwiftInjector + +class RecursiveResolvingTests: XCTestCase { + + + class FirstClass: Equatable { + var name: String = "123" + var secondClass: SecondClass! + + static func == (lhs: FirstClass, rhs: FirstClass) -> Bool { + return "\(Unmanaged.passUnretained(lhs).toOpaque())" == "\(Unmanaged.passUnretained(rhs).toOpaque())" + } + } + + class SecondClass: Equatable { + var name: String = "234" + weak var firstClass: FirstClass! + + static func == (lhs: SecondClass, rhs: SecondClass) -> Bool { + return "\(Unmanaged.passUnretained(lhs).toOpaque())" == "\(Unmanaged.passUnretained(rhs).toOpaque())" + } + + } + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + let container = TestContainer() + + func testClassToClassRegister() { + let firstClass: ViewController? = container.resolve() + let secondClass: TestClass? = container.resolve() + print("---------") + print("---------") + XCTAssertNotNil(firstClass) + XCTAssertNotNil(secondClass) + XCTAssertNotNil(secondClass?.viewController) + XCTAssertEqual(secondClass?.viewController, firstClass) + XCTAssertEqual(firstClass?.testClass, secondClass) + } +} diff --git a/SwiftInjector/SwiftInjectorTests/SwiftInjectorTests.swift b/SwiftInjector/SwiftInjectorTests/SwiftInjectorTests.swift new file mode 100644 index 0000000..9e1fe59 --- /dev/null +++ b/SwiftInjector/SwiftInjectorTests/SwiftInjectorTests.swift @@ -0,0 +1,34 @@ +// +// SwiftInjectorTests.swift +// SwiftInjectorTests +// +// Created by Ghost on 30.06.2019. +// Copyright © 2019 Ghost. All rights reserved. +// + +import XCTest +@testable import SwiftInjector + +class SwiftInjectorTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/SwiftInjector/header.png b/SwiftInjector/header.png new file mode 100644 index 0000000..a66065d Binary files /dev/null and b/SwiftInjector/header.png differ