├── .circleci └── config.yml ├── .gitignore ├── .swift-version ├── CONTRIBUTING.md ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── LICENSE ├── Perform.podspec ├── Perform.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── Perform.xcscheme ├── Podfile ├── README.md ├── Sources └── Perform │ ├── Aspects.swift │ ├── Collection.swift │ ├── Compatibility.swift │ ├── Info.plist │ ├── Perform.h │ ├── Perform.swift │ ├── Segue.swift │ ├── SequenceType.swift │ ├── UIStoryboardSegue.swift │ ├── UITabBarController.swift │ └── UIViewController.swift ├── Tests ├── Harness │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewControllers.swift └── PerformTests │ ├── Info.plist │ └── PerformSpec.swift └── bin ├── bootstrap ├── bootstrap-if-needed ├── test └── update /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build-and-test: 5 | environment: 6 | CARTHAGE_LOG_PATH: log/carthage-build.log 7 | 8 | macos: 9 | xcode: "10.1.0" 10 | environment: 11 | NSUnbufferedIO: YES 12 | 13 | steps: 14 | - checkout 15 | - run: mkdir log 16 | - run: 17 | name: Save Xcode version 18 | command: | 19 | xcodebuild -version | tee .circle-xcode-version 20 | - run: 21 | name: Save Carthage cache version 22 | command: | 23 | echo "$CARTHAGE_CACHE_VERSION" | tee .circle-carthage-cache-version 24 | 25 | - restore_cache: 26 | keys: 27 | - carthage-{{ checksum ".circle-carthage-cache-version" }}-{{ checksum ".circle-xcode-version" }}-{{ checksum "Cartfile.resolved" }} 28 | - carthage-{{ checksum ".circle-carthage-cache-version" }}-{{ checksum ".circle-xcode-version" }} 29 | - carthage-{{ checksum ".circle-carthage-cache-version" }}- 30 | 31 | - run: 32 | name: Install dependencies 33 | command: | 34 | bin/bootstrap-if-needed 35 | 36 | - save_cache: 37 | key: carthage-{{ checksum ".circle-carthage-cache-version" }}-{{ checksum ".circle-xcode-version" }}-{{ checksum "Cartfile.resolved" }} 38 | paths: 39 | - "~/Library/Caches/org.carthage.CarthageKit" 40 | - "Carthage" 41 | 42 | - run: 43 | name: Run tests 44 | command: | 45 | bin/test 46 | - store_artifacts: 47 | path: log 48 | 49 | lint-podspec: 50 | macos: 51 | xcode: "10.1.0" 52 | 53 | steps: 54 | - checkout 55 | - run: 56 | name: Fetch CocoaPods Specs 57 | command: | 58 | curl https://cocoapods-specs.circleci.com/fetch-cocoapods-repo-from-s3.sh \ 59 | | bash -s cf 60 | - run: 61 | name: Lint Podspec 62 | command: | 63 | pod lib lint --allow-warnings 64 | workflows: 65 | version: 2 66 | build-and-deploy: 67 | jobs: 68 | - build-and-test 69 | - lint-podspec 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X Finder 2 | .DS_Store 3 | 4 | # Xcode per-user config 5 | *.mode1 6 | *.mode1v3 7 | *.mode2v3 8 | *.perspective 9 | *.perspectivev3 10 | *.pbxuser 11 | xcuserdata 12 | *.xccheckout 13 | 14 | # Build products 15 | build/ 16 | *.o 17 | *.LinkFileList 18 | *.hmap 19 | 20 | # Automatic backup files 21 | *~.nib/ 22 | *.swp 23 | *~ 24 | *.dat 25 | *.dep 26 | 27 | # Cocoapods 28 | Pods 29 | Podfile.lock 30 | 31 | # Carthage 32 | /Carthage/ 33 | 34 | # AppCode specific files 35 | .idea/ 36 | *.iml 37 | 38 | *.xcscmblueprint 39 | 40 | xcodebuild-test.log 41 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love contributions from everyone. 4 | By participating in this project, 5 | you agree to abide by the thoughtbot [code of conduct]. 6 | 7 | [code of conduct]: https://thoughtbot.com/open-source-code-of-conduct 8 | 9 | We expect everyone to follow the code of conduct 10 | anywhere in thoughtbot's project codebases, 11 | issue trackers, chatrooms, and mailing lists. 12 | 13 | ## Contributing Code 14 | 15 | Fork the repo. 16 | 17 | Install the dependencies: 18 | 19 | bin/setup 20 | 21 | Make sure the tests pass: 22 | 23 | bin/test 24 | 25 | Make your change, with new passing tests. Follow the [style guide][style]. 26 | 27 | [style]: https://github.com/thoughtbot/guides/tree/master/style 28 | 29 | Push to your fork. Write a [good commit message][commit]. Submit a pull request. 30 | 31 | [commit]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 32 | 33 | Others will give constructive feedback. 34 | This is a time for discussion and improvements, 35 | and making the necessary changes will be required before we can 36 | merge the contribution. 37 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "steipete/Aspects" ~> 1.4 2 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" ~> 1.1 2 | github "Quick/Nimble" ~> 7.0.1 3 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v7.3.4" 2 | github "Quick/Quick" "v1.3.4" 3 | github "steipete/Aspects" "1.4.2" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 thoughtbot, inc. 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 | -------------------------------------------------------------------------------- /Perform.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Perform" 3 | s.version = %x(git describe --tags --abbrev=0).chomp 4 | s.summary = "Easy dependency injection for storyboard segues." 5 | 6 | s.homepage = "https://github.com/thoughtbot/Perform" 7 | s.license = { type: "MIT", file: "LICENSE" } 8 | s.author = "Adam Sharp" 9 | s.source = { git: "https://github.com/thoughtbot/Perform.git", tag: s.version.to_s } 10 | 11 | s.ios.deployment_target = "8.0" 12 | 13 | s.source_files = "Sources/Perform/**/*.swift" 14 | 15 | s.dependency "Aspects", "~> 1.4" 16 | end 17 | -------------------------------------------------------------------------------- /Perform.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A95938B1D748EE100EE27AA /* Perform.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A9593811D748EE100EE27AA /* Perform.framework */; }; 11 | 4A9593BB1D7492CB00EE27AA /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 4A9593B71D7492CB00EE27AA /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12 | 4A9593BC1D7492CB00EE27AA /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 4A9593B81D7492CB00EE27AA /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 13 | 4A9593C21D74932000EE27AA /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A9593B71D7492CB00EE27AA /* Nimble.framework */; }; 14 | 4A9593C31D74932000EE27AA /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A9593B81D7492CB00EE27AA /* Quick.framework */; }; 15 | 4A9593C81D7493EC00EE27AA /* Perform.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A9593C61D7493EC00EE27AA /* Perform.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 4A9593F81D74A9F500EE27AA /* Perform.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A9593811D748EE100EE27AA /* Perform.framework */; }; 17 | 4A95940A1D74B16D00EE27AA /* Perform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A9593C91D74940600EE27AA /* Perform.swift */; }; 18 | 4A95940C1D74B19800EE27AA /* PerformSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A9593B11D7490B700EE27AA /* PerformSpec.swift */; }; 19 | 4AA18A491D75D2FE005386FF /* Aspects.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4AA18A481D75D2FE005386FF /* Aspects.framework */; }; 20 | 4AA18A4A1D75D30A005386FF /* Aspects.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4AA18A481D75D2FE005386FF /* Aspects.framework */; }; 21 | 4AA18A4B1D75D30F005386FF /* Aspects.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 4AA18A481D75D2FE005386FF /* Aspects.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 22 | 4AA18A4D1D75D738005386FF /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA18A4C1D75D738005386FF /* Collection.swift */; }; 23 | 4AA18A4F1D75D75D005386FF /* Aspects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA18A4E1D75D75D005386FF /* Aspects.swift */; }; 24 | 4AC9E4CF1D74BAF50064B4E7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC9E4C71D74BAF50064B4E7 /* AppDelegate.swift */; }; 25 | 4AC9E4D01D74BAF50064B4E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AC9E4C81D74BAF50064B4E7 /* Assets.xcassets */; }; 26 | 4AC9E4D11D74BAF50064B4E7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AC9E4C91D74BAF50064B4E7 /* LaunchScreen.storyboard */; }; 27 | 4AC9E4D21D74BAF50064B4E7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AC9E4CB1D74BAF50064B4E7 /* Main.storyboard */; }; 28 | 4AC9E4D61D74BBD70064B4E7 /* Segue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC9E4D51D74BBD70064B4E7 /* Segue.swift */; }; 29 | 4AC9E4E11D74C2C40064B4E7 /* ViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC9E4DF1D74C2390064B4E7 /* ViewControllers.swift */; }; 30 | 4AC9E4E31D74C9620064B4E7 /* UIStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC9E4E21D74C9620064B4E7 /* UIStoryboardSegue.swift */; }; 31 | 4AEFD48A1D760122004E7F66 /* UITabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEFD4891D760122004E7F66 /* UITabBarController.swift */; }; 32 | 4AEFD48C1D7601A4004E7F66 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEFD48B1D7601A4004E7F66 /* UIViewController.swift */; }; 33 | 4AEFD4901D76089F004E7F66 /* SequenceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEFD48F1D76089F004E7F66 /* SequenceType.swift */; }; 34 | CD48AEBF2231C1090025D960 /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD48AEBE2231C1090025D960 /* Compatibility.swift */; }; 35 | CD48AEC02231C17B0025D960 /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD48AEBE2231C1090025D960 /* Compatibility.swift */; }; 36 | /* End PBXBuildFile section */ 37 | 38 | /* Begin PBXContainerItemProxy section */ 39 | 4A95938C1D748EE100EE27AA /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 4A9593781D748EE100EE27AA /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = 4A9593801D748EE100EE27AA; 44 | remoteInfo = Perform; 45 | }; 46 | 4A9593F21D74A71A00EE27AA /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = 4A9593781D748EE100EE27AA /* Project object */; 49 | proxyType = 1; 50 | remoteGlobalIDString = 4A9593CE1D74A59A00EE27AA; 51 | remoteInfo = PerformHarness; 52 | }; 53 | /* End PBXContainerItemProxy section */ 54 | 55 | /* Begin PBXCopyFilesBuildPhase section */ 56 | 4A9593B61D74929900EE27AA /* Copy Frameworks */ = { 57 | isa = PBXCopyFilesBuildPhase; 58 | buildActionMask = 2147483647; 59 | dstPath = ""; 60 | dstSubfolderSpec = 10; 61 | files = ( 62 | 4A9593BB1D7492CB00EE27AA /* Nimble.framework in Copy Frameworks */, 63 | 4A9593BC1D7492CB00EE27AA /* Quick.framework in Copy Frameworks */, 64 | ); 65 | name = "Copy Frameworks"; 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | 4A9593FB1D74AA1700EE27AA /* Copy Frameworks */ = { 69 | isa = PBXCopyFilesBuildPhase; 70 | buildActionMask = 2147483647; 71 | dstPath = ""; 72 | dstSubfolderSpec = 10; 73 | files = ( 74 | 4AA18A4B1D75D30F005386FF /* Aspects.framework in Copy Frameworks */, 75 | ); 76 | name = "Copy Frameworks"; 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXCopyFilesBuildPhase section */ 80 | 81 | /* Begin PBXFileReference section */ 82 | 4A9593811D748EE100EE27AA /* Perform.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Perform.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 83 | 4A95938A1D748EE100EE27AA /* PerformTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PerformTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 84 | 4A9593B01D7490B700EE27AA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 85 | 4A9593B11D7490B700EE27AA /* PerformSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformSpec.swift; sourceTree = ""; }; 86 | 4A9593B71D7492CB00EE27AA /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; 87 | 4A9593B81D7492CB00EE27AA /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; 88 | 4A9593C51D7493EC00EE27AA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 89 | 4A9593C61D7493EC00EE27AA /* Perform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Perform.h; sourceTree = ""; }; 90 | 4A9593C91D74940600EE27AA /* Perform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Perform.swift; sourceTree = ""; }; 91 | 4A9593CF1D74A59A00EE27AA /* PerformHarness.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PerformHarness.app; sourceTree = BUILT_PRODUCTS_DIR; }; 92 | 4AA18A481D75D2FE005386FF /* Aspects.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Aspects.framework; path = Carthage/Build/iOS/Aspects.framework; sourceTree = ""; }; 93 | 4AA18A4C1D75D738005386FF /* Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = ""; }; 94 | 4AA18A4E1D75D75D005386FF /* Aspects.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Aspects.swift; sourceTree = ""; }; 95 | 4AC9E4C71D74BAF50064B4E7 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 96 | 4AC9E4C81D74BAF50064B4E7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97 | 4AC9E4CA1D74BAF50064B4E7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 98 | 4AC9E4CC1D74BAF50064B4E7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 99 | 4AC9E4CD1D74BAF50064B4E7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 100 | 4AC9E4D51D74BBD70064B4E7 /* Segue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Segue.swift; sourceTree = ""; }; 101 | 4AC9E4DF1D74C2390064B4E7 /* ViewControllers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewControllers.swift; sourceTree = ""; }; 102 | 4AC9E4E21D74C9620064B4E7 /* UIStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStoryboardSegue.swift; sourceTree = ""; }; 103 | 4AEFD4891D760122004E7F66 /* UITabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITabBarController.swift; sourceTree = ""; }; 104 | 4AEFD48B1D7601A4004E7F66 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; 105 | 4AEFD48F1D76089F004E7F66 /* SequenceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SequenceType.swift; sourceTree = ""; }; 106 | CD48AEBE2231C1090025D960 /* Compatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Compatibility.swift; sourceTree = ""; }; 107 | /* End PBXFileReference section */ 108 | 109 | /* Begin PBXFrameworksBuildPhase section */ 110 | 4A95937D1D748EE100EE27AA /* Frameworks */ = { 111 | isa = PBXFrameworksBuildPhase; 112 | buildActionMask = 2147483647; 113 | files = ( 114 | 4AA18A491D75D2FE005386FF /* Aspects.framework in Frameworks */, 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | 4A9593871D748EE100EE27AA /* Frameworks */ = { 119 | isa = PBXFrameworksBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | 4A95938B1D748EE100EE27AA /* Perform.framework in Frameworks */, 123 | 4A9593C21D74932000EE27AA /* Nimble.framework in Frameworks */, 124 | 4A9593C31D74932000EE27AA /* Quick.framework in Frameworks */, 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | 4A9593CC1D74A59A00EE27AA /* Frameworks */ = { 129 | isa = PBXFrameworksBuildPhase; 130 | buildActionMask = 2147483647; 131 | files = ( 132 | 4A9593F81D74A9F500EE27AA /* Perform.framework in Frameworks */, 133 | 4AA18A4A1D75D30A005386FF /* Aspects.framework in Frameworks */, 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXFrameworksBuildPhase section */ 138 | 139 | /* Begin PBXGroup section */ 140 | 4A9593771D748EE100EE27AA = { 141 | isa = PBXGroup; 142 | children = ( 143 | 4A9593831D748EE100EE27AA /* Sources */, 144 | 4A9593A61D7490A000EE27AA /* Tests */, 145 | 4A9593BF1D7492D400EE27AA /* Frameworks */, 146 | 4A9593821D748EE100EE27AA /* Products */, 147 | ); 148 | sourceTree = ""; 149 | }; 150 | 4A9593821D748EE100EE27AA /* Products */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 4A9593811D748EE100EE27AA /* Perform.framework */, 154 | 4A95938A1D748EE100EE27AA /* PerformTests.xctest */, 155 | 4A9593CF1D74A59A00EE27AA /* PerformHarness.app */, 156 | ); 157 | name = Products; 158 | sourceTree = ""; 159 | }; 160 | 4A9593831D748EE100EE27AA /* Sources */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 4A9593C41D7493EC00EE27AA /* Perform */, 164 | ); 165 | path = Sources; 166 | sourceTree = ""; 167 | }; 168 | 4A9593A61D7490A000EE27AA /* Tests */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 4AC9E4C61D74BAF50064B4E7 /* Harness */, 172 | 4A9593AF1D7490B700EE27AA /* PerformTests */, 173 | ); 174 | path = Tests; 175 | sourceTree = ""; 176 | }; 177 | 4A9593AF1D7490B700EE27AA /* PerformTests */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 4A9593B11D7490B700EE27AA /* PerformSpec.swift */, 181 | 4A9593B01D7490B700EE27AA /* Info.plist */, 182 | ); 183 | path = PerformTests; 184 | sourceTree = ""; 185 | }; 186 | 4A9593BF1D7492D400EE27AA /* Frameworks */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 4AA18A481D75D2FE005386FF /* Aspects.framework */, 190 | 4A9593B71D7492CB00EE27AA /* Nimble.framework */, 191 | 4A9593B81D7492CB00EE27AA /* Quick.framework */, 192 | ); 193 | name = Frameworks; 194 | sourceTree = ""; 195 | }; 196 | 4A9593C41D7493EC00EE27AA /* Perform */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | 4AA18A4E1D75D75D005386FF /* Aspects.swift */, 200 | 4AA18A4C1D75D738005386FF /* Collection.swift */, 201 | CD48AEBE2231C1090025D960 /* Compatibility.swift */, 202 | 4A9593C51D7493EC00EE27AA /* Info.plist */, 203 | 4A9593C61D7493EC00EE27AA /* Perform.h */, 204 | 4A9593C91D74940600EE27AA /* Perform.swift */, 205 | 4AC9E4D51D74BBD70064B4E7 /* Segue.swift */, 206 | 4AEFD48F1D76089F004E7F66 /* SequenceType.swift */, 207 | 4AC9E4E21D74C9620064B4E7 /* UIStoryboardSegue.swift */, 208 | 4AEFD4891D760122004E7F66 /* UITabBarController.swift */, 209 | 4AEFD48B1D7601A4004E7F66 /* UIViewController.swift */, 210 | ); 211 | path = Perform; 212 | sourceTree = ""; 213 | }; 214 | 4AC9E4C61D74BAF50064B4E7 /* Harness */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | 4AC9E4C71D74BAF50064B4E7 /* AppDelegate.swift */, 218 | 4AC9E4DF1D74C2390064B4E7 /* ViewControllers.swift */, 219 | 4AC9E4C91D74BAF50064B4E7 /* LaunchScreen.storyboard */, 220 | 4AC9E4CB1D74BAF50064B4E7 /* Main.storyboard */, 221 | 4AC9E4C81D74BAF50064B4E7 /* Assets.xcassets */, 222 | 4AC9E4CD1D74BAF50064B4E7 /* Info.plist */, 223 | ); 224 | path = Harness; 225 | sourceTree = ""; 226 | }; 227 | /* End PBXGroup section */ 228 | 229 | /* Begin PBXHeadersBuildPhase section */ 230 | 4A95937E1D748EE100EE27AA /* Headers */ = { 231 | isa = PBXHeadersBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | 4A9593C81D7493EC00EE27AA /* Perform.h in Headers */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXHeadersBuildPhase section */ 239 | 240 | /* Begin PBXNativeTarget section */ 241 | 4A9593801D748EE100EE27AA /* Perform */ = { 242 | isa = PBXNativeTarget; 243 | buildConfigurationList = 4A9593951D748EE100EE27AA /* Build configuration list for PBXNativeTarget "Perform" */; 244 | buildPhases = ( 245 | 4A95937C1D748EE100EE27AA /* Sources */, 246 | 4A95937D1D748EE100EE27AA /* Frameworks */, 247 | 4A95937E1D748EE100EE27AA /* Headers */, 248 | 4A95937F1D748EE100EE27AA /* Resources */, 249 | ); 250 | buildRules = ( 251 | ); 252 | dependencies = ( 253 | ); 254 | name = Perform; 255 | productName = Perform; 256 | productReference = 4A9593811D748EE100EE27AA /* Perform.framework */; 257 | productType = "com.apple.product-type.framework"; 258 | }; 259 | 4A9593891D748EE100EE27AA /* PerformTests */ = { 260 | isa = PBXNativeTarget; 261 | buildConfigurationList = 4A9593981D748EE100EE27AA /* Build configuration list for PBXNativeTarget "PerformTests" */; 262 | buildPhases = ( 263 | 4A9593861D748EE100EE27AA /* Sources */, 264 | 4A9593871D748EE100EE27AA /* Frameworks */, 265 | 4A9593881D748EE100EE27AA /* Resources */, 266 | 4A9593B61D74929900EE27AA /* Copy Frameworks */, 267 | ); 268 | buildRules = ( 269 | ); 270 | dependencies = ( 271 | 4A95938D1D748EE100EE27AA /* PBXTargetDependency */, 272 | 4A9593F31D74A71A00EE27AA /* PBXTargetDependency */, 273 | ); 274 | name = PerformTests; 275 | productName = PerformTests; 276 | productReference = 4A95938A1D748EE100EE27AA /* PerformTests.xctest */; 277 | productType = "com.apple.product-type.bundle.unit-test"; 278 | }; 279 | 4A9593CE1D74A59A00EE27AA /* PerformHarness */ = { 280 | isa = PBXNativeTarget; 281 | buildConfigurationList = 4A9593DE1D74A59A00EE27AA /* Build configuration list for PBXNativeTarget "PerformHarness" */; 282 | buildPhases = ( 283 | 4A9593CB1D74A59A00EE27AA /* Sources */, 284 | 4A9593CC1D74A59A00EE27AA /* Frameworks */, 285 | 4A9593FB1D74AA1700EE27AA /* Copy Frameworks */, 286 | 4A9593CD1D74A59A00EE27AA /* Resources */, 287 | ); 288 | buildRules = ( 289 | ); 290 | dependencies = ( 291 | ); 292 | name = PerformHarness; 293 | productName = PerformHarness; 294 | productReference = 4A9593CF1D74A59A00EE27AA /* PerformHarness.app */; 295 | productType = "com.apple.product-type.application"; 296 | }; 297 | /* End PBXNativeTarget section */ 298 | 299 | /* Begin PBXProject section */ 300 | 4A9593781D748EE100EE27AA /* Project object */ = { 301 | isa = PBXProject; 302 | attributes = { 303 | LastSwiftUpdateCheck = 0800; 304 | LastUpgradeCheck = 0940; 305 | ORGANIZATIONNAME = thoughtbot; 306 | TargetAttributes = { 307 | 4A9593801D748EE100EE27AA = { 308 | CreatedOnToolsVersion = 8.0; 309 | LastSwiftMigration = 0900; 310 | ProvisioningStyle = Automatic; 311 | }; 312 | 4A9593891D748EE100EE27AA = { 313 | CreatedOnToolsVersion = 8.0; 314 | LastSwiftMigration = 0900; 315 | ProvisioningStyle = Automatic; 316 | TestTargetID = 4A9593CE1D74A59A00EE27AA; 317 | }; 318 | 4A9593CE1D74A59A00EE27AA = { 319 | CreatedOnToolsVersion = 8.0; 320 | LastSwiftMigration = 0900; 321 | ProvisioningStyle = Automatic; 322 | }; 323 | }; 324 | }; 325 | buildConfigurationList = 4A95937B1D748EE100EE27AA /* Build configuration list for PBXProject "Perform" */; 326 | compatibilityVersion = "Xcode 3.2"; 327 | developmentRegion = English; 328 | hasScannedForEncodings = 0; 329 | knownRegions = ( 330 | en, 331 | Base, 332 | ); 333 | mainGroup = 4A9593771D748EE100EE27AA; 334 | productRefGroup = 4A9593821D748EE100EE27AA /* Products */; 335 | projectDirPath = ""; 336 | projectRoot = ""; 337 | targets = ( 338 | 4A9593801D748EE100EE27AA /* Perform */, 339 | 4A9593891D748EE100EE27AA /* PerformTests */, 340 | 4A9593CE1D74A59A00EE27AA /* PerformHarness */, 341 | ); 342 | }; 343 | /* End PBXProject section */ 344 | 345 | /* Begin PBXResourcesBuildPhase section */ 346 | 4A95937F1D748EE100EE27AA /* Resources */ = { 347 | isa = PBXResourcesBuildPhase; 348 | buildActionMask = 2147483647; 349 | files = ( 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | 4A9593881D748EE100EE27AA /* Resources */ = { 354 | isa = PBXResourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | ); 358 | runOnlyForDeploymentPostprocessing = 0; 359 | }; 360 | 4A9593CD1D74A59A00EE27AA /* Resources */ = { 361 | isa = PBXResourcesBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | 4AC9E4D21D74BAF50064B4E7 /* Main.storyboard in Resources */, 365 | 4AC9E4D01D74BAF50064B4E7 /* Assets.xcassets in Resources */, 366 | 4AC9E4D11D74BAF50064B4E7 /* LaunchScreen.storyboard in Resources */, 367 | ); 368 | runOnlyForDeploymentPostprocessing = 0; 369 | }; 370 | /* End PBXResourcesBuildPhase section */ 371 | 372 | /* Begin PBXSourcesBuildPhase section */ 373 | 4A95937C1D748EE100EE27AA /* Sources */ = { 374 | isa = PBXSourcesBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | 4AEFD4901D76089F004E7F66 /* SequenceType.swift in Sources */, 378 | 4AA18A4F1D75D75D005386FF /* Aspects.swift in Sources */, 379 | 4AEFD48A1D760122004E7F66 /* UITabBarController.swift in Sources */, 380 | 4AC9E4E31D74C9620064B4E7 /* UIStoryboardSegue.swift in Sources */, 381 | 4AA18A4D1D75D738005386FF /* Collection.swift in Sources */, 382 | 4A95940A1D74B16D00EE27AA /* Perform.swift in Sources */, 383 | 4AEFD48C1D7601A4004E7F66 /* UIViewController.swift in Sources */, 384 | 4AC9E4D61D74BBD70064B4E7 /* Segue.swift in Sources */, 385 | CD48AEBF2231C1090025D960 /* Compatibility.swift in Sources */, 386 | ); 387 | runOnlyForDeploymentPostprocessing = 0; 388 | }; 389 | 4A9593861D748EE100EE27AA /* Sources */ = { 390 | isa = PBXSourcesBuildPhase; 391 | buildActionMask = 2147483647; 392 | files = ( 393 | 4A95940C1D74B19800EE27AA /* PerformSpec.swift in Sources */, 394 | ); 395 | runOnlyForDeploymentPostprocessing = 0; 396 | }; 397 | 4A9593CB1D74A59A00EE27AA /* Sources */ = { 398 | isa = PBXSourcesBuildPhase; 399 | buildActionMask = 2147483647; 400 | files = ( 401 | 4AC9E4E11D74C2C40064B4E7 /* ViewControllers.swift in Sources */, 402 | 4AC9E4CF1D74BAF50064B4E7 /* AppDelegate.swift in Sources */, 403 | CD48AEC02231C17B0025D960 /* Compatibility.swift in Sources */, 404 | ); 405 | runOnlyForDeploymentPostprocessing = 0; 406 | }; 407 | /* End PBXSourcesBuildPhase section */ 408 | 409 | /* Begin PBXTargetDependency section */ 410 | 4A95938D1D748EE100EE27AA /* PBXTargetDependency */ = { 411 | isa = PBXTargetDependency; 412 | target = 4A9593801D748EE100EE27AA /* Perform */; 413 | targetProxy = 4A95938C1D748EE100EE27AA /* PBXContainerItemProxy */; 414 | }; 415 | 4A9593F31D74A71A00EE27AA /* PBXTargetDependency */ = { 416 | isa = PBXTargetDependency; 417 | target = 4A9593CE1D74A59A00EE27AA /* PerformHarness */; 418 | targetProxy = 4A9593F21D74A71A00EE27AA /* PBXContainerItemProxy */; 419 | }; 420 | /* End PBXTargetDependency section */ 421 | 422 | /* Begin PBXVariantGroup section */ 423 | 4AC9E4C91D74BAF50064B4E7 /* LaunchScreen.storyboard */ = { 424 | isa = PBXVariantGroup; 425 | children = ( 426 | 4AC9E4CA1D74BAF50064B4E7 /* Base */, 427 | ); 428 | name = LaunchScreen.storyboard; 429 | sourceTree = ""; 430 | }; 431 | 4AC9E4CB1D74BAF50064B4E7 /* Main.storyboard */ = { 432 | isa = PBXVariantGroup; 433 | children = ( 434 | 4AC9E4CC1D74BAF50064B4E7 /* Base */, 435 | ); 436 | name = Main.storyboard; 437 | sourceTree = ""; 438 | }; 439 | /* End PBXVariantGroup section */ 440 | 441 | /* Begin XCBuildConfiguration section */ 442 | 4A9593931D748EE100EE27AA /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 458 | CLANG_WARN_EMPTY_BODY = YES; 459 | CLANG_WARN_ENUM_CONVERSION = YES; 460 | CLANG_WARN_INFINITE_RECURSION = YES; 461 | CLANG_WARN_INT_CONVERSION = YES; 462 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 463 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 464 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 465 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 466 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 467 | CLANG_WARN_STRICT_PROTOTYPES = YES; 468 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 469 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 470 | CLANG_WARN_UNREACHABLE_CODE = YES; 471 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 472 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 473 | COPY_PHASE_STRIP = NO; 474 | CURRENT_PROJECT_VERSION = 1; 475 | DEBUG_INFORMATION_FORMAT = dwarf; 476 | ENABLE_STRICT_OBJC_MSGSEND = YES; 477 | ENABLE_TESTABILITY = YES; 478 | "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Carthage/Build/iOS"; 479 | "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Carthage/Build/iOS"; 480 | GCC_C_LANGUAGE_STANDARD = gnu99; 481 | GCC_DYNAMIC_NO_PIC = NO; 482 | GCC_NO_COMMON_BLOCKS = YES; 483 | GCC_OPTIMIZATION_LEVEL = 0; 484 | GCC_PREPROCESSOR_DEFINITIONS = ( 485 | "DEBUG=1", 486 | "$(inherited)", 487 | ); 488 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 489 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 490 | GCC_WARN_UNDECLARED_SELECTOR = YES; 491 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 492 | GCC_WARN_UNUSED_FUNCTION = YES; 493 | GCC_WARN_UNUSED_VARIABLE = YES; 494 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 495 | MTL_ENABLE_DEBUG_INFO = YES; 496 | ONLY_ACTIVE_ARCH = YES; 497 | SDKROOT = iphoneos; 498 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 499 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 500 | SWIFT_VERSION = 4.2; 501 | TARGETED_DEVICE_FAMILY = "1,2"; 502 | VERSIONING_SYSTEM = "apple-generic"; 503 | VERSION_INFO_PREFIX = ""; 504 | }; 505 | name = Debug; 506 | }; 507 | 4A9593941D748EE100EE27AA /* Release */ = { 508 | isa = XCBuildConfiguration; 509 | buildSettings = { 510 | ALWAYS_SEARCH_USER_PATHS = NO; 511 | CLANG_ANALYZER_NONNULL = YES; 512 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 513 | CLANG_CXX_LIBRARY = "libc++"; 514 | CLANG_ENABLE_MODULES = YES; 515 | CLANG_ENABLE_OBJC_ARC = YES; 516 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 517 | CLANG_WARN_BOOL_CONVERSION = YES; 518 | CLANG_WARN_COMMA = YES; 519 | CLANG_WARN_CONSTANT_CONVERSION = YES; 520 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 521 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 522 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 523 | CLANG_WARN_EMPTY_BODY = YES; 524 | CLANG_WARN_ENUM_CONVERSION = YES; 525 | CLANG_WARN_INFINITE_RECURSION = YES; 526 | CLANG_WARN_INT_CONVERSION = YES; 527 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 528 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 529 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 530 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 531 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 532 | CLANG_WARN_STRICT_PROTOTYPES = YES; 533 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 534 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 535 | CLANG_WARN_UNREACHABLE_CODE = YES; 536 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 537 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 538 | COPY_PHASE_STRIP = NO; 539 | CURRENT_PROJECT_VERSION = 1; 540 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 541 | ENABLE_NS_ASSERTIONS = NO; 542 | ENABLE_STRICT_OBJC_MSGSEND = YES; 543 | "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Carthage/Build/iOS"; 544 | "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Carthage/Build/iOS"; 545 | GCC_C_LANGUAGE_STANDARD = gnu99; 546 | GCC_NO_COMMON_BLOCKS = YES; 547 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 548 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 549 | GCC_WARN_UNDECLARED_SELECTOR = YES; 550 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 551 | GCC_WARN_UNUSED_FUNCTION = YES; 552 | GCC_WARN_UNUSED_VARIABLE = YES; 553 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 554 | MTL_ENABLE_DEBUG_INFO = NO; 555 | SDKROOT = iphoneos; 556 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 557 | SWIFT_VERSION = 4.2; 558 | TARGETED_DEVICE_FAMILY = "1,2"; 559 | VALIDATE_PRODUCT = YES; 560 | VERSIONING_SYSTEM = "apple-generic"; 561 | VERSION_INFO_PREFIX = ""; 562 | }; 563 | name = Release; 564 | }; 565 | 4A9593961D748EE100EE27AA /* Debug */ = { 566 | isa = XCBuildConfiguration; 567 | buildSettings = { 568 | APPLICATION_EXTENSION_API_ONLY = NO; 569 | CLANG_ENABLE_MODULES = YES; 570 | CODE_SIGN_IDENTITY = ""; 571 | DEFINES_MODULE = YES; 572 | DYLIB_COMPATIBILITY_VERSION = 1; 573 | DYLIB_CURRENT_VERSION = 1; 574 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 575 | FRAMEWORK_SEARCH_PATHS = ( 576 | "$(inherited)", 577 | "$(PROJECT_DIR)/Carthage/Build/iOS", 578 | ); 579 | INFOPLIST_FILE = Sources/Perform/Info.plist; 580 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 581 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 582 | PRODUCT_BUNDLE_IDENTIFIER = com.thoughtbot.Perform; 583 | PRODUCT_NAME = "$(TARGET_NAME)"; 584 | SKIP_INSTALL = YES; 585 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 586 | SWIFT_VERSION = 4.2; 587 | }; 588 | name = Debug; 589 | }; 590 | 4A9593971D748EE100EE27AA /* Release */ = { 591 | isa = XCBuildConfiguration; 592 | buildSettings = { 593 | APPLICATION_EXTENSION_API_ONLY = NO; 594 | CLANG_ENABLE_MODULES = YES; 595 | CODE_SIGN_IDENTITY = ""; 596 | DEFINES_MODULE = YES; 597 | DYLIB_COMPATIBILITY_VERSION = 1; 598 | DYLIB_CURRENT_VERSION = 1; 599 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 600 | FRAMEWORK_SEARCH_PATHS = ( 601 | "$(inherited)", 602 | "$(PROJECT_DIR)/Carthage/Build/iOS", 603 | ); 604 | INFOPLIST_FILE = Sources/Perform/Info.plist; 605 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 606 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 607 | PRODUCT_BUNDLE_IDENTIFIER = com.thoughtbot.Perform; 608 | PRODUCT_NAME = "$(TARGET_NAME)"; 609 | SKIP_INSTALL = YES; 610 | SWIFT_VERSION = 4.2; 611 | }; 612 | name = Release; 613 | }; 614 | 4A9593991D748EE100EE27AA /* Debug */ = { 615 | isa = XCBuildConfiguration; 616 | buildSettings = { 617 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 618 | BUNDLE_LOADER = "$(TEST_HOST)"; 619 | FRAMEWORK_SEARCH_PATHS = ( 620 | "$(inherited)", 621 | "$(PROJECT_DIR)/Carthage/Build/iOS", 622 | ); 623 | INFOPLIST_FILE = Tests/PerformTests/Info.plist; 624 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 625 | PRODUCT_BUNDLE_IDENTIFIER = com.thoughtbot.PerformTests; 626 | PRODUCT_NAME = "$(TARGET_NAME)"; 627 | SWIFT_VERSION = 4.2; 628 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PerformHarness.app/PerformHarness"; 629 | }; 630 | name = Debug; 631 | }; 632 | 4A95939A1D748EE100EE27AA /* Release */ = { 633 | isa = XCBuildConfiguration; 634 | buildSettings = { 635 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 636 | BUNDLE_LOADER = "$(TEST_HOST)"; 637 | FRAMEWORK_SEARCH_PATHS = ( 638 | "$(inherited)", 639 | "$(PROJECT_DIR)/Carthage/Build/iOS", 640 | ); 641 | INFOPLIST_FILE = Tests/PerformTests/Info.plist; 642 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 643 | PRODUCT_BUNDLE_IDENTIFIER = com.thoughtbot.PerformTests; 644 | PRODUCT_NAME = "$(TARGET_NAME)"; 645 | SWIFT_VERSION = 4.2; 646 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PerformHarness.app/PerformHarness"; 647 | }; 648 | name = Release; 649 | }; 650 | 4A9593DF1D74A59A00EE27AA /* Debug */ = { 651 | isa = XCBuildConfiguration; 652 | buildSettings = { 653 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 654 | FRAMEWORK_SEARCH_PATHS = ( 655 | "$(inherited)", 656 | "$(PROJECT_DIR)/Carthage/Build/iOS", 657 | ); 658 | INFOPLIST_FILE = Tests/Harness/Info.plist; 659 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 660 | PRODUCT_BUNDLE_IDENTIFIER = com.thoughtbot.PerformHarness; 661 | PRODUCT_NAME = "$(TARGET_NAME)"; 662 | SWIFT_VERSION = 4.2; 663 | }; 664 | name = Debug; 665 | }; 666 | 4A9593E01D74A59A00EE27AA /* Release */ = { 667 | isa = XCBuildConfiguration; 668 | buildSettings = { 669 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 670 | FRAMEWORK_SEARCH_PATHS = ( 671 | "$(inherited)", 672 | "$(PROJECT_DIR)/Carthage/Build/iOS", 673 | ); 674 | INFOPLIST_FILE = Tests/Harness/Info.plist; 675 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 676 | PRODUCT_BUNDLE_IDENTIFIER = com.thoughtbot.PerformHarness; 677 | PRODUCT_NAME = "$(TARGET_NAME)"; 678 | SWIFT_VERSION = 4.2; 679 | }; 680 | name = Release; 681 | }; 682 | /* End XCBuildConfiguration section */ 683 | 684 | /* Begin XCConfigurationList section */ 685 | 4A95937B1D748EE100EE27AA /* Build configuration list for PBXProject "Perform" */ = { 686 | isa = XCConfigurationList; 687 | buildConfigurations = ( 688 | 4A9593931D748EE100EE27AA /* Debug */, 689 | 4A9593941D748EE100EE27AA /* Release */, 690 | ); 691 | defaultConfigurationIsVisible = 0; 692 | defaultConfigurationName = Release; 693 | }; 694 | 4A9593951D748EE100EE27AA /* Build configuration list for PBXNativeTarget "Perform" */ = { 695 | isa = XCConfigurationList; 696 | buildConfigurations = ( 697 | 4A9593961D748EE100EE27AA /* Debug */, 698 | 4A9593971D748EE100EE27AA /* Release */, 699 | ); 700 | defaultConfigurationIsVisible = 0; 701 | defaultConfigurationName = Release; 702 | }; 703 | 4A9593981D748EE100EE27AA /* Build configuration list for PBXNativeTarget "PerformTests" */ = { 704 | isa = XCConfigurationList; 705 | buildConfigurations = ( 706 | 4A9593991D748EE100EE27AA /* Debug */, 707 | 4A95939A1D748EE100EE27AA /* Release */, 708 | ); 709 | defaultConfigurationIsVisible = 0; 710 | defaultConfigurationName = Release; 711 | }; 712 | 4A9593DE1D74A59A00EE27AA /* Build configuration list for PBXNativeTarget "PerformHarness" */ = { 713 | isa = XCConfigurationList; 714 | buildConfigurations = ( 715 | 4A9593DF1D74A59A00EE27AA /* Debug */, 716 | 4A9593E01D74A59A00EE27AA /* Release */, 717 | ); 718 | defaultConfigurationIsVisible = 0; 719 | defaultConfigurationName = Release; 720 | }; 721 | /* End XCConfigurationList section */ 722 | }; 723 | rootObject = 4A9593781D748EE100EE27AA /* Project object */; 724 | } 725 | -------------------------------------------------------------------------------- /Perform.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Perform.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Perform.xcodeproj/xcshareddata/xcschemes/Perform.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # This Podfile is not intended for development, but provided to encourage 2 | # Circle CI to properly configure CocoaPods for testing purposes. 3 | 4 | platform :ios, "8.0" 5 | 6 | install! "cocoapods", integrate_targets: false 7 | 8 | target "Perform" do 9 | podspec 10 | end 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Perform 2 | 3 | Easy dependency injection for storyboard segues. 4 | 5 | ```swift 6 | import Perform 7 | 8 | // ... 9 | 10 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: NSIndexPath) { 11 | let task = taskList[indexPath.row] 12 | perform(.showTaskDetails) { taskVC in 13 | taskVC.task = task 14 | } 15 | } 16 | ``` 17 | 18 | ## Usage 19 | 20 | Configure your segues: 21 | 22 | ```swift 23 | // Sources/Extensions/Segue.swift 24 | import Perform 25 | 26 | extension Segue { 27 | static var showTaskDetails: Segue { 28 | return .init(identifier: "ShowTaskDetails") 29 | } 30 | } 31 | ``` 32 | 33 | And then use `perform(_:prepare:)` instead of `performSegue(withIdentifier:sender:)`. 34 | That's it! 35 | 36 | #### Type-safe segues 37 | 38 | Perform checks the type of the destination view controller and casts it for you, 39 | raising an error if your destination view controller is an unexpected type. 40 | 41 | #### Still works if your view controller is embedded in a container 42 | 43 | Ever written code like this? 44 | 45 | ```swift 46 | guard let nav = segue.destinationViewController as? UINavigationController, 47 | let content = nav.rootViewController as? MyViewController 48 | else { return } 49 | 50 | // ... finally! 😭 51 | ``` 52 | 53 | Perform takes care of this, searching the view controller hierarchy for a view 54 | controller of the matching type! 55 | 56 | #### No switch statements in `prepareForSegue(_:sender:)` 57 | 58 | Multiple segues from one view controller? No problem, just prepare each 59 | destination view controller right where you perform the segue. No more massive 60 | switch statements. 61 | 62 | #### Further reading 63 | 64 | For more examples, and a discussion about the motivation and design of Perform, 65 | take a look at the [introductory blog post][perform-blog]. 66 | 67 | [perform-blog]: https://robots.thoughtbot.com/introducing-perform-easy-dependency-injection-for-storyboard-segues 68 | 69 | ## Installation 70 | 71 | ### Compatibility 72 | 73 | | Swift Version | Perform Version | 74 | | ------------- | --------------- | 75 | | 3.x | 2.x | 76 | | 2.x | 1.x | 77 | 78 | ### [Carthage][carthage-home] 79 | 80 | Add the following to your Cartfile: 81 | 82 | ``` 83 | github "thoughtbot/Perform" ~> 2.0 84 | ``` 85 | 86 | Then run `carthage update Perform`. 87 | 88 | Follow the instructions in [Carthage's README][carthage-readme] for up-to-date 89 | installation instructions. 90 | 91 | [carthage-home]: https://github.com/Carthage/Carthage 92 | [carthage-readme]: https://github.com/Carthage/Carthage#adding-frameworks-to-an-application 93 | 94 | ### [CocoaPods][cocoapods] 95 | 96 | Add the following to your Podfile: 97 | 98 | ``` 99 | pod "Perform", "~> 2.0" 100 | ``` 101 | 102 | See the [CocoaPods guide][cocoapods-usage] for up-to-date installation 103 | instructions. 104 | 105 | [cocoapods]: https://cocoapods.org 106 | [cocoapods-usage]: https://guides.cocoapods.org/using/using-cocoapods.html 107 | 108 | ## Contributing 109 | 110 | See the [CONTRIBUTING][] document. 111 | 112 | [CONTRIBUTING]: CONTRIBUTING.md 113 | 114 | ## License 115 | 116 | Perform is Copyright (c) 2016 thoughtbot, inc. 117 | It is free software, and may be redistributed 118 | under the terms specified in the [LICENSE] file. 119 | 120 | [LICENSE]: LICENSE 121 | 122 | ## About 123 | 124 | ![thoughtbot](https://thoughtbot.com/logo.png) 125 | 126 | Perform is maintained and funded by thoughtbot, inc. 127 | The names and logos for thoughtbot are trademarks of thoughtbot, inc. 128 | 129 | We love open source software! 130 | See [our other projects][community] 131 | or [hire us][hire] to help build your product. 132 | 133 | [community]: https://thoughtbot.com/community?utm_source=github 134 | [hire]: https://thoughtbot.com/hire-us?utm_source=github 135 | -------------------------------------------------------------------------------- /Sources/Perform/Aspects.swift: -------------------------------------------------------------------------------- 1 | import Aspects 2 | 3 | extension NSObject { 4 | @nonobjc final func aspect_hook(_ selector: Selector, options: AspectOptions, body: @convention(block) @escaping (AspectInfo) -> Void) throws -> AspectToken { 5 | return try self.aspect_hook(selector, with: options, usingBlock: unsafeBitCast(body, to: NSObject.self)) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Sources/Perform/Collection.swift: -------------------------------------------------------------------------------- 1 | extension Collection { 2 | var second: Iterator.Element? { 3 | return index(startIndex, offsetBy: 1, limitedBy: endIndex).map { self[$0] } 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Sources/Perform/Compatibility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Compatibility.swift 3 | // Perform 4 | // 5 | // Created by Patrick Montalto on 3/7/19. 6 | // Copyright © 2019 thoughtbot. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | #if !swift(>=4.2) 12 | extension UIViewController { 13 | var children: [UIViewController] { 14 | return childViewControllers 15 | } 16 | } 17 | extension UIApplication { 18 | typealias LaunchOptionsKey = UIApplicationLaunchOptionsKey 19 | } 20 | #endif 21 | -------------------------------------------------------------------------------- /Sources/Perform/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 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Perform/Perform.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | FOUNDATION_EXPORT double PerformVersionNumber; 4 | FOUNDATION_EXPORT const unsigned char PerformVersionString[]; 5 | -------------------------------------------------------------------------------- /Sources/Perform/Perform.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIViewController { 4 | /// Perform the storyboard segue identified by `segue`, invoking `prepare` 5 | /// with the destination view controller when `prepareForSegue(_:sender:)` 6 | /// is received. 7 | /// 8 | /// **Example** 9 | /// 10 | /// // prepare for segue inside the closure 11 | /// perform(.showTaskDetails) { taskVC in 12 | /// taskVC.task = self.selectedTask 13 | /// } 14 | /// 15 | /// // just perform a segue 16 | /// perform(.signOut) 17 | /// 18 | /// If the destination view controller itself doesn't match the destination 19 | /// type specified by `segue`, its view controller hierarchy is searched 20 | /// until the matching view controller is found. For example, if the desired 21 | /// view controller is embedded inside a `UINavigationController`, it will 22 | /// still be found and passed along to `prepare`. 23 | /// 24 | /// - parameter segue: 25 | /// An instance of `Segue` which specifies the identifier 26 | /// and destination view controller type. 27 | /// 28 | /// - parameter prepare: 29 | /// A function that will be invoked with the matching destination view 30 | /// view controller when `prepareForSegue(_:sender:)` is received. The 31 | /// default value for this parameter does nothing. 32 | /// 33 | /// - note: 34 | /// If no matching view controller is found in the destination 35 | /// view controller hierarchy, this method will raise a fatal error 36 | /// and crash. This usually means that the view controller hasn't 37 | /// been configured with the correct type in the storyboard. 38 | public func perform(_ segue: Segue, prepare: @escaping (Destination) -> Void = { _ in }) { 39 | performSegue(withIdentifier: segue.identifier) { [segueDescription = { String(reflecting: segue) }] segue, _ in 40 | guard let destination = segue.destinationViewController(ofType: Destination.self) else { 41 | #if DEBUG 42 | let printHierarchy = "_printHierarchy" 43 | let hierarchy = segue.destination.perform(Selector(printHierarchy)).takeUnretainedValue() 44 | fatalError("\(segueDescription()): expected destination view controller hierarchy to include \(Destination.self), got:\n\(hierarchy)") 45 | #else 46 | fatalError("\(segueDescription()): expected destination view controller hierarchy to include \(Destination.self)") 47 | #endif 48 | } 49 | 50 | prepare(destination) 51 | } 52 | } 53 | 54 | internal func performSegue(withIdentifier identifier: String, sender: Any? = nil, prepare: @escaping (UIStoryboardSegue, Any?) -> Void) { 55 | _ = try! aspect_hook( 56 | #selector(UIViewController.prepare(for:sender:)), 57 | options: .optionAutomaticRemoval, 58 | body: { info in 59 | let arguments = info.arguments()! 60 | prepare(arguments.first as! UIStoryboardSegue, arguments.second) 61 | } 62 | ) 63 | 64 | performSegue(withIdentifier: identifier, sender: sender) 65 | } 66 | } 67 | 68 | // MARK: - Type checker ambiguity hack 69 | 70 | extension UIViewController { 71 | @available(*, unavailable) 72 | public func perform() { 73 | fatalError( 74 | "This method will never be called, and exists only to remove an " + 75 | "apparent ambiguity resolving the generic method 'perform(_:prepare:)'" 76 | ) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/Perform/Segue.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct Segue { 4 | public let identifier: String 5 | 6 | public init(identifier: String) { 7 | self.identifier = identifier 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/Perform/SequenceType.swift: -------------------------------------------------------------------------------- 1 | extension Sequence { 2 | func first(_ isMatch: (Iterator.Element) -> Result?) -> Result? { 3 | for element in self { 4 | if let result = isMatch(element) { 5 | return result 6 | } 7 | } 8 | 9 | return nil 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/Perform/UIStoryboardSegue.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIStoryboardSegue { 4 | @nonobjc 5 | public final func destinationViewController(ofType type: Destination.Type) -> Destination? { 6 | return destination.childViewController(ofType: type) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Sources/Perform/UITabBarController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UITabBarController { 4 | /// For each view controller in `viewControllers`, iterate over its view 5 | /// controller hierarchy, passing each view controller to `configure`. 6 | /// 7 | /// **Example** 8 | /// 9 | /// class AppController: UITabBarController { 10 | /// let taskList = TaskList.shared 11 | /// 12 | /// override func viewDidLoad() { 13 | /// super.viewDidLoad() 14 | /// 15 | /// configureViewControllers { viewController in 16 | /// if let tasks = viewController as? TasksViewController { 17 | /// tasks.taskList = taskList 18 | /// } 19 | /// } 20 | /// } 21 | /// } 22 | /// 23 | /// - parameter configure: 24 | /// A function that will be invoked once with each view controller 25 | /// in each tab's view controller hierarchy. 26 | public func configureViewControllers(_ configure: (UIViewController) -> Void) { 27 | let hierarchy = (viewControllers ?? []).lazy.flatMap { $0.hierarchy } 28 | hierarchy.forEach(configure) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Perform/UIViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIViewController { 4 | /// Perform a breadth-first search of the receiver's view controller hierarchy, 5 | /// returning the first view controller matching the given `type`, or nil. 6 | /// 7 | /// - parameter type: 8 | /// The type of the view controller to search for. 9 | /// 10 | /// - returns: 11 | /// The first view controller matching the given `type`, or nil. 12 | public func childViewController(ofType type: Child.Type) -> Child? { 13 | return hierarchy.first { $0 as? Child } 14 | } 15 | 16 | var hierarchy: AnySequence { 17 | return AnySequence { () -> AnyIterator in 18 | var queue = [self] 19 | 20 | return AnyIterator { 21 | if let next = queue.popLast() { 22 | queue.insert(contentsOf: next.children, at: 0) 23 | return next 24 | } else { 25 | return nil 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Harness/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | var window: UIWindow? 6 | 7 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 8 | window = UIWindow(frame: UIScreen.main.bounds) 9 | window!.rootViewController = UIViewController() 10 | window!.makeKeyAndVisible() 11 | 12 | return false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/Harness/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Tests/Harness/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /Tests/Harness/Base.lproj/Main.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 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /Tests/Harness/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Tests/Harness/ViewControllers.swift: -------------------------------------------------------------------------------- 1 | import Perform 2 | import UIKit 3 | 4 | final class Detail: UIViewController { 5 | } 6 | 7 | final class Form: UITableViewController { 8 | } 9 | 10 | final class Feed: UITableViewController { 11 | } 12 | 13 | final class Profile: UITableViewController { 14 | } 15 | -------------------------------------------------------------------------------- /Tests/PerformTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/PerformTests/PerformSpec.swift: -------------------------------------------------------------------------------- 1 | @testable import PerformHarness 2 | import Perform 3 | import UIKit 4 | import Quick 5 | import Nimble 6 | 7 | extension Segue { 8 | static var showDetail: Segue { return .init(identifier: "ShowDetail") } 9 | static var showDetailCast: Segue { return .init(identifier: "ShowDetail") } 10 | static var showModalForm: Segue
{ return .init(identifier: "ShowForm") } 11 | } 12 | 13 | final class PerformSpec: QuickSpec { 14 | override func spec() { 15 | let storyboard = UIStoryboard(name: "Main", bundle: nil) 16 | 17 | describe("perform") { 18 | var root: UIViewController? 19 | 20 | beforeEach { 21 | let nav = storyboard.instantiateInitialViewController() as? UINavigationController 22 | root = nav?.topViewController 23 | } 24 | 25 | afterEach { 26 | root = nil 27 | } 28 | 29 | it("passes the destination view controller to the completion block") { 30 | var detail: UIViewController? 31 | 32 | root?.perform(.showDetail) { 33 | detail = $0 34 | } 35 | 36 | expect(detail).to(beAKindOf(UIViewController.self)) 37 | } 38 | 39 | it("casts the destination view controller to the specific view controller type") { 40 | var detail: Detail? 41 | 42 | root?.perform(.showDetailCast) { 43 | detail = $0 44 | } 45 | 46 | expect(detail).to(beAKindOf(Detail.self)) 47 | } 48 | 49 | it("searches for a child view controller of matching view controller type") { 50 | var form: Form? 51 | 52 | root?.perform(.showModalForm) { 53 | form = $0 54 | } 55 | 56 | expect(form).to(beAKindOf(Form.self)) 57 | } 58 | 59 | it("has a default prepare block") { 60 | root?.perform(.showDetail) 61 | expect(root?.navigationController?.topViewController).toEventually(beAKindOf(Detail.self)) 62 | } 63 | } 64 | 65 | describe("configureViewControllers") { 66 | var controller: UITabBarController? 67 | 68 | beforeEach { 69 | controller = storyboard.instantiateViewController(withIdentifier: "Tabs") as? UITabBarController 70 | } 71 | 72 | afterEach { 73 | controller = nil 74 | } 75 | 76 | it("configures each tab in turn") { 77 | var configured: [String] = [] 78 | 79 | controller?.configureViewControllers { viewController in 80 | switch viewController { 81 | case is Feed: 82 | configured.append("Feed") 83 | 84 | case is Profile: 85 | configured.append("Profile") 86 | 87 | default: 88 | break 89 | } 90 | } 91 | 92 | expect(configured).to(equal(["Feed", "Profile"])) 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /bin/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | carthage bootstrap --platform iOS --no-use-binaries 6 | cp Cartfile.resolved Carthage/ 7 | -------------------------------------------------------------------------------- /bin/bootstrap-if-needed: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if ! cmp -s Cartfile.resolved Carthage/Cartfile.resolved; then 4 | exec bin/bootstrap 5 | fi 6 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Runs tests via xcodebuild, formatting results with xcpretty. 4 | # 5 | # Usage: bin/test [options] 6 | # 7 | # Options: 8 | # -k Clean before running the tests. 9 | # -r Emit JUnit XML report. 10 | # -t Use RSpec-style test output. 11 | 12 | set -eo pipefail 13 | 14 | build_actions=(test) 15 | log_file="xcodebuild-test.log" 16 | test_reports_dir="${CIRCLE_TEST_REPORTS:-build}" 17 | xcpretty_options=(--color) 18 | 19 | while getopts kl:rt opt; do 20 | case ${opt} in 21 | k) 22 | build_actions=(clean test) 23 | ;; 24 | l) 25 | log_file="${OPTARG}" 26 | ;; 27 | r) 28 | xcpretty_options+=(--report junit --output "${test_reports_dir}/test-report.xml") 29 | ;; 30 | t) 31 | xcpretty_options+=(--test) 32 | ;; 33 | *) 34 | exit 1 35 | ;; 36 | esac 37 | done 38 | 39 | shift $((OPTIND - 1)) 40 | 41 | mkdir -p "${test_reports_dir}" 42 | 43 | echo "Raw xcodebuild output available at ${log_file}." 2>&1 44 | 45 | env NSUnbufferedIO=YES xcrun xcodebuild \ 46 | -scheme "Perform" \ 47 | -destination 'platform=iOS Simulator,name=iPhone 6s Plus' \ 48 | "${build_actions[@]}" \ 49 | | tee "${log_file}" \ 50 | | xcpretty "${xcpretty_options[@]}" 51 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | carthage update --platform iOS --no-use-binaries "$@" 6 | cp Cartfile.resolved Carthage/ 7 | --------------------------------------------------------------------------------