├── .gitignore ├── .travis.yml ├── Cartfile ├── Cartfile.resolved ├── Carthage.xcconfig ├── Info.plist ├── LICENSE ├── PMKUIKit.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── PMKUIKit.xcscheme ├── Package.resolved ├── Package.swift ├── README.markdown ├── Sources ├── PMKUIKit.h ├── UIImagePickerController+Promise.swift ├── UIView+AnyPromise.h ├── UIView+AnyPromise.m ├── UIView+Promise.swift ├── UIViewController+AnyPromise.h ├── UIViewController+AnyPromise.m └── UIViewPropertyAnimator+Promise.swift ├── Tests ├── TestUIImagePickerController.swift ├── TestUIView.swift ├── TestUIViewController.m └── infrastructure.swift └── UITests ├── Default-568h@2x.png ├── Entitlements.plist ├── TestUIImagePickerController.swift └── app.swift /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcodeproj/**/xcuserdata/ 2 | *.xcscmblueprint 3 | /Carthage 4 | /.build 5 | .DS_Store 6 | .swiftpm/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | language: swift 3 | osx_image: xcode10.2 4 | 5 | branches: 6 | only: 7 | - master 8 | stages: 9 | - lint 10 | - carthage 11 | - test 12 | jobs: 13 | include: 14 | - &pod 15 | stage: lint 16 | osx_image: xcode8.3 17 | env: SWIFT=3.1 18 | before_install: 19 | gem install cocoapods --prerelease --version 1.7.0.beta.3 20 | install: 21 | carthage bootstrap --no-build PromiseKit 22 | script: | 23 | cd Carthage/Checkouts/PromiseKit 24 | mv .github/PromiseKit.podspec . 25 | rm -rf Extensions/UIKit/Sources 26 | cp -R ../../../Sources Extensions/UIKit 27 | pod lib lint --subspec=PromiseKit/UIKit --fail-fast --swift-version=$SWIFT 28 | - <<: *pod 29 | osx_image: xcode9.2 30 | env: SWIFT=3.2 31 | - <<: *pod 32 | osx_image: xcode9.4 33 | env: SWIFT=3.3 34 | - <<: *pod 35 | osx_image: xcode10.1 36 | env: SWIFT=3.4 37 | - <<: *pod 38 | osx_image: xcode9.2 39 | env: SWIFT=4.0 40 | - <<: *pod 41 | osx_image: xcode9.4 42 | env: SWIFT=4.1 43 | - <<: *pod 44 | osx_image: xcode10.1 45 | env: SWIFT=4.2 46 | - <<: *pod 47 | osx_image: xcode10.2 48 | env: SWIFT=5.0 49 | 50 | - &carthage 51 | stage: carthage 52 | osx_image: xcode9.2 53 | script: | 54 | carthage bootstrap --cache-builds 55 | sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj 56 | carthage build --no-skip-current 57 | cache: 58 | directories: 59 | - Carthage 60 | - <<: *carthage 61 | osx_image: xcode9.4 62 | - <<: *carthage 63 | osx_image: xcode10.1 64 | - <<: *carthage 65 | osx_image: xcode10.2 66 | 67 | - &test 68 | stage: test 69 | xcode_scheme: PMKUIKit 70 | xcode_project: PMKUIKit.xcodeproj 71 | xcode_destination: 'platform=iOS Simulator,OS=12.2,name=iPhone SE' 72 | cache: 73 | directories: 74 | - Carthage 75 | before_install: 76 | carthage bootstrap --cache-builds --no-use-binaries 77 | - <<: *test 78 | xcode_destination: 'platform=tvOS Simulator,OS=12.2,name=Apple TV' 79 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "mxcl/PromiseKit" ~> 8.2.0 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "mxcl/PromiseKit" "8.2.0" 2 | -------------------------------------------------------------------------------- /Carthage.xcconfig: -------------------------------------------------------------------------------- 1 | // Created by Kevin Ballard on 12/14/15. 2 | // Copyright © 2015 Postmates. All rights reserved. 3 | 4 | FRAMEWORK_SEARCH_PATHS[sdk=macosx*] = $(SRCROOT)/Carthage/Build/Mac/ $(inherited) 5 | FRAMEWORK_SEARCH_PATHS[sdk=iphone*] = $(SRCROOT)/Carthage/Build/iOS/ $(inherited) 6 | FRAMEWORK_SEARCH_PATHS[sdk=watch*] = $(SRCROOT)/Carthage/Build/watchOS/ $(inherited) 7 | FRAMEWORK_SEARCH_PATHS[sdk=appletv*] = $(SRCROOT)/Carthage/Build/tvOS/ $(inherited) 8 | -------------------------------------------------------------------------------- /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 | $(BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016-present, Max Howell; mxcl@me.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /PMKUIKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6304D6FE1D5F986A00CE6C99 /* PMKUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6304D6FD1D5F986A00CE6C99 /* PMKUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 630B2E161D5D0B3200DC10E9 /* TestUIImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 630B2DF71D5D0AD400DC10E9 /* TestUIImagePickerController.swift */; }; 12 | 632B573520324C4C00FCD5E1 /* UIViewPropertyAnimator+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632B573420324C4C00FCD5E1 /* UIViewPropertyAnimator+Promise.swift */; }; 13 | 6332142B1D83CD17009F67CE /* TestUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6332142A1D83CD17009F67CE /* TestUIView.swift */; }; 14 | 637E2C841D5C2E0B0043E370 /* UIView+AnyPromise.h in Headers */ = {isa = PBXBuildFile; fileRef = 637E2C771D5C2E0B0043E370 /* UIView+AnyPromise.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 637E2C851D5C2E0B0043E370 /* UIView+AnyPromise.m in Sources */ = {isa = PBXBuildFile; fileRef = 637E2C781D5C2E0B0043E370 /* UIView+AnyPromise.m */; }; 16 | 637E2C861D5C2E0B0043E370 /* UIView+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 637E2C791D5C2E0B0043E370 /* UIView+Promise.swift */; }; 17 | 637E2C871D5C2E0B0043E370 /* UIViewController+AnyPromise.h in Headers */ = {isa = PBXBuildFile; fileRef = 637E2C7A1D5C2E0B0043E370 /* UIViewController+AnyPromise.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 637E2C881D5C2E0B0043E370 /* UIViewController+AnyPromise.m in Sources */ = {isa = PBXBuildFile; fileRef = 637E2C7B1D5C2E0B0043E370 /* UIViewController+AnyPromise.m */; }; 19 | 637E2C951D5C2E720043E370 /* TestUIImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 637E2C8E1D5C2E720043E370 /* TestUIImagePickerController.swift */; }; 20 | 637E2C961D5C2E720043E370 /* TestUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 637E2C8F1D5C2E720043E370 /* TestUIViewController.m */; }; 21 | 637E2C9B1D5C2F600043E370 /* infrastructure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 637E2C9A1D5C2F600043E370 /* infrastructure.swift */; }; 22 | 63A686811D88E93300D1C66B /* UIImagePickerController+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63A6867E1D88E93300D1C66B /* UIImagePickerController+Promise.swift */; }; 23 | 63C7FFF71D5C020D003BAE60 /* PMKUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63C7FFA71D5BEE09003BAE60 /* PMKUIKit.framework */; }; 24 | 63C9C4571D5D339900101ECE /* app.swift in Sources */ = {isa = PBXBuildFile; fileRef = 630B2DF41D5D0AD400DC10E9 /* app.swift */; }; 25 | 63C9C4581D5D339B00101ECE /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 630B2DF51D5D0AD400DC10E9 /* Default-568h@2x.png */; }; 26 | 63C9C45E1D5D341600101ECE /* PMKUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63C7FFA71D5BEE09003BAE60 /* PMKUIKit.framework */; }; 27 | 63C9C45F1D5D341600101ECE /* PMKUIKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 63C7FFA71D5BEE09003BAE60 /* PMKUIKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | 63C7FFF81D5C020D003BAE60 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 63C7FF9E1D5BEE09003BAE60 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 63C7FFA61D5BEE09003BAE60; 36 | remoteInfo = PMKUIKit; 37 | }; 38 | 63C9C4591D5D33A900101ECE /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 63C7FF9E1D5BEE09003BAE60 /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 63C9C4441D5D334700101ECE; 43 | remoteInfo = PMKTestsHost; 44 | }; 45 | 63C9C45B1D5D33AB00101ECE /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = 63C7FF9E1D5BEE09003BAE60 /* Project object */; 48 | proxyType = 1; 49 | remoteGlobalIDString = 63C9C4441D5D334700101ECE; 50 | remoteInfo = PMKTestsHost; 51 | }; 52 | 63C9C4601D5D341600101ECE /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = 63C7FF9E1D5BEE09003BAE60 /* Project object */; 55 | proxyType = 1; 56 | remoteGlobalIDString = 63C7FFA61D5BEE09003BAE60; 57 | remoteInfo = PMKUIKit; 58 | }; 59 | /* End PBXContainerItemProxy section */ 60 | 61 | /* Begin PBXCopyFilesBuildPhase section */ 62 | 63C9C4621D5D341600101ECE /* Embed Frameworks */ = { 63 | isa = PBXCopyFilesBuildPhase; 64 | buildActionMask = 2147483647; 65 | dstPath = ""; 66 | dstSubfolderSpec = 10; 67 | files = ( 68 | 63C9C45F1D5D341600101ECE /* PMKUIKit.framework in Embed Frameworks */, 69 | ); 70 | name = "Embed Frameworks"; 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXCopyFilesBuildPhase section */ 74 | 75 | /* Begin PBXFileReference section */ 76 | 6304D6FD1D5F986A00CE6C99 /* PMKUIKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PMKUIKit.h; path = Sources/PMKUIKit.h; sourceTree = SOURCE_ROOT; }; 77 | 630B2DF41D5D0AD400DC10E9 /* app.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = app.swift; path = UITests/app.swift; sourceTree = ""; }; 78 | 630B2DF51D5D0AD400DC10E9 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "UITests/Default-568h@2x.png"; sourceTree = ""; }; 79 | 630B2DF71D5D0AD400DC10E9 /* TestUIImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestUIImagePickerController.swift; path = UITests/TestUIImagePickerController.swift; sourceTree = ""; }; 80 | 630B2E131D5D0AF500DC10E9 /* PMKUIUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PMKUIUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | 632B573420324C4C00FCD5E1 /* UIViewPropertyAnimator+Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewPropertyAnimator+Promise.swift"; sourceTree = ""; }; 82 | 6332142A1D83CD17009F67CE /* TestUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestUIView.swift; path = Tests/TestUIView.swift; sourceTree = SOURCE_ROOT; }; 83 | 637E2C771D5C2E0B0043E370 /* UIView+AnyPromise.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+AnyPromise.h"; path = "Sources/UIView+AnyPromise.h"; sourceTree = SOURCE_ROOT; }; 84 | 637E2C781D5C2E0B0043E370 /* UIView+AnyPromise.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+AnyPromise.m"; path = "Sources/UIView+AnyPromise.m"; sourceTree = SOURCE_ROOT; }; 85 | 637E2C791D5C2E0B0043E370 /* UIView+Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+Promise.swift"; path = "Sources/UIView+Promise.swift"; sourceTree = SOURCE_ROOT; }; 86 | 637E2C7A1D5C2E0B0043E370 /* UIViewController+AnyPromise.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+AnyPromise.h"; path = "Sources/UIViewController+AnyPromise.h"; sourceTree = SOURCE_ROOT; }; 87 | 637E2C7B1D5C2E0B0043E370 /* UIViewController+AnyPromise.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+AnyPromise.m"; path = "Sources/UIViewController+AnyPromise.m"; sourceTree = SOURCE_ROOT; }; 88 | 637E2C8E1D5C2E720043E370 /* TestUIImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestUIImagePickerController.swift; path = Tests/TestUIImagePickerController.swift; sourceTree = SOURCE_ROOT; }; 89 | 637E2C8F1D5C2E720043E370 /* TestUIViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestUIViewController.m; path = Tests/TestUIViewController.m; sourceTree = SOURCE_ROOT; }; 90 | 637E2C9A1D5C2F600043E370 /* infrastructure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = infrastructure.swift; path = Tests/infrastructure.swift; sourceTree = SOURCE_ROOT; }; 91 | 63A6867E1D88E93300D1C66B /* UIImagePickerController+Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImagePickerController+Promise.swift"; path = "Sources/UIImagePickerController+Promise.swift"; sourceTree = SOURCE_ROOT; }; 92 | 63C700091D5C0253003BAE60 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 93 | 63C7FFA71D5BEE09003BAE60 /* PMKUIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PMKUIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 94 | 63C7FFF21D5C020D003BAE60 /* PMKUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PMKUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 95 | 63C9C4451D5D334700101ECE /* PMKTestsHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PMKTestsHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; 96 | 63CCF8121D5C0C4E00503216 /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; 97 | 63CCF8171D5C11B500503216 /* Carthage.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Carthage.xcconfig; sourceTree = ""; }; 98 | /* End PBXFileReference section */ 99 | 100 | /* Begin PBXFrameworksBuildPhase section */ 101 | 630B2E0D1D5D0AF500DC10E9 /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | 63C7FFA31D5BEE09003BAE60 /* Frameworks */ = { 109 | isa = PBXFrameworksBuildPhase; 110 | buildActionMask = 2147483647; 111 | files = ( 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | 63C7FFEF1D5C020D003BAE60 /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | 63C7FFF71D5C020D003BAE60 /* PMKUIKit.framework in Frameworks */, 120 | ); 121 | runOnlyForDeploymentPostprocessing = 0; 122 | }; 123 | 63C9C4421D5D334700101ECE /* Frameworks */ = { 124 | isa = PBXFrameworksBuildPhase; 125 | buildActionMask = 2147483647; 126 | files = ( 127 | 63C9C45E1D5D341600101ECE /* PMKUIKit.framework in Frameworks */, 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | /* End PBXFrameworksBuildPhase section */ 132 | 133 | /* Begin PBXGroup section */ 134 | 630B2DF31D5D0ABF00DC10E9 /* TestsHost */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 630B2DF41D5D0AD400DC10E9 /* app.swift */, 138 | 630B2DF51D5D0AD400DC10E9 /* Default-568h@2x.png */, 139 | 630B2DF71D5D0AD400DC10E9 /* TestUIImagePickerController.swift */, 140 | ); 141 | name = TestsHost; 142 | sourceTree = ""; 143 | }; 144 | 63C7FF9D1D5BEE09003BAE60 = { 145 | isa = PBXGroup; 146 | children = ( 147 | 63C700091D5C0253003BAE60 /* Info.plist */, 148 | 63CCF8121D5C0C4E00503216 /* Cartfile */, 149 | 63CCF8171D5C11B500503216 /* Carthage.xcconfig */, 150 | 63C7FFA91D5BEE09003BAE60 /* Sources */, 151 | 63C7FFF31D5C020D003BAE60 /* Tests */, 152 | 630B2DF31D5D0ABF00DC10E9 /* TestsHost */, 153 | 63C7FFA81D5BEE09003BAE60 /* Products */, 154 | ); 155 | sourceTree = ""; 156 | }; 157 | 63C7FFA81D5BEE09003BAE60 /* Products */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 63C7FFA71D5BEE09003BAE60 /* PMKUIKit.framework */, 161 | 63C7FFF21D5C020D003BAE60 /* PMKUITests.xctest */, 162 | 630B2E131D5D0AF500DC10E9 /* PMKUIUITests.xctest */, 163 | 63C9C4451D5D334700101ECE /* PMKTestsHost.app */, 164 | ); 165 | name = Products; 166 | sourceTree = ""; 167 | }; 168 | 63C7FFA91D5BEE09003BAE60 /* Sources */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 6304D6FD1D5F986A00CE6C99 /* PMKUIKit.h */, 172 | 637E2C771D5C2E0B0043E370 /* UIView+AnyPromise.h */, 173 | 637E2C781D5C2E0B0043E370 /* UIView+AnyPromise.m */, 174 | 637E2C791D5C2E0B0043E370 /* UIView+Promise.swift */, 175 | 637E2C7A1D5C2E0B0043E370 /* UIViewController+AnyPromise.h */, 176 | 637E2C7B1D5C2E0B0043E370 /* UIViewController+AnyPromise.m */, 177 | 63A6867E1D88E93300D1C66B /* UIImagePickerController+Promise.swift */, 178 | 632B573420324C4C00FCD5E1 /* UIViewPropertyAnimator+Promise.swift */, 179 | ); 180 | path = Sources; 181 | sourceTree = SOURCE_ROOT; 182 | }; 183 | 63C7FFF31D5C020D003BAE60 /* Tests */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 637E2C8E1D5C2E720043E370 /* TestUIImagePickerController.swift */, 187 | 637E2C8F1D5C2E720043E370 /* TestUIViewController.m */, 188 | 6332142A1D83CD17009F67CE /* TestUIView.swift */, 189 | 637E2C9A1D5C2F600043E370 /* infrastructure.swift */, 190 | ); 191 | path = Tests; 192 | sourceTree = SOURCE_ROOT; 193 | }; 194 | /* End PBXGroup section */ 195 | 196 | /* Begin PBXHeadersBuildPhase section */ 197 | 63C7FFA41D5BEE09003BAE60 /* Headers */ = { 198 | isa = PBXHeadersBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 6304D6FE1D5F986A00CE6C99 /* PMKUIKit.h in Headers */, 202 | 637E2C871D5C2E0B0043E370 /* UIViewController+AnyPromise.h in Headers */, 203 | 637E2C841D5C2E0B0043E370 /* UIView+AnyPromise.h in Headers */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXHeadersBuildPhase section */ 208 | 209 | /* Begin PBXNativeTarget section */ 210 | 630B2DFF1D5D0AF500DC10E9 /* PMKUIUITests */ = { 211 | isa = PBXNativeTarget; 212 | buildConfigurationList = 630B2E101D5D0AF500DC10E9 /* Build configuration list for PBXNativeTarget "PMKUIUITests" */; 213 | buildPhases = ( 214 | 630B2E041D5D0AF500DC10E9 /* Sources */, 215 | 630B2E0D1D5D0AF500DC10E9 /* Frameworks */, 216 | ); 217 | buildRules = ( 218 | ); 219 | dependencies = ( 220 | 63C9C45A1D5D33A900101ECE /* PBXTargetDependency */, 221 | ); 222 | name = PMKUIUITests; 223 | productName = PMKUIUITests; 224 | productReference = 630B2E131D5D0AF500DC10E9 /* PMKUIUITests.xctest */; 225 | productType = "com.apple.product-type.bundle.ui-testing"; 226 | }; 227 | 63C7FFA61D5BEE09003BAE60 /* PMKUIKit */ = { 228 | isa = PBXNativeTarget; 229 | buildConfigurationList = 63C7FFAF1D5BEE09003BAE60 /* Build configuration list for PBXNativeTarget "PMKUIKit" */; 230 | buildPhases = ( 231 | 63C7FFA21D5BEE09003BAE60 /* Sources */, 232 | 63C7FFA31D5BEE09003BAE60 /* Frameworks */, 233 | 63C7FFA41D5BEE09003BAE60 /* Headers */, 234 | ); 235 | buildRules = ( 236 | ); 237 | dependencies = ( 238 | ); 239 | name = PMKUIKit; 240 | productName = PMKUIKit; 241 | productReference = 63C7FFA71D5BEE09003BAE60 /* PMKUIKit.framework */; 242 | productType = "com.apple.product-type.framework"; 243 | }; 244 | 63C7FFF11D5C020D003BAE60 /* PMKUITests */ = { 245 | isa = PBXNativeTarget; 246 | buildConfigurationList = 63C7FFFA1D5C020D003BAE60 /* Build configuration list for PBXNativeTarget "PMKUITests" */; 247 | buildPhases = ( 248 | 63C7FFEE1D5C020D003BAE60 /* Sources */, 249 | 63C7FFEF1D5C020D003BAE60 /* Frameworks */, 250 | 6332EC8D1D5D085F00480270 /* Embed Carthage Frameworks */, 251 | ); 252 | buildRules = ( 253 | ); 254 | dependencies = ( 255 | 63C7FFF91D5C020D003BAE60 /* PBXTargetDependency */, 256 | 63C9C45C1D5D33AB00101ECE /* PBXTargetDependency */, 257 | ); 258 | name = PMKUITests; 259 | productName = PMKUITests; 260 | productReference = 63C7FFF21D5C020D003BAE60 /* PMKUITests.xctest */; 261 | productType = "com.apple.product-type.bundle.unit-test"; 262 | }; 263 | 63C9C4441D5D334700101ECE /* PMKTestsHost */ = { 264 | isa = PBXNativeTarget; 265 | buildConfigurationList = 63C9C4541D5D334800101ECE /* Build configuration list for PBXNativeTarget "PMKTestsHost" */; 266 | buildPhases = ( 267 | 63C9C4411D5D334700101ECE /* Sources */, 268 | 63C9C4421D5D334700101ECE /* Frameworks */, 269 | 63C9C4431D5D334700101ECE /* Resources */, 270 | 63C9C45D1D5D33E700101ECE /* Embed Carthage Frameworks */, 271 | 63C9C4621D5D341600101ECE /* Embed Frameworks */, 272 | ); 273 | buildRules = ( 274 | ); 275 | dependencies = ( 276 | 63C9C4611D5D341600101ECE /* PBXTargetDependency */, 277 | ); 278 | name = PMKTestsHost; 279 | productName = PMKTestsHost; 280 | productReference = 63C9C4451D5D334700101ECE /* PMKTestsHost.app */; 281 | productType = "com.apple.product-type.application"; 282 | }; 283 | /* End PBXNativeTarget section */ 284 | 285 | /* Begin PBXProject section */ 286 | 63C7FF9E1D5BEE09003BAE60 /* Project object */ = { 287 | isa = PBXProject; 288 | attributes = { 289 | LastSwiftUpdateCheck = 0800; 290 | LastUpgradeCheck = 1020; 291 | ORGANIZATIONNAME = "Max Howell"; 292 | TargetAttributes = { 293 | 630B2DFF1D5D0AF500DC10E9 = { 294 | LastSwiftMigration = 1020; 295 | TestTargetID = 63C9C4441D5D334700101ECE; 296 | }; 297 | 63C7FFA61D5BEE09003BAE60 = { 298 | CreatedOnToolsVersion = 8.0; 299 | LastSwiftMigration = 1020; 300 | ProvisioningStyle = Automatic; 301 | }; 302 | 63C7FFF11D5C020D003BAE60 = { 303 | CreatedOnToolsVersion = 8.0; 304 | LastSwiftMigration = 1020; 305 | ProvisioningStyle = Automatic; 306 | TestTargetID = 63C9C4441D5D334700101ECE; 307 | }; 308 | 63C9C4441D5D334700101ECE = { 309 | CreatedOnToolsVersion = 8.0; 310 | LastSwiftMigration = 1020; 311 | ProvisioningStyle = Automatic; 312 | }; 313 | }; 314 | }; 315 | buildConfigurationList = 63C7FFA11D5BEE09003BAE60 /* Build configuration list for PBXProject "PMKUIKit" */; 316 | compatibilityVersion = "Xcode 3.2"; 317 | developmentRegion = en; 318 | hasScannedForEncodings = 0; 319 | knownRegions = ( 320 | en, 321 | Base, 322 | ); 323 | mainGroup = 63C7FF9D1D5BEE09003BAE60; 324 | productRefGroup = 63C7FFA81D5BEE09003BAE60 /* Products */; 325 | projectDirPath = ""; 326 | projectRoot = ""; 327 | targets = ( 328 | 63C7FFA61D5BEE09003BAE60 /* PMKUIKit */, 329 | 63C7FFF11D5C020D003BAE60 /* PMKUITests */, 330 | 630B2DFF1D5D0AF500DC10E9 /* PMKUIUITests */, 331 | 63C9C4441D5D334700101ECE /* PMKTestsHost */, 332 | ); 333 | }; 334 | /* End PBXProject section */ 335 | 336 | /* Begin PBXResourcesBuildPhase section */ 337 | 63C9C4431D5D334700101ECE /* Resources */ = { 338 | isa = PBXResourcesBuildPhase; 339 | buildActionMask = 2147483647; 340 | files = ( 341 | 63C9C4581D5D339B00101ECE /* Default-568h@2x.png in Resources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | /* End PBXResourcesBuildPhase section */ 346 | 347 | /* Begin PBXShellScriptBuildPhase section */ 348 | 6332EC8D1D5D085F00480270 /* Embed Carthage Frameworks */ = { 349 | isa = PBXShellScriptBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | ); 353 | inputPaths = ( 354 | PromiseKit, 355 | ); 356 | name = "Embed Carthage Frameworks"; 357 | outputPaths = ( 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | shellPath = /bin/sh; 361 | shellScript = "case \"$PLATFORM_NAME\" in\nmacosx) plat=Mac;;\niphone*) plat=iOS;;\nwatch*) plat=watchOS;;\nappletv*) plat=tvOS;;\n*) echo \"error: Unknown PLATFORM_NAME: $PLATFORM_NAME\"; exit 1;;\nesac\nfor (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do\nVAR=SCRIPT_INPUT_FILE_$n\nframework=$(basename \"${!VAR}\")\nexport SCRIPT_INPUT_FILE_$n=\"$SRCROOT\"/Carthage/Build/$plat/\"$framework\".framework\ndone\n\n/usr/local/bin/carthage copy-frameworks || exit\n\nfor (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do\nVAR=SCRIPT_INPUT_FILE_$n\nsource=${!VAR}.dSYM\ndest=${BUILT_PRODUCTS_DIR}/$(basename \"$source\")\nditto \"$source\" \"$dest\" || exit\ndone"; 362 | }; 363 | 63C9C45D1D5D33E700101ECE /* Embed Carthage Frameworks */ = { 364 | isa = PBXShellScriptBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | ); 368 | inputPaths = ( 369 | PromiseKit, 370 | ); 371 | name = "Embed Carthage Frameworks"; 372 | outputPaths = ( 373 | ); 374 | runOnlyForDeploymentPostprocessing = 0; 375 | shellPath = /bin/sh; 376 | shellScript = "case \"$PLATFORM_NAME\" in\nmacosx) plat=Mac;;\niphone*) plat=iOS;;\nwatch*) plat=watchOS;;\nappletv*) plat=tvOS;;\n*) echo \"error: Unknown PLATFORM_NAME: $PLATFORM_NAME\"; exit 1;;\nesac\nfor (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do\nVAR=SCRIPT_INPUT_FILE_$n\nframework=$(basename \"${!VAR}\")\nexport SCRIPT_INPUT_FILE_$n=\"$SRCROOT\"/Carthage/Build/$plat/\"$framework\".framework\ndone\n\n/usr/local/bin/carthage copy-frameworks || exit\n\nfor (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do\nVAR=SCRIPT_INPUT_FILE_$n\nsource=${!VAR}.dSYM\ndest=${BUILT_PRODUCTS_DIR}/$(basename \"$source\")\nditto \"$source\" \"$dest\" || exit\ndone"; 377 | }; 378 | /* End PBXShellScriptBuildPhase section */ 379 | 380 | /* Begin PBXSourcesBuildPhase section */ 381 | 630B2E041D5D0AF500DC10E9 /* Sources */ = { 382 | isa = PBXSourcesBuildPhase; 383 | buildActionMask = 2147483647; 384 | files = ( 385 | 630B2E161D5D0B3200DC10E9 /* TestUIImagePickerController.swift in Sources */, 386 | ); 387 | runOnlyForDeploymentPostprocessing = 0; 388 | }; 389 | 63C7FFA21D5BEE09003BAE60 /* Sources */ = { 390 | isa = PBXSourcesBuildPhase; 391 | buildActionMask = 2147483647; 392 | files = ( 393 | 63A686811D88E93300D1C66B /* UIImagePickerController+Promise.swift in Sources */, 394 | 637E2C861D5C2E0B0043E370 /* UIView+Promise.swift in Sources */, 395 | 637E2C851D5C2E0B0043E370 /* UIView+AnyPromise.m in Sources */, 396 | 632B573520324C4C00FCD5E1 /* UIViewPropertyAnimator+Promise.swift in Sources */, 397 | 637E2C881D5C2E0B0043E370 /* UIViewController+AnyPromise.m in Sources */, 398 | ); 399 | runOnlyForDeploymentPostprocessing = 0; 400 | }; 401 | 63C7FFEE1D5C020D003BAE60 /* Sources */ = { 402 | isa = PBXSourcesBuildPhase; 403 | buildActionMask = 2147483647; 404 | files = ( 405 | 637E2C951D5C2E720043E370 /* TestUIImagePickerController.swift in Sources */, 406 | 637E2C961D5C2E720043E370 /* TestUIViewController.m in Sources */, 407 | 637E2C9B1D5C2F600043E370 /* infrastructure.swift in Sources */, 408 | 6332142B1D83CD17009F67CE /* TestUIView.swift in Sources */, 409 | ); 410 | runOnlyForDeploymentPostprocessing = 0; 411 | }; 412 | 63C9C4411D5D334700101ECE /* Sources */ = { 413 | isa = PBXSourcesBuildPhase; 414 | buildActionMask = 2147483647; 415 | files = ( 416 | 63C9C4571D5D339900101ECE /* app.swift in Sources */, 417 | ); 418 | runOnlyForDeploymentPostprocessing = 0; 419 | }; 420 | /* End PBXSourcesBuildPhase section */ 421 | 422 | /* Begin PBXTargetDependency section */ 423 | 63C7FFF91D5C020D003BAE60 /* PBXTargetDependency */ = { 424 | isa = PBXTargetDependency; 425 | target = 63C7FFA61D5BEE09003BAE60 /* PMKUIKit */; 426 | targetProxy = 63C7FFF81D5C020D003BAE60 /* PBXContainerItemProxy */; 427 | }; 428 | 63C9C45A1D5D33A900101ECE /* PBXTargetDependency */ = { 429 | isa = PBXTargetDependency; 430 | target = 63C9C4441D5D334700101ECE /* PMKTestsHost */; 431 | targetProxy = 63C9C4591D5D33A900101ECE /* PBXContainerItemProxy */; 432 | }; 433 | 63C9C45C1D5D33AB00101ECE /* PBXTargetDependency */ = { 434 | isa = PBXTargetDependency; 435 | target = 63C9C4441D5D334700101ECE /* PMKTestsHost */; 436 | targetProxy = 63C9C45B1D5D33AB00101ECE /* PBXContainerItemProxy */; 437 | }; 438 | 63C9C4611D5D341600101ECE /* PBXTargetDependency */ = { 439 | isa = PBXTargetDependency; 440 | target = 63C7FFA61D5BEE09003BAE60 /* PMKUIKit */; 441 | targetProxy = 63C9C4601D5D341600101ECE /* PBXContainerItemProxy */; 442 | }; 443 | /* End PBXTargetDependency section */ 444 | 445 | /* Begin XCBuildConfiguration section */ 446 | 630B2E111D5D0AF500DC10E9 /* Debug */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | BUNDLE_PACKAGE_TYPE = BNDL; 450 | CLANG_ENABLE_MODULES = YES; 451 | GCC_WARN_INHIBIT_ALL_WARNINGS = YES; 452 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @executable_path/../Frameworks @loader_path/../Frameworks"; 454 | PRODUCT_BUNDLE_IDENTIFIER = org.promisekit.tests.ui.UIKit; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 457 | SWIFT_SUPPRESS_WARNINGS = YES; 458 | SWIFT_VERSION = 4.0; 459 | TEST_TARGET_NAME = PMKTestsHost; 460 | }; 461 | name = Debug; 462 | }; 463 | 630B2E121D5D0AF500DC10E9 /* Release */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | BUNDLE_PACKAGE_TYPE = BNDL; 467 | CLANG_ENABLE_MODULES = YES; 468 | GCC_WARN_INHIBIT_ALL_WARNINGS = YES; 469 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 470 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @executable_path/../Frameworks @loader_path/../Frameworks"; 471 | PRODUCT_BUNDLE_IDENTIFIER = org.promisekit.tests.ui.UIKit; 472 | PRODUCT_NAME = "$(TARGET_NAME)"; 473 | SWIFT_SUPPRESS_WARNINGS = YES; 474 | SWIFT_VERSION = 4.0; 475 | TEST_TARGET_NAME = PMKTestsHost; 476 | }; 477 | name = Release; 478 | }; 479 | 63C7FFAD1D5BEE09003BAE60 /* Debug */ = { 480 | isa = XCBuildConfiguration; 481 | baseConfigurationReference = 63CCF8171D5C11B500503216 /* Carthage.xcconfig */; 482 | buildSettings = { 483 | ALWAYS_SEARCH_USER_PATHS = NO; 484 | BUNDLE_PACKAGE_TYPE = FMWK; 485 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 486 | CLANG_ANALYZER_NONNULL = YES; 487 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 488 | CLANG_CXX_LIBRARY = "libc++"; 489 | CLANG_ENABLE_MODULES = YES; 490 | CLANG_ENABLE_OBJC_ARC = YES; 491 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 492 | CLANG_WARN_BOOL_CONVERSION = YES; 493 | CLANG_WARN_COMMA = YES; 494 | CLANG_WARN_CONSTANT_CONVERSION = YES; 495 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 496 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 497 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 498 | CLANG_WARN_EMPTY_BODY = YES; 499 | CLANG_WARN_ENUM_CONVERSION = YES; 500 | CLANG_WARN_INFINITE_RECURSION = YES; 501 | CLANG_WARN_INT_CONVERSION = YES; 502 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 503 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 504 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 505 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 506 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 507 | CLANG_WARN_STRICT_PROTOTYPES = YES; 508 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 509 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 510 | CLANG_WARN_UNREACHABLE_CODE = YES; 511 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 512 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 513 | CURRENT_PROJECT_VERSION = 1.0.3; 514 | DEBUG_INFORMATION_FORMAT = dwarf; 515 | ENABLE_STRICT_OBJC_MSGSEND = YES; 516 | ENABLE_TESTABILITY = YES; 517 | GCC_C_LANGUAGE_STANDARD = gnu99; 518 | GCC_DYNAMIC_NO_PIC = NO; 519 | GCC_NO_COMMON_BLOCKS = YES; 520 | GCC_OPTIMIZATION_LEVEL = 0; 521 | GCC_PREPROCESSOR_DEFINITIONS = ( 522 | "DEBUG=1", 523 | "$(inherited)", 524 | ); 525 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 526 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 527 | GCC_WARN_UNDECLARED_SELECTOR = YES; 528 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 529 | GCC_WARN_UNUSED_FUNCTION = YES; 530 | GCC_WARN_UNUSED_VARIABLE = YES; 531 | INFOPLIST_FILE = Info.plist; 532 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 533 | MACOSX_DEPLOYMENT_TARGET = 10.10; 534 | MTL_ENABLE_DEBUG_INFO = YES; 535 | ONLY_ACTIVE_ARCH = YES; 536 | PRODUCT_BUNDLE_IDENTIFIER = org.promisekit.UIKit; 537 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos"; 538 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 539 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 540 | SWIFT_VERSION = 4.0; 541 | TARGETED_DEVICE_FAMILY = "1,2,3,4"; 542 | TVOS_DEPLOYMENT_TARGET = 9.0; 543 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 544 | }; 545 | name = Debug; 546 | }; 547 | 63C7FFAE1D5BEE09003BAE60 /* Release */ = { 548 | isa = XCBuildConfiguration; 549 | baseConfigurationReference = 63CCF8171D5C11B500503216 /* Carthage.xcconfig */; 550 | buildSettings = { 551 | ALWAYS_SEARCH_USER_PATHS = NO; 552 | BUNDLE_PACKAGE_TYPE = FMWK; 553 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 554 | CLANG_ANALYZER_NONNULL = YES; 555 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 556 | CLANG_CXX_LIBRARY = "libc++"; 557 | CLANG_ENABLE_MODULES = YES; 558 | CLANG_ENABLE_OBJC_ARC = YES; 559 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 560 | CLANG_WARN_BOOL_CONVERSION = YES; 561 | CLANG_WARN_COMMA = YES; 562 | CLANG_WARN_CONSTANT_CONVERSION = YES; 563 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 564 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 565 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 566 | CLANG_WARN_EMPTY_BODY = YES; 567 | CLANG_WARN_ENUM_CONVERSION = YES; 568 | CLANG_WARN_INFINITE_RECURSION = YES; 569 | CLANG_WARN_INT_CONVERSION = YES; 570 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 571 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 572 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 573 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 574 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 575 | CLANG_WARN_STRICT_PROTOTYPES = YES; 576 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 577 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 578 | CLANG_WARN_UNREACHABLE_CODE = YES; 579 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 580 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 581 | CURRENT_PROJECT_VERSION = 1.0.3; 582 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 583 | ENABLE_NS_ASSERTIONS = NO; 584 | ENABLE_STRICT_OBJC_MSGSEND = YES; 585 | GCC_C_LANGUAGE_STANDARD = gnu99; 586 | GCC_NO_COMMON_BLOCKS = YES; 587 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 588 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 589 | GCC_WARN_UNDECLARED_SELECTOR = YES; 590 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 591 | GCC_WARN_UNUSED_FUNCTION = YES; 592 | GCC_WARN_UNUSED_VARIABLE = YES; 593 | INFOPLIST_FILE = Info.plist; 594 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 595 | MACOSX_DEPLOYMENT_TARGET = 10.10; 596 | MTL_ENABLE_DEBUG_INFO = NO; 597 | PRODUCT_BUNDLE_IDENTIFIER = org.promisekit.UIKit; 598 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos"; 599 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 600 | SWIFT_VERSION = 4.0; 601 | TARGETED_DEVICE_FAMILY = "1,2,3,4"; 602 | TVOS_DEPLOYMENT_TARGET = 9.0; 603 | VALIDATE_PRODUCT = YES; 604 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 605 | }; 606 | name = Release; 607 | }; 608 | 63C7FFB01D5BEE09003BAE60 /* Debug */ = { 609 | isa = XCBuildConfiguration; 610 | buildSettings = { 611 | APPLICATION_EXTENSION_API_ONLY = YES; 612 | CLANG_ENABLE_MODULES = YES; 613 | CODE_SIGN_IDENTITY = ""; 614 | DEFINES_MODULE = YES; 615 | DYLIB_COMPATIBILITY_VERSION = 1; 616 | DYLIB_CURRENT_VERSION = 1; 617 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 618 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 619 | PRODUCT_MODULE_NAME = "${TARGET_NAME}"; 620 | PRODUCT_NAME = "$(TARGET_NAME)"; 621 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 622 | SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; 623 | }; 624 | name = Debug; 625 | }; 626 | 63C7FFB11D5BEE09003BAE60 /* Release */ = { 627 | isa = XCBuildConfiguration; 628 | buildSettings = { 629 | APPLICATION_EXTENSION_API_ONLY = YES; 630 | CLANG_ENABLE_MODULES = YES; 631 | CODE_SIGN_IDENTITY = ""; 632 | DEFINES_MODULE = YES; 633 | DYLIB_COMPATIBILITY_VERSION = 1; 634 | DYLIB_CURRENT_VERSION = 1; 635 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 636 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 637 | PRODUCT_MODULE_NAME = "${TARGET_NAME}"; 638 | PRODUCT_NAME = "$(TARGET_NAME)"; 639 | SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; 640 | }; 641 | name = Release; 642 | }; 643 | 63C7FFFB1D5C020D003BAE60 /* Debug */ = { 644 | isa = XCBuildConfiguration; 645 | buildSettings = { 646 | BUNDLE_PACKAGE_TYPE = BNDL; 647 | CLANG_ENABLE_MODULES = YES; 648 | GCC_WARN_INHIBIT_ALL_WARNINGS = YES; 649 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @executable_path/../Frameworks @loader_path/../Frameworks"; 650 | PRODUCT_BUNDLE_IDENTIFIER = org.promisekit.tests.UIKit; 651 | PRODUCT_NAME = "$(TARGET_NAME)"; 652 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 653 | SWIFT_SUPPRESS_WARNINGS = YES; 654 | SWIFT_VERSION = 4.0; 655 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PMKTestsHost.app/PMKTestsHost"; 656 | }; 657 | name = Debug; 658 | }; 659 | 63C7FFFC1D5C020D003BAE60 /* Release */ = { 660 | isa = XCBuildConfiguration; 661 | buildSettings = { 662 | BUNDLE_PACKAGE_TYPE = BNDL; 663 | CLANG_ENABLE_MODULES = YES; 664 | GCC_WARN_INHIBIT_ALL_WARNINGS = YES; 665 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @executable_path/../Frameworks @loader_path/../Frameworks"; 666 | PRODUCT_BUNDLE_IDENTIFIER = org.promisekit.tests.UIKit; 667 | PRODUCT_NAME = "$(TARGET_NAME)"; 668 | SWIFT_SUPPRESS_WARNINGS = YES; 669 | SWIFT_VERSION = 4.0; 670 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PMKTestsHost.app/PMKTestsHost"; 671 | }; 672 | name = Release; 673 | }; 674 | 63C9C4551D5D334800101ECE /* Debug */ = { 675 | isa = XCBuildConfiguration; 676 | buildSettings = { 677 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 678 | BUNDLE_PACKAGE_TYPE = APPL; 679 | CODE_SIGN_ENTITLEMENTS = UITests/Entitlements.plist; 680 | COPY_PHASE_STRIP = NO; 681 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 682 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 683 | PRODUCT_BUNDLE_IDENTIFIER = org.promisekit.tests.host.UIKit; 684 | PRODUCT_NAME = "$(TARGET_NAME)"; 685 | SDKROOT = iphoneos; 686 | SWIFT_VERSION = 4.0; 687 | }; 688 | name = Debug; 689 | }; 690 | 63C9C4561D5D334800101ECE /* Release */ = { 691 | isa = XCBuildConfiguration; 692 | buildSettings = { 693 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 694 | BUNDLE_PACKAGE_TYPE = APPL; 695 | CODE_SIGN_ENTITLEMENTS = UITests/Entitlements.plist; 696 | COPY_PHASE_STRIP = NO; 697 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 698 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 699 | PRODUCT_BUNDLE_IDENTIFIER = org.promisekit.tests.host.UIKit; 700 | PRODUCT_NAME = "$(TARGET_NAME)"; 701 | SDKROOT = iphoneos; 702 | SWIFT_VERSION = 4.0; 703 | }; 704 | name = Release; 705 | }; 706 | /* End XCBuildConfiguration section */ 707 | 708 | /* Begin XCConfigurationList section */ 709 | 630B2E101D5D0AF500DC10E9 /* Build configuration list for PBXNativeTarget "PMKUIUITests" */ = { 710 | isa = XCConfigurationList; 711 | buildConfigurations = ( 712 | 630B2E111D5D0AF500DC10E9 /* Debug */, 713 | 630B2E121D5D0AF500DC10E9 /* Release */, 714 | ); 715 | defaultConfigurationIsVisible = 0; 716 | defaultConfigurationName = Release; 717 | }; 718 | 63C7FFA11D5BEE09003BAE60 /* Build configuration list for PBXProject "PMKUIKit" */ = { 719 | isa = XCConfigurationList; 720 | buildConfigurations = ( 721 | 63C7FFAD1D5BEE09003BAE60 /* Debug */, 722 | 63C7FFAE1D5BEE09003BAE60 /* Release */, 723 | ); 724 | defaultConfigurationIsVisible = 0; 725 | defaultConfigurationName = Release; 726 | }; 727 | 63C7FFAF1D5BEE09003BAE60 /* Build configuration list for PBXNativeTarget "PMKUIKit" */ = { 728 | isa = XCConfigurationList; 729 | buildConfigurations = ( 730 | 63C7FFB01D5BEE09003BAE60 /* Debug */, 731 | 63C7FFB11D5BEE09003BAE60 /* Release */, 732 | ); 733 | defaultConfigurationIsVisible = 0; 734 | defaultConfigurationName = Release; 735 | }; 736 | 63C7FFFA1D5C020D003BAE60 /* Build configuration list for PBXNativeTarget "PMKUITests" */ = { 737 | isa = XCConfigurationList; 738 | buildConfigurations = ( 739 | 63C7FFFB1D5C020D003BAE60 /* Debug */, 740 | 63C7FFFC1D5C020D003BAE60 /* Release */, 741 | ); 742 | defaultConfigurationIsVisible = 0; 743 | defaultConfigurationName = Release; 744 | }; 745 | 63C9C4541D5D334800101ECE /* Build configuration list for PBXNativeTarget "PMKTestsHost" */ = { 746 | isa = XCConfigurationList; 747 | buildConfigurations = ( 748 | 63C9C4551D5D334800101ECE /* Debug */, 749 | 63C9C4561D5D334800101ECE /* Release */, 750 | ); 751 | defaultConfigurationIsVisible = 0; 752 | defaultConfigurationName = Release; 753 | }; 754 | /* End XCConfigurationList section */ 755 | }; 756 | rootObject = 63C7FF9E1D5BEE09003BAE60 /* Project object */; 757 | } 758 | -------------------------------------------------------------------------------- /PMKUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PMKUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PMKUIKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PMKUIKit.xcodeproj/xcshareddata/xcschemes/PMKUIKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 67 | 68 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 86 | 87 | 88 | 89 | 99 | 100 | 106 | 107 | 108 | 109 | 110 | 111 | 117 | 118 | 124 | 125 | 126 | 127 | 129 | 130 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "PromiseKit", 6 | "repositoryURL": "https://github.com/mxcl/PromiseKit.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "2bc44395edb4f8391902a9ff7c220471882a4d07", 10 | "version": "8.2.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let pkg = Package(name: "PMKUIKit") 6 | pkg.products = [ 7 | .library(name: "PMKUIKit", targets: ["PMKUIKit"]), 8 | ] 9 | pkg.dependencies = [ 10 | .package(url: "https://github.com/mxcl/PromiseKit.git", from: "8.2.0") 11 | ] 12 | pkg.swiftLanguageVersions = [.v4, .v4_2, .v5] 13 | 14 | let target: Target = .target(name: "PMKUIKit") 15 | target.path = "Sources" 16 | target.exclude = ["UIView", "UIViewController"].flatMap { 17 | ["\($0)+AnyPromise.m", "\($0)+AnyPromise.h"] 18 | } 19 | target.exclude.append("PMKUIKit.h") 20 | 21 | target.dependencies = [ 22 | "PromiseKit" 23 | ] 24 | 25 | pkg.targets = [target] 26 | 27 | pkg.platforms = [ 28 | .iOS(.v8) 29 | ] 30 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # PromiseKit UIKit Extensions ![Build Status] 2 | 3 | This project adds promises to Apple’s UIKit framework. 4 | 5 | This project supports Swift 3.0, 3.1, 3.2 and 4.0; iOS 9, 10 and 11; tvOS 10 and 6 | 11; CocoaPods and Carthage; Xcode 8.0, 8.1, 8.2, 8.3 and 9.0. 7 | 8 | ## CocoaPods 9 | 10 | ```ruby 11 | pod "PromiseKit/UIKit", "~> 6.0" 12 | ``` 13 | 14 | The extensions are built into `PromiseKit.framework` thus `import PromiseKit` is 15 | all that is needed. 16 | 17 | ## Carthage 18 | 19 | ```ruby 20 | github "PromiseKit/UIKit" ~> 3.0 21 | ``` 22 | 23 | The extensions are built into their own framework: 24 | 25 | ```swift 26 | // swift 27 | import PromiseKit 28 | import PMKUIKit 29 | ``` 30 | 31 | ```objc 32 | // objc 33 | @import PromiseKit; 34 | @import PMKUIKit; 35 | ``` 36 | 37 | # `UIImagePickerController` 38 | 39 | Due to iOS 10 requiring an entry in your app’s `Info.plist` for any usage of `UIImagePickerController` (even if you don’t actually call it directly), we have removed UIImagePickerController from the default `UIKit` pod. To use it you must add an additional subspec: 40 | 41 | ```ruby 42 | pod "PromiseKit/UIImagePickerController" 43 | ``` 44 | 45 | Sorry, but there’s not an easier way. 46 | 47 | 48 | [Build Status]: https://travis-ci.org/PromiseKit/UIKit.svg?branch=master 49 | -------------------------------------------------------------------------------- /Sources/PMKUIKit.h: -------------------------------------------------------------------------------- 1 | #import "UIView+AnyPromise.h" 2 | #import "UIViewController+AnyPromise.h" 3 | 4 | typedef NS_OPTIONS(NSInteger, PMKAnimationOptions) { 5 | PMKAnimationOptionsNone = 1 << 0, 6 | PMKAnimationOptionsAppear = 1 << 1, 7 | PMKAnimationOptionsDisappear = 1 << 2, 8 | }; 9 | -------------------------------------------------------------------------------- /Sources/UIImagePickerController+Promise.swift: -------------------------------------------------------------------------------- 1 | #if !PMKCocoaPods 2 | import PromiseKit 3 | #endif 4 | import UIKit 5 | 6 | #if !os(tvOS) 7 | 8 | public struct AnimationOptions: OptionSet { 9 | public let rawValue: Int 10 | 11 | public static let appear = AnimationOptions(rawValue: 1 << 1) 12 | public static let disappear = AnimationOptions(rawValue: 1 << 2) 13 | 14 | public init(rawValue: Int) { 15 | self.rawValue = rawValue 16 | } 17 | } 18 | 19 | extension UIViewController { 20 | #if swift(>=4.2) 21 | /// Presents the UIImagePickerController, resolving with the user action. 22 | public func promise(_ vc: UIImagePickerController, animate: AnimationOptions = [.appear, .disappear], completion: (() -> Void)? = nil) -> Promise<[UIImagePickerController.InfoKey: Any]> { 23 | let animated = animate.contains(.appear) 24 | let proxy = UIImagePickerControllerProxy() 25 | vc.delegate = proxy 26 | present(vc, animated: animated, completion: completion) 27 | return proxy.promise.ensure { 28 | vc.presentingViewController?.dismiss(animated: animated, completion: nil) 29 | } 30 | } 31 | #else 32 | /// Presents the UIImagePickerController, resolving with the user action. 33 | public func promise(_ vc: UIImagePickerController, animate: AnimationOptions = [.appear, .disappear], completion: (() -> Void)? = nil) -> Promise<[String: Any]> { 34 | let animated = animate.contains(.appear) 35 | let proxy = UIImagePickerControllerProxy() 36 | vc.delegate = proxy 37 | present(vc, animated: animated, completion: completion) 38 | return proxy.promise.ensure { 39 | vc.presentingViewController?.dismiss(animated: animated, completion: nil) 40 | } 41 | } 42 | #endif 43 | } 44 | 45 | @objc private class UIImagePickerControllerProxy: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 46 | #if swift(>=4.2) 47 | let (promise, seal) = Promise<[UIImagePickerController.InfoKey: Any]>.pending() 48 | #else 49 | let (promise, seal) = Promise<[String: Any]>.pending() 50 | #endif 51 | var retainCycle: AnyObject? 52 | 53 | required override init() { 54 | super.init() 55 | retainCycle = self 56 | } 57 | 58 | #if swift(>=4.2) 59 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { 60 | seal.fulfill(info) 61 | retainCycle = nil 62 | } 63 | #else 64 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) { 65 | seal.fulfill(info) 66 | retainCycle = nil 67 | } 68 | #endif 69 | 70 | func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { 71 | seal.reject(UIImagePickerController.PMKError.cancelled) 72 | retainCycle = nil 73 | } 74 | } 75 | 76 | extension UIImagePickerController { 77 | /// Errors representing PromiseKit UIImagePickerController failures 78 | public enum PMKError: CancellableError { 79 | /// The user cancelled the UIImagePickerController. 80 | case cancelled 81 | /// - Returns: true 82 | public var isCancelled: Bool { 83 | switch self { 84 | case .cancelled: 85 | return true 86 | } 87 | } 88 | } 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /Sources/UIView+AnyPromise.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | // Created by Masafumi Yoshida on 2014/07/11. 5 | // Copyright (c) 2014年 DeNA. All rights reserved. 6 | 7 | /** 8 | To import the `UIView` category: 9 | 10 | use_frameworks! 11 | pod "PromiseKit/UIKit" 12 | 13 | Or `UIKit` is one of the categories imported by the umbrella pod: 14 | 15 | use_frameworks! 16 | pod "PromiseKit" 17 | 18 | And then in your sources: 19 | 20 | @import PromiseKit; 21 | */ 22 | @interface UIView (PromiseKit) 23 | 24 | /** 25 | Animate changes to one or more views using the specified duration. 26 | 27 | @param duration The total duration of the animations, measured in 28 | seconds. If you specify a negative value or 0, the changes are made 29 | without animating them. 30 | 31 | @param animations A block object containing the changes to commit to the 32 | views. 33 | 34 | @return A promise that fulfills with a boolean NSNumber indicating 35 | whether or not the animations actually finished. 36 | */ 37 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations NS_REFINED_FOR_SWIFT; 38 | 39 | /** 40 | Animate changes to one or more views using the specified duration, delay, 41 | options, and completion handler. 42 | 43 | @param duration The total duration of the animations, measured in 44 | seconds. If you specify a negative value or 0, the changes are made 45 | without animating them. 46 | 47 | @param delay The amount of time (measured in seconds) to wait before 48 | beginning the animations. Specify a value of 0 to begin the animations 49 | immediately. 50 | 51 | @param options A mask of options indicating how you want to perform the 52 | animations. For a list of valid constants, see UIViewAnimationOptions. 53 | 54 | @param animations A block object containing the changes to commit to the 55 | views. 56 | 57 | @return A promise that fulfills with a boolean NSNumber indicating 58 | whether or not the animations actually finished. 59 | */ 60 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations NS_REFINED_FOR_SWIFT; 61 | 62 | /** 63 | Performs a view animation using a timing curve corresponding to the 64 | motion of a physical spring. 65 | 66 | @return A promise that fulfills with a boolean NSNumber indicating 67 | whether or not the animations actually finished. 68 | */ 69 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations NS_REFINED_FOR_SWIFT; 70 | 71 | /** 72 | Creates an animation block object that can be used to set up 73 | keyframe-based animations for the current view. 74 | 75 | @return A promise that fulfills with a boolean NSNumber indicating 76 | whether or not the animations actually finished. 77 | */ 78 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options keyframeAnimations:(void (^)(void))animations NS_REFINED_FOR_SWIFT; 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Sources/UIView+AnyPromise.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+PromiseKit_UIAnimation.m 3 | // YahooDenaStudy 4 | // 5 | // Created by Masafumi Yoshida on 2014/07/11. 6 | // Copyright (c) 2014年 DeNA. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIView+AnyPromise.h" 11 | 12 | 13 | #define CopyPasta \ 14 | NSAssert([NSThread isMainThread], @"UIKit animation must be performed on the main thread"); \ 15 | \ 16 | if (![NSThread isMainThread]) { \ 17 | id error = [NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"Animation was attempted on a background thread"}]; \ 18 | return [AnyPromise promiseWithValue:error]; \ 19 | } \ 20 | \ 21 | PMKResolver resolve = nil; \ 22 | AnyPromise *promise = [[AnyPromise alloc] initWithResolver:&resolve]; 23 | 24 | 25 | @implementation UIView (PromiseKit) 26 | 27 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations { 28 | return [self promiseWithDuration:duration delay:0 options:0 animations:animations]; 29 | } 30 | 31 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void(^)(void))animations 32 | { 33 | CopyPasta; 34 | 35 | [UIView animateWithDuration:duration delay:delay options:options animations:animations completion:^(BOOL finished) { 36 | resolve(@(finished)); 37 | }]; 38 | 39 | return promise; 40 | } 41 | 42 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void(^)(void))animations 43 | { 44 | CopyPasta; 45 | 46 | [UIView animateWithDuration:duration delay:delay usingSpringWithDamping:dampingRatio initialSpringVelocity:velocity options:options animations:animations completion:^(BOOL finished) { 47 | resolve(@(finished)); 48 | }]; 49 | 50 | return promise; 51 | } 52 | 53 | + (AnyPromise *)promiseWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options keyframeAnimations:(void(^)(void))animations 54 | { 55 | CopyPasta; 56 | 57 | [UIView animateKeyframesWithDuration:duration delay:delay options:options animations:animations completion:^(BOOL finished) { 58 | resolve(@(finished)); 59 | }]; 60 | 61 | return promise; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Sources/UIView+Promise.swift: -------------------------------------------------------------------------------- 1 | import UIKit.UIView 2 | #if !PMKCocoaPods 3 | import PromiseKit 4 | #endif 5 | 6 | /** 7 | To import the `UIView` category: 8 | 9 | use_frameworks! 10 | pod "PromiseKit/UIKit" 11 | 12 | Or `UIKit` is one of the categories imported by the umbrella pod: 13 | 14 | use_frameworks! 15 | pod "PromiseKit" 16 | 17 | And then in your sources: 18 | 19 | import PromiseKit 20 | */ 21 | public extension UIView { 22 | #if swift(>=4.2) 23 | /** 24 | Animate changes to one or more views using the specified duration, delay, 25 | options, and completion handler. 26 | 27 | - Parameter duration: The total duration of the animations, measured in 28 | seconds. If you specify a negative value or 0, the changes are made 29 | without animating them. 30 | 31 | - Parameter delay: The amount of time (measured in seconds) to wait before 32 | beginning the animations. Specify a value of 0 to begin the animations 33 | immediately. 34 | 35 | - Parameter options: A mask of options indicating how you want to perform the 36 | animations. For a list of valid constants, see UIViewAnimationOptions. 37 | 38 | - Parameter animations: A block object containing the changes to commit to the 39 | views. 40 | 41 | - Returns: A promise that fulfills with a boolean NSNumber indicating 42 | whether or not the animations actually finished. 43 | */ 44 | @discardableResult 45 | static func animate(_: PMKNamespacer, duration: TimeInterval, delay: TimeInterval = 0, options: UIView.AnimationOptions = [], animations: @escaping () -> Void) -> Guarantee { 46 | return Guarantee { animate(withDuration: duration, delay: delay, options: options, animations: animations, completion: $0) } 47 | } 48 | 49 | @discardableResult 50 | static func animate(_: PMKNamespacer, duration: TimeInterval, delay: TimeInterval, usingSpringWithDamping damping: CGFloat, initialSpringVelocity: CGFloat, options: UIView.AnimationOptions = [], animations: @escaping () -> Void) -> Guarantee { 51 | return Guarantee { animate(withDuration: duration, delay: delay, usingSpringWithDamping: damping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: $0) } 52 | } 53 | 54 | @discardableResult 55 | static func transition(_: PMKNamespacer, with view: UIView, duration: TimeInterval, options: UIView.AnimationOptions = [], animations: (() -> Void)?) -> Guarantee { 56 | return Guarantee { transition(with: view, duration: duration, options: options, animations: animations, completion: $0) } 57 | } 58 | 59 | @discardableResult 60 | static func transition(_: PMKNamespacer, from: UIView, to: UIView, duration: TimeInterval, options: UIView.AnimationOptions = []) -> Guarantee { 61 | return Guarantee { transition(from: from, to: to, duration: duration, options: options, completion: $0) } 62 | } 63 | 64 | @discardableResult 65 | static func perform(_: PMKNamespacer, animation: UIView.SystemAnimation, on views: [UIView], options: UIView.AnimationOptions = [], animations: (() -> Void)?) -> Guarantee { 66 | return Guarantee { perform(animation, on: views, options: options, animations: animations, completion: $0) } 67 | } 68 | #else 69 | /** 70 | Animate changes to one or more views using the specified duration, delay, 71 | options, and completion handler. 72 | 73 | - Parameter duration: The total duration of the animations, measured in 74 | seconds. If you specify a negative value or 0, the changes are made 75 | without animating them. 76 | 77 | - Parameter delay: The amount of time (measured in seconds) to wait before 78 | beginning the animations. Specify a value of 0 to begin the animations 79 | immediately. 80 | 81 | - Parameter options: A mask of options indicating how you want to perform the 82 | animations. For a list of valid constants, see UIViewAnimationOptions. 83 | 84 | - Parameter animations: A block object containing the changes to commit to the 85 | views. 86 | 87 | - Returns: A promise that fulfills with a boolean NSNumber indicating 88 | whether or not the animations actually finished. 89 | */ 90 | @discardableResult 91 | static func animate(_: PMKNamespacer, duration: TimeInterval, delay: TimeInterval = 0, options: UIViewAnimationOptions = [], animations: @escaping () -> Void) -> Guarantee { 92 | return Guarantee { animate(withDuration: duration, delay: delay, options: options, animations: animations, completion: $0) } 93 | } 94 | 95 | @discardableResult 96 | static func animate(_: PMKNamespacer, duration: TimeInterval, delay: TimeInterval, usingSpringWithDamping damping: CGFloat, initialSpringVelocity: CGFloat, options: UIViewAnimationOptions = [], animations: @escaping () -> Void) -> Guarantee { 97 | return Guarantee { animate(withDuration: duration, delay: delay, usingSpringWithDamping: damping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: $0) } 98 | } 99 | 100 | @discardableResult 101 | static func transition(_: PMKNamespacer, with view: UIView, duration: TimeInterval, options: UIViewAnimationOptions = [], animations: (() -> Void)?) -> Guarantee { 102 | return Guarantee { transition(with: view, duration: duration, options: options, animations: animations, completion: $0) } 103 | } 104 | 105 | @discardableResult 106 | static func transition(_: PMKNamespacer, from: UIView, to: UIView, duration: TimeInterval, options: UIViewAnimationOptions = []) -> Guarantee { 107 | return Guarantee { transition(from: from, to: to, duration: duration, options: options, completion: $0) } 108 | } 109 | 110 | @discardableResult 111 | static func perform(_: PMKNamespacer, animation: UISystemAnimation, on views: [UIView], options: UIViewAnimationOptions = [], animations: (() -> Void)?) -> Guarantee { 112 | return Guarantee { perform(animation, on: views, options: options, animations: animations, completion: $0) } 113 | } 114 | #endif 115 | } 116 | -------------------------------------------------------------------------------- /Sources/UIViewController+AnyPromise.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | /** 5 | To import the `UIViewController` category: 6 | 7 | use_frameworks! 8 | pod "PromiseKit/UIKit" 9 | 10 | Or `UIKit` is one of the categories imported by the umbrella pod: 11 | 12 | use_frameworks! 13 | pod "PromiseKit" 14 | 15 | And then in your sources: 16 | 17 | @import PromiseKit; 18 | */ 19 | @interface UIViewController (PromiseKit) 20 | 21 | /** 22 | Presents a view controller modally. 23 | 24 | If the view controller is one of the following: 25 | 26 | - MFMailComposeViewController 27 | - MFMessageComposeViewController 28 | - UIImagePickerController 29 | - SLComposeViewController 30 | 31 | Then PromiseKit presents the view controller returning a promise that is 32 | resolved as per the documentation for those classes. Eg. if you present a 33 | `UIImagePickerController` the view controller will be presented for you 34 | and the returned promise will resolve with the media the user selected. 35 | 36 | [self promiseViewController:[MFMailComposeViewController new] animated:YES completion:nil].then(^{ 37 | //… 38 | }); 39 | 40 | Otherwise PromiseKit expects your view controller to implement a 41 | `promise` property. This promise will be returned from this method and 42 | presentation and dismissal of the presented view controller will be 43 | managed for you. 44 | 45 | \@interface MyViewController: UIViewController 46 | @property (readonly) AnyPromise *promise; 47 | @end 48 | 49 | @implementation MyViewController { 50 | PMKResolver resolve; 51 | } 52 | 53 | - (void)viewDidLoad { 54 | _promise = [[AnyPromise alloc] initWithResolver:&resolve]; 55 | } 56 | 57 | - (void)later { 58 | resolve(@"some fulfilled value"); 59 | } 60 | 61 | @end 62 | 63 | [self promiseViewController:[MyViewController new] aniamted:YES completion:nil].then(^(id value){ 64 | // value == @"some fulfilled value" 65 | }); 66 | 67 | @return A promise that can be resolved by the presented view controller. 68 | */ 69 | - (AnyPromise *)promiseViewController:(UIViewController *)vc animated:(BOOL)animated completion:(void (^)(void))block NS_REFINED_FOR_SWIFT; 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Sources/UIViewController+AnyPromise.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "UIViewController+AnyPromise.h" 3 | #import 4 | 5 | #if PMKImagePickerController 6 | #import 7 | #endif 8 | 9 | @interface PMKGenericDelegate : NSObject { 10 | @public 11 | PMKResolver resolve; 12 | } 13 | + (instancetype)delegateWithPromise:(AnyPromise **)promise; 14 | @end 15 | 16 | @interface UIViewController () 17 | - (AnyPromise*) promise; 18 | @end 19 | 20 | @implementation UIViewController (PromiseKit) 21 | 22 | - (AnyPromise *)promiseViewController:(UIViewController *)vc animated:(BOOL)animated completion:(void (^)(void))block { 23 | __kindof UIViewController *vc2present = vc; 24 | AnyPromise *promise = nil; 25 | 26 | if ([vc isKindOfClass:NSClassFromString(@"MFMailComposeViewController")]) { 27 | PMKGenericDelegate *delegate = [PMKGenericDelegate delegateWithPromise:&promise]; 28 | [vc setValue:delegate forKey:@"mailComposeDelegate"]; 29 | } 30 | else if ([vc isKindOfClass:NSClassFromString(@"MFMessageComposeViewController")]) { 31 | PMKGenericDelegate *delegate = [PMKGenericDelegate delegateWithPromise:&promise]; 32 | [vc setValue:delegate forKey:@"messageComposeDelegate"]; 33 | } 34 | #ifdef PMKImagePickerController 35 | else if ([vc isKindOfClass:[UIImagePickerController class]]) { 36 | PMKGenericDelegate *delegate = [PMKGenericDelegate delegateWithPromise:&promise]; 37 | [vc setValue:delegate forKey:@"delegate"]; 38 | } 39 | #endif 40 | else if ([vc isKindOfClass:NSClassFromString(@"SLComposeViewController")]) { 41 | PMKResolver resolve; 42 | promise = [[AnyPromise alloc] initWithResolver:&resolve]; 43 | [vc setValue:^(NSInteger result){ 44 | if (result == 0) { 45 | resolve([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]); 46 | } else { 47 | resolve(@(result)); 48 | } 49 | } forKey:@"completionHandler"]; 50 | } 51 | else if ([vc isKindOfClass:[UINavigationController class]]) 52 | vc = [(id)vc viewControllers].firstObject; 53 | 54 | if (!vc) { 55 | id userInfo = @{NSLocalizedDescriptionKey: @"nil or effective nil passed to promiseViewController"}; 56 | id err = [NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:userInfo]; 57 | return [AnyPromise promiseWithValue:err]; 58 | } 59 | 60 | if (!promise) { 61 | if (![vc respondsToSelector:@selector(promise)]) { 62 | id userInfo = @{NSLocalizedDescriptionKey: @"ViewController is not promisable"}; 63 | id err = [NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:userInfo]; 64 | return [AnyPromise promiseWithValue:err]; 65 | } 66 | 67 | promise = [vc valueForKey:@"promise"]; 68 | 69 | if (![promise isKindOfClass:[AnyPromise class]]) { 70 | id userInfo = @{NSLocalizedDescriptionKey: @"The promise property is nil or not of type AnyPromise"}; 71 | id err = [NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:userInfo]; 72 | return [AnyPromise promiseWithValue:err]; 73 | } 74 | } 75 | 76 | if (!promise.pending) 77 | return promise; 78 | 79 | [self presentViewController:vc2present animated:animated completion:block]; 80 | 81 | promise.ensure(^{ 82 | [vc2present.presentingViewController dismissViewControllerAnimated:animated completion:nil]; 83 | }); 84 | 85 | return promise; 86 | } 87 | 88 | @end 89 | 90 | 91 | 92 | @implementation PMKGenericDelegate { 93 | id retainCycle; 94 | } 95 | 96 | + (instancetype)delegateWithPromise:(AnyPromise **)promise; { 97 | PMKGenericDelegate *d = [PMKGenericDelegate new]; 98 | d->retainCycle = d; 99 | *promise = [[AnyPromise alloc] initWithResolver:&d->resolve]; 100 | return d; 101 | } 102 | 103 | - (void)mailComposeController:(id)controller didFinishWithResult:(int)result error:(NSError *)error { 104 | if (error != nil) { 105 | resolve(error); 106 | } else if (result == 0) { 107 | resolve([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]); 108 | } else { 109 | resolve(@(result)); 110 | } 111 | retainCycle = nil; 112 | } 113 | 114 | - (void)messageComposeViewController:(id)controller didFinishWithResult:(int)result { 115 | if (result == 2) { 116 | id userInfo = @{NSLocalizedDescriptionKey: @"The attempt to save or send the message was unsuccessful."}; 117 | id error = [NSError errorWithDomain:PMKErrorDomain code:PMKOperationFailed userInfo:userInfo]; 118 | resolve(error); 119 | } else { 120 | resolve(@(result)); 121 | } 122 | retainCycle = nil; 123 | } 124 | 125 | #ifdef PMKImagePickerController 126 | 127 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { 128 | id img = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage]; 129 | resolve(PMKManifold(img, info)); 130 | retainCycle = nil; 131 | } 132 | 133 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { 134 | resolve([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]); 135 | retainCycle = nil; 136 | } 137 | 138 | #endif 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /Sources/UIViewPropertyAnimator+Promise.swift: -------------------------------------------------------------------------------- 1 | #if !PMKCocoaPods 2 | import PromiseKit 3 | #endif 4 | import UIKit 5 | 6 | @available(iOS 10, tvOS 10, *) 7 | public extension UIViewPropertyAnimator { 8 | func startAnimation(_: PMKNamespacer) -> Guarantee { 9 | return Guarantee { 10 | addCompletion($0) 11 | startAnimation() 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/TestUIImagePickerController.swift: -------------------------------------------------------------------------------- 1 | import PromiseKit 2 | import PMKUIKit 3 | import XCTest 4 | import UIKit 5 | 6 | #if os(iOS) 7 | 8 | class Test_UIImagePickerController_Swift: XCTestCase { 9 | func test() { 10 | class Mock: UIViewController { 11 | var info = [String:AnyObject]() 12 | 13 | override func present(_ vc: UIViewController, animated flag: Bool, completion: (() -> Void)?) { 14 | let ipc = vc as! UIImagePickerController 15 | after(seconds: 0.05).done { 16 | ipc.delegate?.imagePickerController?(ipc, didFinishPickingMediaWithInfo: self.info) 17 | } 18 | } 19 | } 20 | 21 | let (originalImage, editedImage) = (UIImage(), UIImage()) 22 | 23 | let mockvc = Mock() 24 | mockvc.info = [UIImagePickerControllerOriginalImage: originalImage, UIImagePickerControllerEditedImage: editedImage] 25 | 26 | let ex = expectation(description: "") 27 | mockvc.promise(UIImagePickerController(), animate: []).done { _ in 28 | ex.fulfill() 29 | } 30 | waitForExpectations(timeout: 10, handler: nil) 31 | } 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Tests/TestUIView.swift: -------------------------------------------------------------------------------- 1 | import PromiseKit 2 | import PMKUIKit 3 | import XCTest 4 | import UIKit 5 | 6 | class UIViewTests: XCTestCase { 7 | func test() { 8 | let ex1 = expectation(description: "") 9 | let ex2 = expectation(description: "") 10 | 11 | UIView.animate(.promise, duration: 0.1) { 12 | ex1.fulfill() 13 | }.done { _ in 14 | ex2.fulfill() 15 | } 16 | 17 | waitForExpectations(timeout: 1) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/TestUIViewController.m: -------------------------------------------------------------------------------- 1 | @import PromiseKit; 2 | @import PMKUIKit; 3 | @import XCTest; 4 | @import UIKit; 5 | 6 | 7 | @interface MyViewController: UIViewController 8 | @property AnyPromise *promise; 9 | @end 10 | @implementation MyViewController 11 | @end 12 | 13 | 14 | @implementation UIViewControllerTests: XCTestCase { 15 | UIViewController *rootvc; 16 | } 17 | 18 | - (void)setUp { 19 | rootvc = [UIApplication sharedApplication].keyWindow.rootViewController = [UIViewController new]; 20 | } 21 | 22 | - (void)tearDown { 23 | [UIApplication sharedApplication].keyWindow.rootViewController = nil; 24 | } 25 | 26 | // view controller is presented and dismissed when promise is resolved 27 | - (void)test1 { 28 | XCTestExpectation *ex = [self expectationWithDescription:@""]; 29 | 30 | PMKResolver resolve; 31 | 32 | MyViewController *myvc = [MyViewController new]; 33 | myvc.promise = [[AnyPromise alloc] initWithResolver:&resolve]; 34 | [rootvc promiseViewController:myvc animated:NO completion:nil].then(^{ 35 | // seems to take another tick for the dismissal to complete 36 | }).then(^{ 37 | [ex fulfill]; 38 | }); 39 | 40 | XCTAssertNotNil(rootvc.presentedViewController); 41 | 42 | PMKAfter(1).then(^{ 43 | resolve(@1); 44 | }); 45 | 46 | [self waitForExpectationsWithTimeout:10 handler:nil]; 47 | 48 | XCTAssertNil(rootvc.presentedViewController); 49 | } 50 | 51 | // view controller is not presented if promise is resolved 52 | - (void)test2 { 53 | MyViewController *myvc = [MyViewController new]; 54 | myvc.promise = [AnyPromise promiseWithValue:nil]; 55 | [rootvc promiseViewController:myvc animated:NO completion:nil]; 56 | 57 | XCTAssertNil(rootvc.presentedViewController); 58 | } 59 | 60 | // promise property must be promise 61 | - (void)test3 { 62 | XCTestExpectation *ex = [self expectationWithDescription:@""]; 63 | 64 | MyViewController *myvc = [MyViewController new]; 65 | myvc.promise = (id) @1; 66 | [rootvc promiseViewController:myvc animated:NO completion:nil].catch(^(id err){ 67 | [ex fulfill]; 68 | }); 69 | 70 | XCTAssertNil(rootvc.presentedViewController); 71 | 72 | [self waitForExpectationsWithTimeout:10 handler:nil]; 73 | 74 | XCTAssertNil(rootvc.presentedViewController); 75 | } 76 | 77 | // promise property must not be nil 78 | - (void)test4 { 79 | XCTestExpectation *ex = [self expectationWithDescription:@""]; 80 | 81 | MyViewController *myvc = [MyViewController new]; 82 | [rootvc promiseViewController:myvc animated:NO completion:nil].catch(^(id err){ 83 | [ex fulfill]; 84 | }); 85 | 86 | XCTAssertNil(rootvc.presentedViewController); 87 | 88 | [self waitForExpectationsWithTimeout:10 handler:nil]; 89 | 90 | XCTAssertNil(rootvc.presentedViewController); 91 | } 92 | 93 | // view controller must have a promise property 94 | - (void)test5 { 95 | XCTestExpectation *ex = [self expectationWithDescription:@""]; 96 | 97 | UIViewController *vc = [UIViewController new]; 98 | [rootvc promiseViewController:vc animated:NO completion:nil].catch(^(id err){ 99 | [ex fulfill]; 100 | }); 101 | 102 | XCTAssertNil(rootvc.presentedViewController); 103 | 104 | [self waitForExpectationsWithTimeout:10 handler:nil]; 105 | 106 | XCTAssertNil(rootvc.presentedViewController); 107 | } 108 | 109 | // promised nav controllers use their root vc’s promise property 110 | - (void)test6 { 111 | XCTestExpectation *ex = [self expectationWithDescription:@""]; 112 | 113 | PMKResolver resolve; 114 | 115 | MyViewController *myvc = [MyViewController new]; 116 | UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:myvc]; 117 | myvc.promise = [[AnyPromise alloc] initWithResolver:&resolve]; 118 | [rootvc promiseViewController:nc animated:NO completion:nil].then(^(id obj){ 119 | XCTAssertEqualObjects(@1, obj); 120 | }).then(^{ 121 | [ex fulfill]; 122 | }); 123 | 124 | XCTAssertNotNil(rootvc.presentedViewController); 125 | 126 | PMKAfter(1).then(^{ 127 | resolve(@1); 128 | }); 129 | 130 | [self waitForExpectationsWithTimeout:10 handler:nil]; 131 | 132 | XCTAssertNil(rootvc.presentedViewController); 133 | } 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /Tests/infrastructure.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import UIKit 3 | 4 | extension XCTestCase { 5 | var rootvc: UIViewController { 6 | return UIApplication.shared.keyWindow!.rootViewController! 7 | } 8 | 9 | override open func setUp() { 10 | UIApplication.shared.keyWindow!.rootViewController = UIViewController() 11 | } 12 | 13 | override open func tearDown() { 14 | UIApplication.shared.keyWindow!.rootViewController = nil 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /UITests/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromiseKit/UIKit/c1a344bea8b1a4bf3cc79fac0ea8130f99a0409b/UITests/Default-568h@2x.png -------------------------------------------------------------------------------- /UITests/Entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.private.tcc.allow 6 | 7 | kTCCServiceAddressBook 8 | kTCCServiceCalendar 9 | kTCCServicePhotos 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /UITests/TestUIImagePickerController.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class UIImagePickerControllerTests: XCTestCase { 4 | var button: XCUIElement { 5 | // calling this ensures that any other ViewController has dismissed 6 | // as a side-effect since otherwise the switch won't be found 7 | return XCUIApplication().tables.buttons.element 8 | } 9 | 10 | var value: Bool { 11 | return button.isEnabled 12 | } 13 | 14 | override func setUp() { 15 | super.setUp() 16 | continueAfterFailure = false 17 | XCUIApplication().launch() 18 | XCTAssertFalse(value) 19 | } 20 | 21 | #if !os(tvOS) 22 | // this test works locally but not on travis 23 | // attempting to detect Travis and early-return did not work 24 | func test_rejects_when_cancelled() { 25 | let app = XCUIApplication() 26 | let table = app.tables 27 | table.cells.staticTexts["1"].tap() 28 | table.cells.element(boundBy: 0).tap() 29 | app.navigationBars.buttons["Cancel"].tap() 30 | 31 | XCTAssertTrue(value) 32 | } 33 | 34 | // following two don't seem to work since Xcode 8.1 35 | // The UI-Testing infrastructure cannot “see” the image picking UI 36 | // And… trying to re-record by hand fails. 37 | 38 | func test_fulfills_with_edited_image() { 39 | let app = XCUIApplication() 40 | app.tables.cells.staticTexts["2"].tap() 41 | app.tables.children(matching: .cell).element(boundBy: 1).tap() 42 | app.collectionViews.children(matching: .cell).element(boundBy: 0).tap() 43 | 44 | // XCUITesting fails to tap this button, hence this test disabled 45 | app.buttons["Choose"].tap() 46 | 47 | XCTAssertTrue(value) 48 | } 49 | 50 | func test_fulfills_with_image() { 51 | let app = XCUIApplication() 52 | let tablesQuery = app.tables 53 | tablesQuery.staticTexts["3"].tap() 54 | tablesQuery.children(matching: .cell).element(boundBy: 1).tap() 55 | 56 | app.collectionViews.children(matching: .cell).element(boundBy: 0).tap() 57 | 58 | XCTAssertTrue(value) 59 | } 60 | #endif 61 | } 62 | -------------------------------------------------------------------------------- /UITests/app.swift: -------------------------------------------------------------------------------- 1 | import PromiseKit 2 | import PMKUIKit 3 | import UIKit 4 | 5 | @UIApplicationMain 6 | class App: UITableViewController, UIApplicationDelegate { 7 | var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds) 8 | 9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool { 10 | window!.rootViewController = self 11 | window!.backgroundColor = UIColor.purple 12 | window!.makeKeyAndVisible() 13 | UIView.setAnimationsEnabled(false) 14 | return true 15 | } 16 | 17 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 18 | return Row.count 19 | } 20 | 21 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 22 | let cell = UITableViewCell() 23 | cell.textLabel?.text = Row(indexPath)?.description 24 | return cell 25 | } 26 | 27 | let testSuceededButton = UIButton() 28 | 29 | override func viewDidLoad() { 30 | testSuceededButton.setTitle("unused", for: .normal) 31 | testSuceededButton.sizeToFit() 32 | testSuceededButton.backgroundColor = UIColor.blue 33 | testSuceededButton.isEnabled = false 34 | 35 | view.addSubview(testSuceededButton) 36 | } 37 | 38 | override func viewDidLayoutSubviews() { 39 | testSuceededButton.center = view.center 40 | } 41 | 42 | private func success() { 43 | self.testSuceededButton.isEnabled = true 44 | } 45 | 46 | #if !os(tvOS) 47 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 48 | switch Row(indexPath)! { 49 | case .ImagePickerCancel: 50 | let p = promise(UIImagePickerController()) 51 | p.catch(policy: .allErrors) { error in 52 | guard (error as! CancellableError).isCancelled else { abort() } 53 | self.success() 54 | } 55 | p.catch { error in 56 | abort() 57 | } 58 | case .ImagePickerEditImage: 59 | let picker = UIImagePickerController() 60 | picker.allowsEditing = true 61 | _ = promise(picker).done { _ in 62 | self.success() 63 | } 64 | case .ImagePickerPickImage: 65 | _ = promise(UIImagePickerController()).done { image in 66 | self.success() 67 | } 68 | } 69 | } 70 | #endif 71 | } 72 | 73 | enum Row: Int { 74 | case ImagePickerCancel 75 | case ImagePickerEditImage 76 | case ImagePickerPickImage 77 | 78 | init?(_ indexPath: IndexPath) { 79 | guard let row = Row(rawValue: indexPath.row) else { 80 | return nil 81 | } 82 | self = row 83 | } 84 | 85 | var indexPath: IndexPath { 86 | return IndexPath(row: rawValue, section: 0) 87 | } 88 | 89 | var description: String { 90 | return (rawValue + 1).description 91 | } 92 | 93 | static var count: Int { 94 | var x = 0 95 | while Row(rawValue: x) != nil { 96 | x += 1 97 | } 98 | return x 99 | } 100 | } 101 | --------------------------------------------------------------------------------