├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── BUILD ├── Example ├── PINFuture.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── PINFuture-Example.xcscheme ├── PINFuture.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── PINFuture │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Launch Screen.storyboard │ ├── Main.storyboard │ ├── PINAppDelegate.h │ ├── PINAppDelegate.m │ ├── PINFuture-Info.plist │ ├── PINFuture-Prefix.pch │ ├── PINViewController.h │ ├── PINViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m ├── Podfile ├── Podfile.lock ├── Pods │ ├── Expecta │ │ ├── Expecta │ │ │ ├── EXPBlockDefinedMatcher.h │ │ │ ├── EXPBlockDefinedMatcher.m │ │ │ ├── EXPDefines.h │ │ │ ├── EXPDoubleTuple.h │ │ │ ├── EXPDoubleTuple.m │ │ │ ├── EXPExpect.h │ │ │ ├── EXPExpect.m │ │ │ ├── EXPFloatTuple.h │ │ │ ├── EXPFloatTuple.m │ │ │ ├── EXPMatcher.h │ │ │ ├── EXPUnsupportedObject.h │ │ │ ├── EXPUnsupportedObject.m │ │ │ ├── Expecta.h │ │ │ ├── ExpectaObject.h │ │ │ ├── ExpectaObject.m │ │ │ ├── ExpectaSupport.h │ │ │ ├── ExpectaSupport.m │ │ │ ├── Matchers │ │ │ │ ├── EXPMatcherHelpers.h │ │ │ │ ├── EXPMatcherHelpers.m │ │ │ │ ├── EXPMatchers+beCloseTo.h │ │ │ │ ├── EXPMatchers+beCloseTo.m │ │ │ │ ├── EXPMatchers+beFalsy.h │ │ │ │ ├── EXPMatchers+beFalsy.m │ │ │ │ ├── EXPMatchers+beGreaterThan.h │ │ │ │ ├── EXPMatchers+beGreaterThan.m │ │ │ │ ├── EXPMatchers+beGreaterThanOrEqualTo.h │ │ │ │ ├── EXPMatchers+beGreaterThanOrEqualTo.m │ │ │ │ ├── EXPMatchers+beIdenticalTo.h │ │ │ │ ├── EXPMatchers+beIdenticalTo.m │ │ │ │ ├── EXPMatchers+beInTheRangeOf.h │ │ │ │ ├── EXPMatchers+beInTheRangeOf.m │ │ │ │ ├── EXPMatchers+beInstanceOf.h │ │ │ │ ├── EXPMatchers+beInstanceOf.m │ │ │ │ ├── EXPMatchers+beKindOf.h │ │ │ │ ├── EXPMatchers+beKindOf.m │ │ │ │ ├── EXPMatchers+beLessThan.h │ │ │ │ ├── EXPMatchers+beLessThan.m │ │ │ │ ├── EXPMatchers+beLessThanOrEqualTo.h │ │ │ │ ├── EXPMatchers+beLessThanOrEqualTo.m │ │ │ │ ├── EXPMatchers+beNil.h │ │ │ │ ├── EXPMatchers+beNil.m │ │ │ │ ├── EXPMatchers+beSubclassOf.h │ │ │ │ ├── EXPMatchers+beSubclassOf.m │ │ │ │ ├── EXPMatchers+beSupersetOf.h │ │ │ │ ├── EXPMatchers+beSupersetOf.m │ │ │ │ ├── EXPMatchers+beTruthy.h │ │ │ │ ├── EXPMatchers+beTruthy.m │ │ │ │ ├── EXPMatchers+beginWith.h │ │ │ │ ├── EXPMatchers+beginWith.m │ │ │ │ ├── EXPMatchers+conformTo.h │ │ │ │ ├── EXPMatchers+conformTo.m │ │ │ │ ├── EXPMatchers+contain.h │ │ │ │ ├── EXPMatchers+contain.m │ │ │ │ ├── EXPMatchers+endWith.h │ │ │ │ ├── EXPMatchers+endWith.m │ │ │ │ ├── EXPMatchers+equal.h │ │ │ │ ├── EXPMatchers+equal.m │ │ │ │ ├── EXPMatchers+haveCountOf.h │ │ │ │ ├── EXPMatchers+haveCountOf.m │ │ │ │ ├── EXPMatchers+match.h │ │ │ │ ├── EXPMatchers+match.m │ │ │ │ ├── EXPMatchers+postNotification.h │ │ │ │ ├── EXPMatchers+postNotification.m │ │ │ │ ├── EXPMatchers+raise.h │ │ │ │ ├── EXPMatchers+raise.m │ │ │ │ ├── EXPMatchers+raiseWithReason.h │ │ │ │ ├── EXPMatchers+raiseWithReason.m │ │ │ │ ├── EXPMatchers+respondTo.h │ │ │ │ ├── EXPMatchers+respondTo.m │ │ │ │ └── EXPMatchers.h │ │ │ ├── NSObject+Expecta.h │ │ │ ├── NSValue+Expecta.h │ │ │ └── NSValue+Expecta.m │ │ ├── LICENSE │ │ └── README.md │ ├── Local Podspecs │ │ └── PINFuture.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ ├── Specta │ │ ├── LICENSE │ │ ├── README.md │ │ └── Specta │ │ │ └── Specta │ │ │ ├── SPTCallSite.h │ │ │ ├── SPTCallSite.m │ │ │ ├── SPTCompiledExample.h │ │ │ ├── SPTCompiledExample.m │ │ │ ├── SPTExample.h │ │ │ ├── SPTExample.m │ │ │ ├── SPTExampleGroup.h │ │ │ ├── SPTExampleGroup.m │ │ │ ├── SPTExcludeGlobalBeforeAfterEach.h │ │ │ ├── SPTGlobalBeforeAfterEach.h │ │ │ ├── SPTSharedExampleGroups.h │ │ │ ├── SPTSharedExampleGroups.m │ │ │ ├── SPTSpec.h │ │ │ ├── SPTSpec.m │ │ │ ├── SPTTestSuite.h │ │ │ ├── SPTTestSuite.m │ │ │ ├── Specta.h │ │ │ ├── SpectaDSL.h │ │ │ ├── SpectaDSL.m │ │ │ ├── SpectaTypes.h │ │ │ ├── SpectaUtility.h │ │ │ ├── SpectaUtility.m │ │ │ ├── XCTest+Private.h │ │ │ ├── XCTestCase+Specta.h │ │ │ └── XCTestCase+Specta.m │ └── Target Support Files │ │ ├── Expecta │ │ ├── Expecta-Info.plist │ │ ├── Expecta-dummy.m │ │ ├── Expecta-prefix.pch │ │ ├── Expecta-umbrella.h │ │ ├── Expecta.debug.xcconfig │ │ ├── Expecta.modulemap │ │ ├── Expecta.release.xcconfig │ │ ├── Expecta.xcconfig │ │ └── Info.plist │ │ ├── PINFuture │ │ ├── Info.plist │ │ ├── PINFuture-Info.plist │ │ ├── PINFuture-dummy.m │ │ ├── PINFuture-prefix.pch │ │ ├── PINFuture-umbrella.h │ │ ├── PINFuture.debug.xcconfig │ │ ├── PINFuture.modulemap │ │ ├── PINFuture.release.xcconfig │ │ └── PINFuture.xcconfig │ │ ├── Pods-PINFuture_Example │ │ ├── Info.plist │ │ ├── Pods-PINFuture_Example-Info.plist │ │ ├── Pods-PINFuture_Example-acknowledgements.markdown │ │ ├── Pods-PINFuture_Example-acknowledgements.plist │ │ ├── Pods-PINFuture_Example-dummy.m │ │ ├── Pods-PINFuture_Example-frameworks.sh │ │ ├── Pods-PINFuture_Example-resources.sh │ │ ├── Pods-PINFuture_Example-umbrella.h │ │ ├── Pods-PINFuture_Example.debug.xcconfig │ │ ├── Pods-PINFuture_Example.modulemap │ │ └── Pods-PINFuture_Example.release.xcconfig │ │ ├── Pods-PINFuture_Tests │ │ ├── Info.plist │ │ ├── Pods-PINFuture_Tests-Info.plist │ │ ├── Pods-PINFuture_Tests-acknowledgements.markdown │ │ ├── Pods-PINFuture_Tests-acknowledgements.plist │ │ ├── Pods-PINFuture_Tests-dummy.m │ │ ├── Pods-PINFuture_Tests-frameworks.sh │ │ ├── Pods-PINFuture_Tests-resources.sh │ │ ├── Pods-PINFuture_Tests-umbrella.h │ │ ├── Pods-PINFuture_Tests.debug.xcconfig │ │ ├── Pods-PINFuture_Tests.modulemap │ │ └── Pods-PINFuture_Tests.release.xcconfig │ │ └── Specta │ │ ├── Info.plist │ │ ├── Specta-Info.plist │ │ ├── Specta-dummy.m │ │ ├── Specta-prefix.pch │ │ ├── Specta-umbrella.h │ │ ├── Specta.debug.xcconfig │ │ ├── Specta.modulemap │ │ ├── Specta.release.xcconfig │ │ └── Specta.xcconfig └── Tests │ ├── PINCancelTokenTests.m │ ├── PINFuture+ChainSideEffectTests.m │ ├── PINFuture+CompletionTests.m │ ├── PINFuture+DelayTests.m │ ├── PINFuture+DispatchTests.m │ ├── PINFuture+FlatMapErrorTests.m │ ├── PINFuture+GatherAllTests.m │ ├── PINFuture+GatherSomeTests.m │ ├── PINFuture+MapErrorTests.m │ ├── PINFuture+MapToValueTests.m │ ├── PINFuture+NoValueTests.m │ ├── PINFutureMap+FlatMapTests.m │ ├── PINFutureMap+MapTests.m │ ├── PINFutureOnceTests.m │ ├── PINFuturePendingTests.m │ ├── PINFutureStressTests.m │ ├── PINFutureTests.m │ ├── TestUtil.h │ ├── TestUtil.m │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ └── en.lproj │ └── InfoPlist.strings ├── LICENSE ├── PINFuture.podspec ├── PINFuture ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── Categories │ ├── NSURLSession+PINFuture.h │ ├── NSURLSession+PINFuture.m │ ├── PHImageManager+PINFuture.h │ ├── PHImageManager+PINFuture.m │ ├── PINNSURLSessionDataTaskAndResult.h │ ├── PINNSURLSessionDataTaskAndResult.m │ ├── PINNSURLSessionDataTaskResult.h │ ├── PINNSURLSessionDataTaskResult.m │ ├── PINPHImageManagerImageDataResult.h │ └── PINPHImageManagerImageDataResult.m │ ├── PINCancelToken.h │ ├── PINCancelToken.m │ ├── PINDefines.h │ ├── PINExecutor.h │ ├── PINExecutor.m │ ├── PINFuture+ChainSideEffect.h │ ├── PINFuture+ChainSideEffect.m │ ├── PINFuture+Completion.h │ ├── PINFuture+Completion.m │ ├── PINFuture+Delay.h │ ├── PINFuture+Delay.m │ ├── PINFuture+Dispatch.h │ ├── PINFuture+Dispatch.m │ ├── PINFuture+FlatMapError.h │ ├── PINFuture+FlatMapError.m │ ├── PINFuture+GatherAll.h │ ├── PINFuture+GatherAll.m │ ├── PINFuture+GatherSome.h │ ├── PINFuture+GatherSome.m │ ├── PINFuture+Generated.h │ ├── PINFuture+Generated.m │ ├── PINFuture+MapError.h │ ├── PINFuture+MapError.m │ ├── PINFuture+MapToValue.h │ ├── PINFuture+MapToValue.m │ ├── PINFuture+NoValue.h │ ├── PINFuture+NoValue.m │ ├── PINFuture.h │ ├── PINFutureAndCancelToken.h │ ├── PINFutureAndCancelToken.m │ ├── PINFutureDefinition.h │ ├── PINFutureDefinition.m │ ├── PINFutureError.h │ ├── PINFutureError.m │ ├── PINFutureMap+FlatMap.h │ ├── PINFutureMap+FlatMap.m │ ├── PINFutureMap+Map.h │ ├── PINFutureMap+Map.m │ ├── PINFutureMap.h │ ├── PINFutureMap.m │ ├── PINFutureOnce.h │ ├── PINFutureOnce.m │ ├── PINFuturePending.h │ └── PINFuturePending.m ├── README.md ├── WORKSPACE └── _Pods.xcodeproj /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 4 | on: 5 | push: 6 | branches: 7 | - master 8 | - 'releases/*' 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build: 15 | name: Build 16 | runs-on: macOS-latest 17 | strategy: 18 | matrix: 19 | platform: ['iOS Simulator,name=iPhone 8'] 20 | steps: 21 | - uses: actions/checkout@v1 22 | - name: Analyze and Test 23 | run: | 24 | xcodebuild clean analyze test \ 25 | -destination "platform=${{ matrix.platform }}" \ 26 | -sdk "iphonesimulator" \ 27 | -workspace Example/PINFuture.xcworkspace \ 28 | -scheme PINFuture-Example \ 29 | ONLY_ACTIVE_ARCH=NO \ 30 | CODE_SIGNING_REQUIRED=NO \ 31 | | xcpretty 32 | cocoapods: 33 | name: CocoaPods 34 | runs-on: macOS-latest 35 | steps: 36 | - uses: actions/checkout@v1 37 | - name: Lint 38 | run: pod lib lint 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bazel 23 | bazel-* 24 | 25 | # Bundler 26 | .bundle 27 | 28 | Carthage 29 | # We recommend against adding the Pods directory to your .gitignore. However 30 | # you should judge for yourself, the pros and cons are mentioned at: 31 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 32 | # 33 | # Note: if you ignore the Pods directory, make sure to uncomment 34 | # `pod install` in .travis.yml 35 | # 36 | # Pods/ 37 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | objc_library( 2 | name = "PINFuture", 3 | hdrs = glob(["PINFuture/Classes/**/*.h"]), 4 | srcs = glob(["PINFuture/Classes/**/*.m"]), 5 | includes = [ 6 | "PINFuture/Classes", 7 | "PINFuture/Classes/Categories", 8 | ], 9 | copts = ["-w"], 10 | sdk_frameworks = [ 11 | "AssetsLibrary", 12 | "Photos" 13 | ], 14 | visibility = ["//visibility:public"] 15 | ) 16 | -------------------------------------------------------------------------------- /Example/PINFuture.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/PINFuture.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/PINFuture.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/PINFuture/Images.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 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/PINFuture/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/PINFuture/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 | -------------------------------------------------------------------------------- /Example/PINFuture/PINAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINAppDelegate.h 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/02/2016. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface PINAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/PINFuture/PINAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINAppDelegate.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/02/2016. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINAppDelegate.h" 10 | 11 | @implementation PINAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Example/PINFuture/PINFuture-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/PINFuture/PINFuture-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/PINFuture/PINViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINViewController.h 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/02/2016. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface PINViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/PINFuture/PINViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINViewController.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/02/2016. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINViewController.h" 10 | 11 | @interface PINViewController () 12 | 13 | @end 14 | 15 | @implementation PINViewController 16 | 17 | - (void)viewDidLoad 18 | { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | } 22 | 23 | - (void)didReceiveMemoryWarning 24 | { 25 | [super didReceiveMemoryWarning]; 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Example/PINFuture/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/PINFuture/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/02/2016. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "PINAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([PINAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'PINFuture_Example' do 4 | pod 'PINFuture', :path => '../' 5 | 6 | target 'PINFuture_Tests' do 7 | inherit! :search_paths 8 | 9 | pod 'PINFuture', :path => '../' 10 | pod 'Specta' 11 | pod 'Expecta' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Expecta (1.0.6) 3 | - PINFuture (5.2.1) 4 | - Specta (1.0.7) 5 | 6 | DEPENDENCIES: 7 | - Expecta 8 | - PINFuture (from `../`) 9 | - Specta 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - Expecta 14 | - Specta 15 | 16 | EXTERNAL SOURCES: 17 | PINFuture: 18 | :path: "../" 19 | 20 | SPEC CHECKSUMS: 21 | Expecta: 3b6bd90a64b9a1dcb0b70aa0e10a7f8f631667d5 22 | PINFuture: a3de8180f634e6af833bf8753e7829e4e69e8f9a 23 | Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66 24 | 25 | PODFILE CHECKSUM: 699d06a490a2ae8d8e6dcf963cf46656dad30462 26 | 27 | COCOAPODS: 1.9.1 28 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPBlockDefinedMatcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // EXPRuntimeMatcher.h 3 | // Expecta 4 | // 5 | // Created by Luke Redpath on 26/03/2012. 6 | // Copyright (c) 2012 Peter Jihoon Kim. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "EXPMatcher.h" 11 | #import "EXPDefines.h" 12 | 13 | @interface EXPBlockDefinedMatcher : NSObject { 14 | EXPBoolBlock prerequisiteBlock; 15 | EXPBoolBlock matchBlock; 16 | EXPStringBlock failureMessageForToBlock; 17 | EXPStringBlock failureMessageForNotToBlock; 18 | } 19 | 20 | @property(nonatomic, copy) EXPBoolBlock prerequisiteBlock; 21 | @property(nonatomic, copy) EXPBoolBlock matchBlock; 22 | @property(nonatomic, copy) EXPStringBlock failureMessageForToBlock; 23 | @property(nonatomic, copy) EXPStringBlock failureMessageForNotToBlock; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPBlockDefinedMatcher.m: -------------------------------------------------------------------------------- 1 | // 2 | // EXPRuntimeMatcher.m 3 | // Expecta 4 | // 5 | // Created by Luke Redpath on 26/03/2012. 6 | // Copyright (c) 2012 Peter Jihoon Kim. All rights reserved. 7 | // 8 | 9 | #import "EXPBlockDefinedMatcher.h" 10 | 11 | @implementation EXPBlockDefinedMatcher 12 | 13 | - (void)dealloc 14 | { 15 | self.prerequisiteBlock = nil; 16 | self.matchBlock = nil; 17 | self.failureMessageForToBlock = nil; 18 | self.failureMessageForNotToBlock = nil; 19 | 20 | [super dealloc]; 21 | } 22 | 23 | @synthesize prerequisiteBlock; 24 | @synthesize matchBlock; 25 | @synthesize failureMessageForToBlock; 26 | @synthesize failureMessageForNotToBlock; 27 | 28 | - (BOOL)meetsPrerequesiteFor:(id)actual 29 | { 30 | if (self.prerequisiteBlock) { 31 | return self.prerequisiteBlock(); 32 | } 33 | return YES; 34 | } 35 | 36 | - (BOOL)matches:(id)actual 37 | { 38 | if (self.matchBlock) { 39 | return self.matchBlock(); 40 | } 41 | return YES; 42 | } 43 | 44 | - (NSString *)failureMessageForTo:(id)actual 45 | { 46 | if (self.failureMessageForToBlock) { 47 | return self.failureMessageForToBlock(); 48 | } 49 | return nil; 50 | } 51 | 52 | - (NSString *)failureMessageForNotTo:(id)actual 53 | { 54 | if (self.failureMessageForNotToBlock) { 55 | return self.failureMessageForNotToBlock(); 56 | } 57 | return nil; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // EXPDefines.h 3 | // Expecta 4 | // 5 | // Created by Luke Redpath on 26/03/2012. 6 | // Copyright (c) 2012 Peter Jihoon Kim. All rights reserved. 7 | // 8 | 9 | #ifndef Expecta_EXPDefines_h 10 | #define Expecta_EXPDefines_h 11 | 12 | typedef void (^EXPBasicBlock)(void); 13 | typedef id (^EXPIdBlock)(void); 14 | typedef BOOL (^EXPBoolBlock)(void); 15 | typedef NSString *(^EXPStringBlock)(void); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPDoubleTuple.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface EXPDoubleTuple : NSObject { 4 | double *_values; 5 | size_t _size; 6 | } 7 | 8 | @property (nonatomic, assign) double *values; 9 | @property (nonatomic, assign) size_t size; 10 | 11 | - (instancetype)initWithDoubleValues:(double *)values size:(size_t)size NS_DESIGNATED_INITIALIZER; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPDoubleTuple.m: -------------------------------------------------------------------------------- 1 | #import "EXPDoubleTuple.h" 2 | 3 | #pragma clang diagnostic push 4 | #pragma clang diagnostic ignored "-Wobjc-designated-initializers" 5 | @implementation EXPDoubleTuple 6 | #pragma clang diagnostic pop 7 | 8 | @synthesize values = _values, size = _size; 9 | 10 | - (instancetype)initWithDoubleValues:(double *)values size:(size_t)size { 11 | if ((self = [super init])) { 12 | self.values = malloc(sizeof(double) * size); 13 | memcpy(self.values, values, sizeof(double) * size); 14 | self.size = size; 15 | } 16 | return self; 17 | } 18 | 19 | - (void)dealloc { 20 | free(self.values); 21 | [super dealloc]; 22 | } 23 | 24 | - (BOOL)isEqual:(id)object { 25 | if (![object isKindOfClass:[EXPDoubleTuple class]]) return NO; 26 | EXPDoubleTuple *other = (EXPDoubleTuple *)object; 27 | if (self.size == other.size) { 28 | for (int i = 0; i < self.size; ++i) { 29 | if (self.values[i] != other.values[i]) return NO; 30 | } 31 | return YES; 32 | } 33 | return NO; 34 | } 35 | 36 | - (NSString *)description { 37 | if (self.size == 2) { 38 | return [NSString stringWithFormat:@"Double tuple: {%f, %f}", self.values[0], self.values[1]]; 39 | } else if (self.size == 4) { 40 | return [NSString stringWithFormat:@"Double tuple: {%f, %f, %f, %f}", self.values[0], self.values[1], self.values[2], self.values[3]]; 41 | } 42 | return [NSString stringWithFormat:@"Double tuple of unexpected size %zd, sadly", self.size]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPExpect.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "EXPMatcher.h" 3 | #import "EXPDefines.h" 4 | 5 | @interface EXPExpect : NSObject { 6 | EXPIdBlock _actualBlock; 7 | id _testCase; 8 | int _lineNumber; 9 | char *_fileName; 10 | BOOL _negative; 11 | BOOL _asynchronous; 12 | NSTimeInterval _timeout; 13 | } 14 | 15 | @property(nonatomic, copy) EXPIdBlock actualBlock; 16 | @property(nonatomic, readonly) id actual; 17 | @property(nonatomic, assign) id testCase; 18 | @property(nonatomic) int lineNumber; 19 | @property(nonatomic) const char *fileName; 20 | @property(nonatomic) BOOL negative; 21 | @property(nonatomic) BOOL asynchronous; 22 | @property(nonatomic) NSTimeInterval timeout; 23 | 24 | @property(nonatomic, readonly) EXPExpect *to; 25 | @property(nonatomic, readonly) EXPExpect *toNot; 26 | @property(nonatomic, readonly) EXPExpect *notTo; 27 | @property(nonatomic, readonly) EXPExpect *will; 28 | @property(nonatomic, readonly) EXPExpect *willNot; 29 | @property(nonatomic, readonly) EXPExpect *(^after)(NSTimeInterval timeInterval); 30 | 31 | - (instancetype)initWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName NS_DESIGNATED_INITIALIZER; 32 | + (EXPExpect *)expectWithActualBlock:(id)actualBlock testCase:(id)testCase lineNumber:(int)lineNumber fileName:(const char *)fileName; 33 | 34 | - (void)applyMatcher:(id)matcher; 35 | - (void)applyMatcher:(id)matcher to:(NSObject **)actual; 36 | 37 | @end 38 | 39 | @interface EXPDynamicPredicateMatcher : NSObject { 40 | EXPExpect *_expectation; 41 | SEL _selector; 42 | } 43 | - (instancetype)initWithExpectation:(EXPExpect *)expectation selector:(SEL)selector NS_DESIGNATED_INITIALIZER; 44 | @property (nonatomic, readonly, copy) void (^dispatch)(void); 45 | @end 46 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPFloatTuple.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface EXPFloatTuple : NSObject { 4 | float *_values; 5 | size_t _size; 6 | } 7 | 8 | @property (nonatomic, assign) float *values; 9 | @property (nonatomic, assign) size_t size; 10 | 11 | - (instancetype)initWithFloatValues:(float *)values size:(size_t)size NS_DESIGNATED_INITIALIZER; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPFloatTuple.m: -------------------------------------------------------------------------------- 1 | #import "EXPFloatTuple.h" 2 | 3 | #pragma clang diagnostic push 4 | #pragma clang diagnostic ignored "-Wobjc-designated-initializers" 5 | @implementation EXPFloatTuple 6 | #pragma clang diagnostic pop 7 | 8 | @synthesize values = _values, size = _size; 9 | 10 | - (instancetype)initWithFloatValues:(float *)values size:(size_t)size { 11 | if ((self = [super init])) { 12 | self.values = malloc(sizeof(float) * size); 13 | memcpy(self.values, values, sizeof(float) * size); 14 | self.size = size; 15 | } 16 | return self; 17 | } 18 | 19 | - (void)dealloc { 20 | free(self.values); 21 | [super dealloc]; 22 | } 23 | 24 | - (BOOL)isEqual:(id)object { 25 | if (![object isKindOfClass:[EXPFloatTuple class]]) return NO; 26 | EXPFloatTuple *other = (EXPFloatTuple *)object; 27 | if (self.size == other.size) { 28 | for (int i = 0; i < self.size; ++i) { 29 | if (self.values[i] != other.values[i]) return NO; 30 | } 31 | return YES; 32 | } 33 | return NO; 34 | } 35 | 36 | - (NSUInteger)hash 37 | { 38 | NSUInteger prime = 31; 39 | NSUInteger hash = 0; 40 | for (int i=0; i 10 | 11 | @protocol EXPMatcher 12 | 13 | - (BOOL)matches:(id)actual; 14 | 15 | @optional 16 | - (BOOL)meetsPrerequesiteFor:(id)actual; 17 | - (NSString *)failureMessageForTo:(id)actual; 18 | - (NSString *)failureMessageForNotTo:(id)actual; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPUnsupportedObject.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface EXPUnsupportedObject : NSObject { 4 | NSString *_type; 5 | } 6 | 7 | @property (nonatomic, retain) NSString *type; 8 | 9 | - (instancetype)initWithType:(NSString *)type NS_DESIGNATED_INITIALIZER; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/EXPUnsupportedObject.m: -------------------------------------------------------------------------------- 1 | #import "EXPUnsupportedObject.h" 2 | 3 | #pragma clang diagnostic push 4 | #pragma clang diagnostic ignored "-Wobjc-designated-initializers" 5 | @implementation EXPUnsupportedObject 6 | #pragma clang diagnostic pop 7 | 8 | @synthesize type=_type; 9 | 10 | - (instancetype)initWithType:(NSString *)type { 11 | self = [super init]; 12 | if(self) { 13 | self.type = type; 14 | } 15 | return self; 16 | } 17 | 18 | - (void)dealloc { 19 | self.type = nil; 20 | [super dealloc]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Expecta.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | //! Project version number for Expecta. 4 | FOUNDATION_EXPORT double ExpectaVersionNumber; 5 | 6 | //! Project version string for Expecta. 7 | FOUNDATION_EXPORT const unsigned char ExpectaVersionString[]; 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | // Enable shorthand by default 14 | #define expect(...) EXP_expect((__VA_ARGS__)) 15 | #define failure(...) EXP_failure((__VA_ARGS__)) 16 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/ExpectaObject.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #define EXPObjectify(value) _EXPObjectify(@encode(__typeof__((value))), (value)) 4 | #define EXP_expect(actual) _EXP_expect(self, __LINE__, __FILE__, ^id{ __typeof__((actual)) strongActual = (actual); return EXPObjectify(strongActual); }) 5 | #define EXPMatcherInterface(matcherName, matcherArguments) _EXPMatcherInterface(matcherName, matcherArguments) 6 | #define EXPMatcherImplementationBegin(matcherName, matcherArguments) _EXPMatcherImplementationBegin(matcherName, matcherArguments) 7 | #define EXPMatcherImplementationEnd _EXPMatcherImplementationEnd 8 | #define EXPMatcherAliasImplementation(newMatcherName, oldMatcherName, matcherArguments) _EXPMatcherAliasImplementation(newMatcherName, oldMatcherName, matcherArguments) 9 | 10 | #define EXP_failure(message) EXPFail(self, __LINE__, __FILE__, message) 11 | 12 | 13 | @interface Expecta : NSObject 14 | 15 | + (NSTimeInterval)asynchronousTestTimeout; 16 | + (void)setAsynchronousTestTimeout:(NSTimeInterval)timeout; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/ExpectaObject.m: -------------------------------------------------------------------------------- 1 | #import "ExpectaObject.h" 2 | 3 | @implementation Expecta 4 | 5 | static NSTimeInterval _asynchronousTestTimeout = 1.0; 6 | 7 | + (NSTimeInterval)asynchronousTestTimeout { 8 | return _asynchronousTestTimeout; 9 | } 10 | 11 | + (void)setAsynchronousTestTimeout:(NSTimeInterval)timeout { 12 | _asynchronousTestTimeout = timeout; 13 | } 14 | 15 | @end -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatcherHelpers.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | BOOL EXPIsValuePointer(NSValue *value); 4 | BOOL EXPIsNumberFloat(NSNumber *number); 5 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatcherHelpers.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatcherHelpers.h" 2 | 3 | BOOL EXPIsValuePointer(NSValue *value) { 4 | return [value objCType][0] == @encode(void *)[0]; 5 | } 6 | 7 | BOOL EXPIsNumberFloat(NSNumber *number) { 8 | return strcmp([number objCType], @encode(float)) == 0; 9 | } 10 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beCloseTo.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_beCloseToWithin, (id expected, id within)); 4 | EXPMatcherInterface(beCloseToWithin, (id expected, id within)); 5 | 6 | #define beCloseTo(expected) _beCloseToWithin(EXPObjectify((expected)), nil) 7 | #define beCloseToWithin(expected, range) _beCloseToWithin(EXPObjectify((expected)), EXPObjectify((range))) 8 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beCloseTo.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beCloseTo.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(_beCloseToWithin, (id expected, id within)) { 5 | prerequisite(^BOOL{ 6 | return [actual isKindOfClass:[NSNumber class]] && 7 | [expected isKindOfClass:[NSNumber class]] && 8 | ([within isKindOfClass:[NSNumber class]] || (within == nil)); 9 | }); 10 | 11 | match(^BOOL{ 12 | double actualValue = [actual doubleValue]; 13 | double expectedValue = [expected doubleValue]; 14 | 15 | if (within != nil) { 16 | double withinValue = [within doubleValue]; 17 | double lowerBound = expectedValue - withinValue; 18 | double upperBound = expectedValue + withinValue; 19 | return (actualValue >= lowerBound) && (actualValue <= upperBound); 20 | } else { 21 | double diff = fabs(actualValue - expectedValue); 22 | actualValue = fabs(actualValue); 23 | expectedValue = fabs(expectedValue); 24 | double largest = (expectedValue > actualValue) ? expectedValue : actualValue; 25 | return (diff <= largest * FLT_EPSILON); 26 | } 27 | }); 28 | 29 | failureMessageForTo(^NSString *{ 30 | if (within) { 31 | return [NSString stringWithFormat:@"expected %@ to be close to %@ within %@", 32 | EXPDescribeObject(actual), EXPDescribeObject(expected), EXPDescribeObject(within)]; 33 | } else { 34 | return [NSString stringWithFormat:@"expected %@ to be close to %@", 35 | EXPDescribeObject(actual), EXPDescribeObject(expected)]; 36 | } 37 | }); 38 | 39 | failureMessageForNotTo(^NSString *{ 40 | if (within) { 41 | return [NSString stringWithFormat:@"expected %@ not to be close to %@ within %@", 42 | EXPDescribeObject(actual), EXPDescribeObject(expected), EXPDescribeObject(within)]; 43 | } else { 44 | return [NSString stringWithFormat:@"expected %@ not to be close to %@", 45 | EXPDescribeObject(actual), EXPDescribeObject(expected)]; 46 | } 47 | }); 48 | } 49 | EXPMatcherImplementationEnd -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beFalsy.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(beFalsy, (void)); 4 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beFalsy.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beFalsy.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(beFalsy, (void)) { 5 | match(^BOOL{ 6 | if([actual isKindOfClass:[NSNumber class]]) { 7 | return ![(NSNumber *)actual boolValue]; 8 | } else if([actual isKindOfClass:[NSValue class]]) { 9 | if(EXPIsValuePointer((NSValue *)actual)) { 10 | return ![(NSValue *)actual pointerValue]; 11 | } 12 | } 13 | return !actual; 14 | }); 15 | 16 | failureMessageForTo(^NSString *{ 17 | return [NSString stringWithFormat:@"expected: a falsy value, got: %@, which is truthy", EXPDescribeObject(actual)]; 18 | }); 19 | 20 | failureMessageForNotTo(^NSString *{ 21 | return [NSString stringWithFormat:@"expected: a non-falsy value, got: %@, which is falsy", EXPDescribeObject(actual)]; 22 | }); 23 | } 24 | EXPMatcherImplementationEnd 25 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThan.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_beGreaterThan, (id expected)); 4 | EXPMatcherInterface(beGreaterThan, (id expected)); 5 | 6 | #define beGreaterThan(expected) _beGreaterThan(EXPObjectify((expected))) 7 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThan.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beGreaterThan.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(_beGreaterThan, (id expected)) { 5 | match(^BOOL{ 6 | if ([actual respondsToSelector:@selector(compare:)]) { 7 | return [actual compare:expected] == NSOrderedDescending; 8 | } 9 | return NO; 10 | }); 11 | 12 | failureMessageForTo(^NSString *{ 13 | return [NSString stringWithFormat:@"expected: %@ to be greater than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 14 | }); 15 | 16 | failureMessageForNotTo(^NSString *{ 17 | return [NSString stringWithFormat:@"expected: %@ not to be greater than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 18 | }); 19 | } 20 | EXPMatcherImplementationEnd -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThanOrEqualTo.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_beGreaterThanOrEqualTo, (id expected)); 4 | EXPMatcherInterface(beGreaterThanOrEqualTo, (id expected)); 5 | 6 | #define beGreaterThanOrEqualTo(expected) _beGreaterThanOrEqualTo(EXPObjectify((expected))) 7 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beGreaterThanOrEqualTo.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beGreaterThanOrEqualTo.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(_beGreaterThanOrEqualTo, (id expected)) { 5 | match(^BOOL{ 6 | if ([actual respondsToSelector:@selector(compare:)]) { 7 | return [actual compare:expected] != NSOrderedAscending; 8 | } 9 | return NO; 10 | }); 11 | 12 | failureMessageForTo(^NSString *{ 13 | return [NSString stringWithFormat:@"expected: %@ to be greater than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 14 | }); 15 | 16 | failureMessageForNotTo(^NSString *{ 17 | return [NSString stringWithFormat:@"expected: %@ not to be greater than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 18 | }); 19 | } 20 | EXPMatcherImplementationEnd -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beIdenticalTo.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_beIdenticalTo, (void *expected)); 4 | EXPMatcherInterface(beIdenticalTo, (void *expected)); // to aid code completion 5 | 6 | #if __has_feature(objc_arc) 7 | #define beIdenticalTo(expected) _beIdenticalTo((__bridge void*)expected) 8 | #else 9 | #define beIdenticalTo(expected) _beIdenticalTo(expected) 10 | #endif 11 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beIdenticalTo.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+equal.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(_beIdenticalTo, (void *expected)) { 5 | match(^BOOL{ 6 | if(actual == expected) { 7 | return YES; 8 | } else if([actual isKindOfClass:[NSValue class]] && EXPIsValuePointer((NSValue *)actual)) { 9 | if([(NSValue *)actual pointerValue] == expected) { 10 | return YES; 11 | } 12 | } 13 | return NO; 14 | }); 15 | 16 | failureMessageForTo(^NSString *{ 17 | return [NSString stringWithFormat:@"expected: <%p>, got: <%p>", expected, actual]; 18 | }); 19 | 20 | failureMessageForNotTo(^NSString *{ 21 | return [NSString stringWithFormat:@"expected: not <%p>, got: <%p>", expected, actual]; 22 | }); 23 | } 24 | EXPMatcherImplementationEnd 25 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInTheRangeOf.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound)); 4 | EXPMatcherInterface(beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound)); 5 | 6 | #define beInTheRangeOf(expectedLowerBound, expectedUpperBound) _beInTheRangeOf(EXPObjectify((expectedLowerBound)), EXPObjectify((expectedUpperBound))) 7 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInTheRangeOf.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beInTheRangeOf.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(_beInTheRangeOf, (id expectedLowerBound, id expectedUpperBound)) { 5 | match(^BOOL{ 6 | if ([actual respondsToSelector:@selector(compare:)]) { 7 | NSComparisonResult compareLowerBound = [expectedLowerBound compare: actual]; 8 | NSComparisonResult compareUpperBound = [expectedUpperBound compare: actual]; 9 | if (compareLowerBound == NSOrderedSame) { 10 | return YES; 11 | } 12 | if (compareUpperBound == NSOrderedSame) { 13 | return YES; 14 | } 15 | if ((compareLowerBound == NSOrderedAscending) && (compareUpperBound == NSOrderedDescending)) { 16 | return YES; 17 | } 18 | } 19 | return NO; 20 | }); 21 | 22 | failureMessageForTo(^NSString *{ 23 | return [NSString stringWithFormat:@"expected: %@ to be in the range [%@, %@] (inclusive)", EXPDescribeObject(actual), EXPDescribeObject(expectedLowerBound), EXPDescribeObject(expectedUpperBound)]; 24 | }); 25 | 26 | failureMessageForNotTo(^NSString *{ 27 | return [NSString stringWithFormat:@"expected: %@ not to be in the range [%@, %@] (inclusive)", EXPDescribeObject(actual), EXPDescribeObject(expectedLowerBound), EXPDescribeObject(expectedUpperBound)]; 28 | }); 29 | } 30 | EXPMatcherImplementationEnd -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInstanceOf.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(beInstanceOf, (Class expected)); 4 | EXPMatcherInterface(beAnInstanceOf, (Class expected)); 5 | EXPMatcherInterface(beMemberOf, (Class expected)); 6 | EXPMatcherInterface(beAMemberOf, (Class expected)); 7 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beInstanceOf.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beInstanceOf.h" 2 | 3 | EXPMatcherImplementationBegin(beInstanceOf, (Class expected)) { 4 | BOOL actualIsNil = (actual == nil); 5 | BOOL expectedIsNil = (expected == nil); 6 | 7 | prerequisite(^BOOL{ 8 | return !(actualIsNil || expectedIsNil); 9 | }); 10 | 11 | match(^BOOL{ 12 | return [actual isMemberOfClass:expected]; 13 | }); 14 | 15 | failureMessageForTo(^NSString *{ 16 | if(actualIsNil) return @"the actual value is nil/null"; 17 | if(expectedIsNil) return @"the expected value is nil/null"; 18 | return [NSString stringWithFormat:@"expected: an instance of %@, got: an instance of %@", [expected class], [actual class]]; 19 | }); 20 | 21 | failureMessageForNotTo(^NSString *{ 22 | if(actualIsNil) return @"the actual value is nil/null"; 23 | if(expectedIsNil) return @"the expected value is nil/null"; 24 | return [NSString stringWithFormat:@"expected: not an instance of %@, got: an instance of %@", [expected class], [actual class]]; 25 | }); 26 | } 27 | EXPMatcherImplementationEnd 28 | 29 | EXPMatcherAliasImplementation(beAnInstanceOf, beInstanceOf, (Class expected)); 30 | EXPMatcherAliasImplementation(beMemberOf, beInstanceOf, (Class expected)); 31 | EXPMatcherAliasImplementation(beAMemberOf, beInstanceOf, (Class expected)); 32 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beKindOf.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(beKindOf, (Class expected)); 4 | EXPMatcherInterface(beAKindOf, (Class expected)); 5 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beKindOf.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beKindOf.h" 2 | 3 | EXPMatcherImplementationBegin(beKindOf, (Class expected)) { 4 | BOOL actualIsNil = (actual == nil); 5 | BOOL expectedIsNil = (expected == nil); 6 | 7 | prerequisite(^BOOL{ 8 | return !(actualIsNil || expectedIsNil); 9 | }); 10 | 11 | match(^BOOL{ 12 | return [actual isKindOfClass:expected]; 13 | }); 14 | 15 | failureMessageForTo(^NSString *{ 16 | if(actualIsNil) return @"the actual value is nil/null"; 17 | if(expectedIsNil) return @"the expected value is nil/null"; 18 | return [NSString stringWithFormat:@"expected: a kind of %@, got: an instance of %@, which is not a kind of %@", [expected class], [actual class], [expected class]]; 19 | }); 20 | 21 | failureMessageForNotTo(^NSString *{ 22 | if(actualIsNil) return @"the actual value is nil/null"; 23 | if(expectedIsNil) return @"the expected value is nil/null"; 24 | return [NSString stringWithFormat:@"expected: not a kind of %@, got: an instance of %@, which is a kind of %@", [expected class], [actual class], [expected class]]; 25 | }); 26 | } 27 | EXPMatcherImplementationEnd 28 | 29 | EXPMatcherAliasImplementation(beAKindOf, beKindOf, (Class expected)); 30 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThan.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_beLessThan, (id expected)); 4 | EXPMatcherInterface(beLessThan, (id expected)); 5 | 6 | #define beLessThan(expected) _beLessThan(EXPObjectify((expected))) 7 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThan.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beLessThan.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(_beLessThan, (id expected)) { 5 | match(^BOOL{ 6 | if ([actual respondsToSelector:@selector(compare:)]) { 7 | return [actual compare:expected] == NSOrderedAscending; 8 | } 9 | return NO; 10 | }); 11 | 12 | failureMessageForTo(^NSString *{ 13 | return [NSString stringWithFormat:@"expected: %@ to be less than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 14 | }); 15 | 16 | failureMessageForNotTo(^NSString *{ 17 | return [NSString stringWithFormat:@"expected: %@ not to be less than %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 18 | }); 19 | } 20 | EXPMatcherImplementationEnd -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThanOrEqualTo.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_beLessThanOrEqualTo, (id expected)); 4 | EXPMatcherInterface(beLessThanOrEqualTo, (id expected)); 5 | 6 | #define beLessThanOrEqualTo(expected) _beLessThanOrEqualTo(EXPObjectify((expected))) 7 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beLessThanOrEqualTo.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beLessThanOrEqualTo.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(_beLessThanOrEqualTo, (id expected)) { 5 | match(^BOOL{ 6 | if ([actual respondsToSelector:@selector(compare:)]) { 7 | return [actual compare:expected] != NSOrderedDescending; 8 | } 9 | return NO; 10 | }); 11 | 12 | failureMessageForTo(^NSString *{ 13 | return [NSString stringWithFormat:@"expected: %@ to be less than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 14 | }); 15 | 16 | failureMessageForNotTo(^NSString *{ 17 | return [NSString stringWithFormat:@"expected: %@ not to be less than or equal to %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 18 | }); 19 | } 20 | EXPMatcherImplementationEnd -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beNil.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(beNil, (void)); 4 | EXPMatcherInterface(beNull, (void)); 5 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beNil.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beNil.h" 2 | 3 | EXPMatcherImplementationBegin(beNil, (void)) { 4 | match(^BOOL{ 5 | return actual == nil; 6 | }); 7 | 8 | failureMessageForTo(^NSString *{ 9 | return [NSString stringWithFormat:@"expected: nil/null, got: %@", EXPDescribeObject(actual)]; 10 | }); 11 | 12 | failureMessageForNotTo(^NSString *{ 13 | return [NSString stringWithFormat:@"expected: not nil/null, got: %@", EXPDescribeObject(actual)]; 14 | }); 15 | } 16 | EXPMatcherImplementationEnd 17 | 18 | EXPMatcherAliasImplementation(beNull, beNil, (void)); 19 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSubclassOf.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(beSubclassOf, (Class expected)); 4 | EXPMatcherInterface(beASubclassOf, (Class expected)); 5 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSubclassOf.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beSubclassOf.h" 2 | #import "NSValue+Expecta.h" 3 | #import 4 | 5 | EXPMatcherImplementationBegin(beSubclassOf, (Class expected)) { 6 | __block BOOL actualIsClass = YES; 7 | 8 | prerequisite(^BOOL { 9 | actualIsClass = class_isMetaClass(object_getClass(actual)); 10 | return actualIsClass; 11 | }); 12 | 13 | match(^BOOL{ 14 | return [actual isSubclassOfClass:expected]; 15 | }); 16 | 17 | failureMessageForTo(^NSString *{ 18 | if(!actualIsClass) return @"the actual value is not a Class"; 19 | return [NSString stringWithFormat:@"expected: a subclass of %@, got: a class %@, which is not a subclass of %@", [expected class], actual, [expected class]]; 20 | }); 21 | 22 | failureMessageForNotTo(^NSString *{ 23 | if(!actualIsClass) return @"the actual value is not a Class"; 24 | return [NSString stringWithFormat:@"expected: not a subclass of %@, got: a class %@, which is a subclass of %@", [expected class], actual, [expected class]]; 25 | }); 26 | } 27 | EXPMatcherImplementationEnd 28 | 29 | EXPMatcherAliasImplementation(beASubclassOf, beSubclassOf, (Class expected)); 30 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSupersetOf.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(beSupersetOf, (id subset)); 4 | 5 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beSupersetOf.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+contain.h" 2 | 3 | EXPMatcherImplementationBegin(beSupersetOf, (id subset)) { 4 | BOOL actualIsCompatible = [actual isKindOfClass:[NSDictionary class]] || [actual respondsToSelector:@selector(containsObject:)]; 5 | BOOL subsetIsNil = (subset == nil); 6 | 7 | // For some instances the isKindOfClass: method returns false, even though 8 | // they are both actually dictionaries. e.g. Comparing a NSCFDictionary and a 9 | // NSDictionary. 10 | // Or in cases when you compare NSMutableArray (which implementation is __NSArrayM:NSMutableArray:NSArray) 11 | // and NSArray (which implementation is __NSArrayI:NSArray) 12 | BOOL bothAreIdenticalCollectionClasses = ([actual isKindOfClass:[NSDictionary class]] && [subset isKindOfClass:[NSDictionary class]]) || 13 | ([actual isKindOfClass:[NSArray class]] && [subset isKindOfClass:[NSArray class]]) || 14 | ([actual isKindOfClass:[NSSet class]] && [subset isKindOfClass:[NSSet class]]) || 15 | ([actual isKindOfClass:[NSOrderedSet class]] && [subset isKindOfClass:[NSOrderedSet class]]); 16 | 17 | BOOL classMatches = bothAreIdenticalCollectionClasses || [subset isKindOfClass:[actual class]]; 18 | 19 | prerequisite(^BOOL{ 20 | return actualIsCompatible && !subsetIsNil && classMatches; 21 | }); 22 | 23 | match(^BOOL{ 24 | if(!actualIsCompatible) return NO; 25 | 26 | if([actual isKindOfClass:[NSDictionary class]]) { 27 | for (id key in subset) { 28 | id actualValue = [actual valueForKey:key]; 29 | id subsetValue = [subset valueForKey:key]; 30 | 31 | if (![subsetValue isEqual:actualValue]) return NO; 32 | } 33 | } else { 34 | for (id object in subset) { 35 | if (![actual containsObject:object]) return NO; 36 | } 37 | } 38 | 39 | return YES; 40 | }); 41 | 42 | failureMessageForTo(^NSString *{ 43 | if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSDictionary and does not implement -containsObject:", EXPDescribeObject(actual)]; 44 | 45 | if(subsetIsNil) return @"the expected value is nil/null"; 46 | 47 | if(!classMatches) return [NSString stringWithFormat:@"%@ does not match the class of %@", EXPDescribeObject(subset), EXPDescribeObject(actual)]; 48 | 49 | return [NSString stringWithFormat:@"expected %@ to be a superset of %@", EXPDescribeObject(actual), EXPDescribeObject(subset)]; 50 | }); 51 | 52 | failureMessageForNotTo(^NSString *{ 53 | if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSDictionary and does not implement -containsObject:", EXPDescribeObject(actual)]; 54 | 55 | if(subsetIsNil) return @"the expected value is nil/null"; 56 | 57 | if(!classMatches) return [NSString stringWithFormat:@"%@ does not match the class of %@", EXPDescribeObject(subset), EXPDescribeObject(actual)]; 58 | 59 | return [NSString stringWithFormat:@"expected %@ not to be a superset of %@", EXPDescribeObject(actual), EXPDescribeObject(subset)]; 60 | }); 61 | } 62 | EXPMatcherImplementationEnd 63 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beTruthy.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(beTruthy, (void)); 4 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beTruthy.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beTruthy.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(beTruthy, (void)) { 5 | match(^BOOL{ 6 | if([actual isKindOfClass:[NSNumber class]]) { 7 | return !![(NSNumber *)actual boolValue]; 8 | } else if([actual isKindOfClass:[NSValue class]]) { 9 | if(EXPIsValuePointer((NSValue *)actual)) { 10 | return !![(NSValue *)actual pointerValue]; 11 | } 12 | } 13 | return !!actual; 14 | }); 15 | 16 | failureMessageForTo(^NSString *{ 17 | return [NSString stringWithFormat:@"expected: a truthy value, got: %@, which is falsy", EXPDescribeObject(actual)]; 18 | }); 19 | 20 | failureMessageForNotTo(^NSString *{ 21 | return [NSString stringWithFormat:@"expected: a non-truthy value, got: %@, which is truthy", EXPDescribeObject(actual)]; 22 | }); 23 | } 24 | EXPMatcherImplementationEnd 25 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beginWith.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(beginWith, (id expected)); 4 | EXPMatcherInterface(startWith, (id expected)); 5 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+beginWith.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beginWith.h" 2 | 3 | EXPMatcherImplementationBegin(beginWith, (id expected)) { 4 | BOOL actualIsNil = (actual == nil); 5 | BOOL expectedIsNil = (expected == nil); 6 | //This condition allows the comparison of an immutable string or ordered collection to the mutable type of the same 7 | BOOL actualAndExpectedAreCompatible = (([actual isKindOfClass:[NSString class]] && [expected isKindOfClass:[NSString class]]) 8 | || ([actual isKindOfClass:[NSArray class]] && [expected isKindOfClass:[NSArray class]]) 9 | || ([actual isKindOfClass:[NSOrderedSet class]] && [expected isKindOfClass:[NSOrderedSet class]])); 10 | 11 | prerequisite(^BOOL { 12 | return actualAndExpectedAreCompatible; 13 | }); 14 | 15 | match(^BOOL { 16 | if ([actual isKindOfClass:[NSString class]]) { 17 | return [actual hasPrefix:expected]; 18 | } else if ([actual isKindOfClass:[NSArray class]]) { 19 | if ([expected count] > [actual count] || [expected count] == 0) { 20 | return NO; 21 | } 22 | NSArray *subArray = [actual subarrayWithRange:NSMakeRange(0, [expected count])]; 23 | return [subArray isEqualToArray:expected]; 24 | } else { 25 | if ([expected count] > [actual count] || [expected count] == 0) { 26 | return NO; 27 | } 28 | 29 | NSOrderedSet *subset = [NSOrderedSet orderedSetWithOrderedSet:actual range:NSMakeRange(0, [expected count]) copyItems:NO]; 30 | return [subset isEqualToOrderedSet:expected]; 31 | } 32 | }); 33 | 34 | failureMessageForTo(^NSString *{ 35 | if (actualIsNil) return @"the object is nil/null"; 36 | if (expectedIsNil) return @"the expected value is nil/null"; 37 | if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]]; 38 | return [NSString stringWithFormat:@"expected: %@ to begin with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 39 | }); 40 | 41 | failureMessageForNotTo(^NSString *{ 42 | if (actualIsNil) return @"the object is nil/null"; 43 | if (expectedIsNil) return @"the expected value is nil/null"; 44 | if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]]; 45 | 46 | return [NSString stringWithFormat:@"expected: %@ not to begin with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 47 | }); 48 | } 49 | EXPMatcherImplementationEnd 50 | 51 | EXPMatcherAliasImplementation(startWith, beginWith, (id expected)); 52 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+conformTo.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(conformTo, (Protocol *expected)); 4 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+conformTo.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+conformTo.h" 2 | #import "NSValue+Expecta.h" 3 | #import 4 | 5 | EXPMatcherImplementationBegin(conformTo, (Protocol *expected)) { 6 | BOOL actualIsNil = (actual == nil); 7 | BOOL expectedIsNil = (expected == nil); 8 | 9 | prerequisite(^BOOL{ 10 | return !(actualIsNil || expectedIsNil); 11 | }); 12 | 13 | match(^BOOL{ 14 | return [actual conformsToProtocol:expected]; 15 | }); 16 | 17 | failureMessageForTo(^NSString *{ 18 | if(actualIsNil) return @"the object is nil/null"; 19 | if(expectedIsNil) return @"the protocol is nil/null"; 20 | 21 | NSString *name = NSStringFromProtocol(expected); 22 | return [NSString stringWithFormat:@"expected: %@ to conform to %@", actual, name]; 23 | }); 24 | 25 | failureMessageForNotTo(^NSString *{ 26 | if(actualIsNil) return @"the object is nil/null"; 27 | if(expectedIsNil) return @"the protocol is nil/null"; 28 | 29 | NSString *name = NSStringFromProtocol(expected); 30 | return [NSString stringWithFormat:@"expected: %@ not to conform to %@", actual, name]; 31 | }); 32 | } 33 | EXPMatcherImplementationEnd 34 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+contain.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_contain, (id expected)); 4 | EXPMatcherInterface(contain, (id expected)); // to aid code completion 5 | #define contain(expected) _contain(EXPObjectify((expected))) 6 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+contain.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+contain.h" 2 | 3 | EXPMatcherImplementationBegin(_contain, (id expected)) { 4 | BOOL actualIsCompatible = [actual isKindOfClass:[NSString class]] || [actual conformsToProtocol:@protocol(NSFastEnumeration)]; 5 | BOOL expectedIsNil = (expected == nil); 6 | 7 | prerequisite(^BOOL{ 8 | return actualIsCompatible && !expectedIsNil; 9 | }); 10 | 11 | match(^BOOL{ 12 | if(actualIsCompatible) { 13 | if([actual isKindOfClass:[NSString class]]) { 14 | return [(NSString *)actual rangeOfString:[expected description]].location != NSNotFound; 15 | } else { 16 | for (id object in actual) { 17 | if ([object isEqual:expected]) { 18 | return YES; 19 | } 20 | } 21 | } 22 | } 23 | return NO; 24 | }); 25 | 26 | failureMessageForTo(^NSString *{ 27 | if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString or NSFastEnumeration", EXPDescribeObject(actual)]; 28 | if(expectedIsNil) return @"the expected value is nil/null"; 29 | return [NSString stringWithFormat:@"expected %@ to contain %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 30 | }); 31 | 32 | failureMessageForNotTo(^NSString *{ 33 | if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString or NSFastEnumeration", EXPDescribeObject(actual)]; 34 | if(expectedIsNil) return @"the expected value is nil/null"; 35 | return [NSString stringWithFormat:@"expected %@ not to contain %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 36 | }); 37 | } 38 | EXPMatcherImplementationEnd 39 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+endWith.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(endWith, (id expected)); 4 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+endWith.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+endWith.h" 2 | 3 | EXPMatcherImplementationBegin(endWith, (id expected)) { 4 | BOOL actualIsNil = (actual == nil); 5 | BOOL expectedIsNil = (expected == nil); 6 | //This condition allows the comparison of an immutable string or ordered collection to the mutable type of the same 7 | BOOL actualAndExpectedAreCompatible = (([actual isKindOfClass:[NSString class]] && [expected isKindOfClass:[NSString class]]) 8 | || ([actual isKindOfClass:[NSArray class]] && [expected isKindOfClass:[NSArray class]]) 9 | || ([actual isKindOfClass:[NSOrderedSet class]] && [expected isKindOfClass:[NSOrderedSet class]])); 10 | 11 | prerequisite(^BOOL { 12 | return actualAndExpectedAreCompatible; 13 | }); 14 | 15 | match(^BOOL { 16 | if ([actual isKindOfClass:[NSString class]]) { 17 | return [actual hasSuffix:expected]; 18 | } else if ([actual isKindOfClass:[NSArray class]]) { 19 | if ([expected count] > [actual count] || [expected count] == 0) { 20 | return NO; 21 | } 22 | NSArray *subArray = [actual subarrayWithRange:NSMakeRange([actual count] - [expected count], [expected count])]; 23 | return [subArray isEqualToArray:expected]; 24 | } else { 25 | if ([expected count] > [actual count] || [expected count] == 0) { 26 | return NO; 27 | } 28 | 29 | NSOrderedSet *subset = [NSOrderedSet orderedSetWithOrderedSet:actual range:NSMakeRange([actual count] - [expected count], [expected count]) copyItems:NO]; 30 | return [subset isEqualToOrderedSet:expected]; 31 | } 32 | }); 33 | 34 | failureMessageForTo(^NSString *{ 35 | if (actualIsNil) return @"the object is nil/null"; 36 | if (expectedIsNil) return @"the expected value is nil/null"; 37 | if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]]; 38 | return [NSString stringWithFormat:@"expected: %@ to end with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 39 | }); 40 | 41 | failureMessageForNotTo(^NSString *{ 42 | if (actualIsNil) return @"the object is nil/null"; 43 | if (expectedIsNil) return @"the expected value is nil/null"; 44 | if (!actualAndExpectedAreCompatible) return [NSString stringWithFormat:@"%@ and %@ are not instances of one of %@, %@, or %@", EXPDescribeObject(actual), EXPDescribeObject(expected), [NSString class], [NSArray class], [NSOrderedSet class]]; 45 | 46 | return [NSString stringWithFormat:@"expected: %@ not to end with %@", EXPDescribeObject(actual), EXPDescribeObject(expected)]; 47 | }); 48 | } 49 | EXPMatcherImplementationEnd 50 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+equal.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(_equal, (id expected)); 4 | EXPMatcherInterface(equal, (id expected)); // to aid code completion 5 | #define equal(...) _equal(EXPObjectify((__VA_ARGS__))) 6 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+equal.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+equal.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(_equal, (id expected)) { 5 | match(^BOOL{ 6 | if((actual == expected) || [actual isEqual:expected]) { 7 | return YES; 8 | } else if([actual isKindOfClass:[NSNumber class]] && [expected isKindOfClass:[NSNumber class]]) { 9 | if([actual isKindOfClass:[NSDecimalNumber class]] || [expected isKindOfClass:[NSDecimalNumber class]]) { 10 | NSDecimalNumber *actualDecimalNumber = [NSDecimalNumber decimalNumberWithDecimal:[(NSNumber *) actual decimalValue]]; 11 | NSDecimalNumber *expectedDecimalNumber = [NSDecimalNumber decimalNumberWithDecimal:[(NSNumber *) expected decimalValue]]; 12 | return [actualDecimalNumber isEqualToNumber:expectedDecimalNumber]; 13 | } 14 | else { 15 | if(EXPIsNumberFloat((NSNumber *)actual) || EXPIsNumberFloat((NSNumber *)expected)) { 16 | return [(NSNumber *)actual floatValue] == [(NSNumber *)expected floatValue]; 17 | } 18 | } 19 | } 20 | return NO; 21 | }); 22 | 23 | failureMessageForTo(^NSString *{ 24 | NSString *expectedDescription = EXPDescribeObject(expected); 25 | NSString *actualDescription = EXPDescribeObject(actual); 26 | 27 | if (![expectedDescription isEqualToString:actualDescription]) { 28 | return [NSString stringWithFormat:@"expected: %@, got: %@", EXPDescribeObject(expected), EXPDescribeObject(actual)]; 29 | } else { 30 | return [NSString stringWithFormat:@"expected (%@): %@, got (%@): %@", NSStringFromClass([expected class]), EXPDescribeObject(expected), NSStringFromClass([actual class]), EXPDescribeObject(actual)]; 31 | } 32 | }); 33 | 34 | failureMessageForNotTo(^NSString *{ 35 | return [NSString stringWithFormat:@"expected: not %@, got: %@", EXPDescribeObject(expected), EXPDescribeObject(actual)]; 36 | }); 37 | } 38 | EXPMatcherImplementationEnd 39 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+haveCountOf.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(haveCountOf, (NSUInteger expected)); 4 | EXPMatcherInterface(haveCount, (NSUInteger expected)); 5 | EXPMatcherInterface(haveACountOf, (NSUInteger expected)); 6 | EXPMatcherInterface(haveLength, (NSUInteger expected)); 7 | EXPMatcherInterface(haveLengthOf, (NSUInteger expected)); 8 | EXPMatcherInterface(haveALengthOf, (NSUInteger expected)); 9 | 10 | #define beEmpty() haveCountOf(0) 11 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+haveCountOf.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+haveCountOf.h" 2 | 3 | EXPMatcherImplementationBegin(haveCountOf, (NSUInteger expected)) { 4 | BOOL actualIsStringy = [actual isKindOfClass:[NSString class]] || [actual isKindOfClass:[NSAttributedString class]]; 5 | BOOL actualIsCompatible = actualIsStringy || [actual respondsToSelector:@selector(count)]; 6 | 7 | prerequisite(^BOOL{ 8 | return actualIsCompatible; 9 | }); 10 | 11 | NSUInteger (^count)(id) = ^(id actual) { 12 | if(actualIsStringy) { 13 | return [actual length]; 14 | } else { 15 | return [actual count]; 16 | } 17 | }; 18 | 19 | match(^BOOL{ 20 | if(actualIsCompatible) { 21 | return count(actual) == expected; 22 | } 23 | return NO; 24 | }); 25 | 26 | failureMessageForTo(^NSString *{ 27 | if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString, NSAttributedString, NSArray, NSSet, NSOrderedSet, or NSDictionary", EXPDescribeObject(actual)]; 28 | return [NSString stringWithFormat:@"expected %@ to have a count of %zi but got %zi", EXPDescribeObject(actual), expected, count(actual)]; 29 | }); 30 | 31 | failureMessageForNotTo(^NSString *{ 32 | if(!actualIsCompatible) return [NSString stringWithFormat:@"%@ is not an instance of NSString, NSAttributedString, NSArray, NSSet, NSOrderedSet, or NSDictionary", EXPDescribeObject(actual)]; 33 | return [NSString stringWithFormat:@"expected %@ not to have a count of %zi", EXPDescribeObject(actual), expected]; 34 | }); 35 | } 36 | EXPMatcherImplementationEnd 37 | 38 | EXPMatcherAliasImplementation(haveCount, haveCountOf, (NSUInteger expected)); 39 | EXPMatcherAliasImplementation(haveACountOf, haveCountOf, (NSUInteger expected)); 40 | EXPMatcherAliasImplementation(haveLength, haveCountOf, (NSUInteger expected)); 41 | EXPMatcherAliasImplementation(haveLengthOf, haveCountOf, (NSUInteger expected)); 42 | EXPMatcherAliasImplementation(haveALengthOf, haveCountOf, (NSUInteger expected)); 43 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+match.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(match, (NSString *expected)); 4 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+match.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+match.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(match, (NSString *expected)) { 5 | BOOL actualIsNil = (actual == nil); 6 | BOOL expectedIsNil = (expected == nil); 7 | 8 | __block NSRegularExpression *regex = nil; 9 | __block NSError *regexError = nil; 10 | 11 | prerequisite (^BOOL { 12 | BOOL nilInput = (actualIsNil || expectedIsNil); 13 | if (!nilInput) { 14 | regex = [NSRegularExpression regularExpressionWithPattern:expected options:0 error:®exError]; 15 | } 16 | return !nilInput && regex; 17 | }); 18 | 19 | match(^BOOL { 20 | NSRange range = [regex rangeOfFirstMatchInString:actual options:0 range:NSMakeRange(0, [actual length])]; 21 | return !NSEqualRanges(range, NSMakeRange(NSNotFound, 0)); 22 | }); 23 | 24 | failureMessageForTo(^NSString *{ 25 | if (actualIsNil) return @"the object is nil/null"; 26 | if (expectedIsNil) return @"the expression is nil/null"; 27 | if (regexError) return [NSString stringWithFormat:@"unable to create regular expression from given parameter: %@", [regexError localizedDescription]]; 28 | return [NSString stringWithFormat:@"expected: %@ to match to %@", EXPDescribeObject(actual), expected]; 29 | }); 30 | 31 | failureMessageForNotTo(^NSString *{ 32 | if (actualIsNil) return @"the object is nil/null"; 33 | if (expectedIsNil) return @"the expression is nil/null"; 34 | if (regexError) return [NSString stringWithFormat:@"unable to create regular expression from given parameter: %@", [regexError localizedDescription]]; 35 | return [NSString stringWithFormat:@"expected: %@ not to match to %@", EXPDescribeObject(actual), expected]; 36 | }); 37 | } 38 | EXPMatcherImplementationEnd 39 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+postNotification.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(postNotification, (id expectedNotification)); 4 | EXPMatcherInterface(notify, (id expectedNotification)); 5 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raise.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(raise, (NSString *expectedExceptionName)); 4 | #define raiseAny() raise(nil) 5 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raise.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+raise.h" 2 | #import "EXPDefines.h" 3 | 4 | EXPMatcherImplementationBegin(raise, (NSString *expectedExceptionName)) { 5 | __block NSException *exceptionCaught = nil; 6 | 7 | match(^BOOL{ 8 | BOOL expectedExceptionCaught = NO; 9 | @try { 10 | ((EXPBasicBlock)actual)(); 11 | } @catch(NSException *e) { 12 | exceptionCaught = e; 13 | expectedExceptionCaught = (expectedExceptionName == nil) || [[exceptionCaught name] isEqualToString:expectedExceptionName]; 14 | } 15 | return expectedExceptionCaught; 16 | }); 17 | 18 | failureMessageForTo(^NSString *{ 19 | return [NSString stringWithFormat:@"expected: %@, got: %@", 20 | expectedExceptionName ? expectedExceptionName : @"any exception", 21 | exceptionCaught ? [exceptionCaught name] : @"no exception"]; 22 | }); 23 | 24 | failureMessageForNotTo(^NSString *{ 25 | return [NSString stringWithFormat:@"expected: %@, got: %@", 26 | expectedExceptionName ? [NSString stringWithFormat:@"not %@", expectedExceptionName] : @"no exception", 27 | exceptionCaught ? [exceptionCaught name] : @"no exception"]; 28 | }); 29 | } 30 | EXPMatcherImplementationEnd 31 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raiseWithReason.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(raiseWithReason, (NSString *expectedExceptionName, NSString *expectedReason)); 4 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+raiseWithReason.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+raiseWithReason.h" 2 | #import "EXPDefines.h" 3 | 4 | EXPMatcherImplementationBegin(raiseWithReason, (NSString *expectedExceptionName, NSString *expectedReason)) { 5 | __block NSException *exceptionCaught = nil; 6 | 7 | match(^BOOL{ 8 | BOOL expectedExceptionCaught = NO; 9 | @try { 10 | ((EXPBasicBlock)actual)(); 11 | } @catch(NSException *e) { 12 | exceptionCaught = e; 13 | expectedExceptionCaught = (((expectedExceptionName == nil) || [[exceptionCaught name] isEqualToString:expectedExceptionName]) && 14 | ((expectedReason == nil) || ([[exceptionCaught reason] isEqualToString:expectedReason]))); 15 | } 16 | return expectedExceptionCaught; 17 | }); 18 | 19 | failureMessageForTo(^NSString *{ 20 | return [NSString stringWithFormat:@"expected: %@ (%@), got: %@ (%@)", 21 | expectedExceptionName ?: @"any exception", 22 | expectedReason ?: @"any reason", 23 | exceptionCaught ? [exceptionCaught name] : @"no exception", 24 | exceptionCaught ? [exceptionCaught reason] : @""]; 25 | }); 26 | 27 | failureMessageForNotTo(^NSString *{ 28 | return [NSString stringWithFormat:@"expected: %@ (%@), got: %@ (%@)", 29 | expectedExceptionName ? [NSString stringWithFormat:@"not %@", expectedExceptionName] : @"no exception", 30 | expectedReason ? [NSString stringWithFormat:@"not '%@'", expectedReason] : @"no reason", 31 | exceptionCaught ? [exceptionCaught name] : @"no exception", 32 | exceptionCaught ? [exceptionCaught reason] : @"no reason"]; 33 | }); 34 | } 35 | EXPMatcherImplementationEnd 36 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+respondTo.h: -------------------------------------------------------------------------------- 1 | #import "Expecta.h" 2 | 3 | EXPMatcherInterface(respondTo, (SEL expected)); 4 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers+respondTo.m: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+respondTo.h" 2 | #import "EXPMatcherHelpers.h" 3 | 4 | EXPMatcherImplementationBegin(respondTo, (SEL expected)) { 5 | BOOL actualIsNil = (actual == nil); 6 | BOOL expectedIsNull = (expected == NULL); 7 | 8 | prerequisite (^BOOL { 9 | return !(actualIsNil || expectedIsNull); 10 | }); 11 | 12 | match(^BOOL { 13 | if ([actual respondsToSelector:@selector(instancesRespondToSelector:)] && 14 | [actual instancesRespondToSelector:expected]) { 15 | return YES; 16 | } 17 | return [actual respondsToSelector:expected]; 18 | }); 19 | 20 | failureMessageForTo(^NSString *{ 21 | if (actualIsNil) return @"the object is nil/null"; 22 | if (expectedIsNull) return @"the selector is null"; 23 | return [NSString stringWithFormat:@"expected: %@ to respond to %@", EXPDescribeObject(actual), NSStringFromSelector(expected)]; 24 | }); 25 | 26 | failureMessageForNotTo(^NSString *{ 27 | if (actualIsNil) return @"the object is nil/null"; 28 | if (expectedIsNull) return @"the selector is null"; 29 | return [NSString stringWithFormat:@"expected: %@ not to respond to %@", EXPDescribeObject(actual), NSStringFromSelector(expected)]; 30 | }); 31 | } 32 | EXPMatcherImplementationEnd 33 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/Matchers/EXPMatchers.h: -------------------------------------------------------------------------------- 1 | #import "EXPMatchers+beNil.h" 2 | #import "EXPMatchers+equal.h" 3 | #import "EXPMatchers+beInstanceOf.h" 4 | #import "EXPMatchers+beKindOf.h" 5 | #import "EXPMatchers+beSubclassOf.h" 6 | #import "EXPMatchers+conformTo.h" 7 | #import "EXPMatchers+beTruthy.h" 8 | #import "EXPMatchers+beFalsy.h" 9 | #import "EXPMatchers+contain.h" 10 | #import "EXPMatchers+beSupersetOf.h" 11 | #import "EXPMatchers+haveCountOf.h" 12 | #import "EXPMatchers+beIdenticalTo.h" 13 | #import "EXPMatchers+beGreaterThan.h" 14 | #import "EXPMatchers+beGreaterThanOrEqualTo.h" 15 | #import "EXPMatchers+beLessThan.h" 16 | #import "EXPMatchers+beLessThanOrEqualTo.h" 17 | #import "EXPMatchers+beInTheRangeOf.h" 18 | #import "EXPMatchers+beCloseTo.h" 19 | #import "EXPMatchers+raise.h" 20 | #import "EXPMatchers+raiseWithReason.h" 21 | #import "EXPMatchers+respondTo.h" 22 | #import "EXPMatchers+postNotification.h" 23 | #import "EXPMatchers+beginWith.h" 24 | #import "EXPMatchers+endWith.h" 25 | #import "EXPMatchers+match.h" 26 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/NSObject+Expecta.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NSObject (Expecta) 4 | 5 | - (void)recordFailureWithDescription:(NSString *)description 6 | inFile:(NSString *)filename 7 | atLine:(NSUInteger)lineNumber 8 | expected:(BOOL)expected; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/NSValue+Expecta.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NSValue (Expecta) 4 | 5 | @property (nonatomic) const char *_EXP_objCType; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/Expecta/NSValue+Expecta.m: -------------------------------------------------------------------------------- 1 | #import "NSValue+Expecta.h" 2 | #import 3 | #import "Expecta.h" 4 | 5 | EXPFixCategoriesBug(NSValue_Expecta); 6 | 7 | @implementation NSValue (Expecta) 8 | 9 | static char _EXP_typeKey; 10 | 11 | - (const char *)_EXP_objCType { 12 | return [(NSString *)objc_getAssociatedObject(self, &_EXP_typeKey) cStringUsingEncoding:NSASCIIStringEncoding]; 13 | } 14 | 15 | - (void)set_EXP_objCType:(const char *)_EXP_objCType { 16 | objc_setAssociatedObject(self, &_EXP_typeKey, 17 | @(_EXP_objCType), 18 | OBJC_ASSOCIATION_COPY_NONATOMIC); 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Example/Pods/Expecta/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2015 Specta Team - https://github.com/specta 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 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/PINFuture.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PINFuture", 3 | "version": "5.2.1", 4 | "summary": "An Objective C implementation of the Future pattern that aims to provide maximal type safety.", 5 | "description": "An Objective C implementation of the Future async value pattern that aims to provide maximal type safety.", 6 | "homepage": "https://github.com/pinterest/PINFuture", 7 | "license": { 8 | "type": "Apache License, Version 2.0", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "Chris Danford": "chrisdanford@gmail.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/pinterest/PINFuture.git", 16 | "tag": "5.2.1" 17 | }, 18 | "social_media_url": "https://twitter.com/PinterestEng", 19 | "platforms": { 20 | "ios": "8.0" 21 | }, 22 | "source_files": "PINFuture/Classes/**/*", 23 | "weak_frameworks": "Photos", 24 | "frameworks": "Foundation" 25 | } 26 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Expecta (1.0.6) 3 | - PINFuture (5.2.1) 4 | - Specta (1.0.7) 5 | 6 | DEPENDENCIES: 7 | - Expecta 8 | - PINFuture (from `../`) 9 | - Specta 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - Expecta 14 | - Specta 15 | 16 | EXTERNAL SOURCES: 17 | PINFuture: 18 | :path: "../" 19 | 20 | SPEC CHECKSUMS: 21 | Expecta: 3b6bd90a64b9a1dcb0b70aa0e10a7f8f631667d5 22 | PINFuture: a3de8180f634e6af833bf8753e7829e4e69e8f9a 23 | Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66 24 | 25 | PODFILE CHECKSUM: 699d06a490a2ae8d8e6dcf963cf46656dad30462 26 | 27 | COCOAPODS: 1.9.1 28 | -------------------------------------------------------------------------------- /Example/Pods/Specta/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014 Specta Team. 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 | 21 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTCallSite.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SPTCallSite : NSObject 4 | 5 | @property (nonatomic, copy, readonly) NSString *file; 6 | @property (nonatomic, readonly) NSUInteger line; 7 | 8 | + (instancetype)callSiteWithFile:(NSString *)file line:(NSUInteger)line; 9 | 10 | - (instancetype)initWithFile:(NSString *)file line:(NSUInteger)line; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTCallSite.m: -------------------------------------------------------------------------------- 1 | #import "SPTCallSite.h" 2 | 3 | @implementation SPTCallSite 4 | 5 | + (instancetype)callSiteWithFile:(NSString *)file line:(NSUInteger)line { 6 | return [[self alloc] initWithFile:file line:line]; 7 | } 8 | 9 | - (instancetype)initWithFile:(NSString *)file line:(NSUInteger)line { 10 | self = [super init]; 11 | if (self) { 12 | _file = file; 13 | _line = line; 14 | } 15 | return self; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTCompiledExample.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SpectaTypes.h" 3 | 4 | @interface SPTCompiledExample : NSObject 5 | 6 | @property (nonatomic, copy) NSString *name; 7 | @property (nonatomic, copy) NSString *testCaseName; 8 | @property (nonatomic, copy) SPTSpecBlock block; 9 | 10 | @property (nonatomic) BOOL pending; 11 | @property (nonatomic, getter=isFocused) BOOL focused; 12 | 13 | @property (nonatomic) SEL testMethodSelector; 14 | 15 | - (id)initWithName:(NSString *)name testCaseName:(NSString *)testCaseName block:(SPTSpecBlock)block pending:(BOOL)pending focused:(BOOL)focused; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTCompiledExample.m: -------------------------------------------------------------------------------- 1 | #import "SPTCompiledExample.h" 2 | 3 | @implementation SPTCompiledExample 4 | 5 | - (id)initWithName:(NSString *)name testCaseName:(NSString *)testCaseName block:(SPTSpecBlock)block pending:(BOOL)pending focused:(BOOL)focused { 6 | self = [super init]; 7 | if (self) { 8 | self.name = name; 9 | self.testCaseName = testCaseName; 10 | self.block = block; 11 | self.pending = pending; 12 | self.focused = focused; 13 | } 14 | return self; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTExample.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SpectaTypes.h" 3 | 4 | @class SPTCallSite; 5 | 6 | @interface SPTExample : NSObject 7 | 8 | @property (nonatomic, copy) NSString *name; 9 | @property (nonatomic, retain) SPTCallSite *callSite; 10 | @property (nonatomic, copy) SPTVoidBlock block; 11 | 12 | @property (nonatomic) BOOL pending; 13 | @property (nonatomic, getter=isFocused) BOOL focused; 14 | 15 | - (id)initWithName:(NSString *)name callSite:(SPTCallSite *)callSite focused:(BOOL)focused block:(SPTVoidBlock)block; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTExample.m: -------------------------------------------------------------------------------- 1 | #import "SPTExample.h" 2 | 3 | @implementation SPTExample 4 | 5 | - (id)initWithName:(NSString *)name callSite:(SPTCallSite *)callSite focused:(BOOL)focused block:(SPTVoidBlock)block { 6 | self = [super init]; 7 | if (self) { 8 | self.name = name; 9 | self.callSite = callSite; 10 | self.block = block; 11 | self.focused = focused; 12 | self.pending = block == nil; 13 | } 14 | return self; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTExampleGroup.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "SpectaTypes.h" 4 | 5 | @class SPTExample; 6 | @class SPTCallSite; 7 | 8 | @interface SPTExampleGroup : NSObject 9 | 10 | @property (nonatomic, copy) NSString *name; 11 | @property (nonatomic, weak) SPTExampleGroup *root; 12 | @property (nonatomic, weak) SPTExampleGroup *parent; 13 | @property (nonatomic, strong) NSMutableArray *children; 14 | @property (nonatomic, strong) NSMutableArray *beforeAllArray; 15 | @property (nonatomic, strong) NSMutableArray *afterAllArray; 16 | @property (nonatomic, strong) NSMutableArray *beforeEachArray; 17 | @property (nonatomic, strong) NSMutableArray *afterEachArray; 18 | @property (nonatomic, strong) NSMutableDictionary *sharedExamples; 19 | @property (nonatomic) unsigned int exampleCount; 20 | @property (nonatomic) unsigned int ranExampleCount; 21 | @property (nonatomic) unsigned int pendingExampleCount; 22 | @property (nonatomic, getter=isFocused) BOOL focused; 23 | 24 | - (id)initWithName:(NSString *)name parent:(SPTExampleGroup *)parent root:(SPTExampleGroup *)root; 25 | 26 | - (SPTExampleGroup *)addExampleGroupWithName:(NSString *)name focused:(BOOL)focused; 27 | - (SPTExample *)addExampleWithName:(NSString *)name callSite:(SPTCallSite *)callSite focused:(BOOL)focused block:(SPTVoidBlock)block; 28 | 29 | - (void)addBeforeAllBlock:(SPTVoidBlock)block; 30 | - (void)addAfterAllBlock:(SPTVoidBlock)block; 31 | - (void)addBeforeEachBlock:(SPTVoidBlock)block; 32 | - (void)addAfterEachBlock:(SPTVoidBlock)block; 33 | 34 | - (NSArray *)compileExamplesWithStack:(NSArray *)stack; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTExcludeGlobalBeforeAfterEach.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Specta Team. All rights reserved. 3 | */ 4 | #import 5 | 6 | // This protocol was used for blacklisting classes for global beforeEach and afterEach blocks. 7 | // Now, instead, classes are whitelisted by implementing the SPTGlobalBeforeAfterEach protocol. 8 | __deprecated_msg("Please whitelist classes instead with the SPTGlobalBeforeAfterEach protocol") 9 | @protocol SPTExcludeGlobalBeforeAfterEach 10 | @end 11 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTGlobalBeforeAfterEach.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Specta Team. All rights reserved. 3 | */ 4 | #import 5 | 6 | // This protocol is used for whitelisting classes for global beforeEach and afterEach blocks. 7 | // If you want a class to participate in those just add this protocol to a category and it will be 8 | // included. 9 | @protocol SPTGlobalBeforeAfterEach 10 | 11 | @optional 12 | + (void)beforeEach; 13 | + (void)afterEach; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTSharedExampleGroups.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @class _XCTestCaseImplementation; 6 | 7 | @class SPTExampleGroup; 8 | 9 | @interface SPTSharedExampleGroups : XCTestCase 10 | 11 | + (void)addSharedExampleGroupWithName:(NSString *)name block:(SPTDictionaryBlock)block exampleGroup:(SPTExampleGroup *)exampleGroup; 12 | + (SPTDictionaryBlock)sharedExampleGroupWithName:(NSString *)name exampleGroup:(SPTExampleGroup *)exampleGroup; 13 | 14 | - (void)sharedExampleGroups; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTSharedExampleGroups.m: -------------------------------------------------------------------------------- 1 | #import "SPTSharedExampleGroups.h" 2 | #import "SPTExampleGroup.h" 3 | #import "SPTSpec.h" 4 | #import "SpectaUtility.h" 5 | #import 6 | 7 | NSMutableDictionary *globalSharedExampleGroups = nil; 8 | BOOL initialized = NO; 9 | 10 | @implementation SPTSharedExampleGroups 11 | 12 | + (void)initialize { 13 | Class SPTSharedExampleGroupsClass = [SPTSharedExampleGroups class]; 14 | if ([self class] == SPTSharedExampleGroupsClass) { 15 | if (!initialized) { 16 | initialized = YES; 17 | globalSharedExampleGroups = [[NSMutableDictionary alloc] init]; 18 | 19 | Class *classes = NULL; 20 | int numClasses = objc_getClassList(NULL, 0); 21 | 22 | if (numClasses > 0) { 23 | classes = (Class *)malloc(sizeof(Class) * numClasses); 24 | numClasses = objc_getClassList(classes, numClasses); 25 | 26 | Class klass, superClass; 27 | for(uint i = 0; i < numClasses; i++) { 28 | klass = classes[i]; 29 | superClass = class_getSuperclass(klass); 30 | if (superClass == SPTSharedExampleGroupsClass) { 31 | [[[klass alloc] init] sharedExampleGroups]; 32 | } 33 | } 34 | 35 | free(classes); 36 | } 37 | } 38 | } 39 | } 40 | 41 | + (void)addSharedExampleGroupWithName:(NSString *)name block:(SPTDictionaryBlock)block exampleGroup:(SPTExampleGroup *)exampleGroup { 42 | (exampleGroup == nil ? globalSharedExampleGroups : exampleGroup.sharedExamples)[name] = [block copy]; 43 | } 44 | 45 | + (SPTDictionaryBlock)sharedExampleGroupWithName:(NSString *)name exampleGroup:(SPTExampleGroup *)exampleGroup { 46 | SPTDictionaryBlock sharedExampleGroup = nil; 47 | while (exampleGroup != nil) { 48 | if ((sharedExampleGroup = exampleGroup.sharedExamples[name])) { 49 | return sharedExampleGroup; 50 | } 51 | exampleGroup = exampleGroup.parent; 52 | } 53 | return globalSharedExampleGroups[name]; 54 | } 55 | 56 | - (void)sharedExampleGroups {} 57 | 58 | - (void)spt_handleException:(NSException *)exception { 59 | [SPTCurrentSpec spt_handleException:exception]; 60 | } 61 | 62 | - (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *)filename atLine:(NSUInteger)lineNumber expected:(BOOL)expected { 63 | [SPTCurrentSpec recordFailureWithDescription:description inFile:filename atLine:lineNumber expected:expected]; 64 | } 65 | 66 | - (void)_recordUnexpectedFailureWithDescription:(NSString *)description exception:(NSException *)exception { 67 | [SPTCurrentSpec _recordUnexpectedFailureWithDescription:description exception:exception]; 68 | } 69 | 70 | - (_XCTestCaseImplementation *)internalImplementation { 71 | return [SPTCurrentSpec internalImplementation]; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTSpec.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @class 5 | SPTTestSuite 6 | , SPTCompiledExample 7 | ; 8 | 9 | @interface SPTSpec : XCTestCase 10 | 11 | @property (strong) XCTestCaseRun *spt_run; 12 | @property (nonatomic) BOOL spt_pending; 13 | @property (nonatomic) BOOL spt_skipped; 14 | 15 | + (BOOL)spt_isDisabled; 16 | + (void)spt_setDisabled:(BOOL)disabled; 17 | + (BOOL)spt_focusedExamplesExist; 18 | + (SEL)spt_convertToTestMethod:(SPTCompiledExample *)example; 19 | + (SPTTestSuite *)spt_testSuite; 20 | + (void)spt_setCurrentTestSuite; 21 | + (void)spt_unsetCurrentTestSuite; 22 | + (void)spt_setCurrentTestSuiteFileName:(NSString *)fileName lineNumber:(NSUInteger)lineNumber; 23 | 24 | - (void)spec; 25 | - (BOOL)spt_shouldRunExample:(SPTCompiledExample *)example; 26 | - (void)spt_runExample:(SPTCompiledExample *)example; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTTestSuite.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class 4 | SPTExample 5 | , SPTExampleGroup 6 | ; 7 | 8 | @interface SPTTestSuite : NSObject 9 | 10 | @property (nonatomic, strong) SPTExampleGroup *rootGroup; 11 | @property (nonatomic, strong) NSMutableArray *groupStack; 12 | @property (nonatomic, strong) NSArray *compiledExamples; 13 | @property (nonatomic, copy) NSString *fileName; 14 | @property (nonatomic) NSUInteger lineNumber; 15 | @property (nonatomic, getter = isDisabled) BOOL disabled; 16 | @property (nonatomic) BOOL hasFocusedExamples; 17 | 18 | - (SPTExampleGroup *)currentGroup; 19 | - (void)compile; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SPTTestSuite.m: -------------------------------------------------------------------------------- 1 | #import "SPTTestSuite.h" 2 | #import "SPTExampleGroup.h" 3 | #import "SPTCompiledExample.h" 4 | 5 | @implementation SPTTestSuite 6 | 7 | - (id)init { 8 | self = [super init]; 9 | if (self) { 10 | self.rootGroup = [[SPTExampleGroup alloc] init]; 11 | self.rootGroup.root = self.rootGroup; 12 | self.groupStack = [NSMutableArray arrayWithObject:self.rootGroup]; 13 | } 14 | return self; 15 | } 16 | 17 | - (SPTExampleGroup *)currentGroup { 18 | return [self.groupStack lastObject]; 19 | } 20 | 21 | - (void)compile { 22 | self.compiledExamples = [self.rootGroup compileExamplesWithStack:@[]]; 23 | for (SPTCompiledExample *example in self.compiledExamples) { 24 | if (example.focused) { 25 | self.hasFocusedExamples = YES; 26 | break; 27 | } 28 | } 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/Specta.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | //! Project version number for Specta. 5 | FOUNDATION_EXPORT double SpectaVersionNumber; 6 | 7 | //! Project version string for Specta. 8 | FOUNDATION_EXPORT const unsigned char SpectaVersionString[]; 9 | 10 | // In this header, you should import all the public headers of your framework using statements like #import 11 | 12 | #import 13 | #import 14 | #import 15 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SpectaTypes.h: -------------------------------------------------------------------------------- 1 | @class SPTSpec; 2 | 3 | typedef void (^SPTVoidBlock)(void); 4 | typedef void (^SPTSpecBlock)(SPTSpec *spec); 5 | typedef void (^SPTDictionaryBlock)(NSDictionary *dictionary); 6 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/SpectaUtility.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | extern NSString * const spt_kCurrentTestSuiteKey; 4 | extern NSString * const spt_kCurrentSpecKey; 5 | 6 | #define SPTCurrentTestSuite [[NSThread mainThread] threadDictionary][spt_kCurrentTestSuiteKey] 7 | #define SPTCurrentSpec [[NSThread mainThread] threadDictionary][spt_kCurrentSpecKey] 8 | #define SPTCurrentGroup [SPTCurrentTestSuite currentGroup] 9 | #define SPTGroupStack [SPTCurrentTestSuite groupStack] 10 | 11 | #define SPTReturnUnlessBlockOrNil(block) if ((block) && !SPTIsBlock((block))) return; 12 | #define SPTIsBlock(obj) [(obj) isKindOfClass:NSClassFromString(@"NSBlock")] 13 | 14 | BOOL spt_isSpecClass(Class aClass); 15 | NSString *spt_underscorize(NSString *string); 16 | NSArray *spt_map(NSArray *array, id (^block)(id obj, NSUInteger idx)); 17 | NSArray *spt_shuffle(NSArray *array); 18 | unsigned int spt_seed(void); 19 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/XCTest+Private.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 4 | 5 | @interface XCTestObservationCenter (SPTTestSuspention) 6 | 7 | - (void)_suspendObservationForBlock:(void (^)(void))block; 8 | 9 | @end 10 | 11 | #else 12 | 13 | @interface XCTestObservationCenter : NSObject 14 | 15 | + (id)sharedObservationCenter; 16 | - (void)_suspendObservationForBlock:(void (^)(void))block; 17 | 18 | @end 19 | 20 | @protocol XCTestObservation 21 | @end 22 | 23 | 24 | #endif 25 | 26 | @interface _XCTestDriverTestObserver : NSObject 27 | 28 | - (void)stopObserving; 29 | - (void)startObserving; 30 | 31 | @end 32 | 33 | @interface _XCTestCaseImplementation : NSObject 34 | @end 35 | 36 | @interface XCTestCase () 37 | 38 | - (_XCTestCaseImplementation *)internalImplementation; 39 | - (void)_recordUnexpectedFailureWithDescription:(NSString *)description exception:(NSException *)exception; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/XCTestCase+Specta.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface XCTestCase (Specta) 4 | 5 | - (void)spt_handleException:(NSException *)exception; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /Example/Pods/Specta/Specta/Specta/XCTestCase+Specta.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "XCTestCase+Specta.h" 3 | #import "SPTSpec.h" 4 | #import "SPTExample.h" 5 | #import "SPTSharedExampleGroups.h" 6 | #import "SpectaUtility.h" 7 | #import "XCTest+Private.h" 8 | 9 | @interface XCTestCase (xct_allSubclasses) 10 | 11 | - (NSArray *)allSubclasses; 12 | - (void)_dequeueFailures; 13 | 14 | @end 15 | 16 | @implementation XCTestCase (Specta) 17 | 18 | + (void)load { 19 | Method allSubclasses = class_getClassMethod(self, @selector(allSubclasses)); 20 | Method allSubclasses_swizzle = class_getClassMethod(self , @selector(spt_allSubclasses_swizzle)); 21 | method_exchangeImplementations(allSubclasses, allSubclasses_swizzle); 22 | 23 | Method dequeueFailures = class_getInstanceMethod(self, @selector(_dequeueFailures)); 24 | Method dequeueFailures_swizzle = class_getInstanceMethod(self, @selector(spt_dequeueFailures)); 25 | method_exchangeImplementations(dequeueFailures, dequeueFailures_swizzle); 26 | } 27 | 28 | + (NSArray *)spt_allSubclasses_swizzle { 29 | NSArray *subclasses = [self spt_allSubclasses_swizzle]; // call original 30 | NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[subclasses count]]; 31 | // exclude SPTSpec base class and all subclasses of SPTSharedExampleGroups 32 | for (id subclass in subclasses) { 33 | if (subclass != [SPTSpec class] && ![subclass isKindOfClass:[SPTSharedExampleGroups class]]) { 34 | [filtered addObject:subclass]; 35 | } 36 | } 37 | return spt_shuffle(filtered); 38 | } 39 | 40 | - (void)spt_dequeueFailures { 41 | void(^dequeueFailures)() = ^() { 42 | [self spt_dequeueFailures]; 43 | }; 44 | 45 | BOOL isMainThread = [NSThread isMainThread]; 46 | BOOL isSpectaTest = [self isKindOfClass:[SPTSpec class]]; 47 | 48 | if (!isSpectaTest || isMainThread) { 49 | dequeueFailures(); 50 | } else { 51 | dispatch_sync(dispatch_get_main_queue(), dequeueFailures); 52 | } 53 | } 54 | 55 | - (void)spt_handleException:(NSException *)exception { 56 | NSString *description = [exception reason]; 57 | if ([exception userInfo]) { 58 | id line = [exception userInfo][@"line"]; 59 | id file = [exception userInfo][@"file"]; 60 | if ([line isKindOfClass:[NSNumber class]] && [file isKindOfClass:[NSString class]]) { 61 | [self recordFailureWithDescription:description inFile:file atLine:[line unsignedIntegerValue] expected:YES]; 62 | return; 63 | } 64 | } 65 | [self _recordUnexpectedFailureWithDescription:description exception:exception]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/Expecta-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.6 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/Expecta-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Expecta : NSObject 3 | @end 4 | @implementation PodsDummy_Expecta 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/Expecta-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/Expecta-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "EXPBlockDefinedMatcher.h" 14 | #import "EXPDefines.h" 15 | #import "EXPDoubleTuple.h" 16 | #import "Expecta.h" 17 | #import "ExpectaObject.h" 18 | #import "ExpectaSupport.h" 19 | #import "EXPExpect.h" 20 | #import "EXPFloatTuple.h" 21 | #import "EXPMatcher.h" 22 | #import "EXPUnsupportedObject.h" 23 | #import "EXPMatcherHelpers.h" 24 | #import "EXPMatchers+beCloseTo.h" 25 | #import "EXPMatchers+beFalsy.h" 26 | #import "EXPMatchers+beginWith.h" 27 | #import "EXPMatchers+beGreaterThan.h" 28 | #import "EXPMatchers+beGreaterThanOrEqualTo.h" 29 | #import "EXPMatchers+beIdenticalTo.h" 30 | #import "EXPMatchers+beInstanceOf.h" 31 | #import "EXPMatchers+beInTheRangeOf.h" 32 | #import "EXPMatchers+beKindOf.h" 33 | #import "EXPMatchers+beLessThan.h" 34 | #import "EXPMatchers+beLessThanOrEqualTo.h" 35 | #import "EXPMatchers+beNil.h" 36 | #import "EXPMatchers+beSubclassOf.h" 37 | #import "EXPMatchers+beSupersetOf.h" 38 | #import "EXPMatchers+beTruthy.h" 39 | #import "EXPMatchers+conformTo.h" 40 | #import "EXPMatchers+contain.h" 41 | #import "EXPMatchers+endWith.h" 42 | #import "EXPMatchers+equal.h" 43 | #import "EXPMatchers+haveCountOf.h" 44 | #import "EXPMatchers+match.h" 45 | #import "EXPMatchers+postNotification.h" 46 | #import "EXPMatchers+raise.h" 47 | #import "EXPMatchers+raiseWithReason.h" 48 | #import "EXPMatchers+respondTo.h" 49 | #import "EXPMatchers.h" 50 | #import "NSObject+Expecta.h" 51 | #import "NSValue+Expecta.h" 52 | 53 | FOUNDATION_EXPORT double ExpectaVersionNumber; 54 | FOUNDATION_EXPORT const unsigned char ExpectaVersionString[]; 55 | 56 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/Expecta.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Expecta 2 | ENABLE_BITCODE = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LIBRARY_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" 6 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "XCTest" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Expecta 11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 12 | SKIP_INSTALL = YES 13 | SWIFT_INCLUDE_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" 14 | SYSTEM_FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/Expecta.modulemap: -------------------------------------------------------------------------------- 1 | framework module Expecta { 2 | umbrella header "Expecta-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/Expecta.release.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Expecta 2 | ENABLE_BITCODE = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LIBRARY_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" 6 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "XCTest" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Expecta 11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 12 | SKIP_INSTALL = YES 13 | SWIFT_INCLUDE_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" 14 | SYSTEM_FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/Expecta.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Expecta 2 | ENABLE_BITCODE = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 6 | OTHER_LDFLAGS = -framework "Foundation" -framework "XCTest" 7 | PODS_BUILD_DIR = $BUILD_DIR 8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Expecta 11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 12 | SKIP_INSTALL = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Expecta/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.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/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 | 5.1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/PINFuture-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 | 5.2.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/PINFuture-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_PINFuture : NSObject 3 | @end 4 | @implementation PodsDummy_PINFuture 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/PINFuture-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/PINFuture-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "NSURLSession+PINFuture.h" 14 | #import "PHImageManager+PINFuture.h" 15 | #import "PINNSURLSessionDataTaskAndResult.h" 16 | #import "PINNSURLSessionDataTaskResult.h" 17 | #import "PINPHImageManagerImageDataResult.h" 18 | #import "PINCancelToken.h" 19 | #import "PINDefines.h" 20 | #import "PINExecutor.h" 21 | #import "PINFuture+ChainSideEffect.h" 22 | #import "PINFuture+Completion.h" 23 | #import "PINFuture+Delay.h" 24 | #import "PINFuture+Dispatch.h" 25 | #import "PINFuture+FlatMapError.h" 26 | #import "PINFuture+GatherAll.h" 27 | #import "PINFuture+GatherSome.h" 28 | #import "PINFuture+Generated.h" 29 | #import "PINFuture+MapError.h" 30 | #import "PINFuture+MapToValue.h" 31 | #import "PINFuture+NoValue.h" 32 | #import "PINFuture.h" 33 | #import "PINFutureAndCancelToken.h" 34 | #import "PINFutureError.h" 35 | #import "PINFutureMap+FlatMap.h" 36 | #import "PINFutureMap+Map.h" 37 | #import "PINFutureMap.h" 38 | #import "PINFutureOnce.h" 39 | #import "PINFuturePending.h" 40 | 41 | FOUNDATION_EXPORT double PINFutureVersionNumber; 42 | FOUNDATION_EXPORT const unsigned char PINFutureVersionString[]; 43 | 44 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/PINFuture.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PINFuture 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -weak_framework "Photos" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/PINFuture.modulemap: -------------------------------------------------------------------------------- 1 | framework module PINFuture { 2 | umbrella header "PINFuture-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/PINFuture.release.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PINFuture 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -weak_framework "Photos" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/PINFuture/PINFuture.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/PINFuture 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "Foundation" -weak_framework "Photos" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Example/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.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Example/Pods-PINFuture_Example-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.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Example/Pods-PINFuture_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_PINFuture_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_PINFuture_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Example/Pods-PINFuture_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_PINFuture_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_PINFuture_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Example/Pods-PINFuture_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINFuture" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINFuture/PINFuture.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PINFuture" -weak_framework "Photos" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Example/Pods-PINFuture_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_PINFuture_Example { 2 | umbrella header "Pods-PINFuture_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Example/Pods-PINFuture_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINFuture" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINFuture/PINFuture.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "PINFuture" -weak_framework "Photos" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Tests/Pods-PINFuture_Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Tests/Pods-PINFuture_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_PINFuture_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_PINFuture_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Tests/Pods-PINFuture_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_PINFuture_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_PINFuture_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Tests/Pods-PINFuture_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/Expecta" "${PODS_CONFIGURATION_BUILD_DIR}/PINFuture" "${PODS_CONFIGURATION_BUILD_DIR}/Specta" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Expecta/Expecta.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PINFuture/PINFuture.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Specta/Specta.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "Expecta" -framework "Foundation" -framework "PINFuture" -framework "Specta" -framework "XCTest" -weak_framework "Photos" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Tests/Pods-PINFuture_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_PINFuture_Tests { 2 | umbrella header "Pods-PINFuture_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-PINFuture_Tests/Pods-PINFuture_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/Expecta" "${PODS_CONFIGURATION_BUILD_DIR}/PINFuture" "${PODS_CONFIGURATION_BUILD_DIR}/Specta" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Expecta/Expecta.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PINFuture/PINFuture.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Specta/Specta.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "Expecta" -framework "Foundation" -framework "PINFuture" -framework "Specta" -framework "XCTest" -weak_framework "Photos" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/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.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/Specta-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.7 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/Specta-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Specta : NSObject 3 | @end 4 | @implementation PodsDummy_Specta 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/Specta-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/Specta-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "Specta.h" 14 | #import "SpectaDSL.h" 15 | #import "SpectaTypes.h" 16 | #import "SpectaUtility.h" 17 | #import "SPTCallSite.h" 18 | #import "SPTCompiledExample.h" 19 | #import "SPTExample.h" 20 | #import "SPTExampleGroup.h" 21 | #import "SPTExcludeGlobalBeforeAfterEach.h" 22 | #import "SPTGlobalBeforeAfterEach.h" 23 | #import "SPTSharedExampleGroups.h" 24 | #import "SPTSpec.h" 25 | #import "SPTTestSuite.h" 26 | #import "XCTest+Private.h" 27 | #import "XCTestCase+Specta.h" 28 | 29 | FOUNDATION_EXPORT double SpectaVersionNumber; 30 | FOUNDATION_EXPORT const unsigned char SpectaVersionString[]; 31 | 32 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/Specta.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Specta 2 | ENABLE_BITCODE = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LIBRARY_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" 6 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "XCTest" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Specta 11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 12 | SKIP_INSTALL = YES 13 | SWIFT_INCLUDE_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" 14 | SYSTEM_FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/Specta.modulemap: -------------------------------------------------------------------------------- 1 | framework module Specta { 2 | umbrella header "Specta-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/Specta.release.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Specta 2 | ENABLE_BITCODE = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LIBRARY_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" 6 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "XCTest" 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Specta 11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 12 | SKIP_INSTALL = YES 13 | SWIFT_INCLUDE_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" 14 | SYSTEM_FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Specta/Specta.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Specta 2 | ENABLE_BITCODE = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 6 | OTHER_LDFLAGS = -framework "Foundation" -framework "XCTest" 7 | PODS_BUILD_DIR = $BUILD_DIR 8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Specta 11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 12 | SKIP_INSTALL = YES 13 | -------------------------------------------------------------------------------- /Example/Tests/PINCancelTokenTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINCancelTokenTests.m 3 | // PINFuture 4 | // 5 | // Created by Brandon Kase on 12/30/16. 6 | // Copyright © 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "TestUtil.h" 10 | 11 | // TODO: Do real cancel tests 12 | /*SpecBegin(PINCancelTokenSpecs) 13 | 14 | describe(@"cancel tokens", ^{ 15 | it(@"cancellation works", ^{ 16 | NSNumber *valueA = numberFixture(); 17 | NSString *valueB = stringFixture(); 18 | PINTask *taskA = [PINTask value:valueA]; 19 | PINTask *taskB = [PINTaskMap executor:[PINExecutor immediate] map:taskA success:^PINFuture *(NSNumber *fromValue) { 20 | return [PINFuture withValue:valueB]; 21 | }]; 22 | runTaskAndExpectToFulfillWith(self, taskB, valueB); 23 | }); 24 | }); 25 | 26 | SpecEnd*/ 27 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+ChainSideEffectTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+ChainSideEffectTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 1/27/17. 6 | // Copyright © 2017 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | #import "TestUtil.h" 11 | 12 | SpecBegin(PINFutureChainSideEffectSpecs) 13 | 14 | describe(@"chainSideEffect", ^{ 15 | 16 | // This executor always dispatches to the main queue to ensure that the future won't resolve until the callstack has unwound. 17 | id delayedExecutor = [PINExecutor queue:dispatch_get_main_queue()]; 18 | 19 | it(@"expected callbacks called for a fulfilled Future", ^{ 20 | NSString *value = stringFixture(); 21 | PINFuture *chained = [PINFuture withValue:value]; 22 | __block BOOL successCalled = NO; 23 | __block BOOL failureCalled = NO; 24 | __block BOOL completionCalled = NO; 25 | chained = [chained executor:delayedExecutor chainSuccess:^(NSString *value) { 26 | successCalled = YES; 27 | } failure:^(NSError *error) { 28 | failureCalled = YES; 29 | }]; 30 | chained = [chained executor:delayedExecutor chainCompletion:^{ 31 | completionCalled = YES; 32 | }]; 33 | expectFutureToFulfillWith(self, chained, value); 34 | expect(successCalled).to.beTruthy(); 35 | expect(failureCalled).to.beFalsy(); 36 | expect(completionCalled).to.beTruthy(); 37 | }); 38 | 39 | it(@"expected callbacks called for a rejected Future", ^{ 40 | NSError *error = errorFixture(); 41 | PINFuture *chained = [PINFuture withError:error]; 42 | __block BOOL successCalled = NO; 43 | __block BOOL failureCalled = NO; 44 | __block BOOL completionCalled = NO; 45 | chained = [chained executor:delayedExecutor chainSuccess:^(NSString *value) { 46 | successCalled = YES; 47 | } failure:^(NSError *error) { 48 | failureCalled = YES; 49 | }]; 50 | chained = [chained executor:delayedExecutor chainCompletion:^{ 51 | completionCalled = YES; 52 | }]; 53 | expectFutureToRejectWith(self, chained, error); 54 | expect(successCalled).to.beFalsy(); 55 | expect(failureCalled).to.beTruthy(); 56 | expect(completionCalled).to.beTruthy(); 57 | }); 58 | 59 | it(@"chainSuccess:failure: doesn't blow up if callbacks are nil", ^{ 60 | NSString *value = stringFixture(); 61 | PINFuture *chained = [PINFuture withValue:value]; 62 | chained = [chained executor:delayedExecutor chainSuccess:nil failure:nil]; 63 | expectFutureToFulfillWith(self, chained, value); 64 | }); 65 | }); 66 | 67 | SpecEnd 68 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+CompletionTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+CompletionTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 1/28/17. 6 | // Copyright © 2017 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | #import "TestUtil.h" 11 | 12 | SpecBegin(PINFutureCompletionSpecs) 13 | 14 | describe(@"completion", ^{ 15 | 16 | // This executor always dispatches to the main queue to ensure that the future won't resolve until the callstack has unwound. 17 | id delayedExecutor = [PINExecutor queue:dispatch_get_main_queue()]; 18 | 19 | it(@"expected callbacks called for a fulfilled Future", ^{ 20 | NSString *value = stringFixture(); 21 | PINFuture *future = [PINFuture withValue:value]; 22 | __block BOOL completionCalled = NO; 23 | [future executor:delayedExecutor completion:^{ 24 | completionCalled = YES; 25 | }]; 26 | expectFutureToFulfillWith(self, future, value); 27 | expect(completionCalled).to.beTruthy(); 28 | }); 29 | 30 | it(@"expected callbacks called for a rejected Future", ^{ 31 | NSError *error = errorFixture(); 32 | PINFuture *future = [PINFuture withError:error]; 33 | __block BOOL completionCalled = NO; 34 | [future executor:delayedExecutor completion:^{ 35 | completionCalled = YES; 36 | }]; 37 | expectFutureToRejectWith(self, future, error); 38 | expect(completionCalled).to.beTruthy(); 39 | }); 40 | }); 41 | 42 | SpecEnd 43 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+DelayTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+Delay.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 1/31/17. 6 | // Copyright © 2018 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | #import "TestUtil.h" 11 | 12 | SpecBegin(PINFutureDelaySpecs) 13 | 14 | describe(@"Delay", ^{ 15 | it(@"can delay", ^{ 16 | PINFutureNoValue *delay = [PINFuture delay:0]; 17 | expectFutureToFulfillWith(self, delay, [NSNull null]); 18 | }); 19 | }); 20 | 21 | SpecEnd 22 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+DispatchTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/5/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | // https://github.com/Specta/Specta 10 | 11 | #import "PINFuture.h" 12 | 13 | #import "TestUtil.h" 14 | 15 | dispatch_queue_t backgroundQueue() { 16 | return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 17 | } 18 | 19 | SpecBegin(PINFutureDispatchSpecs) 20 | 21 | describe(@"dispatch", ^{ 22 | id executor = [PINExecutor background]; 23 | 24 | it(@"resolve on background queue", ^{ 25 | NSString *value = stringFixture(); 26 | PINFuture *future = [PINFuture executor:executor block:^PINFuture * _Nonnull{ 27 | return [PINFuture withValue:value]; 28 | }]; 29 | expectFutureToFulfillWith(self, future, value); 30 | }); 31 | 32 | it(@"reject on background queue", ^{ 33 | NSError *error = errorFixture(); 34 | PINFuture *future = [PINFuture executor:executor block:^PINFuture * _Nonnull{ 35 | return [PINFuture withError:error]; 36 | }]; 37 | expectFutureToRejectWith(self, future, error); 38 | }); 39 | }); 40 | 41 | SpecEnd 42 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+FlatMapErrorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+FlatMapError.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | #import "TestUtil.h" 11 | 12 | SpecBegin(PINFutureFlatMapErrorSpecs) 13 | 14 | describe(@"flatMapError", ^{ 15 | it(@"can return a resolved future", ^{ 16 | NSError *error1 = errorFixture(); 17 | NSString *value2 = stringFixture(); 18 | PINFuture *source = [PINFuture withError:error1]; 19 | PINFuture *mapped = [source executor:[PINExecutor immediate] flatMapError:^PINFuture *(NSError *error) { 20 | return [PINFuture withValue:value2]; 21 | }]; 22 | expectFutureToFulfillWith(self, mapped, value2); 23 | }); 24 | 25 | it(@"can return a rejected future", ^{ 26 | NSError *error1 = errorFixture(); 27 | NSError *error2 = errorFixture(); 28 | PINFuture *source = [PINFuture withError:error1]; 29 | PINFuture *mapped = [source executor:[PINExecutor immediate] flatMapError:^PINFuture *(NSError *error) { 30 | return [PINFuture withError:error2]; 31 | }]; 32 | expectFutureToRejectWith(self, mapped, error2); 33 | }); 34 | 35 | it(@"raises exception when nil is returned instead of a PINFuture", ^{ 36 | NSError *error1 = errorFixture(); 37 | PINFuture *source = [PINFuture withError:error1]; 38 | expect(^{ 39 | __unused PINFuture *mapped = [source executor:[PINExecutor immediate] flatMapError:^PINFuture *(NSError *error) { 40 | return nil; 41 | }]; 42 | }).to.raiseAny(); 43 | }); 44 | }); 45 | 46 | SpecEnd 47 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+GatherAllTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+GatherAllTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/5/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | // https://github.com/Specta/Specta 10 | 11 | #import "PINFuture.h" 12 | #import "TestUtil.h" 13 | 14 | SpecBegin(PINFutureGatherAllSpecs) 15 | 16 | describe(@"gatherAll", ^{ 17 | it(@"resolves if all source futures resolve", ^{ 18 | NSString *value1 = stringFixture(); 19 | NSString *value2 = stringFixture(); 20 | PINFuture *source1 = [PINFuture withValue:value1]; 21 | PINFuture *source2 = [PINFuture withValue:value2]; 22 | PINFuture *> *arrayFuture = [PINFuture gatherAll:@[source1, source2]]; 23 | expectFutureToFulfillWith(self, arrayFuture, @[value1, value2]); 24 | }); 25 | 26 | it(@"rejects if any source future rejects", ^{ 27 | NSString *value1 = stringFixture(); 28 | NSError *error2 = errorFixture(); 29 | PINFuture *source1 = [PINFuture withValue:value1]; 30 | PINFuture *source2 = [PINFuture withError:error2]; 31 | PINFuture *> *arrayFuture = [PINFuture gatherAll:@[source1, source2]]; 32 | expectFutureToRejectWith(self, arrayFuture, error2); 33 | }); 34 | }); 35 | 36 | SpecEnd 37 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+GatherSomeTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+GatherSomeTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 8/16/17. 6 | // Copyright © 2017 Pinterest. All rights reserved. 7 | // 8 | 9 | 10 | // https://github.com/Specta/Specta 11 | 12 | #import "PINFuture.h" 13 | #import "TestUtil.h" 14 | 15 | SpecBegin(PINFutureGatherSomeSpecs) 16 | 17 | describe(@"gatherSome", ^{ 18 | it(@"resolves with a mix of resolved and rejected futures ", ^{ 19 | NSError *error1 = errorFixture(); 20 | NSString *value2 = stringFixture(); 21 | PINFuture *source1 = [PINFuture withError:error1]; 22 | PINFuture *source2 = [PINFuture withValue:value2]; 23 | PINFuture *arrayFuture = [PINFuture gatherSome:@[source1, source2]]; 24 | expectFutureToFulfillWith(self, arrayFuture, @[[NSNull null], value2]); 25 | }); 26 | }); 27 | 28 | SpecEnd 29 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+MapErrorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+MapError.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | #import "TestUtil.h" 11 | 12 | SpecBegin(PINFutureMapErrorSpecs) 13 | 14 | describe(@"mapError", ^{ 15 | it(@"can return a new error", ^{ 16 | NSError *error = errorFixture(); 17 | NSString *value = stringFixture(); 18 | PINFuture *source = [PINFuture withError:error]; 19 | PINFuture *mapped = [source executor:[PINExecutor immediate] mapError:^NSString *(NSError *error) { 20 | return value; 21 | }]; 22 | expectFutureToFulfillWith(self, mapped, value); 23 | }); 24 | }); 25 | 26 | SpecEnd 27 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+MapToValueTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+MapToValue.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 1/31/17. 6 | // Copyright © 2017 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | #import "TestUtil.h" 11 | 12 | SpecBegin(PINFutureMapToValueSpecs) 13 | 14 | describe(@"mapToValue", ^{ 15 | it(@"can mapToValue", ^{ 16 | NSNumber *numberValue = numberFixture(); 17 | NSString *stringValue = stringFixture(); 18 | PINFuture *source = [PINFuture withValue:numberValue]; 19 | PINFuture *mapped = [PINFuture map:source toValue:stringValue]; 20 | expectFutureToFulfillWith(self, mapped, stringValue); 21 | }); 22 | }); 23 | 24 | SpecEnd 25 | -------------------------------------------------------------------------------- /Example/Tests/PINFuture+NoValueTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+NoValueTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 1/31/17. 6 | // Copyright © 2018 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | #import "TestUtil.h" 11 | 12 | SpecBegin(PINFutureNoValueSpecs) 13 | 14 | describe(@"NoValue", ^{ 15 | it(@"can mapToNoValue", ^{ 16 | NSString *value = stringFixture(); 17 | PINFuture *source = [PINFuture withValue:value]; 18 | PINFutureNoValue *mapped = [source mapToNoValue]; 19 | 20 | expectFutureToFulfillWith(self, mapped, [NSNull null]); 21 | }); 22 | 23 | it(@"can mapToNoValue", ^{ 24 | NSString *value = stringFixture(); 25 | PINFuture *source = [PINFuture withValue:value]; 26 | PINFutureNoValue *mapped = [source mapToNoValue]; 27 | 28 | expectFutureToFulfillWith(self, mapped, [NSNull null]); 29 | }); 30 | }); 31 | 32 | SpecEnd 33 | -------------------------------------------------------------------------------- /Example/Tests/PINFutureMap+FlatMapTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FlatMapTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/4/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | // https://github.com/Specta/Specta 10 | 11 | #import "PINFuture.h" 12 | #import "TestUtil.h" 13 | 14 | SpecBegin(PINFutureMapFlatMapSpecs) 15 | 16 | describe(@"flatMap", ^{ 17 | it(@"can return resolved future", ^{ 18 | NSNumber *valueA = numberFixture(); 19 | NSString *valueB = stringFixture(); 20 | PINFuture *futureA = [PINFuture withValue:valueA]; 21 | PINFuture *futureB = [PINFutureMap flatMap:futureA executor:[PINExecutor immediate] transform:^PINFuture * _Nonnull(NSNumber * _Nonnull fromValue) { 22 | return [PINFuture withValue:valueB]; 23 | }]; 24 | expectFutureToFulfillWith(self, futureB, valueB); 25 | }); 26 | 27 | it(@"can return rejected future", ^{ 28 | NSString *valueA = stringFixture(); 29 | NSError *errorB = errorFixture(); 30 | PINFuture *futureA = [PINFuture withValue:valueA]; 31 | PINFuture *futureB = [PINFutureMap flatMap:futureA executor:[PINExecutor immediate] transform:^PINFuture * _Nonnull(NSString * _Nonnull fromValue) { 32 | return [PINFuture withError:errorB]; 33 | }]; 34 | expectFutureToRejectWith(self, futureB, errorB); 35 | }); 36 | 37 | it(@"raises exception when nil is returned instead of a PINFuture", ^{ 38 | NSString *valueA = stringFixture(); 39 | PINFuture *futureA = [PINFuture withValue:valueA]; 40 | expect(^{ 41 | __unused PINFuture *futureB = [PINFutureMap flatMap:futureA executor:[PINExecutor immediate] transform:^PINFuture * _Nonnull(NSString * _Nonnull fromValue) { 42 | return nil; 43 | }]; 44 | }).to.raiseAny(); 45 | }); 46 | }); 47 | 48 | SpecEnd 49 | -------------------------------------------------------------------------------- /Example/Tests/PINFutureMap+MapTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MapTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/4/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | // https://github.com/Specta/Specta 10 | 11 | #import "PINFuture.h" 12 | #import "TestUtil.h" 13 | 14 | SpecBegin(PINFutureMapMapSpecs) 15 | 16 | describe(@"map", ^{ 17 | it(@"can return a value of a different type", ^{ 18 | NSNumber *valueA = numberFixture(); 19 | NSString *valueB = stringFixture(); 20 | PINFuture *futureA = [PINFuture withValue:valueA]; 21 | PINFuture *futureB = [PINFutureMap map:futureA executor:[PINExecutor immediate] transform:^NSString * _Nonnull(NSNumber * _Nonnull fromValue) { 22 | return valueB; 23 | }]; 24 | expectFutureToFulfillWith(self, futureB, valueB); 25 | }); 26 | }); 27 | 28 | describe(@"mapToValue", ^{ 29 | it(@"can return a value of a different type", ^{ 30 | NSNumber *valueA = numberFixture(); 31 | NSString *valueB = stringFixture(); 32 | PINFuture *futureA = [PINFuture withValue:valueA]; 33 | PINFuture *futureB = [PINFutureMap mapToValue:futureA value:valueB]; 34 | expectFutureToFulfillWith(self, futureB, valueB); 35 | }); 36 | }); 37 | 38 | SpecEnd 39 | -------------------------------------------------------------------------------- /Example/Tests/PINFutureOnceTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureOnceTests.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/14/16. 6 | // Copyright © 2016 Chris Danford. All rights reserved. 7 | // 8 | 9 | // https://github.com/Specta/Specta 10 | 11 | #import "PINFutureOnce.h" 12 | #import "TestUtil.h" 13 | 14 | SpecBegin(PINFutureOnceSpecs) 15 | 16 | describe(@"once", ^{ 17 | it(@"only calls block once", ^{ 18 | PINFutureOnce *once = [PINFutureOnce new]; 19 | __block NSUInteger callCount = 0; 20 | [once performOnce:^{ 21 | callCount++; 22 | }]; 23 | [once performOnce:^{ 24 | callCount++; 25 | }]; 26 | expect(callCount).to.equal(1); 27 | }); 28 | }); 29 | 30 | SpecEnd 31 | -------------------------------------------------------------------------------- /Example/Tests/PINFutureStressTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureStressTests.m 3 | // PINFutureTests 4 | // 5 | // Created by Chris Danford on 12/5/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | // https://github.com/Specta/Specta 10 | 11 | #import "PINFuture.h" 12 | #import "TestUtil.h" 13 | 14 | SpecBegin(PINFutureStressSpecs) 15 | 16 | describe(@"stress test", ^{ 17 | it(@"handles large numbers of callbacks", ^{ 18 | NSUInteger numChildren = 10000; 19 | NSNumber *value = numberFixture(); 20 | PINFuture *sourceFuture = [PINFuture withValue:value]; 21 | 22 | NSMutableArray *> *sourceFutures = [[NSMutableArray alloc] initWithCapacity:numChildren]; 23 | for (NSUInteger i = 0; i < numChildren; i++) { 24 | [sourceFutures addObject:sourceFuture]; 25 | } 26 | 27 | PINFuture *> *arrayFuture = [PINFuture gatherAll:sourceFutures]; 28 | NSMutableArray *expectedValue = [[NSMutableArray alloc] initWithCapacity:numChildren]; 29 | for (NSUInteger i = 0; i < numChildren; i++) { 30 | [expectedValue addObject:value]; 31 | } 32 | 33 | expectFutureToFulfillWith(self, arrayFuture, expectedValue); 34 | }); 35 | }); 36 | 37 | SpecEnd 38 | -------------------------------------------------------------------------------- /Example/Tests/PINFutureTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureTests.m 3 | // PINFutureTests 4 | // 5 | // Created by Chris Danford on 12/02/2016. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | // https://github.com/Specta/Specta 10 | 11 | #import "PINFuture.h" 12 | #import "TestUtil.h" 13 | 14 | SpecBegin(PINFutureSpecs) 15 | 16 | describe(@"future", ^{ 17 | it(@"create with value", ^{ 18 | NSString *value = stringFixture(); 19 | PINFuture *future = [PINFuture withValue:value]; 20 | expectFutureToFulfillWith(self, future, value); 21 | }); 22 | 23 | it(@"create with error", ^{ 24 | NSError *error = errorFixture(); 25 | PINFuture *future = [PINFuture withError:error]; 26 | expectFutureToRejectWith(self, future, error); 27 | }); 28 | 29 | it(@"resolves only once", ^{ 30 | NSString *value = stringFixture(); 31 | // Calls to resolve or reject after the first should be ignored. 32 | PINFuture *future = [PINFuture withBlock:^(void (^ _Nonnull resolve)(NSString * _Nonnull), void (^ _Nonnull reject)(NSError * _Nonnull)) { 33 | resolve(value); 34 | resolve(value); 35 | reject(errorFixture()); 36 | }]; 37 | expectFutureToFulfillWith(self, future, value); 38 | }); 39 | 40 | it(@"rejects only once", ^{ 41 | NSError *error = errorFixture(); 42 | // Calls to resolve or reject after the first should be ignored. 43 | PINFuture *future = [PINFuture withBlock:^(void (^ _Nonnull resolve)(NSString * _Nonnull), void (^ _Nonnull reject)(NSError * _Nonnull)) { 44 | reject(error); 45 | reject(error); 46 | resolve(stringFixture()); 47 | }]; 48 | expectFutureToRejectWith(self, future, error); 49 | }); 50 | 51 | it(@"tolerates success callback being NULL when fulfilled", ^{ 52 | NSString *value = stringFixture(); 53 | PINFuture *future = [PINFuture withValue:value]; 54 | [future executor:[PINExecutor immediate] success:NULL failure:NULL]; 55 | expectFutureToFulfillWith(self, future, value); 56 | }); 57 | 58 | it(@"tolerates failure callback being null when rejected", ^{ 59 | NSError *error = errorFixture(); 60 | PINFuture *future = [PINFuture withError:error]; 61 | [future executor:[PINExecutor immediate] success:NULL failure:NULL]; 62 | expectFutureToRejectWith(self, future, error); 63 | }); 64 | }); 65 | 66 | SpecEnd 67 | -------------------------------------------------------------------------------- /Example/Tests/TestUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestUtil.h 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/4/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | 11 | void expectFutureToFulfillWith(id testCase, PINFuture *future, id expectedValue); 12 | void expectFutureToRejectWith(id testCase, PINFuture *future, NSError *expectedError); 13 | 14 | NSNumber *numberFixture(void); 15 | NSString *stringFixture(void); 16 | NSError *errorFixture(void); 17 | -------------------------------------------------------------------------------- /Example/Tests/TestUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestUtil.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/4/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "TestUtil.h" 10 | 11 | void expectFutureToFulfillWith(id testCase, PINFuture *future, id expectedValue) { 12 | waitUntil(^(DoneCallback done) { 13 | [future executor:[PINExecutor immediate] success:^(id _Nonnull value) { 14 | id self = testCase; 15 | expect(value).to.equal(expectedValue); 16 | done(); 17 | } failure:^(NSError * _Nonnull error) { 18 | NSCAssert(NO, @"expected to resolve, not reject"); 19 | done(); 20 | }]; 21 | }); 22 | } 23 | 24 | void expectFutureToRejectWith(id testCase, PINFuture *future, NSError *expectedError) { 25 | waitUntil(^(DoneCallback done) { 26 | [future executor:[PINExecutor immediate] success:^(id _Nonnull value) { 27 | NSCAssert(NO, @"expected to reject, not resolve"); 28 | done(); 29 | } failure:^(NSError * _Nonnull error) { 30 | id self = testCase; 31 | expect(error).to.equal(expectedError); 32 | done(); 33 | }]; 34 | }); 35 | } 36 | 37 | static NSInteger counter = 0; 38 | 39 | NSNumber *numberFixture() { 40 | return @(counter++); 41 | } 42 | 43 | NSString *stringFixture() { 44 | return [NSString stringWithFormat:@"fixture%ld", (long)counter++]; 45 | } 46 | 47 | NSError *errorFixture() { 48 | return [NSError errorWithDomain:NSCocoaErrorDomain code:counter++ userInfo:nil]; 49 | } 50 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | @import Specta; 6 | @import Expecta; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /PINFuture.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint PINFuture.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'PINFuture' 11 | s.version = '5.2.1' 12 | s.summary = 'An Objective C implementation of the Future pattern that aims to provide maximal type safety.' 13 | s.description = <<-DESC 14 | An Objective C implementation of the Future async value pattern that aims to provide maximal type safety. 15 | DESC 16 | 17 | s.homepage = 'https://github.com/pinterest/PINFuture' 18 | s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' } 19 | s.author = { 'Chris Danford' => 'chrisdanford@gmail.com' } 20 | s.source = { :git => 'https://github.com/pinterest/PINFuture.git', :tag => s.version.to_s } 21 | s.social_media_url = 'https://twitter.com/PinterestEng' 22 | 23 | s.ios.deployment_target = '8.0' 24 | 25 | s.source_files = 'PINFuture/Classes/**/*' 26 | s.weak_frameworks = 'Photos' 27 | s.frameworks = 'Foundation' 28 | end 29 | -------------------------------------------------------------------------------- /PINFuture/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/PINFuture/45452c873628abe43dd2aa4c03e0b5b0e826c66d/PINFuture/Assets/.gitkeep -------------------------------------------------------------------------------- /PINFuture/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/PINFuture/45452c873628abe43dd2aa4c03e0b5b0e826c66d/PINFuture/Classes/.gitkeep -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/NSURLSession+PINFuture.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSession+PINFuture.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface NSURLSession (PINFuture) 17 | 18 | /** 19 | * Returns a Future that never rejects. The value type has an error field. 20 | * that is set in non-exceptional cases such as 4xx response codes. 21 | */ 22 | - (PINNSURLSessionDataTaskAndResult *)pinfuture_dataTaskWithRequest:(NSURLRequest *)request; 23 | 24 | /** 25 | * Returns a Future that never rejects. The value type has an error field. 26 | * that is set in non-exceptional cases such as 4xx response codes. 27 | */ 28 | - (PINNSURLSessionDataTaskAndResult *)pinfuture_dataTaskWithRequest:(NSURLRequest *)request 29 | priority:(float)priority; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/NSURLSession+PINFuture.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSession+PINFuture.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "NSURLSession+PINFuture.h" 10 | #import "PINFutureDefinition.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @implementation NSURLSession (PINFuture) 15 | 16 | - (PINNSURLSessionDataTaskAndResult *)pinfuture_dataTaskWithRequest:(NSURLRequest *)request 17 | { 18 | return [self pinfuture_dataTaskWithRequest:request priority:NSURLSessionTaskPriorityDefault]; 19 | } 20 | 21 | - (PINNSURLSessionDataTaskAndResult *)pinfuture_dataTaskWithRequest:(NSURLRequest *)request 22 | priority:(float)priority 23 | { 24 | __block NSURLSessionDataTask *task; 25 | PINFuture *future; 26 | future = [PINFuture withBlock:^(void (^ _Nonnull resolve)(id _Nonnull), void (^ _Nonnull reject)(NSError * _Nonnull)) { 27 | task = [self dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 28 | PINNSURLSessionDataTaskResult *result = [PINNSURLSessionDataTaskResult resultWithData:data response:response error:error]; 29 | resolve(result); 30 | }]; 31 | task.priority = priority; 32 | [task resume]; 33 | }]; 34 | 35 | return [PINNSURLSessionDataTaskAndResult resultWithTask:task resultFuture:future]; 36 | } 37 | 38 | @end 39 | 40 | NS_ASSUME_NONNULL_END 41 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/PHImageManager+PINFuture.h: -------------------------------------------------------------------------------- 1 | // 2 | // PHImageManager+PINFuture.h 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 11/22/16. 6 | // Copyright © 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface PHImageManager (PINFuture) 17 | 18 | - (PINFuture *)pinfuture_requestImageDataForAsset:(PHAsset *)asset options:(nullable PHImageRequestOptions *)options; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/PHImageManager+PINFuture.m: -------------------------------------------------------------------------------- 1 | // 2 | // PHImageManager+PINFuture.m 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 11/22/16. 6 | // Copyright © 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PHImageManager+PINFuture.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @implementation PHImageManager (PINFuture) 14 | 15 | - (PINFuture *)pinfuture_requestImageDataForAsset:(PHAsset *)asset options:(nullable PHImageRequestOptions *)options 16 | { 17 | return [PINFuture withBlock:^(void (^ _Nonnull resolve)(PINPHImageManagerImageDataResult * _Nonnull), void (^ _Nonnull reject)(NSError * _Nonnull)) { 18 | [self requestImageDataForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) { 19 | if (imageData) { 20 | PINPHImageManagerImageDataResult *result = [PINPHImageManagerImageDataResult resultWithImageData:imageData 21 | dataUTI:dataUTI 22 | orientation:orientation 23 | info:info]; 24 | resolve(result); 25 | } else { 26 | NSString *failureReason = NSLocalizedString(@"Invalid Image Data", @"Failure reason for Invalid Image Data"); 27 | NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain 28 | code:NSFileReadUnknownError 29 | userInfo:@{NSLocalizedFailureReasonErrorKey : failureReason}]; 30 | reject(error); 31 | } 32 | }]; 33 | }]; 34 | } 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/PINNSURLSessionDataTaskAndResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINNSURLSessionDataTaskAndResult.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 9/11/17. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface PINNSURLSessionDataTaskAndResult : NSObject 15 | 16 | @property (nonatomic, readonly) NSURLSessionDataTask * task; 17 | @property (nonatomic, readonly) PINFuture * resultFuture; 18 | 19 | + (instancetype)resultWithTask:(NSURLSessionDataTask *)task 20 | resultFuture:(PINFuture *)resultFuture; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/PINNSURLSessionDataTaskAndResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINNSURLSessionDataTaskAndResult.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 9/11/17. 6 | // 7 | // 8 | 9 | #import "PINNSURLSessionDataTaskAndResult.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface PINNSURLSessionDataTaskAndResult () 14 | @property (nonatomic) NSURLSessionDataTask * task; 15 | @property (nonatomic) PINFuture * resultFuture; 16 | @end 17 | 18 | @implementation PINNSURLSessionDataTaskAndResult 19 | 20 | + (instancetype)resultWithTask:(NSURLSessionDataTask *)task 21 | resultFuture:(PINFuture *)resultFuture 22 | { 23 | PINNSURLSessionDataTaskAndResult *taskAndResult = [[PINNSURLSessionDataTaskAndResult alloc] init]; 24 | taskAndResult.task = task; 25 | taskAndResult.resultFuture = resultFuture; 26 | return taskAndResult; 27 | } 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/PINNSURLSessionDataTaskResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINNSURLSessionDataTaskResult.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface PINNSURLSessionDataTaskResult : NSObject 15 | 16 | @property (nonatomic, copy, readonly) NSData * _Nullable data; 17 | @property (nonatomic, readonly) NSURLResponse * _Nullable response; 18 | @property (nonatomic, readonly) NSError * _Nullable error; 19 | 20 | + (instancetype)resultWithData:(NSData * _Nullable)data 21 | response:(NSURLResponse * _Nullable)response 22 | error:(NSError * _Nullable)error; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/PINNSURLSessionDataTaskResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINNSURLSessionDataTaskResult.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/13/17. 6 | // 7 | // 8 | 9 | #import "PINNSURLSessionDataTaskResult.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface PINNSURLSessionDataTaskResult () 14 | @property (nonatomic, copy) NSData * _Nullable data; //intentionally use copy as the network data might be NSMutableData under the hood 15 | @property (nonatomic) NSURLResponse * _Nullable response; 16 | @property (nonatomic) NSError * _Nullable error; 17 | @end 18 | 19 | @implementation PINNSURLSessionDataTaskResult 20 | 21 | + (instancetype)resultWithData:(NSData * _Nullable)data 22 | response:(NSURLResponse * _Nullable)response 23 | error:(NSError * _Nullable)error 24 | { 25 | PINNSURLSessionDataTaskResult *result = [[PINNSURLSessionDataTaskResult alloc] init]; 26 | result.data = data; 27 | result.response = response; 28 | result.error = error; 29 | return result; 30 | } 31 | 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/PINPHImageManagerImageDataResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINPHImageManagerImageDataResult.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/13/17. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | /** 16 | * Immutable. Futures need to resolve to a single value, so this struct hold all of the values normally passed to the callback. 17 | */ 18 | @interface PINPHImageManagerImageDataResult : NSObject 19 | 20 | // Exposed consumers of PINFuture to write stubs that return fake data. 21 | + (instancetype)resultWithImageData:(NSData *)imageData 22 | dataUTI:(NSString *)dataUTI 23 | orientation:(UIImageOrientation)orientation 24 | info:(NSDictionary *)info; 25 | 26 | @property (nonatomic, readonly) NSData *imageData; 27 | @property (nonatomic, copy, readonly) NSString *dataUTI; 28 | @property (nonatomic, readonly) UIImageOrientation orientation; 29 | @property (nonatomic, readonly) NSDictionary *info; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /PINFuture/Classes/Categories/PINPHImageManagerImageDataResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINPHImageManagerImageDataResult.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/13/17. 6 | // 7 | // 8 | 9 | #import "PINPHImageManagerImageDataResult.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface PINPHImageManagerImageDataResult () 14 | @property (nonatomic) NSData *imageData; 15 | @property (nonatomic, copy) NSString *dataUTI; 16 | @property (nonatomic) UIImageOrientation orientation; 17 | @property (nonatomic) NSDictionary *info; 18 | @end 19 | 20 | @implementation PINPHImageManagerImageDataResult 21 | 22 | + (instancetype)resultWithImageData:(NSData *)imageData 23 | dataUTI:(NSString *)dataUTI 24 | orientation:(UIImageOrientation)orientation 25 | info:(NSDictionary *)info 26 | { 27 | PINPHImageManagerImageDataResult *result = [[PINPHImageManagerImageDataResult alloc] init]; 28 | result.imageData = imageData; 29 | result.dataUTI = dataUTI; 30 | result.orientation = orientation; 31 | result.info = info; 32 | return result; 33 | } 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINCancelToken.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Brandon Kase on 12/21/16. 3 | // 4 | 5 | #import 6 | 7 | #import 8 | 9 | typedef dispatch_block_t PINCancellationBlock; 10 | 11 | @interface PINCancelToken : NSObject 12 | 13 | +(PINCancelToken *)fold:(NSArray *)tokens; 14 | 15 | -(PINCancelToken *)initWithExecutor:(id)executor andBlock:(PINCancellationBlock)block; 16 | 17 | -(PINCancelToken *)and:(PINCancelToken *)other; 18 | 19 | -(void)cancel; 20 | 21 | @end 22 | 23 | // TODO: Why doesn't this work? 24 | // static PINCancelToken * PINCancelTokenIdentity = [[PINCancelToken alloc] initWithContext:[PINExecution immediate] andBlock:^{}]; 25 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINCancelToken.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Brandon Kase on 12/21/16. 3 | // 4 | 5 | #import "PINCancelToken.h" 6 | #import "PINExecutor.h" 7 | #import "PINFutureOnce.h" 8 | 9 | @interface PINCancelToken () 10 | @property (nonatomic, strong) PINCancellationBlock cancellationBlock; 11 | @end 12 | 13 | @implementation PINCancelToken { 14 | 15 | } 16 | 17 | + (PINCancelToken *)fold:(NSArray *)tokens { 18 | 19 | // TODO: replace with identity constant 20 | PINCancelToken * accumulator = [[PINCancelToken alloc] initWithExecutor:[PINExecutor immediate] andBlock:^{}]; 21 | if (tokens == nil) { 22 | return accumulator; 23 | } 24 | 25 | for (PINCancelToken * token in tokens) { 26 | accumulator = [accumulator and:token]; 27 | } 28 | return accumulator; 29 | } 30 | 31 | - (PINCancelToken *)initWithExecutor:(id)executor andBlock:(PINCancellationBlock)block { 32 | if (self = [super init]) { 33 | PINFutureOnce *once = [[PINFutureOnce alloc] init]; 34 | self.cancellationBlock = ^{ 35 | [executor execute:^{ 36 | [once performOnce:block]; 37 | }]; 38 | }; 39 | } 40 | return self; 41 | } 42 | 43 | // TODO: Reconsider parallel cancellation 44 | // * Downside is an extra async dispatch 45 | - (PINCancelToken *)and:(PINCancelToken *)other { 46 | if (other == nil) { 47 | return self; 48 | } 49 | 50 | __weak typeof(self) weakSelf = self; 51 | return [[PINCancelToken alloc] initWithExecutor:[PINExecutor immediate] andBlock:^{ 52 | weakSelf.cancellationBlock(); 53 | other.cancellationBlock(); 54 | }]; 55 | } 56 | 57 | - (void)cancel { 58 | self.cancellationBlock(); 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINDefines.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/19/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #define PIN_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) 10 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINExecutor.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINExecutor.h 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/7/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol PINExecutor 14 | - (void)execute:(dispatch_block_t)block; 15 | @end 16 | 17 | @interface PINExecutor : NSObject 18 | 19 | /** 20 | * Can't be created. This exists only for class methods. 21 | */ 22 | - (instancetype)init NS_UNAVAILABLE; 23 | 24 | /** 25 | * Executes a block on the main thread. The block is executed immediately if the executor is invoked from the Main thread. 26 | * If the executor is not invoked from the Main thread, then the block is dispatched to Main GCD queue. 27 | */ 28 | + (id)main; 29 | 30 | /** 31 | * Disaptches to background queue. 32 | */ 33 | + (id)background; 34 | 35 | /** 36 | * Executes immediately on whatever the current thread is without trampolining. 37 | * Use this as an optimization only if your block: 38 | * - will execute extremely quickly 39 | * - has no restirctions on where it can be executed from 40 | * - the overhead of executing the block is low compared to the cost of what the Executor does (usually a dispatch_async). 41 | */ 42 | + (id)immediate; 43 | 44 | /** 45 | * Disaptches on a specified queue. 46 | */ 47 | + (id)queue:(dispatch_queue_t)queue; 48 | 49 | /** 50 | * Disaptches on a specified dispatch queue. 51 | */ 52 | + (id)operationQueue:(NSOperationQueue *)operationQueue; 53 | 54 | @end 55 | 56 | NS_ASSUME_NONNULL_END 57 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINExecutor.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINExecutor.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 12/7/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINExecutor.h" 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | static inline BOOL isCurrentThreadMain() 16 | { 17 | return 0 != pthread_main_np(); 18 | } 19 | 20 | 21 | @interface PINExecutor () 22 | 23 | @property (atomic) void (^executorBlock)(dispatch_block_t); 24 | 25 | @end 26 | 27 | // This is very hot code path, so cache where we can to avoid creating Executor garbage. 28 | #define SHARED_INSTANCE(Type, constructorBlock) \ 29 | static Type value = nil; \ 30 | static dispatch_once_t onceToken; \ 31 | dispatch_once(&onceToken, ^{ \ 32 | value = constructorBlock(); \ 33 | }); \ 34 | return value; 35 | 36 | #define SHARED_EXECUTOR(constructorBlock) \ 37 | SHARED_INSTANCE(id, constructorBlock); 38 | 39 | @implementation PINExecutor 40 | 41 | - (instancetype)initWithExecutorBlock:(void (^)(dispatch_block_t))executorBlock 42 | { 43 | self = [super init]; 44 | if (self) { 45 | _executorBlock = executorBlock; 46 | } 47 | return self; 48 | } 49 | 50 | - (void)execute:(dispatch_block_t)block 51 | { 52 | self.executorBlock(block); 53 | } 54 | 55 | + (id)immediate 56 | { 57 | SHARED_EXECUTOR(^{ 58 | return [[PINExecutor alloc] initWithExecutorBlock:^(dispatch_block_t block) { 59 | block(); 60 | }]; 61 | }); 62 | } 63 | 64 | + (id)main 65 | { 66 | SHARED_EXECUTOR(^{ 67 | return [[PINExecutor alloc] initWithExecutorBlock:^(dispatch_block_t block) { 68 | if (isCurrentThreadMain()) { 69 | return [[self immediate] execute:block]; 70 | } else { 71 | return [[self queue:dispatch_get_main_queue()] execute:block]; 72 | } 73 | }]; 74 | }); 75 | } 76 | 77 | + (id)queue:(dispatch_queue_t)queue 78 | { 79 | return [[PINExecutor alloc] initWithExecutorBlock:^(dispatch_block_t block) { 80 | dispatch_async(queue, block); 81 | }]; 82 | } 83 | 84 | + (id)operationQueue:(NSOperationQueue *)operationQueue 85 | { 86 | return [[PINExecutor alloc] initWithExecutorBlock:^(dispatch_block_t block) { 87 | [operationQueue addOperationWithBlock:block]; 88 | }]; 89 | } 90 | 91 | + (id)background 92 | { 93 | SHARED_EXECUTOR(^{ 94 | return [self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; 95 | }); 96 | } 97 | 98 | @end 99 | 100 | NS_ASSUME_NONNULL_END 101 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+ChainSideEffect.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+ChainSideEffect.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/22/17. 6 | // Copyright (c) 2017 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface PINFuture (ChainSideEffect) 15 | 16 | /** 17 | * Like `executor:success:failure:`, but instead of returning `void` returns a PINFuture with the same value that won't resolve until the supplied callbacks have finished executing. 18 | */ 19 | - (PINFuture *)executor:(id)executor chainSuccess:(nullable void(^)(ObjectType value))success failure:(nullable void (^)(NSError * _Nonnull))failure PIN_WARN_UNUSED_RESULT; 20 | 21 | /** 22 | * Like `executor:completion:`, but instead of returning `void` returns a PINFuture with the same value that won't resolve until the supplied callback has finished executing. 23 | */ 24 | - (PINFuture *)executor:(id)executor chainCompletion:(void(^)(void))completion PIN_WARN_UNUSED_RESULT; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+ChainSideEffect.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+ChainSideEffect.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/22/17. 6 | // Copyright (c) 2017 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture+ChainSideEffect.h" 10 | 11 | #import "PINFutureMap+Map.h" 12 | #import "PINFuture+FlatMapError.h" 13 | 14 | @implementation PINFuture (ChainSideEffect) 15 | 16 | - (PINFuture *)executor:(id)executor chainSuccess:(nullable void(^)(id value))success failure:(nullable void (^)(NSError * _Nonnull))failure 17 | { 18 | PINFuture *chained = self; 19 | if (success != nil) { 20 | chained = [PINFutureMap map:chained executor:executor transform:^id _Nonnull(id _Nonnull value) { 21 | success(value); 22 | return value; 23 | }]; 24 | }; 25 | if (failure != nil) { 26 | chained = [chained executor:executor flatMapError:^PINFuture *(NSError *error) { 27 | failure(error); 28 | return [PINFuture withError:error]; 29 | }]; 30 | } 31 | return chained; 32 | } 33 | 34 | - (PINFuture *)executor:(id)executor chainCompletion:(void(^)(void))completion 35 | { 36 | NSParameterAssert(completion); 37 | return [self executor:executor chainSuccess:^(id _Nonnull value) { 38 | completion(); 39 | } failure:^(NSError * _Nonnull error) { 40 | completion(); 41 | }]; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+Completion.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+Completion.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/28/17. 6 | // Copyright (c) 2017 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface PINFuture (Completion) 14 | 15 | /** 16 | * Execute a block on success or on failure. Use this if you want to have a side-effect and nothing 17 | * needs to wait on your side-effect to complete. 18 | */ 19 | - (void)executor:(id)executor completion:(void(^)(void))completion; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+Completion.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+Completion.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/28/17. 6 | // Copyright (c) 2017 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture+Completion.h" 10 | 11 | @implementation PINFuture (Completion) 12 | 13 | - (void)executor:(id)executor completion:(void(^)(void))completion 14 | { 15 | return [self executor:executor success:^(id _Nonnull value) { 16 | completion(); 17 | } failure:^(NSError * _Nonnull error) { 18 | completion(); 19 | }]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+Delay.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+Delay.h 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 4/16/18. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | typedef PINFuture PINFutureNoValue; 13 | 14 | @interface PINFuture (Delay) 15 | 16 | /** Return a future that resolves after the specified amount of time. */ 17 | + (PINFutureNoValue *)delay:(NSTimeInterval)seconds; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+Delay.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+Delay.m 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 4/16/18. 6 | // 7 | 8 | #import "PINFuture+Delay.h" 9 | 10 | @implementation PINFuture (Delay) 11 | 12 | + (PINFutureNoValue *)delay:(NSTimeInterval)seconds 13 | { 14 | return [PINFutureNoValue withBlock:^(void (^_Nonnull resolve)(NSNull *_Nonnull), void (^_Nonnull reject)(NSError *_Nonnull)) { 15 | 16 | // Ideally, we'd dispatch to the same dispatch queue that callbacks attached to this future want to be on. 17 | // Since we can't know that though, we'll choose to dispatch to the background queue. This queue is more likely 18 | // to be serviced quickly than tha main queue. 19 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 20 | 21 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), queue, ^{ 22 | resolve([NSNull null]); 23 | }); 24 | }]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+Dispatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+Dispatch.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/5/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface PINFuture (Dispatch) 15 | 16 | /** 17 | * Create a future by executing a block. 18 | */ 19 | + (PINFuture *)executor:(id)executor block:(PINFuture * (^)(void))block PIN_WARN_UNUSED_RESULT; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+Dispatch.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+Dispatch.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/5/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture+Dispatch.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @implementation PINFuture (Dispatch) 14 | 15 | + (PINFuture *)executor:(id)executor block:(PINFuture * (^)(void))block; 16 | { 17 | NSParameterAssert(executor); 18 | return [PINFuture withBlock:^(void (^ _Nonnull resolve)(id _Nonnull), void (^ _Nonnull reject)(NSError * _Nonnull)) { 19 | // contextify, and execute 20 | [executor execute:^{ 21 | PINFuture *future = block(); 22 | [future executor:[PINExecutor immediate] success:^(id _Nonnull value) { 23 | resolve(value); 24 | } failure:^(NSError * _Nonnull error) { 25 | reject(error); 26 | }]; 27 | }]; 28 | }]; 29 | } 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+FlatMapError.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+FlatMapError.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface PINFuture (FlatMapError) 13 | 14 | - (PINFuture *)executor:(id)executor flatMapError:(PINFuture *(^)(NSError *error))flatMapError PIN_WARN_UNUSED_RESULT; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+FlatMapError.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+FlatMapError.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture+FlatMapError.h" 10 | 11 | #import "PINFutureError.h" 12 | 13 | @implementation PINFuture (FlatMapError) 14 | 15 | - (PINFuture *)executor:(id)executor flatMapError:(PINFuture *(^)(NSError *error))flatMapError 16 | { 17 | return [PINFuture withBlock:^(void (^resolve)(id), void (^reject)(NSError *)) { 18 | [self executor:executor success:^(id _Nonnull value) { 19 | // A value is passed through 20 | resolve(value); 21 | } failure:^(NSError * _Nonnull error) { 22 | // An error is given a chance to recover. 23 | PINFuture *recoveredFuture = flatMapError(error); 24 | NSString *reason = @"A flatMapError block returned nil, but it must return a PINFuture."; 25 | NSAssert(recoveredFuture != nil, reason); 26 | if (recoveredFuture == nil) { 27 | NSError *error = [PINFutureError errorWithReason:reason]; 28 | reject(error); 29 | } else { 30 | [recoveredFuture executor:executor success:^(id _Nonnull value) { 31 | resolve(value); 32 | } failure:^(NSError * _Nonnull error) { 33 | reject(error); 34 | }]; 35 | } 36 | }]; 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+GatherAll.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureUtil.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/5/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface PINFuture (GatherAll) 14 | 15 | /** 16 | * From an array of Futures, create one new future that resolves with an array of values. 17 | * If any of the original futures reject, then the returned future rejects with the error of the earliest rejection. 18 | */ 19 | + (PINFuture *> *)gatherAll:(NSArray *> *)sourceFutures PIN_WARN_UNUSED_RESULT; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+GatherAll.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+GatherAll.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/5/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @implementation PINFuture (GatherAll) 14 | 15 | + (PINFuture *)gatherAll:(NSArray *)sourceFutures 16 | { 17 | // A very naive implementation. 18 | if (sourceFutures.count == 0) { 19 | return [PINFuture withValue:@[]]; 20 | } else { 21 | PINFuture *future = [PINFuture withBlock:^(void (^ _Nonnull resolve)(id _Nonnull), void (^ _Nonnull reject)(NSError * _Nonnull)) { 22 | NSMutableArray *resolvedValues = [[NSMutableArray alloc] initWithCapacity:sourceFutures.count]; 23 | for (NSUInteger i = 0; i < sourceFutures.count; i++) { 24 | [resolvedValues addObject:[NSNull null]]; 25 | } 26 | __block NSUInteger remaining = sourceFutures.count; 27 | [sourceFutures enumerateObjectsUsingBlock:^(PINFuture * _Nonnull sourceFuture, NSUInteger index, BOOL * _Nonnull stop) { 28 | // Dispatch to be off of main. This work does not need to be on main. 29 | [sourceFuture executor:[PINExecutor background] 30 | success:^(id _Nonnull value) { 31 | @synchronized (resolvedValues) { 32 | resolvedValues[index] = value; 33 | remaining = remaining - 1; 34 | if (remaining == 0) { 35 | resolve(resolvedValues); 36 | } 37 | } 38 | } 39 | failure:^(NSError * _Nonnull error) { 40 | reject(error); 41 | }]; 42 | }]; 43 | }]; 44 | return future; 45 | } 46 | }; 47 | 48 | @end 49 | 50 | NS_ASSUME_NONNULL_END 51 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+GatherSome.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+GatherSome.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 8/16/17. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface PINFuture (GatherSome) 15 | 16 | /** 17 | * EXPERIMENTAL. This API may change to improve type safety. 18 | * 19 | * From an array of Futures, create one new future that resolves with an array of future values and/or nulls. 20 | * For an original future that resolve, the corresponding location in the final array is filled a value. 21 | * For an original future that rejects, the corresponding location in the final array is filled with `[NSNull null]`. 22 | */ 23 | + (PINFuture *)gatherSome:(NSArray *> *)sourceFutures PIN_WARN_UNUSED_RESULT; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+GatherSome.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+GatherSome.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 8/16/17. 6 | // 7 | // 8 | 9 | #import "PINFuture+GatherSome.h" 10 | #import "PINFuture+MapError.h" 11 | #import "PINFuture+GatherAll.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @implementation PINFuture (GatherSome) 16 | 17 | + (PINFuture *)gatherSome:(NSArray *)sourceFutures 18 | { 19 | NSMutableArray *recoverToNullFutures = [[NSMutableArray alloc] init]; 20 | for (PINFuture *future in sourceFutures) { 21 | PINFuture *recoverToNullFuture = [future executor:[PINExecutor immediate] mapError:^id(NSError *error) { 22 | return [NSNull null]; 23 | }]; 24 | [recoverToNullFutures addObject:recoverToNullFuture]; 25 | } 26 | return [PINFuture gatherAll:recoverToNullFutures]; 27 | }; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+Generated.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+Generated.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/28/17. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | // TODO(chris): Generate files in this directory. 16 | 17 | @interface PINFuture (Generated) 18 | 19 | - (void)executeOnMainSuccess:(nullable void(^)(ObjectType value))success failure:(nullable void(^)(NSError *error))failure; 20 | - (void)executeOnBackgroundSuccess:(nullable void(^)(ObjectType value))success failure:(nullable void(^)(NSError *error))failure; 21 | 22 | - (void)executeOnMainCompletion:(void(^)(void))completion; 23 | - (void)executeOnBackgroundCompletion:(void(^)(void))completion; 24 | 25 | + (PINFuture *)executeOnMainBlock:(PINFuture * (^)(void))block PIN_WARN_UNUSED_RESULT; 26 | + (PINFuture *)executeOnBackgroundBlock:(PINFuture * (^)(void))block PIN_WARN_UNUSED_RESULT; 27 | 28 | - (PINFuture *)executeOnMainChainSuccess:(nullable void(^)(ObjectType value))success failure:(nullable void (^)(NSError * error))failure PIN_WARN_UNUSED_RESULT; 29 | - (PINFuture *)executeOnBackgroundChainSuccess:(nullable void(^)(ObjectType value))success failure:(nullable void (^)(NSError * error))failure PIN_WARN_UNUSED_RESULT; 30 | 31 | - (PINFuture *)executeOnMainChainCompletion:(void(^)(void))completion PIN_WARN_UNUSED_RESULT; 32 | - (PINFuture *)executeOnBackgroundChainCompletion:(void(^)(void))completion PIN_WARN_UNUSED_RESULT; 33 | 34 | @end 35 | 36 | @interface PINFutureMap (Generated) 37 | 38 | + (PINFuture *)map:(PINFuture *)sourceFuture executeOnMainTransform:(ToType (^)(FromType fromValue))transform PIN_WARN_UNUSED_RESULT; 39 | + (PINFuture *)map:(PINFuture *)sourceFuture executeOnBackgroundTransform:(ToType (^)(FromType fromValue))transform PIN_WARN_UNUSED_RESULT; 40 | 41 | + (PINFuture *)flatMap:(PINFuture *)sourceFuture executeOnMainTransform:(PINFuture *(^)(FromType fromValue))transform PIN_WARN_UNUSED_RESULT; 42 | + (PINFuture *)flatMap:(PINFuture *)sourceFuture executeOnBackgroundTransform:(PINFuture *(^)(FromType fromValue))transform PIN_WARN_UNUSED_RESULT; 43 | 44 | @end 45 | 46 | NS_ASSUME_NONNULL_END 47 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+MapError.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+MapError.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface PINFuture (MapError) 13 | 14 | - (PINFuture *)executor:(id)executor mapError:(ObjectType (^)(NSError *error))mapError PIN_WARN_UNUSED_RESULT; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+MapError.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+MapError.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture+MapError.h" 10 | 11 | #import "PINFuture+FlatMapError.h" 12 | 13 | @implementation PINFuture (MapError) 14 | 15 | - (PINFuture *)executor:(id)executor mapError:(id (^)(NSError *error))mapError 16 | { 17 | return [self executor:executor flatMapError:^PINFuture * _Nonnull(NSError * _Nonnull error) { 18 | return [PINFuture withValue:mapError(error)]; 19 | }]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+MapToValue.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+MapToValue.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface PINFuture (MapToValue) 13 | 14 | + (PINFuture *)map:(PINFuture *)future toValue:(ObjectType)value PIN_WARN_UNUSED_RESULT; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+MapToValue.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+MapToValue.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFuture+MapToValue.h" 10 | 11 | #import "PINFutureMap+Map.h" 12 | 13 | @implementation PINFuture (MapToValue) 14 | 15 | + (PINFuture *)map:(PINFuture *)future toValue:(id)value 16 | { 17 | return [PINFutureMap map:future executor:[PINExecutor immediate] transform:^id _Nonnull(id _Nonnull fromValue) { 18 | return value; 19 | }]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+NoValue.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+NoValue.h 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 4/16/18. 6 | // 7 | 8 | #import 9 | 10 | /** 11 | * A Future that has no meaningful value. Technically, it does have a value (an NSNull). 12 | */ 13 | typedef PINFuture PINFutureNoValue; 14 | 15 | @interface PINFuture (NoValue) 16 | 17 | + (PINFutureNoValue *)withNull; 18 | 19 | - (PINFutureNoValue *)mapToNoValue; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture+NoValue.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture+NoValue.m 3 | // PINFuture 4 | // 5 | // Created by Chris Danford on 4/16/18. 6 | // 7 | 8 | #import "PINFuture+NoValue.h" 9 | 10 | #import "PINFuture+MapToValue.h" 11 | 12 | @implementation PINFuture (NoValue) 13 | 14 | + (PINFutureNoValue *)withNull 15 | { 16 | return [PINFuture withValue:[NSNull null]]; 17 | } 18 | 19 | - (PINFutureNoValue *)mapToNoValue 20 | { 21 | return [PINFuture map:self toValue:[NSNull null]]; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuture.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuture.h 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 11/14/16. 6 | // Copyright © 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | #import 34 | #import 35 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureAndCancelToken.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureAndCancelToken.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/16/17. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface PINFutureAndCancelToken : NSObject 15 | + (instancetype)newWithCancelToken:(PINCancelToken *)cancelToken future:(PINFuture *)future PIN_WARN_UNUSED_RESULT; 16 | 17 | - (PINCancelToken *)cancelToken; 18 | - (PINFuture *)future; 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureAndCancelToken.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureAndCancelToken.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 1/16/17. 6 | // 7 | 8 | #import "PINFutureAndCancelToken.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | /** 13 | * It's common that a function will return both a Future and a CancelToken to aborting that work. 14 | * This class composes a Future and a CancelToken so that it can easily be returned as a single 15 | * value from a function. 16 | */ 17 | @interface PINFutureAndCancelToken () 18 | @property (nonatomic) PINCancelToken *cancelToken; 19 | @property (nonatomic) PINFuture *future; 20 | @end 21 | 22 | @implementation PINFutureAndCancelToken 23 | 24 | + (instancetype)newWithCancelToken:(PINCancelToken *)cancelToken future:(PINFuture *)future 25 | { 26 | PINFutureAndCancelToken *ret = [[self alloc] init]; 27 | ret.cancelToken = cancelToken; 28 | ret.future = future; 29 | return ret; 30 | } 31 | 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureDefinition.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureDefinition.h 3 | // Copyright © 2022-present, Pinterest, Inc. All rights reserved. 4 | // 5 | 6 | #ifndef PINFutureDefinition_h 7 | #define PINFutureDefinition_h 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | /** 15 | * A super-simple future implementation. 16 | * This is a pure objective C implementation that: 17 | * - is thread-safe 18 | * - preserves type safety as much as possible 19 | * 20 | * Non-goals: 21 | * - Don't catch Exceptions in callbacks like Promises/A+. On this platform, Exceptions are generally fatal 22 | * and Errors aren't. 23 | * 24 | * Future improvement ideas: 25 | * - Support progress and cancellation. 26 | */ 27 | @interface PINFuture<__covariant ObjectType> : NSObject 28 | 29 | - (instancetype)init NS_UNAVAILABLE; 30 | 31 | /** 32 | * Return a future that is immediately resolved. 33 | */ 34 | + (PINFuture *)withValue:(ObjectType)value PIN_WARN_UNUSED_RESULT; 35 | 36 | /** 37 | * Return a future that is immediately rejected. 38 | */ 39 | + (PINFuture *)withError:(NSError *)error PIN_WARN_UNUSED_RESULT; 40 | 41 | /** 42 | * Construct a future from a block that eventually calls resolve or reject. 43 | * This is slightly dangerous and should only be used when adapting from callbacks to a Future because there's no compiler enforcement 44 | * that all paths of your block will eventually call either `resolve` or `reject`. 45 | */ 46 | + (PINFuture *)withBlock:(void(^)(void(^resolve)(ObjectType), void(^reject)(NSError *)))block PIN_WARN_UNUSED_RESULT; 47 | 48 | #pragma mark - attach callbacks 49 | 50 | /** 51 | * Execute a block on success or on failure. Use this if you want to have a side-effect and nothing 52 | * needs to wait on your side-effect to complete. 53 | */ 54 | - (void)executor:(id)executor success:(nullable void(^)(ObjectType value))success failure:(nullable void(^)(NSError *error))failure; 55 | 56 | @end 57 | 58 | NS_ASSUME_NONNULL_END 59 | 60 | 61 | #endif /* PINFutureDefinition_h */ 62 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureError.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINError.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 8/11/17. 6 | // 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface PINFutureError : NSObject 14 | + (NSError *)errorWithReason:(NSString *)reason; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureError.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINError.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 8/11/17. 6 | // 7 | // 8 | 9 | #import "PINFutureError.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | NSString *const PINFutureErrorDomain = @"com.coldbrewlabs.PINFuture"; 14 | 15 | @implementation PINFutureError 16 | 17 | + (NSError *)errorWithReason:(NSString *)reason 18 | { 19 | NSMutableDictionary *errorUserInfo = [NSMutableDictionary dictionary]; 20 | 21 | if ([reason length] > 0) { 22 | [errorUserInfo setValue:reason forKey:NSLocalizedFailureReasonErrorKey]; 23 | } 24 | 25 | NSError *error = [NSError errorWithDomain:PINFutureErrorDomain 26 | code:0 27 | userInfo:errorUserInfo]; 28 | return error; 29 | } 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureMap+FlatMap.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureMap+FlatMap.h 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 11/23/16. 6 | // Copyright © 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | @interface PINFutureMap (FlatMap) 19 | 20 | + (PINFuture *)flatMap:(PINFuture *)sourceFuture 21 | executor:(id)executor 22 | transform:(PINFuture *(^)(FromType fromValue))transform PIN_WARN_UNUSED_RESULT; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureMap+FlatMap.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureMap+FlatMap.m 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 11/23/16. 6 | // Copyright © 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFutureMap.h" 10 | 11 | #import "PINFuture.h" 12 | #import "PINFutureError.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @implementation PINFutureMap (FlatMap) 17 | 18 | + (PINFuture *)flatMap:(PINFuture *)sourceFuture 19 | executor:(id)executor 20 | transform:(PINFuture *(^)(id fromValue))transform 21 | { 22 | return [PINFuture withBlock:^(void (^resolve)(id), void (^reject)(NSError *)) { 23 | [sourceFuture executor:executor success:^(id value) { 24 | PINFuture *newFuture = transform(value); 25 | NSString *reason = @"A flatMap block returned nil, but it must return a PINFuture."; 26 | NSAssert(newFuture != nil, reason); 27 | if (newFuture == nil) { 28 | NSError *error = [PINFutureError errorWithReason:reason]; 29 | reject(error); 30 | } else { 31 | [newFuture executor:executor success:^(id value) { 32 | resolve(value); 33 | } failure:^(NSError *error) { 34 | reject(error); 35 | }]; 36 | } 37 | } failure:^(NSError *error) { 38 | reject(error); 39 | }]; 40 | }]; 41 | } 42 | 43 | @end 44 | 45 | NS_ASSUME_NONNULL_END 46 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureMap+Map.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureMap+Map.h 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 11/23/16. 6 | // Copyright © 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface PINFutureMap (Map) 16 | 17 | + (PINFuture *)map:(PINFuture *)sourceFuture 18 | executor:(id)executor 19 | transform:(ToType (^)(FromType fromValue))transform PIN_WARN_UNUSED_RESULT; 20 | 21 | @end 22 | 23 | @interface PINFutureMap (MapConvenience) 24 | 25 | + (PINFuture *)mapToValue:(PINFuture *)sourceFuture 26 | value:(ToType)value PIN_WARN_UNUSED_RESULT; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureMap+Map.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureMap+FlatMap.m 3 | // Pinterest 4 | // 5 | // Created by Chris Danford on 11/23/16. 6 | // Copyright © 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFutureMap+FlatMap.h" 10 | 11 | #import "PINFuture.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @implementation PINFutureMap (Map) 16 | 17 | + (PINFuture *)map:(PINFuture *)sourceFuture 18 | executor:(id)executor 19 | transform:(id (^)(id fromValue))transform 20 | { 21 | return [self flatMap:sourceFuture executor:executor transform:^PINFuture * _Nonnull(id _Nonnull fromValue) { 22 | return [PINFuture withValue:transform(fromValue)]; 23 | }]; 24 | } 25 | 26 | @end 27 | 28 | @implementation PINFutureMap (MapConvenience) 29 | 30 | + (PINFuture *)mapToValue:(PINFuture *)sourceFuture 31 | value:(id)value 32 | { 33 | return [self map:sourceFuture executor:[PINExecutor immediate] transform:^id _Nonnull(id _Nonnull fromValue) { 34 | return value; 35 | }]; 36 | } 37 | 38 | @end 39 | 40 | NS_ASSUME_NONNULL_END 41 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureMap.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureMap.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * This exists as a separate class so that the class can have two type parameters. 13 | */ 14 | @interface PINFutureMap : NSObject 15 | @end 16 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureMap.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureMap.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/8/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFutureMap.h" 10 | 11 | @implementation PINFutureMap 12 | @end 13 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureOnce.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureOnce.h 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/14/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | 13 | @interface PINFutureOnce : NSObject 14 | + (instancetype)new PIN_WARN_UNUSED_RESULT; 15 | - (void)performOnce:(dispatch_block_t)block; 16 | @end 17 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFutureOnce.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFutureOnce.m 3 | // Pods 4 | // 5 | // Created by Chris Danford on 12/14/16. 6 | // Copyright (c) 2016 Pinterest. All rights reserved. 7 | // 8 | 9 | #import "PINFutureOnce.h" 10 | 11 | @interface PINFutureOnce () 12 | @property (nonatomic) NSLock *lock; 13 | @property (nonatomic) BOOL performed; 14 | @end 15 | 16 | @implementation PINFutureOnce 17 | 18 | + (instancetype)new 19 | { 20 | return [[PINFutureOnce alloc] init]; 21 | } 22 | 23 | - (instancetype)init 24 | { 25 | self = [super init]; 26 | if (self != nil) { 27 | _lock = [NSLock new]; 28 | _performed = NO; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)performOnce:(dispatch_block_t)block 34 | { 35 | [self.lock lock]; 36 | if (self.performed == NO) { 37 | self.performed = YES; 38 | block(); 39 | } 40 | [self.lock unlock]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuturePending.h: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuturePending.h 3 | // Pods 4 | // 5 | // Created by Ray Cho on 2/20/20. 6 | // 7 | 8 | #import 9 | 10 | @interface PINFuturePending : NSObject 11 | 12 | - (instancetype)init; 13 | 14 | - (PINFuture *)future; 15 | 16 | - (void)fulfillWithValue:(ObjectType)value; 17 | - (void)rejectWithError:(NSError *)error; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PINFuture/Classes/PINFuturePending.m: -------------------------------------------------------------------------------- 1 | // 2 | // PINFuturePending.m 3 | // Pods 4 | // 5 | // Created by Ray Cho on 2/20/20. 6 | // 7 | 8 | #import "PINFuturePending.h" 9 | 10 | @interface PINFuturePending () 11 | @property (nonatomic, strong) PINFuture *pendingFuture; 12 | @property (nonatomic, copy) void (^resolve)(id value); 13 | @property (nonatomic, copy) void (^reject)(NSError *error); 14 | @end 15 | 16 | @implementation PINFuturePending 17 | 18 | - (instancetype)init 19 | { 20 | self = [super init]; 21 | if (self) { 22 | _pendingFuture = [PINFuture withBlock:^(void (^_Nonnull resolve)(id), void (^_Nonnull reject)(NSError *_Nonnull)) { 23 | self.resolve = resolve; 24 | self.reject = reject; 25 | }]; 26 | } 27 | return self; 28 | } 29 | 30 | - (PINFuture *)future 31 | { 32 | return self.pendingFuture; 33 | } 34 | 35 | - (void)fulfillWithValue:(id)value 36 | { 37 | NSParameterAssert(value != nil); 38 | NSParameterAssert([value isKindOfClass:[NSError class]] == NO); 39 | NSAssert(self.resolve, @"Underlying future has already been resolved."); 40 | 41 | if (self.resolve) { 42 | self.resolve(value); 43 | } 44 | 45 | self.resolve = nil; 46 | self.reject = nil; 47 | } 48 | 49 | - (void)rejectWithError:(NSError *)error 50 | { 51 | NSParameterAssert(error != nil); 52 | NSParameterAssert([error isKindOfClass:[NSError class]]); 53 | NSAssert(self.reject, @"Underlying future has already been resolved."); 54 | 55 | if (self.reject) { 56 | self.reject(error); 57 | } 58 | 59 | self.resolve = nil; 60 | self.reject = nil; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinterest/PINFuture/45452c873628abe43dd2aa4c03e0b5b0e826c66d/WORKSPACE -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------