├── .swift-version ├── Cartfile ├── Cartfile.resolved ├── adding-dip-tag-in-ib.png ├── Package.swift ├── DipUI ├── DipUI.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── DipUI.xcscheme │ └── project.pbxproj ├── DipUITests │ ├── Info.plist │ ├── NSStoryboard.storyboard │ ├── TVStoryboard.storyboard │ ├── UIStoryboard.storyboard │ └── DipUITests.swift └── DipUI │ ├── Info.plist │ └── DipUI.h ├── .gitignore ├── Dip-UI.podspec ├── LICENSE ├── .travis.yml ├── CHANGELOG.md ├── README.md └── Sources └── StoryboardInstantiatable.swift /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "AliSoftware/Dip" ~> 6.0 2 | 3 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "AliSoftware/Dip" "6.0" 2 | -------------------------------------------------------------------------------- /adding-dip-tag-in-ib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AliSoftware/Dip-UI/HEAD/adding-dip-tag-in-ib.png -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "DipUI", 5 | dependencies: [ 6 | .Package(url: "https://github.com/AliSoftware/Dip.git", majorVersion: 6) 7 | ] 8 | ) 9 | -------------------------------------------------------------------------------- /DipUI/DipUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | *.xcscmblueprint 17 | profile 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | 23 | # Bundler 24 | .bundle 25 | 26 | Carthage 27 | # We recommend against adding the Pods directory to your .gitignore. However 28 | # you should judge for yourself, the pros and cons are mentioned at: 29 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 30 | # 31 | # Note: if you ignore the Pods directory, make sure to uncomment 32 | # `pod install` in .travis.yml 33 | # 34 | # Pods/ 35 | 36 | # SPM 37 | .build/ 38 | -------------------------------------------------------------------------------- /DipUI/DipUITests/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 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /DipUI/DipUI/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 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Dip-UI.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Dip-UI" 3 | s.version = "2.0" 4 | s.summary = "Dip UI extension" 5 | 6 | s.description = <<-DESC 7 | Dip-UI is a simple extension for Dip - Dependency Injection container for Swift. 8 | It adds features to support dependency injection for objects 9 | created by storyboards or loaded from nib files. 10 | 11 | DESC 12 | 13 | s.homepage = "https://github.com/AliSoftware/Dip-UI" 14 | s.license = 'MIT' 15 | s.authors = { "Ilya Puchka" => "ilya@puchka.me", "Olivier Halligon" => "olivier@halligon.net" } 16 | s.source = { :git => "https://github.com/AliSoftware/Dip-UI.git", :tag => s.version.to_s } 17 | s.social_media_url = 'https://twitter.com/aligatr' 18 | 19 | s.ios.deployment_target = '8.0' 20 | s.osx.deployment_target = '10.10' 21 | s.tvos.deployment_target = '9.0' 22 | s.watchos.deployment_target = '2.0' 23 | 24 | s.requires_arc = true 25 | 26 | s.source_files = 'Sources/**/*.swift' 27 | 28 | s.dependency 'Dip', '~> 6.0' 29 | 30 | end 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Olivier Halligon , Ilya Puchka 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | 4 | # cache: cocoapods 5 | # before_install: 6 | # - gem install cocoapods # Since Travis is not always on latest version 7 | 8 | install: 9 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 10 | - gem install cocoapods --version 1.1.0.rc.2 --no-document 11 | - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.26.2/Carthage.pkg" && sudo installer -pkg "Carthage.pkg" -target / && rm "Carthage.pkg" 12 | 13 | script: 14 | - carthage bootstrap 15 | - set -o pipefail && xcodebuild -project DipUI/DipUI.xcodeproj -scheme DipUI -destination "platform=macOS" test | xcpretty 16 | - set -o pipefail && xcodebuild -project DipUI/DipUI.xcodeproj -scheme DipUI -destination "platform=iOS Simulator,name=iPhone 6S" test | xcpretty 17 | - set -o pipefail && xcodebuild -project DipUI/DipUI.xcodeproj -scheme DipUI -destination "platform=tvOS Simulator,name=Apple TV 1080p" test | xcpretty 18 | - set -o pipefail && xcodebuild -project DipUI/DipUI.xcodeproj -scheme DipUI -destination "platform=watchOS Simulator,name=Apple Watch - 38mm" build | xcpretty 19 | - pod repo update --silent && pod spec lint 20 | - carthage build --no-skip-current 21 | 22 | notifications: 23 | email: false 24 | -------------------------------------------------------------------------------- /DipUI/DipUI/DipUI.h: -------------------------------------------------------------------------------- 1 | // 2 | // DipUI 3 | // 4 | // Copyright (c) 2016 Ilya Puchka 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | #import 27 | 28 | //! Project version number for DipUI. 29 | FOUNDATION_EXPORT double DipUIVersionNumber; 30 | 31 | //! Project version string for DipUI. 32 | FOUNDATION_EXPORT const unsigned char DipUIVersionString[]; 33 | 34 | // In this header, you should import all the public headers of your framework using statements like #import 35 | 36 | 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 2.0.0 4 | 5 | * Swift 4 support 6 | 7 | ## 1.1 8 | 9 | * Dropped Swift 2.3 support. 10 | [#27](https://github.com/AliSoftware/Dip/issues/27), [@ilyapuchka](https://github.com/ilyapuchka) 11 | 12 | ## 1.0.2 13 | 14 | * Added Swift 2.3 compatibility. `swift2.3` brunch is no longer maintained. 15 | [#24](https://github.com/AliSoftware/Dip/issues/24), [@ilyapuchka](https://github.com/ilyapuchka) 16 | 17 | ## 1.0.1 18 | 19 | * Fixed Podspec 20 | 21 | ## 1.0.0 22 | 23 | * Swift 3 support 24 | [#14](https://github.com/AliSoftware/Dip/pull/14), [@ilyapuchka](https://github.com/ilyapuchka) 25 | * Added watchOS support 26 | [#12](https://github.com/AliSoftware/Dip/pull/12), [#13](https://github.com/AliSoftware/Dip/pull/13), [@ilyapuchka](https://github.com/ilyapuchka) 27 | 28 | ## 0.2.2 29 | 30 | * Fixed build warning when calling non-throwing `resolve` with `try`. 31 | 32 | ## 0.2.1 33 | 34 | * Fixed resolving instances using multiple UI containers. 35 | 36 | ## 0.2.0 37 | 38 | * Multiple UI containers 39 | [#7](https://github.com/AliSoftware/Dip/pull/7), [@ilyapuchka](https://github.com/ilyapuchka) 40 | 41 | * dipTag can be `nil` 42 | [#6](https://github.com/AliSoftware/Dip/pull/6), [@ilyapuchka](https://github.com/ilyapuchka) 43 | 44 | ## 0.1.0 45 | 46 | * Fixed Dip version in podspec. 47 | 48 | ## 0.0.2 49 | 50 | * Added Swift Package Manager support. 51 | [@ilyapuchka](https://github.com/ilyapuchka) 52 | 53 | ## 0.0.1 54 | 55 | #### Initial release 56 | 57 | * Implemented support for injecting dependencies into objects created by stoaryboards. 58 | [#1](https://github.com/AliSoftware/Dip/pull/1), [@ilyapuchka](https://github.com/ilyapuchka) 59 | 60 | -------------------------------------------------------------------------------- /DipUI/DipUITests/NSStoryboard.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 | -------------------------------------------------------------------------------- /DipUI/DipUI.xcodeproj/xcshareddata/xcschemes/DipUI.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /DipUI/DipUITests/TVStoryboard.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 | -------------------------------------------------------------------------------- /DipUI/DipUITests/UIStoryboard.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 2 | [![Version](https://img.shields.io/cocoapods/v/Dip-UI.svg?style=flat)](http://cocoapods.org/pods/Dip-UI) 3 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | [![License](https://img.shields.io/cocoapods/l/Dip-UI.svg?style=flat)](http://cocoapods.org/pods/Dip-UI) 5 | [![Platform](https://img.shields.io/cocoapods/p/Dip-UI.svg?style=flat)](http://cocoapods.org/pods/Dip-UI) 6 | [![Swift Version](https://img.shields.io/badge/Swift-3.0--3.1-F16D39.svg?style=flat)](https://developer.apple.com/swift) 7 | 8 | __Dip-UI is a part of Dip since version 7.0.0. Thir repository will no longer be maintained.__ 9 | 10 | # Dip-UI 11 | 12 | Dip-UI is an extension for [Dip](https://github.com/AliSoftware/Dip) that provides support for dependency injection using Dip in applications that utilize storyboards and nib files. 13 | 14 | ## Installation 15 | 16 | You can install Dip-UI using your favorite dependency manager: 17 | 18 | CocoaPods: `pod "Dip-UI"` 19 | 20 | > You need at least 1.1.0.rc.2 version of CocoaPods. 21 | 22 | Carthage: `github "AliSoftware/Dip-UI"` 23 | 24 | ## Usage 25 | 26 | Dip-UI provides a unified and simple pattern to resolve dependencies of view controllers (or any other `NSObject`'s) created by storyboards. 27 | 28 | Let's say you want to use Dip to inject dependencies in `MyViewController` class defined like this: 29 | 30 | ```swift 31 | class MyViewController: UIViewController { 32 | var logger: Logger? 33 | var tracker: Tracker? 34 | var router: Router? 35 | var presenter: MyViewControllerPresenter? 36 | var service: MyViewControllerService? 37 | 38 | /*...*/ 39 | } 40 | 41 | ``` 42 | > Note 1: Though constructor injection is a preferred way to inject dependencies, in this case we can not use it - we can not make storyboards to use custom constructor. We could do it using subclass of UI(NS)Storyboard and method-swizzling, but you don't expect such things in a Swift framework. 43 | 44 | > Note 2: Implicitly unwrapped optionals are used here to indicate that these dependencies are required for this class. You don't have to follow this pattern and are free to use plain optionals if you prefer. 45 | 46 | To inject dependencies in this view controller when it is instantiated from storyboard you need to follow next steps: 47 | 48 | - Register the dependencies in the `DependencyContainer`, as well as `MyViewController`: 49 | 50 | ```swift 51 | import DipUI 52 | 53 | @UIApplicationMain 54 | class AppDelegate: UIResponder, UIApplicationDelegate { 55 | 56 | let container = DependencyContainer { container in 57 | container.register(.singleton) { LoggerImp() as Logger } 58 | container.register(.singleton) { TrackerImp() as Tracker } 59 | container.register(.singleton) { RouterImp() as Router } 60 | container.register { MyViewControllerPresenterImp() as MyViewControllerPresenter } 61 | container.register { MyViewControllerServiceImp() as MyViewControllerService } 62 | 63 | container.register(storyboardType: MyViewController.self, tag: "myVC") 64 | .resolvingProperties { container, controller in 65 | controller.logger = try container.resolve() as Logger 66 | controller.tracker = try container.resolve() as Tracker 67 | controller.router = try container.resolve() as Router 68 | controller.presenter = try container.resolve() as MyViewControllerPresenter 69 | controller.service = try container.resolve() as MyViewControllerService 70 | } 71 | 72 | DependencyContainer.uiContainers = [container] 73 | } 74 | } 75 | ``` 76 | 77 | > Note: All the depdencies are registered as implementations of abstractions (protocols). `MyViewController` is registered as concrete type. To register your view controller as a protocol read [here](#storyboardinstantiatable). 78 | 79 | - Set the container as one that will be used to inject dependencies in objects created by storyboards. You do it by setting static `uiContainers` property of `DependencyContainer ` class: 80 | 81 | ```swift 82 | DependencyContainer.uiContainers = [container] 83 | ``` 84 | 85 | - Make your view controller class conform to `StoryboardInstantiatable` protocol: 86 | 87 | ```swift 88 | extension MyViewController: StoryboardInstantiatable { } 89 | ``` 90 | 91 | > Tip: Do that in the Composition Root to avoid coupling your view controller's code with Dip. 92 | 93 | - In a storyboard (or in a nib file) set _Dip Tag_ attribute on your view controller. This value will be used to lookup definition for view controller, so it should be the same value that you used to register view controller in the container. 94 | 95 | ![img](adding-dip-tag-in-ib.png?raw=true) 96 | 97 | > Note: remember that `DependencyContainer` fallbacks to not-tagged definition if it does not find tagged definition, so you may register your view controller without tag, but you still need to set it in a storyboard. In this case you can use `Nil` attribute type instead of `String`. 98 | 99 | Now when view controller will be loaded from a storyboard Dip-UI will intercept the setter of `dipTag` property and will ask `DependencyContainer.uiContainer` to resolve its dependencies. 100 | 101 | ### StoryboardInstantiatable 102 | 103 | `StoryboardInstantiatable` protocol defines single method `didInstantiateFromStoryboard(_:tag:)` and provides its default implementation. In most cases you will not need to override it. But if you register your view controller as an impementation of some protocol instead of concrete type, or want to perform some pre/post actions, you will need to override it like this: 104 | 105 | ```swift 106 | container.register { MyViewController() as MyScene } 107 | extension MyViewController: StoryboardInstantiatable { 108 | func didInstantiateFromStoryboard(container: DependencyContainer, tag: DependencyContainer.Tag?) throws { 109 | try container.resolveDependenciesOf(self as MyScene, tag: tag) 110 | } 111 | } 112 | ``` 113 | 114 | 115 | ## License 116 | 117 | **Dip-UI** is available under the **MIT license**. See the `LICENSE` file for more info. 118 | -------------------------------------------------------------------------------- /DipUI/DipUITests/DipUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DipUI 3 | // 4 | // Copyright (c) 2016 Ilya Puchka 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import XCTest 26 | @testable import DipUI 27 | 28 | #if os(iOS) || os(tvOS) 29 | import UIKit 30 | typealias Storyboard = UIStoryboard 31 | typealias ViewController = UIViewController 32 | typealias StoryboardName = String 33 | 34 | extension UIStoryboard { 35 | @nonobjc 36 | @discardableResult func instantiateViewControllerWithIdentifier(_ identifier: String) -> UIViewController { 37 | return instantiateViewController(withIdentifier: identifier) 38 | } 39 | } 40 | 41 | #else 42 | import AppKit 43 | typealias Storyboard = NSStoryboard 44 | typealias ViewController = NSViewController 45 | typealias StoryboardName = NSStoryboard.Name 46 | 47 | extension NSStoryboard { 48 | @discardableResult func instantiateViewControllerWithIdentifier(_ identifier: String) -> NSViewController { 49 | return instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(identifier)) as! NSViewController 50 | } 51 | } 52 | 53 | #endif 54 | 55 | #if os(iOS) 56 | let storyboardName = "UIStoryboard" 57 | #elseif os(tvOS) 58 | let storyboardName = "TVStoryboard" 59 | #else 60 | let storyboardName = "NSStoryboard" 61 | #endif 62 | 63 | class DipViewController: ViewController, StoryboardInstantiatable {} 64 | class NilTagViewController: ViewController, StoryboardInstantiatable {} 65 | 66 | class DipUITests: XCTestCase { 67 | 68 | let storyboard: Storyboard = { 69 | let bundle = Bundle(for: DipUITests.self) 70 | return Storyboard(name: StoryboardName(storyboardName), bundle: bundle) 71 | }() 72 | 73 | func testThatViewControllerHasDipTagProperty() { 74 | let viewController = storyboard.instantiateViewControllerWithIdentifier("DipViewController") 75 | XCTAssertEqual(viewController.dipTag, "vc") 76 | } 77 | 78 | func testThatItDoesNotResolveIfContainerIsNotSet() { 79 | let container = DependencyContainer() 80 | container.register(tag: "vc") { ViewController() } 81 | .resolvingProperties { _, _ in 82 | XCTFail("Should not resolve when container is not set.") 83 | } 84 | 85 | storyboard.instantiateViewControllerWithIdentifier("DipViewController") 86 | } 87 | 88 | func testThatItDoesNotResolveIfTagIsNotSet() { 89 | let container = DependencyContainer() 90 | container.register(tag: "vc") { ViewController() } 91 | .resolvingProperties { _, _ in 92 | XCTFail("Should not resolve when container is not set.") 93 | } 94 | 95 | DependencyContainer.uiContainers = [container] 96 | storyboard.instantiateViewControllerWithIdentifier("ViewController") 97 | } 98 | 99 | func testThatItResolvesIfContainerAndStringTagAreSet() { 100 | var resolved = false 101 | let container = DependencyContainer() 102 | container.register(storyboardType: DipViewController.self, tag: "vc") 103 | .resolvingProperties { _, _ in 104 | resolved = true 105 | } 106 | 107 | DependencyContainer.uiContainers = [container] 108 | storyboard.instantiateViewControllerWithIdentifier("DipViewController") 109 | XCTAssertTrue(resolved, "Should resolve when container and tag are set.") 110 | } 111 | 112 | func testThatItResolvesIfContainerAndNilTagAreSet() { 113 | var resolved = false 114 | let container = DependencyContainer() 115 | container.register(storyboardType: NilTagViewController.self) 116 | .resolvingProperties { _, _ in 117 | resolved = true 118 | } 119 | 120 | DependencyContainer.uiContainers = [container] 121 | storyboard.instantiateViewControllerWithIdentifier("NilTagViewController") 122 | XCTAssertTrue(resolved, "Should resolve when container and nil tag are set.") 123 | } 124 | 125 | func testThatItDoesNotResolveIfTagDoesNotMatch() { 126 | let container = DependencyContainer() 127 | container.register(storyboardType: DipViewController.self, tag: "wrong tag") 128 | .resolvingProperties { _, _ in 129 | XCTFail("Should not resolve when container is not set.") 130 | } 131 | 132 | DependencyContainer.uiContainers = [container] 133 | storyboard.instantiateViewControllerWithIdentifier("DipViewController") 134 | } 135 | 136 | func testThatItResolvesWithDefinitionWithNoTag() { 137 | var resolved = false 138 | let container = DependencyContainer() 139 | container.register(storyboardType: DipViewController.self) 140 | .resolvingProperties { _, _ in 141 | resolved = true 142 | } 143 | 144 | DependencyContainer.uiContainers = [container] 145 | storyboard.instantiateViewControllerWithIdentifier("DipViewController") 146 | XCTAssertTrue(resolved, "Should fallback to definition with no tag.") 147 | } 148 | 149 | func testThatItIteratesUIContainers() { 150 | var resolved = false 151 | let container1 = DependencyContainer() 152 | let container2 = DependencyContainer() 153 | container2.register(storyboardType: DipViewController.self, tag: "vc") 154 | .resolvingProperties { container, _ in 155 | XCTAssertTrue(container === container2) 156 | resolved = true 157 | } 158 | 159 | DependencyContainer.uiContainers = [container1, container2] 160 | storyboard.instantiateViewControllerWithIdentifier("DipViewController") 161 | XCTAssertTrue(resolved, "Should resolve using second container") 162 | } 163 | } 164 | 165 | protocol SomeService: class { 166 | weak var delegate: SomeServiceDelegate? { get set } 167 | } 168 | protocol SomeServiceDelegate: class { } 169 | class SomeServiceImp: SomeService { 170 | weak var delegate: SomeServiceDelegate? 171 | init(delegate: SomeServiceDelegate) { 172 | self.delegate = delegate 173 | } 174 | init(){} 175 | } 176 | 177 | protocol OtherService: class { 178 | weak var delegate: OtherServiceDelegate? { get set } 179 | } 180 | protocol OtherServiceDelegate: class {} 181 | class OtherServiceImp: OtherService { 182 | weak var delegate: OtherServiceDelegate? 183 | init(delegate: OtherServiceDelegate){ 184 | self.delegate = delegate 185 | } 186 | init(){} 187 | } 188 | 189 | 190 | protocol SomeScreen: class { 191 | var someService: SomeService? { get set } 192 | var otherService: OtherService? { get set } 193 | } 194 | 195 | class ViewControllerImp: SomeScreen, SomeServiceDelegate, OtherServiceDelegate { 196 | var someService: SomeService? 197 | var otherService: OtherService? 198 | init(){} 199 | } 200 | 201 | extension DipUITests { 202 | 203 | func testThatItDoesNotCreateNewInstanceWhenResolvingDependenciesOfExternalInstance() { 204 | let container = DependencyContainer() 205 | 206 | //given 207 | var factoryCalled = false 208 | container.register(.shared) { () -> SomeScreen in 209 | factoryCalled = true 210 | return ViewControllerImp() as SomeScreen 211 | } 212 | 213 | //when 214 | let screen = ViewControllerImp() 215 | try! container.resolveDependencies(of: screen as SomeScreen) 216 | 217 | //then 218 | XCTAssertFalse(factoryCalled, "Container should not create new instance when resolving dependencies of external instance.") 219 | } 220 | 221 | func testThatItResolvesInstanceThatImplementsSeveralProtocols() { 222 | let container = DependencyContainer() 223 | 224 | //given 225 | container.register(.shared) { ViewControllerImp() as SomeScreen } 226 | .resolvingProperties { container, resolved in 227 | 228 | //manually provide resolved instance for the delegate properties 229 | resolved.someService = try container.resolve() as SomeService 230 | resolved.someService?.delegate = resolved as? SomeServiceDelegate 231 | resolved.otherService = try container.resolve(arguments: resolved as! OtherServiceDelegate) as OtherService 232 | } 233 | 234 | container.register(.shared) { SomeServiceImp() as SomeService } 235 | container.register(.shared) { OtherServiceImp(delegate: $0) as OtherService } 236 | 237 | //when 238 | let screen = try! container.resolve() as SomeScreen 239 | 240 | //then 241 | XCTAssertNotNil(screen.someService) 242 | XCTAssertNotNil(screen.otherService) 243 | 244 | XCTAssertTrue(screen.someService?.delegate === screen) 245 | XCTAssertTrue(screen.otherService?.delegate === screen) 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /Sources/StoryboardInstantiatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DipUI 3 | // 4 | // Copyright (c) 2016 Ilya Puchka 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dip 26 | 27 | extension DependencyContainer { 28 | ///Containers that will be used to resolve dependencies of instances, created by stroyboards. 29 | static public var uiContainers: [DependencyContainer] = { 30 | #if os(watchOS) 31 | swizzleAwakeWithContext 32 | #endif 33 | return [] 34 | }() 35 | 36 | /** 37 | Resolves dependencies of passed in instance. 38 | Use this method to resolve dependencies of object created by storyboard. 39 | The type of the instance should be registered in the container. 40 | 41 | You should call this method only from implementation of `didInstantiateFromStoryboard(_:tag:)` 42 | of `StoryboardInstantiatable` protocol if you override its default implementation. 43 | 44 | This method will do the same as `resolve(tag:) as T`, but instead of creating 45 | a new intance with a registered factory it will use passed in instance as a resolved instance. 46 | 47 | - parameters: 48 | - instance: The object which dependencies should be resolved 49 | - tag: An optional tag used to register the type (`T`) in the container 50 | 51 | **Example**: 52 | 53 | ```swift 54 | class ViewController: UIViewController, ServiceDelegate, StoryboardInstantiatable { 55 | var service: Service? 56 | 57 | func didInstantiateFromStoryboard(_ container: DependencyContainer, tag: DependencyContainer.Tag?) throws { 58 | try container.resolveDependencies(of: self as ServiceDelegate, tag: "vc") 59 | } 60 | } 61 | 62 | class ServiceImp: Service { 63 | weak var delegate: ServiceDelegate? 64 | } 65 | 66 | container.register(tag: "vc") { ViewController() } 67 | .resolvingProperties { container, controller in 68 | controller.service = try container.resolve() as Service 69 | controller.service.delegate = controller 70 | } 71 | 72 | container.register { ServiceImp() as Service } 73 | ``` 74 | 75 | - seealso: `register(_:type:tag:factory:)`, `didInstantiateFromStoryboard(_:tag:)` 76 | 77 | */ 78 | public func resolveDependencies(of instance: T, tag: Tag? = nil) throws { 79 | _ = try resolve(tag: tag) { (_: () throws -> T) in instance } 80 | } 81 | 82 | /** 83 | Register storyboard type `T` which has to conform to `StoryboardInstantiatable` and associate it with an optional tag. 84 | 85 | - parameters: 86 | - type: Storyboard type to register definition for. 87 | - tag: The arbitrary tag to associate this factory with. Pass `nil` to associate with any tag. Default value is `nil`. 88 | 89 | - returns: A registered definition. 90 | 91 | - note: This method will register concrete types. If you need to register 92 | as abstract types you should use standard `register` method from Dip. 93 | You should cast the factory return type to the protocol you want to 94 | register it for (unless you want to register concrete type) or 95 | provide `type` parameter. 96 | 97 | - seealso: `Definition`, `ComponentScope`, `DependencyTagConvertible` 98 | 99 | **Example**: 100 | ```swift 101 | // Register MyViewController 102 | container.register(storyboardType: MyViewController.self) 103 | // or 104 | container.register(tag: "myVC") { MyViewController() as MyViewControllerType } 105 | ``` 106 | */ 107 | public func register(storyboardType type: T.Type, tag: DependencyTagConvertible? = nil) -> Dip.Definition where T: StoryboardInstantiatable { 108 | return register(.unique, type: type, tag: tag, factory: { T() }) 109 | } 110 | 111 | } 112 | 113 | #if os(watchOS) 114 | public protocol StoryboardInstantiatableType {} 115 | #else 116 | public typealias StoryboardInstantiatableType = NSObjectProtocol 117 | #endif 118 | 119 | public protocol StoryboardInstantiatable: StoryboardInstantiatableType { 120 | 121 | /** 122 | This method will be called if you set a `dipTag` attirbute on the object in a storyboard 123 | that conforms to `StoryboardInstantiatable` protocol. 124 | 125 | - parameters: 126 | - tag: The tag value, that was set on the object in a storyboard 127 | - container: The `DependencyContainer` associated with storyboards 128 | 129 | The type that implements `StoryboardInstantiatable` protocol should be registered in `UIStoryboard.container`. 130 | Default implementation of that method calls `resolveDependenciesOf(_:tag:)` 131 | and pass it `self` instance and the tag. 132 | 133 | Usually you will not need to override the default implementation of this method 134 | if you registered the type of the instance as a concrete type in the container. 135 | Then you only need to add conformance to `StoryboardInstantiatable`. 136 | 137 | You may want to override it if you want to add custom logic before/after resolving dependencies 138 | or you want to resolve the instance as implementation of some protocol which it conforms to. 139 | 140 | - warning: This method will be called after `init?(coder:)` but before `awakeFromNib` method of `NSObject`. 141 | On watchOS this method will be called before `awakeWithContext(_:)`. 142 | 143 | **Example**: 144 | 145 | ```swift 146 | extension MyViewController: SomeProtocol { ... } 147 | 148 | extension MyViewController: StoryboardInstantiatable { 149 | func didInstantiateFromStoryboard(_ container: DependencyContainer, tag: DependencyContainer.Tag) throws { 150 | //resolve dependencies of the instance as SomeProtocol type 151 | try container.resolveDependencies(of: self as SomeProtocol, tag: tag) 152 | //do some additional setup here 153 | } 154 | } 155 | ``` 156 | */ 157 | func didInstantiateFromStoryboard(_ container: DependencyContainer, tag: DependencyContainer.Tag?) throws 158 | 159 | } 160 | 161 | extension StoryboardInstantiatable { 162 | public func didInstantiateFromStoryboard(_ container: DependencyContainer, tag: DependencyContainer.Tag?) throws { 163 | try container.resolveDependencies(of: self, tag: tag) 164 | } 165 | } 166 | 167 | #if os(iOS) || os(tvOS) || os(OSX) 168 | 169 | #if os(iOS) || os(tvOS) 170 | import UIKit 171 | #elseif os(OSX) 172 | import AppKit 173 | #endif 174 | 175 | let DipTagAssociatedObjectKey = UnsafeMutablePointer.allocate(capacity: 1) 176 | 177 | extension NSObject { 178 | 179 | ///A string tag that will be used to resolve dependencies of this instance 180 | ///if it implements `StoryboardInstantiatable` protocol. 181 | @objc private(set) public var dipTag: String? { 182 | get { 183 | return objc_getAssociatedObject(self, DipTagAssociatedObjectKey) as? String 184 | } 185 | set { 186 | objc_setAssociatedObject(self, DipTagAssociatedObjectKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) 187 | guard let instantiatable = self as? StoryboardInstantiatable else { return } 188 | 189 | let tag = dipTag.map(DependencyContainer.Tag.String) 190 | 191 | for (index, container) in DependencyContainer.uiContainers.enumerated() { 192 | do { 193 | log("Trying to resolve \(type(of: self)) with UI container at index \(index)") 194 | try instantiatable.didInstantiateFromStoryboard(container, tag: tag) 195 | log("Resolved \(type(of: self))") 196 | return 197 | } catch { } 198 | } 199 | } 200 | } 201 | 202 | } 203 | 204 | func log(_ message: Any) { 205 | if Dip.LogLevel.Errors.rawValue <= Dip.logLevel.rawValue { 206 | Dip.logger(logLevel, message) 207 | } 208 | } 209 | 210 | #else 211 | import WatchKit 212 | 213 | let swizzleAwakeWithContext: Void = { 214 | let originalSelector = #selector(WKInterfaceController.awake(withContext:)) 215 | let swizzledSelector = #selector(WKInterfaceController.dip_awake(withContext:)) 216 | 217 | guard let originalMethod = class_getInstanceMethod(WKInterfaceController.self, originalSelector), 218 | let swizzledMethod = class_getInstanceMethod(WKInterfaceController.self, swizzledSelector) else { return } 219 | 220 | let didAddMethod = class_addMethod(WKInterfaceController.self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) 221 | 222 | if didAddMethod { 223 | class_replaceMethod(WKInterfaceController.self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) 224 | } else { 225 | method_exchangeImplementations(originalMethod, swizzledMethod) 226 | } 227 | }() 228 | 229 | extension WKInterfaceController: StoryboardInstantiatableType { 230 | 231 | @objc func dip_awake(withContext context: AnyObject?) { 232 | defer { self.dip_awake(withContext: context) } 233 | guard let instantiatable = self as? StoryboardInstantiatable else { return } 234 | 235 | for container in DependencyContainer.uiContainers { 236 | guard let _ = try? instantiatable.didInstantiateFromStoryboard(container, tag: nil) else { continue } 237 | break 238 | } 239 | } 240 | 241 | } 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /DipUI/DipUI.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 09B0361E1C5E8029001EA5B7 /* DipUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B0361D1C5E8029001EA5B7 /* DipUITests.swift */; }; 11 | 09B036711C5E864C001EA5B7 /* DipUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 09B036111C5E8028001EA5B7 /* DipUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 09B036771C5E86BE001EA5B7 /* StoryboardInstantiatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B036281C5E812D001EA5B7 /* StoryboardInstantiatable.swift */; }; 13 | 09D387641D8E97E600483D0D /* DipUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09B0365A1C5E836B001EA5B7 /* DipUI.framework */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | 092624801C5E8ACD00A31D1A /* UIStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = UIStoryboard.storyboard; sourceTree = ""; }; 18 | 092624831C5E8AFE00A31D1A /* NSStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NSStoryboard.storyboard; sourceTree = ""; }; 19 | 092BACA31D63AFA50017CE47 /* TVStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = TVStoryboard.storyboard; sourceTree = ""; }; 20 | 0969D1B91C60099600A2142B /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = ""; }; 21 | 0969D1BA1C60099600A2142B /* Dip-UI.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; name = "Dip-UI.podspec"; path = "../Dip-UI.podspec"; sourceTree = ""; }; 22 | 0969D1BB1C60099600A2142B /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 23 | 0969D1BC1C60099600A2142B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 24 | 09B036111C5E8028001EA5B7 /* DipUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DipUI.h; sourceTree = ""; }; 25 | 09B036131C5E8028001EA5B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | 09B036181C5E8029001EA5B7 /* DipUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DipUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 09B0361D1C5E8029001EA5B7 /* DipUITests.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = DipUITests.swift; sourceTree = ""; tabWidth = 2; }; 28 | 09B0361F1C5E8029001EA5B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 09B036281C5E812D001EA5B7 /* StoryboardInstantiatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StoryboardInstantiatable.swift; path = ../../Sources/StoryboardInstantiatable.swift; sourceTree = ""; }; 30 | 09B0365A1C5E836B001EA5B7 /* DipUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DipUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 09B036151C5E8029001EA5B7 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | 09D387641D8E97E600483D0D /* DipUI.framework in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | 09B036561C5E836B001EA5B7 /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXFrameworksBuildPhase section */ 50 | 51 | /* Begin PBXGroup section */ 52 | 0969D1B81C60098400A2142B /* Podspec Metadata */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 0969D1B91C60099600A2142B /* CHANGELOG.md */, 56 | 0969D1BA1C60099600A2142B /* Dip-UI.podspec */, 57 | 0969D1BB1C60099600A2142B /* LICENSE */, 58 | 0969D1BC1C60099600A2142B /* README.md */, 59 | ); 60 | name = "Podspec Metadata"; 61 | sourceTree = ""; 62 | }; 63 | 09B036041C5E8028001EA5B7 = { 64 | isa = PBXGroup; 65 | children = ( 66 | 09B036101C5E8028001EA5B7 /* DipUI */, 67 | 09B0361C1C5E8029001EA5B7 /* DipUITests */, 68 | 09B0360F1C5E8028001EA5B7 /* Products */, 69 | 0969D1B81C60098400A2142B /* Podspec Metadata */, 70 | ); 71 | indentWidth = 2; 72 | sourceTree = ""; 73 | tabWidth = 2; 74 | }; 75 | 09B0360F1C5E8028001EA5B7 /* Products */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 09B036181C5E8029001EA5B7 /* DipUITests.xctest */, 79 | 09B0365A1C5E836B001EA5B7 /* DipUI.framework */, 80 | ); 81 | name = Products; 82 | sourceTree = ""; 83 | }; 84 | 09B036101C5E8028001EA5B7 /* DipUI */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 09B036111C5E8028001EA5B7 /* DipUI.h */, 88 | 09B036281C5E812D001EA5B7 /* StoryboardInstantiatable.swift */, 89 | 09B036131C5E8028001EA5B7 /* Info.plist */, 90 | ); 91 | path = DipUI; 92 | sourceTree = ""; 93 | }; 94 | 09B0361C1C5E8029001EA5B7 /* DipUITests */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 09B0361D1C5E8029001EA5B7 /* DipUITests.swift */, 98 | 092624801C5E8ACD00A31D1A /* UIStoryboard.storyboard */, 99 | 092BACA31D63AFA50017CE47 /* TVStoryboard.storyboard */, 100 | 092624831C5E8AFE00A31D1A /* NSStoryboard.storyboard */, 101 | 09B0361F1C5E8029001EA5B7 /* Info.plist */, 102 | ); 103 | path = DipUITests; 104 | sourceTree = ""; 105 | }; 106 | /* End PBXGroup section */ 107 | 108 | /* Begin PBXHeadersBuildPhase section */ 109 | 09B036571C5E836B001EA5B7 /* Headers */ = { 110 | isa = PBXHeadersBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | 09B036711C5E864C001EA5B7 /* DipUI.h in Headers */, 114 | ); 115 | runOnlyForDeploymentPostprocessing = 0; 116 | }; 117 | /* End PBXHeadersBuildPhase section */ 118 | 119 | /* Begin PBXNativeTarget section */ 120 | 09B036171C5E8029001EA5B7 /* DipUITests */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = 09B036251C5E8029001EA5B7 /* Build configuration list for PBXNativeTarget "DipUITests" */; 123 | buildPhases = ( 124 | 09B036141C5E8029001EA5B7 /* Sources */, 125 | 09B036151C5E8029001EA5B7 /* Frameworks */, 126 | 0966BAEF1D651EB7009C6CC3 /* ShellScript */, 127 | 09B036161C5E8029001EA5B7 /* Resources */, 128 | 0966BAF01D652F19009C6CC3 /* ShellScript */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = DipUITests; 135 | productName = DipUITests; 136 | productReference = 09B036181C5E8029001EA5B7 /* DipUITests.xctest */; 137 | productType = "com.apple.product-type.bundle.unit-test"; 138 | }; 139 | 09B036591C5E836B001EA5B7 /* DipUI */ = { 140 | isa = PBXNativeTarget; 141 | buildConfigurationList = 09B0366B1C5E836B001EA5B7 /* Build configuration list for PBXNativeTarget "DipUI" */; 142 | buildPhases = ( 143 | 09B036551C5E836B001EA5B7 /* Sources */, 144 | 09B036561C5E836B001EA5B7 /* Frameworks */, 145 | 09B036571C5E836B001EA5B7 /* Headers */, 146 | 09B036581C5E836B001EA5B7 /* Resources */, 147 | ); 148 | buildRules = ( 149 | ); 150 | dependencies = ( 151 | ); 152 | name = DipUI; 153 | productName = "DipUI-OSX"; 154 | productReference = 09B0365A1C5E836B001EA5B7 /* DipUI.framework */; 155 | productType = "com.apple.product-type.framework"; 156 | }; 157 | /* End PBXNativeTarget section */ 158 | 159 | /* Begin PBXProject section */ 160 | 09B036051C5E8028001EA5B7 /* Project object */ = { 161 | isa = PBXProject; 162 | attributes = { 163 | LastSwiftUpdateCheck = 0720; 164 | LastUpgradeCheck = 0900; 165 | ORGANIZATIONNAME = ""; 166 | TargetAttributes = { 167 | 09B036171C5E8029001EA5B7 = { 168 | CreatedOnToolsVersion = 7.2; 169 | DevelopmentTeam = 87BXKFRW6K; 170 | }; 171 | 09B036591C5E836B001EA5B7 = { 172 | CreatedOnToolsVersion = 7.2; 173 | }; 174 | }; 175 | }; 176 | buildConfigurationList = 09B036081C5E8028001EA5B7 /* Build configuration list for PBXProject "DipUI" */; 177 | compatibilityVersion = "Xcode 3.2"; 178 | developmentRegion = English; 179 | hasScannedForEncodings = 0; 180 | knownRegions = ( 181 | en, 182 | ); 183 | mainGroup = 09B036041C5E8028001EA5B7; 184 | productRefGroup = 09B0360F1C5E8028001EA5B7 /* Products */; 185 | projectDirPath = ""; 186 | projectRoot = ""; 187 | targets = ( 188 | 09B036591C5E836B001EA5B7 /* DipUI */, 189 | 09B036171C5E8029001EA5B7 /* DipUITests */, 190 | ); 191 | }; 192 | /* End PBXProject section */ 193 | 194 | /* Begin PBXResourcesBuildPhase section */ 195 | 09B036161C5E8029001EA5B7 /* Resources */ = { 196 | isa = PBXResourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | 09B036581C5E836B001EA5B7 /* Resources */ = { 203 | isa = PBXResourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 0966BAEF1D651EB7009C6CC3 /* ShellScript */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | outputPaths = ( 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | shellPath = /bin/sh; 223 | shellScript = "ibtool --compilation-directory \"${TARGET_TEMP_DIR}\" \"${SRCROOT}/DipUITests/${STORYBOARD_NAME_PREFIX}Storyboard.storyboard\"\nibtool --link \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\" \"${TARGET_TEMP_DIR}/${STORYBOARD_NAME_PREFIX}Storyboard.storyboardc\"\n"; 224 | }; 225 | 0966BAF01D652F19009C6CC3 /* ShellScript */ = { 226 | isa = PBXShellScriptBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | ); 230 | inputPaths = ( 231 | ); 232 | outputPaths = ( 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | shellPath = /bin/sh; 236 | shellScript = "for path in $FRAMEWORK_SEARCH_PATHS\ndo\n if [ -d \"${path}/Dip.framework\" ] && [[ $path == *\"Carthage\"* ]]; then\n export SCRIPT_INPUT_FILE_COUNT=1\n export SCRIPT_INPUT_FILE_0=\"${path}/Dip.framework\"\n /usr/local/bin/carthage copy-frameworks\n break\n fi\ndone\n\n"; 237 | }; 238 | /* End PBXShellScriptBuildPhase section */ 239 | 240 | /* Begin PBXSourcesBuildPhase section */ 241 | 09B036141C5E8029001EA5B7 /* Sources */ = { 242 | isa = PBXSourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | 09B0361E1C5E8029001EA5B7 /* DipUITests.swift in Sources */, 246 | ); 247 | runOnlyForDeploymentPostprocessing = 0; 248 | }; 249 | 09B036551C5E836B001EA5B7 /* Sources */ = { 250 | isa = PBXSourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 09B036771C5E86BE001EA5B7 /* StoryboardInstantiatable.swift in Sources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | /* End PBXSourcesBuildPhase section */ 258 | 259 | /* Begin XCBuildConfiguration section */ 260 | 09B036201C5E8029001EA5B7 /* Debug */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | ALWAYS_SEARCH_USER_PATHS = NO; 264 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 265 | CLANG_CXX_LIBRARY = "libc++"; 266 | CLANG_ENABLE_MODULES = YES; 267 | CLANG_ENABLE_OBJC_ARC = YES; 268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 269 | CLANG_WARN_BOOL_CONVERSION = YES; 270 | CLANG_WARN_COMMA = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 273 | CLANG_WARN_EMPTY_BODY = YES; 274 | CLANG_WARN_ENUM_CONVERSION = YES; 275 | CLANG_WARN_INFINITE_RECURSION = YES; 276 | CLANG_WARN_INT_CONVERSION = YES; 277 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 281 | CLANG_WARN_STRICT_PROTOTYPES = YES; 282 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 283 | CLANG_WARN_UNREACHABLE_CODE = YES; 284 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 285 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 286 | COPY_PHASE_STRIP = NO; 287 | CURRENT_PROJECT_VERSION = 2.0.0; 288 | DEBUG_INFORMATION_FORMAT = dwarf; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | ENABLE_TESTABILITY = YES; 291 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../Carthage/Build/Mac"; 292 | "FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]" = "$(SRCROOT)/../Carthage/Build/tvOS"; 293 | "FRAMEWORK_SEARCH_PATHS[sdk=appletvsimulator*]" = "$(SRCROOT)/../Carthage/Build/tvOS"; 294 | "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "$(SRCROOT)/../Carthage/Build/iOS"; 295 | "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(SRCROOT)/../Carthage/Build/iOS"; 296 | "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = "$(SRCROOT)/../Carthage/Build/Mac"; 297 | "FRAMEWORK_SEARCH_PATHS[sdk=watchos*]" = "$(SRCROOT)/../Carthage/Build/watchOS"; 298 | "FRAMEWORK_SEARCH_PATHS[sdk=watchsimulator*]" = "$(SRCROOT)/../Carthage/Build/watchOS"; 299 | GCC_C_LANGUAGE_STANDARD = gnu99; 300 | GCC_DYNAMIC_NO_PIC = NO; 301 | GCC_NO_COMMON_BLOCKS = YES; 302 | GCC_OPTIMIZATION_LEVEL = 0; 303 | GCC_PREPROCESSOR_DEFINITIONS = ( 304 | "DEBUG=1", 305 | "$(inherited)", 306 | ); 307 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 308 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 309 | GCC_WARN_UNDECLARED_SELECTOR = YES; 310 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 311 | GCC_WARN_UNUSED_FUNCTION = YES; 312 | GCC_WARN_UNUSED_VARIABLE = YES; 313 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 314 | MACOSX_DEPLOYMENT_TARGET = 10.10; 315 | MTL_ENABLE_DEBUG_INFO = YES; 316 | ONLY_ACTIVE_ARCH = YES; 317 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 318 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 319 | SWIFT_VERSION = 4.0; 320 | TARGETED_DEVICE_FAMILY = "1,2,3,4"; 321 | TVOS_DEPLOYMENT_TARGET = 9.0; 322 | VERSIONING_SYSTEM = "apple-generic"; 323 | VERSION_INFO_PREFIX = ""; 324 | WATCHOS_DEPLOYMENT_TARGET = 2.2; 325 | }; 326 | name = Debug; 327 | }; 328 | 09B036211C5E8029001EA5B7 /* Release */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ALWAYS_SEARCH_USER_PATHS = NO; 332 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 333 | CLANG_CXX_LIBRARY = "libc++"; 334 | CLANG_ENABLE_MODULES = YES; 335 | CLANG_ENABLE_OBJC_ARC = YES; 336 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 337 | CLANG_WARN_BOOL_CONVERSION = YES; 338 | CLANG_WARN_COMMA = YES; 339 | CLANG_WARN_CONSTANT_CONVERSION = YES; 340 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 341 | CLANG_WARN_EMPTY_BODY = YES; 342 | CLANG_WARN_ENUM_CONVERSION = YES; 343 | CLANG_WARN_INFINITE_RECURSION = YES; 344 | CLANG_WARN_INT_CONVERSION = YES; 345 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 346 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 347 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 348 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 349 | CLANG_WARN_STRICT_PROTOTYPES = YES; 350 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 351 | CLANG_WARN_UNREACHABLE_CODE = YES; 352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 353 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 354 | COPY_PHASE_STRIP = NO; 355 | CURRENT_PROJECT_VERSION = 2.0.0; 356 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 357 | ENABLE_NS_ASSERTIONS = NO; 358 | ENABLE_STRICT_OBJC_MSGSEND = YES; 359 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../Carthage/Build/Mac"; 360 | "FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]" = "$(SRCROOT)/../Carthage/Build/tvOS"; 361 | "FRAMEWORK_SEARCH_PATHS[sdk=appletvsimulator*]" = "$(SRCROOT)/../Carthage/Build/tvOS"; 362 | "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "$(SRCROOT)/../Carthage/Build/iOS"; 363 | "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(SRCROOT)/../Carthage/Build/iOS"; 364 | "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = "$(SRCROOT)/../Carthage/Build/Mac"; 365 | "FRAMEWORK_SEARCH_PATHS[sdk=watchos*]" = "$(SRCROOT)/../Carthage/Build/watchOS"; 366 | "FRAMEWORK_SEARCH_PATHS[sdk=watchsimulator*]" = "$(SRCROOT)/../Carthage/Build/watchOS"; 367 | GCC_C_LANGUAGE_STANDARD = gnu99; 368 | GCC_NO_COMMON_BLOCKS = YES; 369 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 370 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 371 | GCC_WARN_UNDECLARED_SELECTOR = YES; 372 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 373 | GCC_WARN_UNUSED_FUNCTION = YES; 374 | GCC_WARN_UNUSED_VARIABLE = YES; 375 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 376 | MACOSX_DEPLOYMENT_TARGET = 10.10; 377 | MTL_ENABLE_DEBUG_INFO = NO; 378 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 379 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 380 | SWIFT_VERSION = 4.0; 381 | TARGETED_DEVICE_FAMILY = "1,2,3,4"; 382 | TVOS_DEPLOYMENT_TARGET = 9.0; 383 | VALIDATE_PRODUCT = YES; 384 | VERSIONING_SYSTEM = "apple-generic"; 385 | VERSION_INFO_PREFIX = ""; 386 | WATCHOS_DEPLOYMENT_TARGET = 2.2; 387 | }; 388 | name = Release; 389 | }; 390 | 09B036261C5E8029001EA5B7 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | COMBINE_HIDPI_IMAGES = YES; 394 | INFOPLIST_FILE = DipUITests/Info.plist; 395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 396 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks @loader_path/../Frameworks"; 397 | PRODUCT_BUNDLE_IDENTIFIER = com.alisoftware.DipUITests; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | STORYBOARD_NAME_PREFIX = NS; 400 | "STORYBOARD_NAME_PREFIX[sdk=appletvos*]" = TV; 401 | "STORYBOARD_NAME_PREFIX[sdk=appletvsimulator*]" = TV; 402 | "STORYBOARD_NAME_PREFIX[sdk=iphoneos*]" = UI; 403 | "STORYBOARD_NAME_PREFIX[sdk=iphonesimulator*]" = UI; 404 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator"; 405 | TARGETED_DEVICE_FAMILY = "1,2,3"; 406 | }; 407 | name = Debug; 408 | }; 409 | 09B036271C5E8029001EA5B7 /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | COMBINE_HIDPI_IMAGES = YES; 413 | INFOPLIST_FILE = DipUITests/Info.plist; 414 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 415 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks @loader_path/../Frameworks"; 416 | PRODUCT_BUNDLE_IDENTIFIER = com.alisoftware.DipUITests; 417 | PRODUCT_NAME = "$(TARGET_NAME)"; 418 | STORYBOARD_NAME_PREFIX = ""; 419 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator"; 420 | TARGETED_DEVICE_FAMILY = "1,2,3"; 421 | }; 422 | name = Release; 423 | }; 424 | 09B0366C1C5E836B001EA5B7 /* Debug */ = { 425 | isa = XCBuildConfiguration; 426 | buildSettings = { 427 | "CODE_SIGN_IDENTITY[sdk=*]" = ""; 428 | COMBINE_HIDPI_IMAGES = YES; 429 | DEFINES_MODULE = YES; 430 | DYLIB_COMPATIBILITY_VERSION = 1; 431 | DYLIB_CURRENT_VERSION = 1; 432 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 433 | FRAMEWORK_VERSION = A; 434 | INFOPLIST_FILE = DipUI/Info.plist; 435 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = com.alisoftware.DipUI; 438 | PRODUCT_NAME = DipUI; 439 | SKIP_INSTALL = YES; 440 | }; 441 | name = Debug; 442 | }; 443 | 09B0366D1C5E836B001EA5B7 /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | "CODE_SIGN_IDENTITY[sdk=*]" = ""; 447 | COMBINE_HIDPI_IMAGES = YES; 448 | DEFINES_MODULE = YES; 449 | DYLIB_COMPATIBILITY_VERSION = 1; 450 | DYLIB_CURRENT_VERSION = 1; 451 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 452 | FRAMEWORK_VERSION = A; 453 | INFOPLIST_FILE = DipUI/Info.plist; 454 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 455 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 456 | PRODUCT_BUNDLE_IDENTIFIER = com.alisoftware.DipUI; 457 | PRODUCT_NAME = DipUI; 458 | SKIP_INSTALL = YES; 459 | }; 460 | name = Release; 461 | }; 462 | /* End XCBuildConfiguration section */ 463 | 464 | /* Begin XCConfigurationList section */ 465 | 09B036081C5E8028001EA5B7 /* Build configuration list for PBXProject "DipUI" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 09B036201C5E8029001EA5B7 /* Debug */, 469 | 09B036211C5E8029001EA5B7 /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 09B036251C5E8029001EA5B7 /* Build configuration list for PBXNativeTarget "DipUITests" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 09B036261C5E8029001EA5B7 /* Debug */, 478 | 09B036271C5E8029001EA5B7 /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | 09B0366B1C5E836B001EA5B7 /* Build configuration list for PBXNativeTarget "DipUI" */ = { 484 | isa = XCConfigurationList; 485 | buildConfigurations = ( 486 | 09B0366C1C5E836B001EA5B7 /* Debug */, 487 | 09B0366D1C5E836B001EA5B7 /* Release */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | /* End XCConfigurationList section */ 493 | }; 494 | rootObject = 09B036051C5E8028001EA5B7 /* Project object */; 495 | } 496 | --------------------------------------------------------------------------------