├── .gitignore ├── .gitmodules ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── .travis.yml ├── Assets ├── AnimalViewControllerScreenshot1.png └── AnimalViewControllerScreenshot2.png ├── Cartfile ├── Cartfile.resolved ├── Configurations ├── Base │ ├── Common.xcconfig │ ├── Configurations │ │ ├── Debug.xcconfig │ │ ├── Profile.xcconfig │ │ ├── Release.xcconfig │ │ └── Test.xcconfig │ └── Targets │ │ ├── Application.xcconfig │ │ ├── Framework.xcconfig │ │ └── StaticLibrary.xcconfig ├── Mac OS X │ ├── Mac-Application.xcconfig │ ├── Mac-Base.xcconfig │ ├── Mac-DynamicLibrary.xcconfig │ ├── Mac-Framework.xcconfig │ └── Mac-StaticLibrary.xcconfig ├── iOS │ ├── iOS-Application.xcconfig │ ├── iOS-Base.xcconfig │ ├── iOS-Framework.xcconfig │ └── iOS-StaticLibrary.xcconfig ├── tvOS │ ├── tvOS-Application.xcconfig │ ├── tvOS-Base.xcconfig │ ├── tvOS-Framework.xcconfig │ └── tvOS-StaticLibrary.xcconfig └── watchOS │ ├── watchOS-Application.xcconfig │ ├── watchOS-Base.xcconfig │ ├── watchOS-Framework.xcconfig │ └── watchOS-StaticLibrary.xcconfig ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources ├── Box.swift ├── Container+SwinjectStoryboard.swift ├── DispatchQueue+Once.swift ├── Info.plist ├── InjectionVerifiable.swift ├── OSX │ ├── NSStoryboard+Swizzling.swift │ └── _SwinjectStoryboardBase(OSX).swift ├── ObjectiveC │ └── Others │ │ └── SwinjectStoryboard+SetUp.m ├── RegistrationNameAssociatable.swift ├── SwinjectStoryboard+StoryboardReference.swift ├── SwinjectStoryboard.swift ├── SwinjectStoryboardOption.swift ├── SwinjectStoryboardProtocol.swift ├── UnavailableItems.swift ├── ViewController+Swinject.swift └── iOS-tvOS │ ├── UIStoryboard+Swizzling.swift │ └── _SwinjectStoryboardBase(iOS-tvOS).swift ├── SwinjectStoryboard.podspec ├── SwinjectStoryboard.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── SwinjectStoryboard-OSX.xcscheme │ ├── SwinjectStoryboard-iOS.xcscheme │ └── SwinjectStoryboard-tvOS.xcscheme ├── Tests ├── Animal.swift ├── Container+SwinjectStoryboardTests.swift ├── Food.swift ├── Info.plist ├── NSWindowController+SwinjectTests.swift ├── OSX │ ├── AnimalPagesViewController.swift │ ├── AnimalViewController.swift │ ├── AnimalWindowController.swift │ ├── Animals.storyboard │ ├── Pages.storyboard │ ├── RelationshipReference1.storyboard │ ├── RelationshipReference2.storyboard │ ├── Storyboard1.storyboard │ ├── Storyboard2.storyboard │ ├── SwinjectStoryboardTests.swift │ ├── Tabs.storyboard │ └── ViewController1.swift ├── Storyboard+SwizzlingTests.swift ├── ViewController+SwinjectTests.swift └── iOS-tvOS │ ├── AnimalPagesViewController.swift │ ├── AnimalPlayerViewController.swift │ ├── AnimalViewController.swift │ ├── Storyboards │ ├── iOS │ │ ├── AnimalPlayerViewController.storyboard │ │ ├── Animals.storyboard │ │ ├── Pages.storyboard │ │ ├── RelationshipReference1.storyboard │ │ ├── RelationshipReference2.storyboard │ │ ├── Storyboard1.storyboard │ │ ├── Storyboard2.storyboard │ │ └── Tabs.storyboard │ └── tvOS │ │ ├── AnimalPlayerViewController.storyboard │ │ ├── Animals.storyboard │ │ ├── Pages.storyboard │ │ ├── RelationshipReference1.storyboard │ │ ├── RelationshipReference2.storyboard │ │ ├── Storyboard1.storyboard │ │ ├── Storyboard2.storyboard │ │ └── Tabs.storyboard │ └── SwinjectStoryboardTests.swift └── carthage-build.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # https://github.com/github/gitignore/blob/master/Swift.gitignore 3 | # 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | Carthage/Checkouts 52 | Carthage/Build 53 | 54 | # fastlane 55 | # 56 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 57 | # screenshots whenever they are needed. 58 | # For more information about the recommended setup visit: 59 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 60 | 61 | fastlane/report.xml 62 | fastlane/screenshots 63 | 64 | ## Mac 65 | .DS_Store 66 | 67 | ## SwinjectStoryboard 68 | SwinjectStoryboard.framework.zip 69 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # reference: http://www.objc.io/issue-6/travis-ci.html 2 | 3 | language: objective-c 4 | osx_image: xcode11 5 | env: 6 | global: 7 | - LC_CTYPE=en_US.UTF-8 8 | - LANG=en_US.UTF-8 9 | - PROJECT=SwinjectStoryboard.xcodeproj 10 | matrix: 11 | include: 12 | - env: JOB="POD_LINT" 13 | osx_image: xcode11 14 | script: 15 | - bundle exec pod repo update 16 | - bundle exec pod lib lint 17 | - env: JOB="XCODE" DEST="OS=10.3.1,name=iPhone 6" SCHEME="SwinjectStoryboard-iOS" SDK="iphonesimulator" ACTION="test" 18 | osx_image: xcode11 19 | - env: JOB="XCODE" DEST="OS=11.0.1,name=iPhone 7 Plus" SCHEME="SwinjectStoryboard-iOS" SDK="iphonesimulator" ACTION="test" 20 | osx_image: xcode11 21 | - env: JOB="XCODE" DEST="OS=11.2,name=iPhone 8 Plus" SCHEME="SwinjectStoryboard-iOS" SDK="iphonesimulator" ACTION="test" 22 | osx_image: xcode11 23 | - env: JOB="XCODE" DEST="OS=12.2,name=iPhone X" SCHEME="SwinjectStoryboard-iOS" SDK="iphonesimulator" ACTION="test" 24 | osx_image: xcode11 25 | - env: JOB="XCODE" DEST="OS=13.0,name=iPhone 11 Pro" SCHEME="SwinjectStoryboard-iOS" SDK="iphonesimulator" ACTION="test" 26 | osx_image: xcode11 27 | - env: JOB="XCODE" DEST="arch=x86_64" SCHEME="SwinjectStoryboard-OSX" SDK="macosx" ACTION="test" 28 | osx_image: xcode11 29 | - env: JOB="XCODE" DEST="arch=x86_64" SCHEME="SwinjectStoryboard-OSX" SDK="macosx" ACTION="test" 30 | osx_image: xcode11 31 | - env: JOB="XCODE" DEST="OS=10.2,name=Apple TV 1080p" SCHEME="SwinjectStoryboard-tvOS" SDK="appletvsimulator" ACTION="test" 32 | osx_image: xcode11 33 | - env: JOB="XCODE" DEST="OS=11.0,name=Apple TV 1080p" SCHEME="SwinjectStoryboard-tvOS" SDK="appletvsimulator" ACTION="test" 34 | osx_image: xcode11 35 | - env: JOB="XCODE" DEST="OS=13.0,name=Apple TV 4K" SCHEME="SwinjectStoryboard-tvOS" SDK="appletvsimulator" ACTION="test" 36 | osx_image: xcode11 37 | before_install: 38 | - curl -L -O https://github.com/Carthage/Carthage/releases/download/0.33.0/Carthage.pkg 39 | - sudo installer -pkg Carthage.pkg -target / 40 | - rm Carthage.pkg 41 | - carthage bootstrap --platform iOS,macOS,tvOS --cache-builds 42 | script: 43 | - set -o pipefail 44 | - xcodebuild "$ACTION" -project "$PROJECT" -scheme "$SCHEME" -sdk "$SDK" -destination "$DEST" -configuration Release ENABLE_TESTABILITY=YES | xcpretty 45 | notifications: 46 | email: 47 | on_success: never 48 | 49 | cache: 50 | directories: 51 | - Carthage 52 | -------------------------------------------------------------------------------- /Assets/AnimalViewControllerScreenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Swinject/SwinjectStoryboard/7a446cc8d8880a403519a4a43e1b64c086dd265c/Assets/AnimalViewControllerScreenshot1.png -------------------------------------------------------------------------------- /Assets/AnimalViewControllerScreenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Swinject/SwinjectStoryboard/7a446cc8d8880a403519a4a43e1b64c086dd265c/Assets/AnimalViewControllerScreenshot2.png -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "Swinject/Swinject" ~> 2.7.1 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Swinject/Swinject" "2.7.1" 2 | -------------------------------------------------------------------------------- /Configurations/Base/Common.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines common settings that should be enabled for every new 3 | // project. Typically, you want to use Debug, Release, or a similar variant 4 | // instead. 5 | // 6 | 7 | // Disable legacy-compatible header searching 8 | ALWAYS_SEARCH_USER_PATHS = NO 9 | 10 | // Architectures to build 11 | ARCHS = $(ARCHS_STANDARD) 12 | 13 | // Whether to warn when a floating-point value is used as a loop counter 14 | CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES 15 | 16 | // Whether to warn about use of rand() and random() being used instead of arc4random() 17 | CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES 18 | 19 | // Whether to warn about strcpy() and strcat() 20 | CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES 21 | 22 | // Whether to enable module imports 23 | CLANG_ENABLE_MODULES = YES 24 | 25 | // Enable ARC 26 | CLANG_ENABLE_OBJC_ARC = YES 27 | 28 | // Warn about block captures of implicitly autoreleasing parameters. 29 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES 30 | 31 | // Warn about implicit conversions to boolean values that are suspicious. 32 | // For example, writing 'if (foo)' with 'foo' being the name a function will trigger a warning. 33 | CLANG_WARN_BOOL_CONVERSION = YES 34 | 35 | // Warn about suspicious uses of the comma operator. 36 | CLANG_WARN_COMMA = YES 37 | 38 | // Warn about implicit conversions of constant values that cause the constant value to change, 39 | // either through a loss of precision, or entirely in its meaning. 40 | CLANG_WARN_CONSTANT_CONVERSION = YES 41 | 42 | // Whether to warn when overriding deprecated methods 43 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES 44 | 45 | // Warn about direct accesses to the Objective-C 'isa' pointer instead of using a runtime API. 46 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR 47 | 48 | // Warn about declaring the same method more than once within the same @interface. 49 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 50 | 51 | // Warn about loop bodies that are suspiciously empty. 52 | CLANG_WARN_EMPTY_BODY = YES 53 | 54 | // Warn about implicit conversions between different kinds of enum values. 55 | // For example, this can catch issues when using the wrong enum flag as an argument to a function or method. 56 | CLANG_WARN_ENUM_CONVERSION = YES 57 | 58 | // Whether to warn on implicit conversions between signed/unsigned types 59 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO 60 | 61 | // Warn about implicit conversions between pointers and integers. 62 | // For example, this can catch issues when one incorrectly intermixes using NSNumbers and raw integers. 63 | CLANG_WARN_INT_CONVERSION = YES 64 | 65 | // Warn about non-literal expressions that evaluate to zero being treated as a null pointer. 66 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES 67 | 68 | // Warn about implicit capture of self (e.g. direct ivar access) 69 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 70 | 71 | // Warn about implicit conversions from Objective-C literals to values of incompatible type. 72 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES 73 | 74 | // Don't warn about repeatedly using a weak reference without assigning the weak reference to a strong reference. Too many false positives. 75 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO 76 | 77 | // Warn about classes that unintentionally do not subclass a root class (such as NSObject). 78 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR 79 | 80 | // Warn about ranged-based for loops. 81 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES 82 | 83 | // Whether to warn on suspicious implicit conversions 84 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES 85 | 86 | // Warn about non-prototype declarations. 87 | CLANG_WARN_STRICT_PROTOTYPES = YES 88 | 89 | // Warn if an API that is newer than the deployment target is used without "if (@available(...))" guards. 90 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES 91 | 92 | // Warn about incorrect uses of nullable values 93 | CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES 94 | 95 | // Warn for missing nullability attributes 96 | CLANG_ANALYZER_NONNULL = YES 97 | 98 | // Warn when a non-localized string is passed to a user-interface method expecting a localized string 99 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES 100 | 101 | // Warn about potentially unreachable code 102 | CLANG_WARN_UNREACHABLE_CODE = YES 103 | 104 | // The format of debugging symbols 105 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym 106 | 107 | // Whether to compile assertions in 108 | ENABLE_NS_ASSERTIONS = YES 109 | 110 | // Whether to require objc_msgSend to be cast before invocation 111 | ENABLE_STRICT_OBJC_MSGSEND = YES 112 | 113 | // Which C variant to use 114 | GCC_C_LANGUAGE_STANDARD = gnu99 115 | 116 | // Whether to enable exceptions for Objective-C 117 | GCC_ENABLE_OBJC_EXCEPTIONS = YES 118 | 119 | // Whether to generate debugging symbols 120 | GCC_GENERATE_DEBUGGING_SYMBOLS = YES 121 | 122 | // Whether to precompile the prefix header (if one is specified) 123 | GCC_PRECOMPILE_PREFIX_HEADER = YES 124 | 125 | // Whether to enable strict aliasing, meaning that two pointers of different 126 | // types (other than void * or any id type) cannot point to the same memory 127 | // location 128 | GCC_STRICT_ALIASING = YES 129 | 130 | // Whether symbols not explicitly exported are hidden by default (this primarily 131 | // only affects C++ code) 132 | GCC_SYMBOLS_PRIVATE_EXTERN = NO 133 | 134 | // Whether static variables are thread-safe by default 135 | GCC_THREADSAFE_STATICS = NO 136 | 137 | // Which compiler to use 138 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0 139 | 140 | // Whether warnings are treated as errors 141 | GCC_TREAT_WARNINGS_AS_ERRORS = YES 142 | SWIFT_TREAT_WARNINGS_AS_ERRORS = YES 143 | 144 | // Whether to warn about 64-bit values being implicitly shortened to 32 bits 145 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES 146 | 147 | // Whether to warn about fields missing from structure initializers (only if 148 | // designated initializers aren't used) 149 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES 150 | 151 | // Whether to warn about missing function prototypes 152 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO 153 | 154 | // Whether to warn about implicit conversions in the signedness of the type 155 | // a pointer is pointing to (e.g., 'int *' getting converted to 'unsigned int *') 156 | GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES 157 | 158 | // Whether to warn when the value returned from a function/method/block does not 159 | // match its return type 160 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR 161 | 162 | // Whether to warn on a class not implementing all the required methods of 163 | // a protocol it declares conformance to 164 | GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES 165 | 166 | // Whether to warn when switching on an enum value, and all possibilities are 167 | // not accounted for 168 | GCC_WARN_CHECK_SWITCH_STATEMENTS = YES 169 | 170 | // Whether to warn about the use of four-character constants 171 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES 172 | 173 | // Whether to warn about an aggregate data type's initializer not being fully 174 | // bracketed (e.g., array initializer syntax) 175 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES 176 | 177 | // Whether to warn about missing braces or parentheses that make the meaning of 178 | // the code ambiguous 179 | GCC_WARN_MISSING_PARENTHESES = YES 180 | 181 | // Whether to warn about unsafe comparisons between values of different 182 | // signedness 183 | GCC_WARN_SIGN_COMPARE = YES 184 | 185 | // Whether to warn about the arguments to printf-style functions not matching 186 | // the format specifiers 187 | GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES 188 | 189 | // Warn if a "@selector(...)" expression referring to an undeclared selector is found 190 | GCC_WARN_UNDECLARED_SELECTOR = YES 191 | 192 | // Warn if a variable might be clobbered by a setjmp call or if an automatic variable is used without prior initialization. 193 | GCC_WARN_UNINITIALIZED_AUTOS = YES 194 | 195 | // Whether to warn about static functions that are unused 196 | GCC_WARN_UNUSED_FUNCTION = YES 197 | 198 | // Whether to warn about labels that are unused 199 | GCC_WARN_UNUSED_LABEL = YES 200 | 201 | // Whether to warn about variables that are never used 202 | GCC_WARN_UNUSED_VARIABLE = YES 203 | 204 | // Whether to run the static analyzer with every build 205 | RUN_CLANG_STATIC_ANALYZER = YES 206 | 207 | // Don't treat unknown warnings as errors, and disable GCC compatibility warnings and unused static const variable warnings 208 | WARNING_CFLAGS = -Wno-error=unknown-warning-option -Wno-gcc-compat -Wno-unused-const-variable 209 | 210 | // This setting is on for new projects as of Xcode ~6.3, though it is still not 211 | // the default. It warns if the same variable is declared in two binaries that 212 | // are linked together. 213 | GCC_NO_COMMON_BLOCKS = YES 214 | 215 | // This warnings detects when a function will recursively call itself on every 216 | // code path though that function. More information can be found here: 217 | // http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20131216/096004.html 218 | CLANG_WARN_INFINITE_RECURSION = YES 219 | 220 | // This warning detects suspicious uses of std::move. 221 | CLANG_WARN_SUSPICIOUS_MOVE = YES 222 | -------------------------------------------------------------------------------- /Configurations/Base/Configurations/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines the base configuration for a Debug build of any project. 3 | // This should be set at the project level for the Debug configuration. 4 | // 5 | 6 | #include "../Common.xcconfig" 7 | 8 | // Whether to strip debugging symbols when copying resources (like included 9 | // binaries) 10 | COPY_PHASE_STRIP = NO 11 | 12 | // The optimization level (0, 1, 2, 3, s) for the produced binary 13 | GCC_OPTIMIZATION_LEVEL = 0 14 | 15 | // Preproccessor definitions to apply to each file compiled 16 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 17 | 18 | // Allow @testable imports 19 | ENABLE_TESTABILITY = YES 20 | 21 | // Whether to enable link-time optimizations (such as inlining across translation 22 | // units) 23 | LLVM_LTO = NO 24 | 25 | // Whether to only build the active architecture 26 | ONLY_ACTIVE_ARCH = YES 27 | 28 | // Other compiler flags 29 | // 30 | // These settings catch some errors in integer arithmetic 31 | OTHER_CFLAGS = -ftrapv 32 | 33 | // Other flags to pass to the Swift compiler 34 | // 35 | // This enables conditional compilation with #if DEBUG 36 | OTHER_SWIFT_FLAGS = -D DEBUG 37 | 38 | // Xcode 8 introduced a new flag for conditional compilation 39 | // 40 | // This enables conditional compilation with #if DEBUG 41 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG 42 | 43 | // Whether to strip debugging symbols when copying the built product to its 44 | // final installation location 45 | STRIP_INSTALLED_PRODUCT = NO 46 | 47 | // The optimization level (-Onone, -O, -Ofast) for the produced Swift binary 48 | SWIFT_OPTIMIZATION_LEVEL = -Onone 49 | 50 | // Disable Developer ID timestamping 51 | OTHER_CODE_SIGN_FLAGS = --timestamp=none 52 | -------------------------------------------------------------------------------- /Configurations/Base/Configurations/Profile.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines the base configuration for an optional profiling-specific 3 | // build of any project. To use these settings, create a Profile configuration 4 | // in your project, and use this file at the project level for the new 5 | // configuration. 6 | // 7 | 8 | // based on the Release configuration, with some stuff related to debugging 9 | // symbols re-enabled 10 | #include "Release.xcconfig" 11 | 12 | // Whether to strip debugging symbols when copying resources (like included 13 | // binaries) 14 | COPY_PHASE_STRIP = NO 15 | 16 | // Whether to only build the active architecture 17 | ONLY_ACTIVE_ARCH = YES 18 | 19 | // Whether to strip debugging symbols when copying the built product to its 20 | // final installation location 21 | STRIP_INSTALLED_PRODUCT = NO 22 | 23 | // Whether to perform App Store validation checks 24 | VALIDATE_PRODUCT = NO 25 | 26 | // Disable Developer ID timestamping 27 | OTHER_CODE_SIGN_FLAGS = --timestamp=none 28 | -------------------------------------------------------------------------------- /Configurations/Base/Configurations/Release.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines the base configuration for a Release build of any project. 3 | // This should be set at the project level for the Release configuration. 4 | // 5 | 6 | #include "../Common.xcconfig" 7 | 8 | // Whether to strip debugging symbols when copying resources (like included 9 | // binaries) 10 | COPY_PHASE_STRIP = YES 11 | 12 | // The optimization level (0, 1, 2, 3, s) for the produced binary 13 | GCC_OPTIMIZATION_LEVEL = s 14 | 15 | // Preproccessor definitions to apply to each file compiled 16 | GCC_PREPROCESSOR_DEFINITIONS = NDEBUG=1 17 | 18 | // Whether to enable link-time optimizations (such as inlining across translation 19 | // units) 20 | LLVM_LTO = NO 21 | 22 | // Whether to only build the active architecture 23 | ONLY_ACTIVE_ARCH = NO 24 | 25 | // Whether to strip debugging symbols when copying the built product to its 26 | // final installation location 27 | STRIP_INSTALLED_PRODUCT = YES 28 | 29 | // The optimization level (-Onone, -O, -Owholemodule) for the produced Swift binary 30 | SWIFT_OPTIMIZATION_LEVEL = -Owholemodule 31 | 32 | // Whether to perform App Store validation checks 33 | VALIDATE_PRODUCT = YES 34 | 35 | -------------------------------------------------------------------------------- /Configurations/Base/Configurations/Test.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines the base configuration for a Test build of any project. 3 | // This should be set at the project level for the Test configuration. 4 | // 5 | 6 | #include "Debug.xcconfig" 7 | 8 | // Sandboxed apps can't be unit tested since they can't load some random 9 | // external bundle. So we disable sandboxing for testing. 10 | CODE_SIGN_ENTITLEMENTS = 11 | 12 | // Allow @testable imports 13 | ENABLE_TESTABILITY = YES 14 | -------------------------------------------------------------------------------- /Configurations/Base/Targets/Application.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for an application. Typically, you want to use a platform-specific variant 4 | // instead. 5 | // 6 | 7 | // Whether to strip out code that isn't called from anywhere 8 | DEAD_CODE_STRIPPING = NO 9 | 10 | // Sets the @rpath for the application such that it can include frameworks in 11 | // the application bundle (inside the "Frameworks" folder) 12 | LD_RUNPATH_SEARCH_PATHS = @executable_path/../Frameworks @loader_path/../Frameworks @executable_path/Frameworks 13 | -------------------------------------------------------------------------------- /Configurations/Base/Targets/Framework.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a framework. Typically, you want to use a platform-specific variant 4 | // instead. 5 | // 6 | 7 | // Disable code signing for successful device builds with Xcode 8. Frameworks do 8 | // need to be signed, but they don't need to be signed at compile time because 9 | // they'll be re-signed when you include them in your app. 10 | CODE_SIGNING_REQUIRED = NO 11 | CODE_SIGN_IDENTITY = 12 | 13 | // Whether to strip out code that isn't called from anywhere 14 | DEAD_CODE_STRIPPING = NO 15 | 16 | // Whether this framework should define an LLVM module 17 | DEFINES_MODULE = YES 18 | 19 | // Whether function calls should be position-dependent (should always be 20 | // disabled for library code) 21 | GCC_DYNAMIC_NO_PIC = NO 22 | 23 | // Default frameworks to the name of the project, instead of any 24 | // platform-specific target 25 | PRODUCT_NAME = $(PROJECT_NAME) 26 | 27 | // Enables the framework to be included from any location as long as the 28 | // loader’s runpath search paths includes it. For example from an application 29 | // bundle (inside the "Frameworks" folder) or shared folder 30 | INSTALL_PATH = @rpath 31 | LD_DYLIB_INSTALL_NAME = @rpath/$(PRODUCT_NAME).$(WRAPPER_EXTENSION)/$(PRODUCT_NAME) 32 | SKIP_INSTALL = YES 33 | 34 | // Disallows use of APIs that are not available 35 | // to app extensions and linking to frameworks 36 | // that have not been built with this setting enabled. 37 | APPLICATION_EXTENSION_API_ONLY = YES 38 | -------------------------------------------------------------------------------- /Configurations/Base/Targets/StaticLibrary.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a static library. Typically, you want to use a platform-specific variant 4 | // instead. 5 | // 6 | 7 | // Whether to strip out code that isn't called from anywhere 8 | DEAD_CODE_STRIPPING = NO 9 | 10 | // Whether to strip debugging symbols when copying resources (like included 11 | // binaries). 12 | // 13 | // Overrides Release.xcconfig when used at the target level. 14 | COPY_PHASE_STRIP = NO 15 | 16 | // Whether function calls should be position-dependent (should always be 17 | // disabled for library code) 18 | GCC_DYNAMIC_NO_PIC = NO 19 | 20 | // Copy headers to "include/LibraryName" in the build folder by default. This 21 | // lets consumers use #import syntax even for static 22 | // libraries 23 | PUBLIC_HEADERS_FOLDER_PATH = include/$PRODUCT_NAME 24 | 25 | // Don't include in an xcarchive 26 | SKIP_INSTALL = YES 27 | 28 | // Disallows use of APIs that are not available 29 | // to app extensions and linking to frameworks 30 | // that have not been built with this setting enabled. 31 | APPLICATION_EXTENSION_API_ONLY = YES 32 | 33 | -------------------------------------------------------------------------------- /Configurations/Mac OS X/Mac-Application.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for an application on Mac OS X. This should be set at the target level for 4 | // each project configuration. 5 | // 6 | 7 | // Import base application settings 8 | #include "../Base/Targets/Application.xcconfig" 9 | 10 | // Apply common settings specific to Mac OS X 11 | #include "Mac-Base.xcconfig" 12 | 13 | // Whether function calls should be position-dependent (should always be 14 | // disabled for library code) 15 | GCC_DYNAMIC_NO_PIC = YES 16 | -------------------------------------------------------------------------------- /Configurations/Mac OS X/Mac-Base.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for Mac OS X. This file is not standalone -- it is meant to be included into 4 | // a configuration file for a specific type of target. 5 | // 6 | 7 | // Whether to combine multiple image resolutions into a multirepresentational 8 | // TIFF 9 | COMBINE_HIDPI_IMAGES = YES 10 | 11 | // Where to find embedded frameworks 12 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks 13 | 14 | // The base SDK to use (if no version is specified, the latest version is 15 | // assumed) 16 | SDKROOT = macosx 17 | 18 | // Supported build architectures 19 | VALID_ARCHS = x86_64 20 | -------------------------------------------------------------------------------- /Configurations/Mac OS X/Mac-DynamicLibrary.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a dynamic library on Mac OS X. This should be set at the target level 4 | // for each project configuration. 5 | // 6 | 7 | // Import common settings specific to Mac OS X 8 | #include "Mac-Base.xcconfig" 9 | 10 | // Whether to strip out code that isn't called from anywhere 11 | DEAD_CODE_STRIPPING = NO 12 | 13 | // Whether function calls should be position-dependent (should always be 14 | // disabled for library code) 15 | GCC_DYNAMIC_NO_PIC = NO 16 | 17 | // Don't include in an xcarchive 18 | SKIP_INSTALL = YES 19 | -------------------------------------------------------------------------------- /Configurations/Mac OS X/Mac-Framework.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a framework on OS X. This should be set at the target level for each 4 | // project configuration. 5 | // 6 | 7 | // Import base framework settings 8 | #include "../Base/Targets/Framework.xcconfig" 9 | 10 | // Import common settings specific to Mac OS X 11 | #include "Mac-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/Mac OS X/Mac-StaticLibrary.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a static library on Mac OS X. This should be set at the target level for 4 | // each project configuration. 5 | // 6 | 7 | // Import base static library settings 8 | #include "../Base/Targets/StaticLibrary.xcconfig" 9 | 10 | // Apply common settings specific to Mac OS X 11 | #include "Mac-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/iOS/iOS-Application.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for an application on iOS. This should be set at the target level for each 4 | // project configuration. 5 | // 6 | 7 | // Import base application settings 8 | #include "../Base/Targets/Application.xcconfig" 9 | 10 | // Apply common settings specific to iOS 11 | #include "iOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/iOS/iOS-Base.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for iOS. This file is not standalone -- it is meant to be included into 4 | // a configuration file for a specific type of target. 5 | // 6 | 7 | // Xcode needs this to find archived headers if SKIP_INSTALL is set 8 | HEADER_SEARCH_PATHS = $(OBJROOT)/UninstalledProducts/include 9 | 10 | // Where to find embedded frameworks 11 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks 12 | 13 | // The base SDK to use (if no version is specified, the latest version is 14 | // assumed) 15 | SDKROOT = iphoneos 16 | 17 | // Supported device families (1 is iPhone, 2 is iPad) 18 | TARGETED_DEVICE_FAMILY = 1,2 19 | -------------------------------------------------------------------------------- /Configurations/iOS/iOS-Framework.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a framework on iOS. This should be set at the target level for each 4 | // project configuration. 5 | // 6 | 7 | // Import base framework settings 8 | #include "../Base/Targets/Framework.xcconfig" 9 | 10 | // Import common settings specific to iOS 11 | #include "iOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/iOS/iOS-StaticLibrary.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a static library on iOS. This should be set at the target level for each 4 | // project configuration. 5 | // 6 | 7 | // Import base static library settings 8 | #include "../Base/Targets/StaticLibrary.xcconfig" 9 | 10 | // Apply common settings specific to iOS 11 | #include "iOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/tvOS/tvOS-Application.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for an application on tvOS. This should be set at the target level for 4 | // each project configuration. 5 | // 6 | 7 | // Import base application settings 8 | #include "../Base/Targets/Application.xcconfig" 9 | 10 | // Apply common settings specific to tvOS 11 | #include "tvOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/tvOS/tvOS-Base.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for tvOS. This file is not standalone -- it is meant to be included into 4 | // a configuration file for a specific type of target. 5 | // 6 | 7 | // Where to find embedded frameworks 8 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks 9 | 10 | // The base SDK to use (if no version is specified, the latest version is 11 | // assumed) 12 | SDKROOT = appletvos 13 | 14 | // Supported device families 15 | TARGETED_DEVICE_FAMILY = 3 16 | -------------------------------------------------------------------------------- /Configurations/tvOS/tvOS-Framework.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a framework on tvOS. This should be set at the target level for each 4 | // project configuration. 5 | // 6 | 7 | // Import base framework settings 8 | #include "../Base/Targets/Framework.xcconfig" 9 | 10 | // Import common settings specific to iOS 11 | #include "tvOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/tvOS/tvOS-StaticLibrary.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a static library on tvOS. This should be set at the target level for 4 | // each project configuration. 5 | // 6 | 7 | // Import base static library settings 8 | #include "../Base/Targets/StaticLibrary.xcconfig" 9 | 10 | // Apply common settings specific to tvOS 11 | #include "tvOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/watchOS/watchOS-Application.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for an application on watchOS. This should be set at the target level for 4 | // each project configuration. 5 | // 6 | 7 | // Import base application settings 8 | #include "../Base/Targets/Application.xcconfig" 9 | 10 | // Apply common settings specific to watchOS 11 | #include "watchOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/watchOS/watchOS-Base.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for watchOS. This file is not standalone -- it is meant to be included into 4 | // a configuration file for a specific type of target. 5 | // 6 | 7 | // Where to find embedded frameworks 8 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks 9 | 10 | // The base SDK to use (if no version is specified, the latest version is 11 | // assumed) 12 | SDKROOT = watchos 13 | 14 | // Supported device families 15 | TARGETED_DEVICE_FAMILY = 4 16 | -------------------------------------------------------------------------------- /Configurations/watchOS/watchOS-Framework.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a framework on watchOS. This should be set at the target level for each 4 | // project configuration. 5 | // 6 | 7 | // Import base framework settings 8 | #include "../Base/Targets/Framework.xcconfig" 9 | 10 | // Import common settings specific to iOS 11 | #include "watchOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Configurations/watchOS/watchOS-StaticLibrary.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // This file defines additional configuration options that are appropriate only 3 | // for a static library on watchOS. This should be set at the target level for 4 | // each project configuration. 5 | // 6 | 7 | // Import base static library settings 8 | #include "../Base/Targets/StaticLibrary.xcconfig" 9 | 10 | // Apply common settings specific to watchOS 11 | #include "watchOS-Base.xcconfig" 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "cocoapods", "1.8.1" 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.0) 5 | activesupport (4.2.10) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | atomos (0.1.3) 11 | claide (1.0.2) 12 | cocoapods (1.6.0.beta.1) 13 | activesupport (>= 4.0.2, < 5) 14 | claide (>= 1.0.2, < 2.0) 15 | cocoapods-core (= 1.6.0.beta.1) 16 | cocoapods-deintegrate (>= 1.0.2, < 2.0) 17 | cocoapods-downloader (>= 1.2.1, < 2.0) 18 | cocoapods-plugins (>= 1.0.0, < 2.0) 19 | cocoapods-search (>= 1.0.0, < 2.0) 20 | cocoapods-stats (>= 1.0.0, < 2.0) 21 | cocoapods-trunk (>= 1.3.1, < 2.0) 22 | cocoapods-try (>= 1.1.0, < 2.0) 23 | colored2 (~> 3.1) 24 | escape (~> 0.0.4) 25 | fourflusher (~> 2.0.1) 26 | gh_inspector (~> 1.0) 27 | molinillo (~> 0.6.6) 28 | nap (~> 1.0) 29 | ruby-macho (~> 1.2) 30 | xcodeproj (>= 1.6.0, < 2.0) 31 | cocoapods-core (1.6.0.beta.1) 32 | activesupport (>= 4.0.2, < 6) 33 | fuzzy_match (~> 2.0.4) 34 | nap (~> 1.0) 35 | cocoapods-deintegrate (1.0.2) 36 | cocoapods-downloader (1.2.1) 37 | cocoapods-plugins (1.0.0) 38 | nap 39 | cocoapods-search (1.0.0) 40 | cocoapods-stats (1.0.0) 41 | cocoapods-trunk (1.3.1) 42 | nap (>= 0.8, < 2.0) 43 | netrc (~> 0.11) 44 | cocoapods-try (1.1.0) 45 | colored2 (3.1.2) 46 | concurrent-ruby (1.0.5) 47 | escape (0.0.4) 48 | fourflusher (2.0.1) 49 | fuzzy_match (2.0.4) 50 | gh_inspector (1.1.3) 51 | i18n (0.9.5) 52 | concurrent-ruby (~> 1.0) 53 | minitest (5.11.3) 54 | molinillo (0.6.6) 55 | nanaimo (0.2.6) 56 | nap (1.1.0) 57 | netrc (0.11.0) 58 | ruby-macho (1.3.1) 59 | thread_safe (0.3.6) 60 | tzinfo (1.2.5) 61 | thread_safe (~> 0.1) 62 | xcodeproj (1.6.0) 63 | CFPropertyList (>= 2.3.3, < 4.0) 64 | atomos (~> 0.1.3) 65 | claide (>= 1.0.2, < 2.0) 66 | colored2 (~> 3.1) 67 | nanaimo (~> 0.2.6) 68 | 69 | PLATFORMS 70 | ruby 71 | 72 | DEPENDENCIES 73 | cocoapods (= 1.6.0.beta.1) 74 | 75 | BUNDLED WITH 76 | 1.16.4 77 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Swinject Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Run help command as the default. 3 | # 4 | all: help 5 | 6 | # 7 | # Shows help message. 8 | # 9 | .PHONY: help 10 | help: 11 | @echo 'To release a version of SwinjectStoryboard, run the following commands in order on the branch where you make a release.' 12 | @echo ' $$ make VERSION= set-new-version (e.g. make VERSION=1.2.3 set-new-version)' 13 | @echo ' $$ make [UPSTREAM=] push-to-upstream (e.g. make push-to-upstream)' 14 | @echo ' $$ make carthage-release' 15 | @echo ' $$ make cocoapods-release' 16 | 17 | # 18 | # Set a specified version in Info.plist and podspec files, and tag the version. 19 | # 20 | .PHONY: set-new-version 21 | set-new-version: 22 | ifndef VERSION 23 | $(error Specify a new version. E.g. $$ make VERSION=1.2.3 set-new-version) 24 | endif 25 | ifeq ($(wildcard ./SwinjectStoryboard.podspec),) 26 | $(error Run the command in the directory containing SwinjectStoryboard.podspec.) 27 | endif 28 | ifeq ($(shell git diff --quiet; echo $$?), 1) 29 | $(error Your current git working tree is dirty. Stash all to run this command.) 30 | endif 31 | agvtool new-marketing-version $(VERSION) 32 | sed -i '' -e 's/\(s\.version.*=\).*/\1 "$(VERSION)"/' ./SwinjectStoryboard.podspec 33 | git commit -a -m "Update the version to $(VERSION)" 34 | git tag $(VERSION) 35 | 36 | # 37 | # Push the commit and tag for the new version to upstream. 38 | # 39 | VERSION_TAG=$(shell git describe --tags --abbrev=0) 40 | ifndef UPSTREAM 41 | UPSTREAM=upstream 42 | endif 43 | .PHONY: push-to-upstream 44 | push-to-upstream: 45 | git push $(UPSTREAM) $(shell git rev-parse --abbrev-ref HEAD) 46 | git push $(UPSTREAM) $(VERSION_TAG) 47 | 48 | # 49 | # Make a release for Carthage. 50 | # 51 | VERSION_TAG=$(shell git describe --tags --abbrev=0) 52 | .PHONY: carthage-release 53 | carthage-release: 54 | ifeq ($(shell which gh),) 55 | $(error gh command not found. Install gh command and run `gh auth login` beforehand.) 56 | endif 57 | ifneq ($(VERSION_TAG),$(shell agvtool what-marketing-version -terse1)) 58 | $(error The version tag $(VERSION_TAG) does not match the version in Info.plist) 59 | endif 60 | gh release create $(VERSION_TAG) --draft --title v$(VERSION_TAG) --notes "" 61 | @echo Open https://github.com/Swinject/SwinjectStoryboard/releases/edit/$(VERSION_TAG) to describe the release and publish it. 62 | 63 | # 64 | # Make a release for Cocoapods. 65 | # 66 | .PHONY: cocoapods-release 67 | cocoapods-release: 68 | pod lib lint 69 | pod trunk push SwinjectStoryboard.podspec 70 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Swinject", 6 | "repositoryURL": "https://github.com/Swinject/Swinject.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "8a76d2c74bafbb455763487cc6a08e91bad1f78b", 10 | "version": "2.7.1" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "SwinjectStoryboard", 6 | platforms: [ 7 | .macOS(.v10_10), 8 | .iOS(.v9), 9 | .tvOS(.v9), 10 | ], 11 | products: [ 12 | .library(name: "SwinjectStoryboard", targets: ["SwinjectStoryboard"]), 13 | ], 14 | dependencies: [ 15 | .package(url: "https://github.com/Swinject/Swinject.git", .upToNextMajor(from: "2.7.1")), 16 | ], 17 | targets: [ 18 | .target( 19 | name: "SwinjectStoryboard-ObjC", 20 | path: "Sources/ObjectiveC", 21 | cSettings: [ 22 | .headerSearchPath("Others") 23 | ] 24 | ), 25 | .target( 26 | name: "SwinjectStoryboard", 27 | dependencies: [ 28 | "Swinject", 29 | "SwinjectStoryboard-ObjC" 30 | ], 31 | path: "Sources", 32 | exclude: [ 33 | "ObjectiveC", 34 | "Info.plist" 35 | ] 36 | ), 37 | ] 38 | ) 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SwinjectStoryboard 2 | ======== 3 | 4 | [![Build Status](https://travis-ci.org/Swinject/SwinjectStoryboard.svg?branch=master)](https://travis-ci.org/Swinject/SwinjectStoryboard) 5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | [![CocoaPods Version](https://img.shields.io/cocoapods/v/SwinjectStoryboard.svg?style=flat)](http://cocoapods.org/pods/SwinjectStoryboard) 7 | [![License](https://img.shields.io/cocoapods/l/SwinjectStoryboard.svg?style=flat)](http://cocoapods.org/pods/SwinjectStoryboard) 8 | [![Platform](https://img.shields.io/cocoapods/p/SwinjectStoryboard.svg?style=flat)](http://cocoapods.org/pods/SwinjectStoryboard) 9 | [![Swift Version](https://img.shields.io/badge/Swift-5-F16D39.svg?style=flat)](https://developer.apple.com/swift) 10 | [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 11 | 12 | SwinjectStoryboard is an extension of Swinject to automatically inject dependency to view controllers instantiated by a storyboard. 13 | 14 | ## Requirements 15 | 16 | - iOS 8.0+ / Mac OS X 10.10+ / tvOS 9.0+ 17 | - Xcode 8+ 18 | 19 | ## Installation 20 | 21 | Swinject is available through [Carthage](https://github.com/Carthage/Carthage) or [CocoaPods](https://cocoapods.org). 22 | 23 | ### Carthage 24 | 25 | To install Swinject with Carthage, add the following line to your `Cartfile`. 26 | 27 | ``` 28 | github "Swinject/Swinject" 29 | github "Swinject/SwinjectStoryboard" 30 | ``` 31 | 32 | Then run `carthage update --no-use-binaries` command or just `carthage update`. For details of the installation and usage of Carthage, visit [its project page](https://github.com/Carthage/Carthage). 33 | 34 | ### CocoaPods 35 | 36 | To install Swinject with CocoaPods, add the following lines to your `Podfile`. 37 | 38 | ```ruby 39 | source 'https://github.com/CocoaPods/Specs.git' 40 | platform :ios, '8.0' # or platform :osx, '10.10' if your target is OS X. 41 | use_frameworks! 42 | 43 | pod 'Swinject' 44 | pod 'SwinjectStoryboard' 45 | ``` 46 | 47 | Then run `pod install` command. For details of the installation and usage of CocoaPods, visit [its official website](https://cocoapods.org). 48 | 49 | ### Swift Package Manager 50 | 51 | To integrate using Apple's Swift package manager, add the following as a dependency to your `Package.swift`: 52 | 53 | ```swift 54 | .package(url: "https://github.com/Swinject/SwinjectStoryboard.git", .upToNextMajor(from: "2.2.0")) 55 | ``` 56 | 57 | and then specify `"SwinjectStoryboard"` as a dependency of the Target in which you wish to use SwinjectStoryboard. 58 | 59 | ## Usage 60 | 61 | Swinject supports automatic dependency injection to view controllers instantiated by `SwinjectStoryboard`. This class inherits `UIStoryboard` (or `NSStoryboard` in case of OS X). To register dependencies of a view controller, use `storyboardInitCompleted` method. In the same way as a registration of a service type, a view controller can be registered with or without a name. 62 | 63 | **NOTE**: Do NOT explicitly resolve the view controllers registered by `storyboardInitCompleted` method. The view controllers are intended to be resolved by `SwinjectStoryboard` implicitly. 64 | 65 | ### Registration 66 | 67 | #### Registration without Name 68 | 69 | Here is a simple example to register a dependency of a view controller without a registration name: 70 | 71 | ```swift 72 | let container = Container() 73 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 74 | c.animal = r.resolve(Animal.self) 75 | } 76 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 77 | ``` 78 | 79 | Next, we create an instance of `SwinjectStoryboard` with the container specified. If the container is not specified, `SwinjectStoryboard.defaultContainer` is used instead. `instantiateViewControllerWithIdentifier` method creates an instance of the view controller with its dependencies injected: 80 | 81 | ```swift 82 | let sb = SwinjectStoryboard.create( 83 | name: "Animals", bundle: nil, container: container) 84 | let controller = sb.instantiateViewControllerWithIdentifier("Animal") 85 | as! AnimalViewController 86 | print(controller.animal! is Cat) // prints "true" 87 | print(controller.animal!.name) // prints "Mimi" 88 | ``` 89 | 90 | Where the classes and protocol are: 91 | 92 | ```swift 93 | class AnimalViewController: UIViewController { 94 | var animal: Animal? 95 | 96 | required init?(coder aDecoder: NSCoder) { 97 | super.init(coder: aDecoder) 98 | } 99 | } 100 | 101 | protocol Animal { 102 | var name: String { get set } 103 | } 104 | 105 | class Cat: Animal { 106 | var name: String 107 | 108 | init(name: String) { 109 | self.name = name 110 | } 111 | } 112 | ``` 113 | 114 | and the storyboard named `Animals.storyboard` has `AnimalViewController` with storyboard ID `Animal`. 115 | 116 | ![AnimalViewController in Animals.storyboard](./Assets/AnimalViewControllerScreenshot1.png) 117 | 118 | #### Registration with Name 119 | 120 | If a storyboard has more than one view controller with the same type, dependencies should be registered with registration names. 121 | 122 | ```swift 123 | let container = Container() 124 | container.storyboardInitCompleted(AnimalViewController.self, name: "cat") { 125 | r, c in c.animal = r.resolve(Animal.self, name: "mimi") 126 | } 127 | container.storyboardInitCompleted(AnimalViewController.self, name: "dog") { 128 | r, c in c.animal = r.resolve(Animal.self, name: "hachi") 129 | } 130 | container.register(Animal.self, name: "mimi") { 131 | _ in Cat(name: "Mimi") 132 | } 133 | container.register(Animal.self, name: "hachi") { 134 | _ in Dog(name: "Hachi") 135 | } 136 | ``` 137 | 138 | Then view controllers are instantiated with storyboard IDs similarly to the case without registration names: 139 | 140 | ```swift 141 | let sb = SwinjectStoryboard.create( 142 | name: "Animals", bundle: nil, container: container) 143 | let catController = sb.instantiateViewControllerWithIdentifier("Cat") 144 | as! AnimalViewController 145 | let dogController = sb.instantiateViewControllerWithIdentifier("Dog") 146 | as! AnimalViewController 147 | print(catController.animal!.name) // prints "Mimi" 148 | print(dogController.animal!.name) // prints "Hachi" 149 | ``` 150 | 151 | Where `Dog` class is: 152 | 153 | ```swift 154 | class Dog: Animal { 155 | var name: String 156 | 157 | init(name: String) { 158 | self.name = name 159 | } 160 | } 161 | ``` 162 | 163 | and the storyboard named `Animals.storyboard` has `AnimalViewController`s with storyboard IDs `Cat` and `Dog`. In addition to the storyboard IDs, user defined runtime attributes are specified as `cat` and `dog` for the key `swinjectRegistrationName`, respectively. 164 | 165 | ![AnimalViewControllers with user defined runtime attribute in Animals.storyboard](./Assets/AnimalViewControllerScreenshot2.png) 166 | 167 | ### UIWindow and Root View Controller Instantiation 168 | 169 | #### Implicit Instantiation from "Main" Storyboard 170 | 171 | If you implicitly instantiate `UIWindow` and its root view controller from "Main" storyboard, implement `setup` class method as an extension of `SwinjectStoryboard` to register dependencies to `defaultContainer`. When the root view controller (initial view controller) is instantiated by runtime, dependencies registered to `defaultContainer` are injected. 172 | 173 | **Note that `@objc` attribute is mandatory here in swift 4.** 174 | 175 | ```swift 176 | extension SwinjectStoryboard { 177 | @objc class func setup() { 178 | defaultContainer.storyboardInitCompleted(AnimalViewController.self) { r, c in 179 | c.animal = r.resolve(Animal.self) 180 | } 181 | defaultContainer.register(Animal.self) { _ in Cat(name: "Mimi") } 182 | } 183 | } 184 | ``` 185 | 186 | #### Explicit Instantiation in AppDelegate 187 | 188 | If you prefer explicit instantiation of UIWindow and its root view controller, instantiate `SwinjectStoryboard` with a container in `application:didFinishLaunchingWithOptions:` method. 189 | 190 | ```swift 191 | @UIApplicationMain 192 | class AppDelegate: UIResponder, UIApplicationDelegate { 193 | var window: UIWindow? 194 | var container: Container = { 195 | let container = Container() 196 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 197 | c.animal = r.resolve(Animal.self) 198 | } 199 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 200 | return container 201 | }() 202 | 203 | func application( 204 | _ application: UIApplication, 205 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { 206 | 207 | let window = UIWindow(frame: UIScreen.mainScreen().bounds) 208 | window.makeKeyAndVisible() 209 | self.window = window 210 | 211 | let storyboard = SwinjectStoryboard.create(name: "Main", bundle: nil, container: container) 212 | window.rootViewController = storyboard.instantiateInitialViewController() 213 | 214 | return true 215 | } 216 | } 217 | ``` 218 | 219 | Notice that you should delete the `Main storyboard file base name` item (or `UIMainStoryboardFile` item if you are displaying raw keys/values) in `Info.plist` of your app. 220 | 221 | ### Storyboard References 222 | 223 | Storyboard Reference introduced with Xcode 7 is supported by `SwinjectStoryboard`. To enable dependency injection when an instance is created from a referenced storyboard, register dependencies to `defaultContainer` static property of `SwinjectStoryboard`. 224 | 225 | ```swift 226 | let container = SwinjectStoryboard.defaultContainer 227 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 228 | c.animal = r.resolve(Animal.self) 229 | } 230 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 231 | ``` 232 | 233 | If you implicitly instantiate `UIWindow` and its root view controller, the registrations setup for "Main" storyboard can be shared with the referenced storyboard since `defaultContainer` is configured in `setup` method. 234 | 235 | ## For Maintainers 236 | 237 | ### Making a new release version 238 | 239 | Our release procedure is described as [Makefile](https://github.com/Swinject/SwinjectStoryboard/blob/master/Makefile). Run `make help` command for more info. 240 | 241 | ## Credits 242 | 243 | SwinjectStoryboard is inspired by: 244 | 245 | - [Typhoon](https://github.com/appsquickly/typhoon) - [Jasper Blues](https://github.com/jasperblues), [Aleksey Garbarev](https://github.com/alexgarbarev) and [contributors](https://github.com/appsquickly/Typhoon/graphs/contributors). 246 | - [BlindsidedStoryboard](https://github.com/briancroom/BlindsidedStoryboard) - [Brian Croom](https://github.com/briancroom). 247 | 248 | ## License 249 | 250 | MIT license. See the [LICENSE file](LICENSE.txt) for details. 251 | -------------------------------------------------------------------------------- /Sources/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 11/27/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal final class Box { 12 | internal let value: T 13 | 14 | internal init(_ value: T) { 15 | self.value = value 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Container+SwinjectStoryboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Container+SwinjectStoryboard.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 11/28/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | #if canImport(UIKit) 10 | import UIKit 11 | #endif 12 | import Swinject 13 | 14 | #if os(iOS) || os(OSX) || os(tvOS) 15 | extension Container { 16 | /// Adds a registration of the specified view or window controller that is configured in a storyboard. 17 | /// 18 | /// - Note: Do NOT explicitly resolve the controller registered by this method. 19 | /// The controller is intended to be resolved by `SwinjectStoryboard` implicitly. 20 | /// 21 | /// - Parameters: 22 | /// - controllerType: The controller type to register as a service type. 23 | /// The type is `UIViewController` in iOS, `NSViewController` or `NSWindowController` in OS X. 24 | /// - name: A registration name, which is used to differentiate from other registrations 25 | /// that have the same view or window controller type. 26 | /// - initCompleted: A closure to specify how the dependencies of the view or window controller are injected. 27 | /// It is invoked by the `Container` when the view or window controller is instantiated by `SwinjectStoryboard`. 28 | public func storyboardInitCompleted(_ controllerType: C.Type, name: String? = nil, initCompleted: @escaping (Resolver, C) -> ()) { 29 | // Xcode 7.1 workaround for Issue #10. This workaround is not necessary with Xcode 7. 30 | // https://github.com/Swinject/Swinject/issues/10 31 | let factory = { (_: Resolver, controller: Controller) in controller } 32 | let wrappingClosure: (Resolver, Controller) -> () = { r, c in initCompleted(r, c as! C) } 33 | let option = SwinjectStoryboardOption(controllerType: controllerType) 34 | _register(Controller.self, factory: factory, name: name, option: option) 35 | .initCompleted(wrappingClosure) 36 | } 37 | } 38 | #endif 39 | 40 | 41 | extension Container { 42 | #if os(iOS) || os(tvOS) 43 | /// The typealias to UIViewController. 44 | public typealias Controller = UIViewController 45 | 46 | #elseif os(OSX) 47 | /// The typealias to AnyObject, which should be actually NSViewController or NSWindowController. 48 | /// See the reference of NSStoryboard.instantiateInitialController method. 49 | public typealias Controller = Any 50 | 51 | #endif 52 | } 53 | -------------------------------------------------------------------------------- /Sources/DispatchQueue+Once.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | internal extension DispatchQueue { 4 | 5 | private static var _onceTracker: [String] = [] 6 | 7 | static func once(token: String, block: () -> Void) { 8 | objc_sync_enter(self) 9 | defer { objc_sync_exit(self) } 10 | 11 | if _onceTracker.contains(token) { 12 | return 13 | } 14 | 15 | _onceTracker.append(token) 16 | block() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.2.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016 Swinject Contributors. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/InjectionVerifiable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InjectionVerifiable.swift 3 | // Swinject 4 | // 5 | // Created by Jakub Vaňo on 28/10/16. 6 | // Copyright © 2016 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @objc internal protocol InjectionVerifiable: AnyObject { 12 | var wasInjected: Bool { get set } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/OSX/NSStoryboard+Swizzling.swift: -------------------------------------------------------------------------------- 1 | #if canImport(Cocoa) && !targetEnvironment(macCatalyst) 2 | import Cocoa 3 | 4 | extension NSStoryboard { 5 | static func swizzling() { 6 | DispatchQueue.once(token: "swinject.storyboard.init") { 7 | let aClass: AnyClass = object_getClass(self)! 8 | 9 | let originalSelector = #selector(NSStoryboard.init(name:bundle:)) 10 | let swizzledSelector = #selector(swinject_init(name:bundle:)) 11 | 12 | let originalMethod = class_getInstanceMethod(aClass, originalSelector)! 13 | let swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector)! 14 | 15 | let didAddMethod = class_addMethod(aClass, originalSelector, 16 | method_getImplementation(swizzledMethod), 17 | method_getTypeEncoding(swizzledMethod)) 18 | 19 | guard didAddMethod else { 20 | method_exchangeImplementations(originalMethod, swizzledMethod) 21 | return 22 | } 23 | class_replaceMethod(aClass, swizzledSelector, 24 | method_getImplementation(originalMethod), 25 | method_getTypeEncoding(originalMethod)) 26 | } 27 | } 28 | 29 | @objc class func swinject_init(name: String, bundle: Bundle?) -> NSStoryboard { 30 | guard self == NSStoryboard.self else { 31 | return self.swinject_init(name: name, bundle: bundle) 32 | } 33 | // Instantiate SwinjectStoryboard if NSStoryboard is trying to be instantiated. 34 | if SwinjectStoryboard.isCreatingStoryboardReference { 35 | return SwinjectStoryboard.createReferenced(name: name, bundle: bundle) 36 | } else { 37 | return SwinjectStoryboard.create(name: name, bundle: bundle) 38 | } 39 | } 40 | } 41 | #endif 42 | -------------------------------------------------------------------------------- /Sources/OSX/_SwinjectStoryboardBase(OSX).swift: -------------------------------------------------------------------------------- 1 | #if canImport(Cocoa) && !targetEnvironment(macCatalyst) 2 | import Cocoa 3 | import Swinject 4 | 5 | @objcMembers 6 | public class _SwinjectStoryboardBase: NSStoryboard { 7 | public class func _create(_ name: String, bundle storyboardBundleOrNil: Bundle?) -> Self { 8 | let storyboard = perform(#selector(NSStoryboard.init(name:bundle:)), with: name, with: storyboardBundleOrNil)? 9 | .takeUnretainedValue() 10 | return storyboard as! Self 11 | } 12 | } 13 | 14 | extension SwinjectStoryboard { 15 | @objc public static func configure() { 16 | NSStoryboard.swizzling() 17 | DispatchQueue.once(token: "swinject.storyboard.setup") { 18 | guard SwinjectStoryboard.responds(to: _Selector("setup")) else { return } 19 | SwinjectStoryboard.perform(_Selector("setup")) 20 | } 21 | } 22 | 23 | static func _Selector(_ str: String) -> Selector { 24 | return Selector(str) 25 | } 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /Sources/ObjectiveC/Others/SwinjectStoryboard+SetUp.m: -------------------------------------------------------------------------------- 1 | // 2 | // SwinjectStoryboard+SetUp.m 3 | // SwinjectStoryboard 4 | // 5 | // Created by Mark DiFranco on 2017-05-27. 6 | // Copyright © 2017 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | __attribute__((constructor)) static void swinjectStoryboardSetupEntry(void) { 12 | Class swinjectStoryboard = NSClassFromString(@"SwinjectStoryboard"); 13 | #pragma clang diagnostic push 14 | #pragma clang diagnostic ignored "-Wundeclared-selector" 15 | if ([swinjectStoryboard respondsToSelector:@selector(configure)]) { 16 | [swinjectStoryboard performSelector:@selector(configure)]; 17 | } 18 | #pragma clang diagnostic pop 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Sources/RegistrationNameAssociatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RegistrationNameAssociatable.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 8/1/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @objc internal protocol RegistrationNameAssociatable: AnyObject { 12 | var swinjectRegistrationName: String? { get set } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/SwinjectStoryboard+StoryboardReference.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwinjectStoryboard+StoryboardReference. 3 | // SwinjectStoryboard 4 | // 5 | // Created by Jakub Vaňo on 01/09/16. 6 | // Copyright © 2016 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if canImport(UIKit) 11 | import UIKit 12 | #elseif canImport(Cocoa) 13 | import Cocoa 14 | #endif 15 | 16 | internal extension SwinjectStoryboard { 17 | 18 | static func pushInstantiatingStoryboard(_ storyboard: SwinjectStoryboard) { 19 | storyboardStack.append(storyboard) 20 | } 21 | 22 | @discardableResult 23 | static func popInstantiatingStoryboard() -> SwinjectStoryboard? { 24 | return storyboardStack.popLast() 25 | } 26 | 27 | class var isCreatingStoryboardReference: Bool { 28 | return referencingStoryboard != nil 29 | } 30 | 31 | static var referencingStoryboard: SwinjectStoryboard? { 32 | return storyboardStack.last 33 | } 34 | #if os(iOS) || os(tvOS) 35 | class func createReferenced(name: String, bundle storyboardBundleOrNil: Bundle?) -> SwinjectStoryboard { 36 | if let container = referencingStoryboard?.container.value { 37 | return create(name: name, bundle: storyboardBundleOrNil, container: container) 38 | } else { 39 | return create(name: name, bundle: storyboardBundleOrNil) 40 | } 41 | } 42 | #elseif os(OSX) 43 | class func createReferenced(name: NSStoryboard.Name, bundle storyboardBundleOrNil: Bundle?) -> SwinjectStoryboard { 44 | if let container = referencingStoryboard?.container.value { 45 | return create(name: name, bundle: storyboardBundleOrNil, container: container) 46 | } else { 47 | return create(name: name, bundle: storyboardBundleOrNil) 48 | } 49 | } 50 | #endif 51 | } 52 | 53 | private var storyboardStack = [SwinjectStoryboard]() 54 | -------------------------------------------------------------------------------- /Sources/SwinjectStoryboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwinjectStoryboard.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 7/31/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Swinject 10 | #if canImport(UIKit) 11 | import UIKit 12 | #elseif canImport(Cocoa) 13 | import Cocoa 14 | #endif 15 | 16 | #if os(iOS) || os(tvOS) || os(OSX) 17 | 18 | /// The `SwinjectStoryboard` provides the features to inject dependencies of view/window controllers in a storyboard. 19 | /// 20 | /// To specify a registration name of a view/window controller registered to the `Container` as a service type, 21 | /// add a user defined runtime attribute with the following settings: 22 | /// 23 | /// - Key Path: `swinjectRegistrationName` 24 | /// - Type: String 25 | /// - Value: Registration name to the `Container` 26 | /// 27 | /// in User Defined Runtime Attributes section on Indentity Inspector pane. 28 | /// If no name is supplied to the registration, no runtime attribute should be specified. 29 | @objcMembers 30 | @objc(SwinjectStoryboard) 31 | public class SwinjectStoryboard: _SwinjectStoryboardBase, SwinjectStoryboardProtocol { 32 | /// A shared container used by SwinjectStoryboard instances that are instantiated without specific containers. 33 | /// 34 | /// Typical usecases of this property are: 35 | /// - Implicit instantiation of UIWindow and its root view controller from "Main" storyboard. 36 | /// - Storyboard references to transit from a storyboard to another. 37 | public static var defaultContainer = Container() 38 | 39 | // Boxing to workaround a runtime error [Xcode 7.1.1 and Xcode 7.2 beta 4] 40 | // If container property is Resolver type and a Resolver instance is assigned to the property, 41 | // the program crashes by EXC_BAD_ACCESS, which looks a bug of Swift. 42 | internal var container: Box! 43 | 44 | private override init() { 45 | super.init() 46 | } 47 | 48 | #if os(iOS) || os(tvOS) 49 | /// Creates the new instance of `SwinjectStoryboard`. This method is used instead of an initializer. 50 | /// 51 | /// - Parameters: 52 | /// - name: The name of the storyboard resource file without the filename extension. 53 | /// - storyboardBundleOrNil: The bundle containing the storyboard file and its resources. Specify nil to use the main bundle. 54 | /// 55 | /// - Note: 56 | /// The shared singleton container `SwinjectStoryboard.defaultContainer` is used as the container. 57 | /// 58 | /// - Returns: The new instance of `SwinjectStoryboard`. 59 | public class func create( 60 | name: String, 61 | bundle storyboardBundleOrNil: Bundle?) -> SwinjectStoryboard { 62 | return SwinjectStoryboard.create(name: name, bundle: storyboardBundleOrNil, 63 | container: SwinjectStoryboard.defaultContainer) 64 | } 65 | 66 | /// Creates the new instance of `SwinjectStoryboard`. This method is used instead of an initializer. 67 | /// 68 | /// - Parameters: 69 | /// - name: The name of the storyboard resource file without the filename extension. 70 | /// - storyboardBundleOrNil: The bundle containing the storyboard file and its resources. Specify nil to use the main bundle. 71 | /// - container: The container with registrations of the view/window controllers in the storyboard and their dependencies. 72 | /// 73 | /// - Returns: The new instance of `SwinjectStoryboard`. 74 | public class func create( 75 | name: String, 76 | bundle storyboardBundleOrNil: Bundle?, 77 | container: Resolver) -> SwinjectStoryboard 78 | { 79 | // Use this factory method to create an instance because the initializer of UI/NSStoryboard is "not inherited". 80 | let storyboard = SwinjectStoryboard._create(name, bundle: storyboardBundleOrNil) 81 | storyboard.container = Box(container) 82 | return storyboard 83 | } 84 | 85 | /// Instantiates the view controller with the specified identifier. 86 | /// The view controller and its child controllers have their dependencies injected 87 | /// as specified in the `Container` passed to the initializer of the `SwinjectStoryboard`. 88 | /// 89 | /// - Parameter identifier: The identifier set in the storyboard file. 90 | /// 91 | /// - Returns: The instantiated view controller with its dependencies injected. 92 | public override func instantiateViewController(withIdentifier identifier: String) -> UIViewController { 93 | SwinjectStoryboard.pushInstantiatingStoryboard(self) 94 | let viewController = super.instantiateViewController(withIdentifier: identifier) 95 | SwinjectStoryboard.popInstantiatingStoryboard() 96 | 97 | injectDependency(to: viewController) 98 | 99 | return viewController 100 | } 101 | 102 | private func injectDependency(to viewController: UIViewController) { 103 | guard !viewController.wasInjected else { return } 104 | defer { viewController.wasInjected = true } 105 | 106 | let registrationName = viewController.swinjectRegistrationName 107 | 108 | // Xcode 7.1 workaround for Issue #10. This workaround is not necessary with Xcode 7. 109 | // If a future update of Xcode fixes the problem, replace the resolution with the following code and fix storyboardInitCompleted too. 110 | // https://github.com/Swinject/Swinject/issues/10 111 | if let container = container.value as? _Resolver { 112 | let option = SwinjectStoryboardOption(controllerType: type(of: viewController)) 113 | typealias FactoryType = ((Resolver, Container.Controller)) -> Any 114 | let _ = container._resolve(name: registrationName, option: option) { (factory: FactoryType) in factory((self.container.value, viewController)) as Any } as Container.Controller? 115 | } else { 116 | fatalError("A type conforming Resolver protocol must conform _Resolver protocol too.") 117 | } 118 | 119 | #if swift(>=4.2) 120 | for child in viewController.children { 121 | injectDependency(to: child) 122 | } 123 | #else 124 | for child in viewController.childViewControllers { 125 | injectDependency(to: child) 126 | } 127 | #endif 128 | } 129 | 130 | #elseif os(OSX) 131 | /// Creates the new instance of `SwinjectStoryboard`. This method is used instead of an initializer. 132 | /// 133 | /// - Parameters: 134 | /// - name: The name of the storyboard resource file without the filename extension. 135 | /// - storyboardBundleOrNil: The bundle containing the storyboard file and its resources. Specify nil to use the main bundle. 136 | /// 137 | /// - Note: 138 | /// The shared singleton container `SwinjectStoryboard.defaultContainer` is used as the container. 139 | /// 140 | /// - Returns: The new instance of `SwinjectStoryboard`. 141 | public class func create( 142 | name: NSStoryboard.Name, 143 | bundle storyboardBundleOrNil: Bundle?) -> SwinjectStoryboard { 144 | return SwinjectStoryboard.create(name: name, bundle: storyboardBundleOrNil, 145 | container: SwinjectStoryboard.defaultContainer) 146 | } 147 | 148 | /// Creates the new instance of `SwinjectStoryboard`. This method is used instead of an initializer. 149 | /// 150 | /// - Parameters: 151 | /// - name: The name of the storyboard resource file without the filename extension. 152 | /// - storyboardBundleOrNil: The bundle containing the storyboard file and its resources. Specify nil to use the main bundle. 153 | /// - container: The container with registrations of the view/window controllers in the storyboard and their dependencies. 154 | /// 155 | /// - Returns: The new instance of `SwinjectStoryboard`. 156 | public class func create( 157 | name: NSStoryboard.Name, 158 | bundle storyboardBundleOrNil: Bundle?, 159 | container: Resolver) -> SwinjectStoryboard 160 | { 161 | // Use this factory method to create an instance because the initializer of UI/NSStoryboard is "not inherited". 162 | let storyboard = SwinjectStoryboard._create(name, bundle: storyboardBundleOrNil) 163 | storyboard.container = Box(container) 164 | return storyboard 165 | } 166 | 167 | /// Instantiates the view/Window controller with the specified identifier. 168 | /// The view/window controller and its child controllers have their dependencies injected 169 | /// as specified in the `Container` passed to the initializer of the `SwinjectStoryboard`. 170 | /// 171 | /// - Parameter identifier: The identifier set in the storyboard file. 172 | /// 173 | /// - Returns: The instantiated view/window controller with its dependencies injected. 174 | public override func instantiateController(withIdentifier identifier: NSStoryboard.SceneIdentifier) -> Any { 175 | SwinjectStoryboard.pushInstantiatingStoryboard(self) 176 | let controller = super.instantiateController(withIdentifier: identifier) 177 | SwinjectStoryboard.popInstantiatingStoryboard() 178 | 179 | injectDependency(to: controller) 180 | 181 | return controller 182 | } 183 | 184 | private func injectDependency(to controller: Container.Controller) { 185 | guard let controller = controller as? InjectionVerifiable, !controller.wasInjected else { return } 186 | defer { controller.wasInjected = true } 187 | 188 | let registrationName = (controller as? RegistrationNameAssociatable)?.swinjectRegistrationName 189 | 190 | // Xcode 7.1 workaround for Issue #10. This workaround is not necessary with Xcode 7. 191 | // If a future update of Xcode fixes the problem, replace the resolution with the following code and fix storyboardInitCompleted too: 192 | // https://github.com/Swinject/Swinject/issues/10 193 | if let container = container.value as? _Resolver { 194 | let option = SwinjectStoryboardOption(controllerType: type(of: controller)) 195 | typealias FactoryType = ((Resolver, Container.Controller)) -> Any 196 | let _ = container._resolve(name: registrationName, option: option) { (factory: FactoryType) -> Any in factory((self.container.value, controller)) } as Container.Controller? 197 | } else { 198 | fatalError("A type conforming Resolver protocol must conform _Resolver protocol too.") 199 | } 200 | if let windowController = controller as? NSWindowController, let viewController = windowController.contentViewController { 201 | injectDependency(to: viewController) 202 | } 203 | if let viewController = controller as? NSViewController { 204 | #if swift(>=4.2) 205 | for child in viewController.children { 206 | injectDependency(to: child) 207 | } 208 | #else 209 | for child in viewController.childViewControllers { 210 | injectDependency(to: child) 211 | } 212 | #endif 213 | } 214 | } 215 | #endif 216 | } 217 | 218 | #endif 219 | -------------------------------------------------------------------------------- /Sources/SwinjectStoryboardOption.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwinjectStoryboardOption.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 2/28/16. 6 | // Copyright © 2016 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Swinject 10 | 11 | #if os(iOS) || os(OSX) || os(tvOS) 12 | internal struct SwinjectStoryboardOption: ServiceKeyOption { 13 | internal let controllerType: String 14 | 15 | internal init(controllerType: Container.Controller.Type) { 16 | self.controllerType = String(reflecting: controllerType) 17 | } 18 | 19 | internal func isEqualTo(_ another: ServiceKeyOption) -> Bool { 20 | guard let another = another as? SwinjectStoryboardOption else { 21 | return false 22 | } 23 | 24 | return self.controllerType == another.controllerType 25 | } 26 | 27 | internal var hashValue: Int { 28 | return controllerType.hashValue 29 | } 30 | 31 | internal var description: String { 32 | return "Storyboard: \(controllerType)" 33 | } 34 | 35 | func hash(into: inout Hasher) { 36 | into.combine(controllerType) 37 | } 38 | } 39 | #endif 40 | -------------------------------------------------------------------------------- /Sources/SwinjectStoryboardProtocol.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objc 4 | public protocol SwinjectStoryboardProtocol { 5 | 6 | /// Called by Swinject framework once before SwinjectStoryboard is instantiated. 7 | /// 8 | /// - Note: 9 | /// Implement this method and setup the default container if you implicitly instantiate UIWindow 10 | /// and its root view controller from "Main" storyboard. 11 | /// 12 | /// ```swift 13 | /// extension SwinjectStoryboard { 14 | /// @objc class func setup() { 15 | /// let container = defaultContainer 16 | /// container.register(SomeType.self) { 17 | /// _ in 18 | /// SomeClass() 19 | /// } 20 | /// container.storyboardInitCompleted(ViewController.self) { 21 | /// r, c in 22 | /// c.something = r.resolve(SomeType.self) 23 | /// } 24 | /// } 25 | /// } 26 | /// ``` 27 | @objc optional static func setup() 28 | } 29 | -------------------------------------------------------------------------------- /Sources/UnavailableItems.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnavailableItems.swift 3 | // SwinjectStoryboard 4 | // 5 | // Created by Yoichi Tagaya on 12/10/16. 6 | // Copyright © 2016 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | // MARK: For auto migration to Swinject v1. 10 | @available(*, unavailable, renamed: "SwinjectStoryboardProtocol") 11 | public protocol SwinjectStoryboardType { } 12 | -------------------------------------------------------------------------------- /Sources/ViewController+Swinject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController+Swinject.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 7/31/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import ObjectiveC 10 | 11 | #if canImport(UIKit) 12 | import UIKit 13 | 14 | private var uivcRegistrationNameKey: String = "UIViewController.swinjectRegistrationName" 15 | private var uivcWasInjectedKey: String = "UIViewController.wasInjected" 16 | 17 | extension UIViewController: RegistrationNameAssociatable, InjectionVerifiable { 18 | public var swinjectRegistrationName: String? { 19 | get { return getAssociatedString(key: &uivcRegistrationNameKey) } 20 | set { setAssociatedString(newValue, key: &uivcRegistrationNameKey) } 21 | } 22 | 23 | public var wasInjected: Bool { 24 | get { return getAssociatedBool(key: &uivcWasInjectedKey) ?? false } 25 | set { setAssociatedBool(newValue, key: &uivcWasInjectedKey) } 26 | } 27 | } 28 | 29 | #elseif canImport(Cocoa) 30 | import Cocoa 31 | 32 | private var nsvcRegistrationNameKey: String = "NSViewController.swinjectRegistrationName" 33 | private var nswcRegistrationNameKey: String = "NSWindowController.swinjectRegistrationName" 34 | private var nsvcWasInjectedKey: String = "NSViewController.wasInjected" 35 | private var nswcWasInjectedKey: String = "NSWindowController.wasInjected" 36 | 37 | extension NSViewController: RegistrationNameAssociatable, InjectionVerifiable { 38 | internal var swinjectRegistrationName: String? { 39 | get { return getAssociatedString(key: &nsvcRegistrationNameKey) } 40 | set { setAssociatedString(newValue, key: &nsvcRegistrationNameKey) } 41 | } 42 | 43 | internal var wasInjected: Bool { 44 | get { return getAssociatedBool(key: &nsvcWasInjectedKey) ?? false } 45 | set { setAssociatedBool(newValue, key: &nsvcWasInjectedKey) } 46 | } 47 | } 48 | 49 | extension NSWindowController: RegistrationNameAssociatable, InjectionVerifiable { 50 | internal var swinjectRegistrationName: String? { 51 | get { return getAssociatedString(key: &nsvcRegistrationNameKey) } 52 | set { setAssociatedString(newValue, key: &nsvcRegistrationNameKey) } 53 | } 54 | 55 | internal var wasInjected: Bool { 56 | get { return getAssociatedBool(key: &nswcWasInjectedKey) ?? false } 57 | set { setAssociatedBool(newValue, key: &nswcWasInjectedKey) } 58 | } 59 | } 60 | 61 | #endif 62 | 63 | extension NSObject { 64 | fileprivate func getAssociatedString(key: UnsafeRawPointer) -> String? { 65 | return objc_getAssociatedObject(self, key) as? String 66 | } 67 | 68 | fileprivate func setAssociatedString(_ string: String?, key: UnsafeRawPointer) { 69 | objc_setAssociatedObject(self, key, string, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY) 70 | } 71 | 72 | fileprivate func getAssociatedBool(key: UnsafeRawPointer) -> Bool? { 73 | return (objc_getAssociatedObject(self, key) as? NSNumber)?.boolValue 74 | } 75 | 76 | fileprivate func setAssociatedBool(_ bool: Bool, key: UnsafeRawPointer) { 77 | objc_setAssociatedObject(self, key, NSNumber(value: bool), objc_AssociationPolicy.OBJC_ASSOCIATION_COPY) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/iOS-tvOS/UIStoryboard+Swizzling.swift: -------------------------------------------------------------------------------- 1 | #if canImport(UIKit) 2 | import UIKit 3 | 4 | extension UIStoryboard { 5 | static func swizzling() { 6 | DispatchQueue.once(token: "swinject.storyboard.init") { 7 | let aClass: AnyClass = object_getClass(self)! 8 | 9 | let originalSelector = #selector(UIStoryboard.init(name:bundle:)) 10 | let swizzledSelector = #selector(swinject_init(name:bundle:)) 11 | 12 | let originalMethod = class_getInstanceMethod(aClass, originalSelector)! 13 | let swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector)! 14 | 15 | let didAddMethod = class_addMethod(aClass, originalSelector, 16 | method_getImplementation(swizzledMethod), 17 | method_getTypeEncoding(swizzledMethod)) 18 | 19 | guard didAddMethod else { 20 | method_exchangeImplementations(originalMethod, swizzledMethod) 21 | return 22 | } 23 | class_replaceMethod(aClass, swizzledSelector, 24 | method_getImplementation(originalMethod), 25 | method_getTypeEncoding(originalMethod)) 26 | } 27 | } 28 | 29 | @objc class func swinject_init(name: String, bundle: Bundle?) -> UIStoryboard { 30 | guard self == UIStoryboard.self else { 31 | return self.swinject_init(name: name, bundle: bundle) 32 | } 33 | // Instantiate SwinjectStoryboard if UIStoryboard is trying to be instantiated. 34 | if SwinjectStoryboard.isCreatingStoryboardReference { 35 | return SwinjectStoryboard.createReferenced(name: name, bundle: bundle) 36 | } else { 37 | return SwinjectStoryboard.create(name: name, bundle: bundle) 38 | } 39 | } 40 | } 41 | #endif 42 | -------------------------------------------------------------------------------- /Sources/iOS-tvOS/_SwinjectStoryboardBase(iOS-tvOS).swift: -------------------------------------------------------------------------------- 1 | #if canImport(UIKit) 2 | import UIKit 3 | import Swinject 4 | 5 | @objcMembers 6 | public class _SwinjectStoryboardBase: UIStoryboard { 7 | public class func _create(_ name: String, bundle storyboardBundleOrNil: Bundle?) -> Self { 8 | let storyboard = perform(#selector(UIStoryboard.init(name:bundle:)), with: name, with: storyboardBundleOrNil)? 9 | .takeUnretainedValue() 10 | return storyboard as! Self 11 | } 12 | } 13 | 14 | extension SwinjectStoryboard { 15 | @objc public static func configure() { 16 | UIStoryboard.swizzling() 17 | DispatchQueue.once(token: "swinject.storyboard.setup") { 18 | guard SwinjectStoryboard.responds(to: _Selector("setup")) else { return } 19 | SwinjectStoryboard.perform(_Selector("setup")) 20 | } 21 | } 22 | 23 | static func _Selector(_ str: String) -> Selector { 24 | return Selector(str) 25 | } 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /SwinjectStoryboard.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SwinjectStoryboard" 3 | s.version = "2.2.3" 4 | s.summary = "Swinject extension for automatic dependency injection via Storyboard" 5 | s.description = <<-DESC 6 | SwinjectStoryboard is an extension of Swinject to automatically inject dependency to view controllers instantiated by a storyboard. 7 | DESC 8 | s.homepage = "https://github.com/Swinject/SwinjectStoryboard" 9 | s.license = 'MIT' 10 | s.author = 'Swinject Contributors' 11 | s.source = { :git => "https://github.com/Swinject/SwinjectStoryboard.git", :tag => s.version.to_s } 12 | 13 | objc_files = 'Sources/ObjectiveC/Others/*.{swift,m,h}' 14 | core_files = 'Sources/*.{swift,m,h}' 15 | s.swift_versions = '5.0' 16 | s.ios.source_files = core_files, objc_files, 'Sources/iOS-tvOS/*.{swift,h,m}' 17 | s.osx.source_files = core_files, objc_files, 'Sources/OSX/*.{swift,h,m}' 18 | s.tvos.source_files = core_files, objc_files, 'Sources/iOS-tvOS/*.{swift,h,m}' 19 | s.ios.deployment_target = '8.0' 20 | s.osx.deployment_target = '10.10' 21 | s.tvos.deployment_target = '9.0' 22 | s.dependency 'Swinject', '~> 2.7' 23 | s.requires_arc = true 24 | end 25 | -------------------------------------------------------------------------------- /SwinjectStoryboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwinjectStoryboard.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwinjectStoryboard.xcodeproj/xcshareddata/xcschemes/SwinjectStoryboard-OSX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /SwinjectStoryboard.xcodeproj/xcshareddata/xcschemes/SwinjectStoryboard-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /SwinjectStoryboard.xcodeproj/xcshareddata/xcschemes/SwinjectStoryboard-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Tests/Animal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Animal.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 7/27/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal protocol Animal { 12 | var name: String? { get set } 13 | } 14 | 15 | internal class Cat: Animal { 16 | var name: String? 17 | var sleeping = false 18 | var favoriteFood: Food? 19 | 20 | init() { 21 | } 22 | 23 | init(name: String) { 24 | self.name = name 25 | } 26 | 27 | init(name: String, sleeping: Bool) { 28 | self.name = name 29 | self.sleeping = sleeping 30 | } 31 | } 32 | 33 | internal class Siamese: Cat { 34 | } 35 | 36 | internal class Dog: Animal { 37 | var name: String? 38 | 39 | init() { 40 | } 41 | 42 | init(name: String) { 43 | self.name = name 44 | } 45 | } 46 | 47 | internal struct Turtle: Animal { 48 | var name: String? 49 | } 50 | -------------------------------------------------------------------------------- /Tests/Container+SwinjectStoryboardTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Container+SwinjectStoryboardTests.swift 3 | // SwinjectStoryboard 4 | // 5 | // Created by Yoichi Tagaya on 2021/07/03. 6 | // Copyright © 2021 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Swinject 11 | 12 | #if os(iOS) || os(OSX) || os(tvOS) 13 | 14 | class Container_SwinjectStoryboardTests: XCTestCase { 15 | func testCustomStringConvertibleDescribesRegistrationWithStoryboardOption() { 16 | let container = Container() 17 | let controllerType = String(describing: Container.Controller.self) // "UIViewController" for iOS/tvOS, "AnyObject" for OSX. 18 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in } 19 | 20 | let expected = "[\n" 21 | + " { Service: \(controllerType), Storyboard: SwinjectStoryboardTests.AnimalViewController, " 22 | + "Factory: (Resolver, \(controllerType)) -> \(controllerType), ObjectScope: graph, InitCompleted: Specified 1 closures }\n" 23 | + "]" 24 | XCTAssertEqual(container.description, expected) 25 | } 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /Tests/Food.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Food.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 7/29/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal protocol Food { } 12 | 13 | internal class Sushi: Food { } 14 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 2.2.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/NSWindowController+SwinjectTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSWindowController+SwinjectTests.swift 3 | // SwinjectStoryboard 4 | // 5 | // Created by Yoichi Tagaya on 2021/07/03. 6 | // Copyright © 2021 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | #if os(OSX) 10 | 11 | import XCTest 12 | @testable import SwinjectStoryboard 13 | 14 | class NSWindowController_SwinjectTests: XCTestCase { 15 | func testPropertyToStoreSwinjectContainerRegistrationName() { 16 | let controller1 = NSWindowController(window: nil) 17 | let controller2 = NSWindowController(window: nil) 18 | controller1.swinjectRegistrationName = "1" 19 | controller2.swinjectRegistrationName = "2" 20 | 21 | XCTAssertEqual(controller1.swinjectRegistrationName, "1") 22 | XCTAssertEqual(controller2.swinjectRegistrationName, "2") 23 | } 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /Tests/OSX/AnimalPagesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimalPagesViewController.swift 3 | // Swinject 4 | // 5 | // Created by Jakub Vaňo on 27/10/16. 6 | // Copyright © 2016 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | import Swinject 11 | 12 | internal class AnimalPagesViewController: NSPageController { 13 | let animalPage: AnimalViewController 14 | 15 | required init?(coder aDecoder: NSCoder) { 16 | animalPage = NSStoryboard( 17 | name: NSStoryboard.Name("Pages"), 18 | bundle: Bundle(for: AnimalPagesViewController.self) 19 | ).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("AnimalPage")) as! AnimalViewController 20 | 21 | super.init(coder: aDecoder) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/OSX/AnimalViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimalViewController.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 8/1/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | 11 | internal class AnimalViewController: NSViewController { 12 | internal var animal: Animal? 13 | 14 | internal required init?(coder: NSCoder) { 15 | super.init(coder: coder) 16 | } 17 | 18 | internal func hasAnimal(named name: String) -> Bool { 19 | return animal?.name == name 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/OSX/AnimalWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimalWindowController.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 8/1/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | 11 | internal class AnimalWindowController: NSWindowController { 12 | internal var animal: Animal? 13 | 14 | internal required init?(coder: NSCoder) { 15 | super.init(coder: coder) 16 | } 17 | 18 | internal func hasAnimal(named name: String) -> Bool { 19 | return animal?.name == name 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/OSX/Animals.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Tests/OSX/Pages.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Tests/OSX/RelationshipReference1.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Tests/OSX/RelationshipReference2.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Tests/OSX/Storyboard1.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Tests/OSX/Storyboard2.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/OSX/SwinjectStoryboardTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwinjectStoryboardTests.swift 3 | // SwinjectStoryboard-OSXTests 4 | // 5 | // Created by Yoichi Tagaya on 2021/07/03. 6 | // Copyright © 2021 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Swinject 11 | @testable import SwinjectStoryboard 12 | 13 | private var swinjectStoryboardSetupCount = 0 14 | extension SwinjectStoryboard { 15 | static func setup() { 16 | swinjectStoryboardSetupCount += 1 17 | } 18 | } 19 | 20 | class SwinjectStoryboardTests: XCTestCase { 21 | let bundle = Bundle(for: SwinjectStoryboardTests.self) 22 | var container: Container! 23 | 24 | override func setUpWithError() throws { 25 | container = Container() 26 | } 27 | 28 | // MARK: Instantiation from storyboard 29 | func testSwinjectStoryboardInjectsViewControllerDependencyDefindedByInitCompletedHandler() { 30 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 31 | c.animal = r.resolve(Animal.self) 32 | } 33 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 34 | 35 | let storyboard = SwinjectStoryboard.create(name: .animals, bundle: bundle, container: container) 36 | let animalViewController = storyboard.instantiateController(withIdentifier: .animalView) as! AnimalViewController 37 | XCTAssert(animalViewController.hasAnimal(named: "Mimi")) 38 | } 39 | 40 | func testSwinjectStoryboardInjectsWindowControllerDependencyDefindedByInitCompletedHandler() { 41 | container.storyboardInitCompleted(AnimalWindowController.self) { r, c in 42 | c.animal = r.resolve(Animal.self) 43 | } 44 | container.register(Animal.self) { _ in Cat(name: "Mew") } 45 | 46 | let storyboard = SwinjectStoryboard.create(name: .animals, bundle: bundle, container: container) 47 | let animalViewController = storyboard.instantiateController(withIdentifier: .animalWindow) as! AnimalWindowController 48 | XCTAssert(animalViewController.hasAnimal(named: "Mew")) 49 | } 50 | 51 | func testSwinjectStoryboardInjectsDependencyToChildViewControllers() { 52 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 53 | c.animal = r.resolve(Animal.self) 54 | } 55 | container.register(Animal.self) { _ in Cat() } 56 | .inObjectScope(.container) 57 | 58 | let storyboard = SwinjectStoryboard.create(name: .tabs, bundle: bundle, container: container) 59 | let tabBarController = storyboard.instantiateController(withIdentifier: .tabBarController) as! NSTabViewController 60 | #if swift(>=4.2) 61 | let animalViewController1 = tabBarController.children[0] as! AnimalViewController 62 | let animalViewController2 = tabBarController.children[1] as! AnimalViewController 63 | #else 64 | let animalViewController1 = tabBarController.childViewControllers[0] as! AnimalViewController 65 | let animalViewController2 = tabBarController.childViewControllers[1] as! AnimalViewController 66 | #endif 67 | let cat1 = animalViewController1.animal as! Cat 68 | let cat2 = animalViewController2.animal as! Cat 69 | XCTAssert(cat1 === cat2) 70 | } 71 | 72 | func testSwinjectStoryboardInjectsViewContrllerDependencyDefindedByInitCompletedHandlerWithRegistrationNameAsUserDefinedRuntimeAttributeOnInterfaceBuilder() { 73 | // The registration name "hachi" is set in the storyboard. 74 | container.storyboardInitCompleted(AnimalViewController.self, name: "hachi") { r, c in 75 | c.animal = r.resolve(Animal.self) 76 | } 77 | container.register(Animal.self) { _ in Dog(name: "Hachi") } 78 | 79 | // This registration should not be resolved. 80 | container.storyboardInitCompleted(AnimalViewController.self) { _, c in 81 | c.animal = Cat(name: "Mimi") 82 | } 83 | 84 | let storyboard = SwinjectStoryboard.create(name: .animals, bundle: bundle, container: container) 85 | let animalViewController = storyboard.instantiateController(withIdentifier: .hachiView) as! AnimalViewController 86 | XCTAssert(animalViewController.hasAnimal(named: "Hachi")) 87 | } 88 | 89 | func testSwinjectStoryboardInjectsWindowContrllerDependencyDefindedByInitCompletedHandlerWithRegistrationNameAsUserDefinedRuntimeAttributeOnInterfaceBuilder() { 90 | // The registration name "hachi" is set in the storyboard. 91 | container.storyboardInitCompleted(AnimalWindowController.self, name: "pochi") { r, c in 92 | c.animal = r.resolve(Animal.self) 93 | } 94 | container.register(Animal.self) { _ in Dog(name: "Pochi") } 95 | 96 | // This registration should not be resolved. 97 | container.storyboardInitCompleted(AnimalWindowController.self) { _, c in 98 | c.animal = Cat(name: "Mimi") 99 | } 100 | 101 | let storyboard = SwinjectStoryboard.create(name: .animals, bundle: bundle, container: container) 102 | let animalViewController = storyboard.instantiateController(withIdentifier: .pochiWindow) as! AnimalWindowController 103 | XCTAssert(animalViewController.hasAnimal(named: "Pochi")) 104 | } 105 | 106 | func testSwinjectStoryboardInjectsViewControllerDependencyDefindedInParentContainerWithContainerHierarchy() { 107 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 108 | c.animal = r.resolve(Animal.self) 109 | } 110 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 111 | let childContainer = Container(parent: container) 112 | 113 | let storyboard = SwinjectStoryboard.create(name: .animals, bundle: bundle, container: childContainer) 114 | let animalViewController = storyboard.instantiateController(withIdentifier: .animalView) as! AnimalViewController 115 | XCTAssert(animalViewController.hasAnimal(named: "Mimi")) 116 | } 117 | 118 | func testSwinjectStoryboardInjectsSecondControllerWithSecondControllerInstantiationDuringInstantiationOfInitialOne() { 119 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 120 | c.animal = r.resolve(Animal.self) 121 | } 122 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 123 | 124 | let storyboard = SwinjectStoryboard.create(name: .pages, bundle: bundle, container: container) 125 | let pagesController = storyboard.instantiateInitialController() as! AnimalPagesViewController 126 | XCTAssert(pagesController.animalPage.hasAnimal(named: "Mimi")) 127 | } 128 | 129 | // MARK: Initial controller 130 | func testInitialViewControllerInjectsDependencyDefindedByInitCompletedHandler() { 131 | container.storyboardInitCompleted(AnimalWindowController.self) { r, c in 132 | c.animal = r.resolve(Animal.self) 133 | } 134 | container.register(Animal.self) { _ in Cat(name: "Mew") } 135 | 136 | let storyboard = SwinjectStoryboard.create(name: .animals, bundle: bundle, container: container) 137 | let animalViewController = storyboard.instantiateInitialController() as! AnimalWindowController 138 | XCTAssert(animalViewController.hasAnimal(named: "Mew")) 139 | } 140 | 141 | // MARK: Factory method 142 | func testFactoryMethodUsesDefaultSharedContainerIfNoContainerIsPassed() { 143 | SwinjectStoryboard.defaultContainer.storyboardInitCompleted(AnimalViewController.self) { _, _ in } 144 | defer { 145 | SwinjectStoryboard.defaultContainer.removeAll() 146 | } 147 | 148 | let storyboard = SwinjectStoryboard.create(name: .animals, bundle: bundle) 149 | let animalViewController = storyboard.instantiateController(withIdentifier: .animalView) 150 | XCTAssertNotNil(animalViewController) 151 | } 152 | 153 | // MARK: Storyboard reference 154 | func testStoryboardReferenceInjectsDependencyToViewControllerInReferencedStoryboard() { 155 | SwinjectStoryboard.defaultContainer.storyboardInitCompleted(AnimalViewController.self) { r, c in 156 | c.animal = r.resolve(Animal.self) 157 | } 158 | SwinjectStoryboard.defaultContainer.register(Animal.self) { _ in Cat(name: "Mimi") } 159 | defer { 160 | SwinjectStoryboard.defaultContainer.removeAll() 161 | } 162 | 163 | let storyboard1 = SwinjectStoryboard.create(name: .storyboard1, bundle: bundle) 164 | let windowController = storyboard1.instantiateInitialController() as! NSWindowController 165 | let viewController1 = windowController.contentViewController as! ViewController1 166 | viewController1.performSegue(withIdentifier: .toStoryboard2, sender: nil) 167 | 168 | assertEventually { 169 | viewController1.animalViewController?.hasAnimal(named: "Mimi") == true 170 | } 171 | } 172 | 173 | func testStoryboardReferenceShouldInjectDependenciesOnceIfReferencingStoryboardViaRelationshipSegue() { 174 | var injectedTimes = 0 175 | SwinjectStoryboard.defaultContainer.storyboardInitCompleted(ViewController1.self) { r, c in 176 | injectedTimes += 1 177 | } 178 | defer { 179 | SwinjectStoryboard.defaultContainer.removeAll() 180 | } 181 | 182 | let storyboard = SwinjectStoryboard.create(name: .relationshipReference1, bundle: bundle) 183 | storyboard.instantiateInitialController() 184 | 185 | XCTAssertEqual(injectedTimes, 1) 186 | } 187 | 188 | func testStoryboardReferenceInjectsDependencyToViewControllerpenedViaSegueIfNotUsingDefaultContainer() { 189 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 190 | c.animal = r.resolve(Animal.self) 191 | } 192 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 193 | 194 | let storyboard = SwinjectStoryboard.create(name: .relationshipReference1, bundle: bundle, container: container) 195 | let windowController = storyboard.instantiateInitialController() as! NSWindowController 196 | let viewController1 = windowController.contentViewController as! ViewController1 197 | viewController1.performSegue(withIdentifier: .toAnimalViewController, sender: nil) 198 | 199 | assertEventually { 200 | viewController1.animalViewController?.hasAnimal(named: "Mimi") == true 201 | } 202 | } 203 | 204 | // MARK: Setup 205 | func testSetupIsCalledOnlyOnce() { 206 | _ = SwinjectStoryboard.create(name: .animals, bundle: bundle) 207 | _ = SwinjectStoryboard.create(name: .animals, bundle: bundle) 208 | XCTAssertEqual(swinjectStoryboardSetupCount, 1) 209 | } 210 | } 211 | 212 | private extension NSStoryboard.Name { 213 | static let animals = NSStoryboard.Name("Animals") 214 | static let storyboard1 = NSStoryboard.Name("Storyboard1") 215 | static let relationshipReference1 = NSStoryboard.Name("RelationshipReference1") 216 | static let pages = NSStoryboard.Name("Pages") 217 | static let tabs = NSStoryboard.Name("Tabs") 218 | } 219 | 220 | private extension NSStoryboard.SceneIdentifier { 221 | static let animalView = NSStoryboard.SceneIdentifier("AnimalView") 222 | static let animalWindow = NSStoryboard.SceneIdentifier("AnimalWindow") 223 | static let tabBarController = NSStoryboard.SceneIdentifier("TabBarController") 224 | static let hachiView = NSStoryboard.SceneIdentifier("HachiView") 225 | static let pochiWindow = NSStoryboard.SceneIdentifier("PochiWindow") 226 | } 227 | 228 | private extension NSStoryboardSegue.Identifier { 229 | static let toStoryboard2 = NSStoryboardSegue.Identifier("ToStoryboard2") 230 | static let toAnimalViewController = NSStoryboardSegue.Identifier("ToAnimalViewController") 231 | } 232 | 233 | // Similar to toEventually of Nimble. 234 | private func assertEventually(expression: @escaping () -> Bool) { 235 | let expectation = XCTestExpectation() 236 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 237 | XCTAssert(expression()) 238 | expectation.fulfill() 239 | } 240 | XCTWaiter().wait(for: [expectation], timeout: 1.0) 241 | } 242 | -------------------------------------------------------------------------------- /Tests/OSX/Tabs.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Tests/OSX/ViewController1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController1.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 11/2/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController1: NSViewController { 12 | var animalViewController: AnimalViewController? 13 | 14 | override func prepare(for segue: NSStoryboardSegue, sender: Any?) { 15 | super.prepare(for: segue, sender: sender) 16 | self.animalViewController = segue.destinationController as? AnimalViewController 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/Storyboard+SwizzlingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Storyboard+SwizzlingTests.swift 3 | // SwinjectStoryboard 4 | // 5 | // Created by Yoichi Tagaya on 2021/07/03. 6 | // Copyright © 2021 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwinjectStoryboard 11 | 12 | #if os(iOS) || os(tvOS) 13 | private typealias Storyboard = UIStoryboard 14 | private typealias Name = String 15 | #elseif os(OSX) 16 | private typealias Storyboard = NSStoryboard 17 | private typealias Name = NSStoryboard.Name 18 | #endif 19 | 20 | #if os(iOS) || os(OSX) || os(tvOS) 21 | 22 | class Storyboard_SwizzlingTests: XCTestCase { 23 | let bundle = Bundle(for: Storyboard_SwizzlingTests.self) 24 | 25 | func testSwinjectStoryboardIsInstantiatedWhenStoryboardIsTriedToBeInstantiated() { 26 | let storyboard = Storyboard(name: Name("Animals"), bundle: bundle) 27 | XCTAssert(storyboard is SwinjectStoryboard) 28 | } 29 | 30 | func testSwinjectStoryboardDoesNotHaveInfinitCallsOfSwizzledMethodWhenExplicitlyInstantiated() { 31 | let storyboard = SwinjectStoryboard.create(name: Name("Animals"), bundle: bundle) 32 | XCTAssertNotNil(storyboard) 33 | } 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Tests/ViewController+SwinjectTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController+SwinjectTests.swift 3 | // SwinjectStoryboard 4 | // 5 | // Created by Yoichi Tagaya on 2021/07/03. 6 | // Copyright © 2021 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwinjectStoryboard 11 | 12 | #if os(iOS) || os(tvOS) 13 | private let createViewController = { UIViewController(nibName: nil, bundle: nil) } 14 | #elseif os(OSX) 15 | private let createViewController = { NSViewController(nibName: nil, bundle: nil) } 16 | #endif 17 | 18 | #if os(iOS) || os(OSX) || os(tvOS) 19 | 20 | class ViewController_SwinjectTests: XCTestCase { 21 | 22 | func testPropertyToStoreSwinjectContainerRegistrationName() { 23 | let viewController1 = createViewController() 24 | let viewController2 = createViewController() 25 | viewController1.swinjectRegistrationName = "1" 26 | viewController2.swinjectRegistrationName = "2" 27 | 28 | XCTAssertEqual(viewController1.swinjectRegistrationName, "1") 29 | XCTAssertEqual(viewController2.swinjectRegistrationName, "2") 30 | } 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/AnimalPagesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimalPagesViewController.swift 3 | // Swinject 4 | // 5 | // Created by Jakub Vaňo on 27/10/16. 6 | // Copyright © 2016 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Swinject 11 | 12 | internal class AnimalPagesViewController: UIPageViewController { 13 | let animalPage: AnimalViewController 14 | 15 | required init?(coder aDecoder: NSCoder) { 16 | animalPage = UIStoryboard( 17 | name: "Pages", 18 | bundle: Bundle(for: AnimalPagesViewController.self) 19 | ).instantiateViewController(withIdentifier: "AnimalPage") as! AnimalViewController 20 | 21 | super.init(coder: aDecoder) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/AnimalPlayerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimalPlayerViewController.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 11/1/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import AVKit 10 | 11 | internal class AnimalPlayerViewController: AVPlayerViewController { 12 | internal var animal: Animal? 13 | 14 | internal required init?(coder aDecoder: NSCoder) { 15 | super.init(coder: aDecoder) 16 | } 17 | 18 | internal func hasAnimal(named name: String) -> Bool { 19 | return animal?.name == name 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/AnimalViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimalViewController.swift 3 | // Swinject 4 | // 5 | // Created by Yoichi Tagaya on 7/31/15. 6 | // Copyright © 2015 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | internal class AnimalViewController: UIViewController { 12 | internal var animal: Animal? 13 | 14 | internal required init?(coder aDecoder: NSCoder) { 15 | super.init(coder: aDecoder) 16 | } 17 | 18 | internal func hasAnimal(named name: String) -> Bool { 19 | return animal?.name == name 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/iOS/AnimalPlayerViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/iOS/Animals.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/iOS/Pages.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/iOS/RelationshipReference1.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/iOS/RelationshipReference2.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/iOS/Storyboard1.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/iOS/Storyboard2.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/iOS/Tabs.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/tvOS/AnimalPlayerViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/tvOS/Animals.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/tvOS/Pages.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/tvOS/RelationshipReference1.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/tvOS/RelationshipReference2.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/tvOS/Storyboard1.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/tvOS/Storyboard2.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/Storyboards/tvOS/Tabs.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Tests/iOS-tvOS/SwinjectStoryboardTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwinjectStoryboardTests.swift 3 | // SwinjectStoryboard 4 | // 5 | // Created by Yoichi Tagaya on 2021/07/03. 6 | // Copyright © 2021 Swinject Contributors. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Swinject 11 | @testable import SwinjectStoryboard 12 | 13 | private var swinjectStoryboardSetupCount = 0 14 | extension SwinjectStoryboard { 15 | static func setup() { 16 | swinjectStoryboardSetupCount += 1 17 | } 18 | } 19 | 20 | class SwinjectStoryboardTests: XCTestCase { 21 | let bundle = Bundle(for: SwinjectStoryboardTests.self) 22 | var container: Container! 23 | 24 | override func setUpWithError() throws { 25 | container = Container() 26 | } 27 | 28 | // MARK: Instantiation from storyboard 29 | func testSwinjectStoryboardInjectsDependencyDefindedByInitCompletedHandler() { 30 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 31 | c.animal = r.resolve(Animal.self) 32 | } 33 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 34 | 35 | let storyboard = SwinjectStoryboard.create(name: "Animals", bundle: bundle, container: container) 36 | let animalViewController = storyboard.instantiateViewController(withIdentifier: "AnimalAsCat") as! AnimalViewController 37 | XCTAssert(animalViewController.hasAnimal(named: "Mimi")) 38 | } 39 | 40 | func testSwinjectStoryboardInjectsDependencyToChildViewControllers() { 41 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 42 | c.animal = r.resolve(Animal.self) 43 | } 44 | container.register(Animal.self) { _ in Cat() } 45 | .inObjectScope(.container) 46 | 47 | let storyboard = SwinjectStoryboard.create(name: "Tabs", bundle: bundle, container: container) 48 | let tabBarController = storyboard.instantiateViewController(withIdentifier: "TabBarController") 49 | #if swift(>=4.2) 50 | let animalViewController1 = tabBarController.children[0] as! AnimalViewController 51 | let animalViewController2 = tabBarController.children[1] as! AnimalViewController 52 | #else 53 | let animalViewController1 = tabBarController.childViewControllers[0] as! AnimalViewController 54 | let animalViewController2 = tabBarController.childViewControllers[1] as! AnimalViewController 55 | #endif 56 | let cat1 = animalViewController1.animal as! Cat 57 | let cat2 = animalViewController2.animal as! Cat 58 | XCTAssert(cat1 === cat2) 59 | } 60 | 61 | func testSwinjectStoryboardInjectsDependencyDefindedByInitCompletedHandlerWithRegistrationNameAsUserDefinedRuntimeAttributeOnInterfaceBuilder() { 62 | // The registration name "hachi" is set in the storyboard. 63 | container.storyboardInitCompleted(AnimalViewController.self, name: "hachi") { r, c in 64 | c.animal = r.resolve(Animal.self) 65 | } 66 | container.register(Animal.self) { _ in Dog(name: "Hachi") } 67 | 68 | // This registration should not be resolved. 69 | container.storyboardInitCompleted(AnimalViewController.self) { _, c in 70 | c.animal = Cat(name: "Mimi") 71 | } 72 | 73 | let storyboard = SwinjectStoryboard.create(name: "Animals", bundle: bundle, container: container) 74 | let animalViewController = storyboard.instantiateViewController(withIdentifier: "AnimalAsDog") as! AnimalViewController 75 | XCTAssert(animalViewController.hasAnimal(named: "Hachi")) 76 | } 77 | 78 | func testSwinjectStoryboardInjectsViewControllerDependencyDefindedInParentContainerWithContainerHierarchy() { 79 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 80 | c.animal = r.resolve(Animal.self) 81 | } 82 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 83 | let childContainer = Container(parent: container) 84 | 85 | let storyboard = SwinjectStoryboard.create(name: "Animals", bundle: bundle, container: childContainer) 86 | let animalViewController = storyboard.instantiateViewController(withIdentifier: "AnimalAsCat") as! AnimalViewController 87 | XCTAssert(animalViewController.hasAnimal(named: "Mimi")) 88 | } 89 | 90 | func testSwinjectStoryboardInjectsSecondControllerWithSecondControllerInstantiationDuringInstantiationOfInitialOne() { 91 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 92 | c.animal = r.resolve(Animal.self) 93 | } 94 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 95 | 96 | let storyboard = SwinjectStoryboard.create(name: "Pages", bundle: bundle, container: container) 97 | let pagesController = storyboard.instantiateInitialViewController() as! AnimalPagesViewController 98 | XCTAssert(pagesController.animalPage.hasAnimal(named: "Mimi")) 99 | } 100 | 101 | // MARK: Initial view controller 102 | func testInitialViewControllerInjectsDependencyDefindedByInitCompletedHandler() { 103 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 104 | c.animal = r.resolve(Animal.self) 105 | } 106 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 107 | 108 | let storyboard = SwinjectStoryboard.create(name: "Animals", bundle: bundle, container: container) 109 | let animalViewController = storyboard.instantiateInitialViewController() as! AnimalViewController 110 | XCTAssert(animalViewController.hasAnimal(named: "Mimi")) 111 | } 112 | 113 | // MARK: AVPlayerViewController 114 | // Test for Issue #18 115 | func testAVPlayerViewControllerIsAbleToInjectSubclassOfAVPlayerViewController() { 116 | container.storyboardInitCompleted(AnimalPlayerViewController.self) { r, c in 117 | c.animal = r.resolve(Animal.self) 118 | } 119 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 120 | 121 | let storyboard = SwinjectStoryboard.create(name: "AnimalPlayerViewController", bundle: bundle, container: container) 122 | let animalPlayerViewController = storyboard.instantiateInitialViewController() as! AnimalPlayerViewController 123 | XCTAssert(animalPlayerViewController.hasAnimal(named: "Mimi")) 124 | } 125 | 126 | // MARK: Factory method 127 | func testFactoryMethodUsesDefaultSharedContainerIfNoContainerIsPassed() { 128 | SwinjectStoryboard.defaultContainer.storyboardInitCompleted(AnimalViewController.self) { _, _ in } 129 | defer { 130 | SwinjectStoryboard.defaultContainer.removeAll() 131 | } 132 | 133 | let storyboard = SwinjectStoryboard.create(name: "Animals", bundle: bundle) 134 | let animalViewController = storyboard.instantiateViewController(withIdentifier: "AnimalAsCat") 135 | XCTAssertNotNil(animalViewController) 136 | } 137 | 138 | // MARK: Storyboard reference 139 | func testStoryboardReferenceInjectsDependencyToViewControllerInReferencedStoryboard() { 140 | SwinjectStoryboard.defaultContainer.storyboardInitCompleted(AnimalViewController.self) { r, c in 141 | c.animal = r.resolve(Animal.self) 142 | } 143 | SwinjectStoryboard.defaultContainer.register(Animal.self) { _ in Cat(name: "Mimi") } 144 | defer { 145 | SwinjectStoryboard.defaultContainer.removeAll() 146 | } 147 | 148 | let storyboard1 = SwinjectStoryboard.create(name: "Storyboard1", bundle: bundle) 149 | let navigationController = storyboard1.instantiateInitialViewController() as! UINavigationController 150 | navigationController.performSegue(withIdentifier: "ToStoryboard2", sender: navigationController) 151 | let animalViewController = navigationController.topViewController as! AnimalViewController 152 | XCTAssert(animalViewController.hasAnimal(named: "Mimi")) 153 | } 154 | 155 | func testStoryboardReferenceShouldInjectDependenciesOnceIfReferencingStoryboardViaRelationshipSegue() { 156 | var injectedTimes = 0 157 | SwinjectStoryboard.defaultContainer.storyboardInitCompleted(UIViewController.self) { r, c in 158 | injectedTimes += 1 159 | } 160 | defer { 161 | SwinjectStoryboard.defaultContainer.removeAll() 162 | } 163 | 164 | let storyboard = SwinjectStoryboard.create(name: "RelationshipReference1", bundle: bundle) 165 | storyboard.instantiateInitialViewController() 166 | 167 | XCTAssertEqual(injectedTimes, 1) 168 | } 169 | 170 | func testStoryboardReferenceInjectsDependencyToViewControllerpenedViaSegueIfNotUsingDefaultContainer() { 171 | container.storyboardInitCompleted(AnimalViewController.self) { r, c in 172 | c.animal = r.resolve(Animal.self) 173 | } 174 | container.register(Animal.self) { _ in Cat(name: "Mimi") } 175 | 176 | let storyboard = SwinjectStoryboard.create(name: "RelationshipReference1", bundle: bundle, container: container) 177 | let navigationController = storyboard.instantiateInitialViewController() as! UINavigationController 178 | navigationController.topViewController!.performSegue(withIdentifier: "ToAnimalViewController", sender: nil) 179 | let animalViewController = navigationController.topViewController as! AnimalViewController 180 | 181 | XCTAssert(animalViewController.hasAnimal(named: "Mimi")) 182 | } 183 | 184 | // MARK: Setup 185 | func testSetupIsCalledOnlyOnce() { 186 | _ = SwinjectStoryboard.create(name: "Animals", bundle: bundle) 187 | _ = SwinjectStoryboard.create(name: "Animals", bundle: bundle) 188 | XCTAssertEqual(swinjectStoryboardSetupCount, 1) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /carthage-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # carthage.sh 4 | # Usage example: ./carthage-build.sh build --platform iOS 5 | 6 | set -euo pipefail 7 | 8 | xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX) 9 | trap 'rm -f "$xcconfig"' INT TERM HUP EXIT 10 | 11 | # For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise 12 | # the build will fail on lipo due to duplicate architectures. 13 | echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig 14 | 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 15 | 16 | export XCODE_XCCONFIG_FILE="$xcconfig" 17 | carthage "$@" --------------------------------------------------------------------------------