diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a119ed5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# OS X +.DS_Store + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +*.xccheckout +profile +*.moved-aside +DerivedData +*.hmap +*.ipa + +# Bundler +.bundle + +Carthage +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control +# +# Note: if you ignore the Pods directory, make sure to uncomment +# `pod install` in .travis.yml +# +Pods/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3b5fb4b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +# references: +# * http://www.objc.io/issue-6/travis-ci.html +# * https://github.com/supermarin/xcpretty#usage + +osx_image: xcode7.3 +language: objective-c +cache: cocoapods +podfile: Example/Podfile +before_install: +- gem install cocoapods +- pod install --project-directory=Example +script: +- set -o pipefail && xcodebuild test -workspace Example/AYPromise.xcworkspace -scheme AYPromise-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty +- pod lib lint diff --git a/AYPromise.podspec b/AYPromise.podspec new file mode 100644 index 0000000..48c5913 --- /dev/null +++ b/AYPromise.podspec @@ -0,0 +1,42 @@ +# +# Be sure to run `pod lib lint AYPromise.podspec' to ensure this is a +# valid spec before submitting. +# +# Any lines starting with a # are optional, but their use is encouraged +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# + +Pod::Spec.new do |s| + s.name = 'AYPromise' + s.version = '1.0.0' + s.summary = 'Promise for objective-c.' + +# This description is used to generate tags and improve search results. +# * Think: What does it do? Why did you write it? What is the focus? +# * Try to keep it short, snappy and to the point. +# * Write the description between the DESC delimiters below. +# * Finally, don't worry about the indent, CocoaPods strips it! + +# s.description = <<-DESC +# TODO: Add long description of the pod here. +# DESC + + s.homepage = 'https://github.com/alan-yeh/AYPromise' + # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.author = { 'Alan Yeh' => 'alan@yerl.cn' } + s.source = { :git => 'https://github.com/alan-yeh/AYPromise.git', :tag => s.version.to_s } + # s.social_media_url = 'https://twitter.com/' + + s.ios.deployment_target = '6.0' + + s.source_files = 'AYPromise/Classes/**/*' + + # s.resource_bundles = { + # 'AYPromise' => ['AYPromise/Assets/*.png'] + # } + + # s.public_header_files = 'Pod/Classes/**/*.h' + # s.frameworks = 'UIKit', 'MapKit' + # s.dependency 'AFNetworking', '~> 2.3' +end diff --git a/AYPromise/Assets/.gitkeep b/AYPromise/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AYPromise/Classes/.gitkeep b/AYPromise/Classes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AYPromise/Classes/AYPromise.h b/AYPromise/Classes/AYPromise.h new file mode 100644 index 0000000..3d28761 --- /dev/null +++ b/AYPromise/Classes/AYPromise.h @@ -0,0 +1,121 @@ +// +// AYPromise.h +// AYPromise +// +// Created by PoiSon on 16/2/15. +// Copyright © 2016年 PoiSon. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT NSString * const AYPromiseInternalErrorsKey; +/** + * 快速构建NSError对象 + * + * @param localizedDescription 错误描述 + * @param internalErrors 内部错误,通过error.userInfo[AYPromiseInternalErrorsKey]可以获得 + */ +FOUNDATION_EXPORT NSError *NSErrorMake(id _Nullable internalErrors, NSString *localizedDescription, ...) NS_FORMAT_FUNCTION(2,3); + +typedef void (^PSResolve)(id __nullable result); + +typedef NS_ENUM(NSUInteger, AYPromiseState) { + AYPromiseStatePending = 1 << 0, /**< 待执行状态 */ + AYPromiseStateFulfilled = 1 << 1, /**< 成功状态 */ + AYPromiseStateRejected = 1 << 2 /**< 失败状态 */ +}; + +@interface AYPromise : NSObject +- (instancetype)init __attribute__((unavailable("不允许直接实例化"))); ++ (instancetype)new __attribute__((unavailable("不允许直接实例化"))); + +@property (nonatomic, readonly, assign) AYPromiseState state; /**< Promise当前状态 */ +@property (nonatomic, readonly) id value; /**< Promise的执行结果,失败时,执行结果为NSError对象 */ +@end + +/** + * CommonJS Promise/A 标准接口 + */ +@interface AYPromise (CommonJS) +/** + * 接受的参数如果是Promise对象就直接返回 + * 如果参数是NSError对象,就会生成一个失败态(rejected)的promise,并传递给之后的catch + * 参数为其它的值则生成一个成功态(fulfilled)的promise,并传递给之后的then + */ ++ (AYPromise *(^)(id _Nullable value))resolve; + +/** + * AYPromise.all用来包装一系列的promise对象,返回一个包装后的promise对象,我们称之为A + * 1. 当所有的promise对象都变成成功态(fulfilled)后,这个包装后的A才会把自己变成成功状态。 + * A会等最慢的那个promise对象变成成功态(fulfilled)后才把自己变成成功态。 + * 2. 只要其中一个promise对象变成失败态(rejected),包装后的A就变成rejected, + * 并且第一个rejected传递的值,会传递给A后面的catch。 + */ ++ (AYPromise *(^)(NSArray *promises))all; + +/** + * AYPromise.race用来包装一系列的promise对象,返回一个包装后的promise对象,我们称之为R + * 1. 只要其中的一个promise对象变成成功态(fulfilled)后,这个包装后的R就会变成成功态(fulfilled), + * 并且其它的promise不再执行。 + * 2. 当所有的promise对象都变成失败态(rejected)后,这个包装后的R才会把自己变成失败状态。 + */ ++ (AYPromise *(^)(NSArray *promises))race; + +/** + * then接受成功回调 + * 如果Promise对象处于预备状态就等待,直到状态改变才开始执行 + * 如果Promise对象处于成功态,再用then添加回调就直接调用对应的回调 + * 如果then的返回值不是Promise,会作为下一个then的参数 + * 如果then的返回值是Promise对象,那么之后的then添加的操作函数会被托管给返回的Promise对象 + * 如果value是一个Promise,则认为then的返回值是Promise对象 + */ +- (AYPromise *(^)(id value))then; + +/** + * catch接受失败回调 + * 如果promise对象处于预备状态就等待,直到状态改变才开始执行 + * 如果promise对象处于失败态,再用catch添加回调就直接调用对应的回调 + * 如果catch的返回值不是promise,会作为下一个then的参数 + * 如果catch的返回值是一个新的promise对象,那么之后的then添加的操作函数会被托管给新的promise对象 + */ +- (AYPromise *(^)(id block))catch; +@end + +/** + * 标准接口之外添加的便利方法 + */ +@interface AYPromise (Extension) +- (AYPromise *(^)(id block))thenAsync;/**< 异步执行 */ +- (AYPromise *(^)(NSTimeInterval delaySecond, id block))thenDelay;/**< 延迟执行 */ +- (AYPromise *(^)(dispatch_queue_t queue, id block))thenOn;/**< 在指定线程执行 */ +- (AYPromise *(^)(void (^resolver)(id result, PSResolve resolve)))thenPromise;/**< 需要回调的任务 */ +- (AYPromise *(^)(id block))catchAsync;/**< 异步处理错误 */ +- (AYPromise *(^)(dispatch_queue_t queue, id block))catchOn;/**< 在指定线程处理错误 */ +- (AYPromise *(^)(id block))always;/**< 无论错误还是正确都执行 */ +@end +/** + * 创建Promise对象 + * + * 如果value是block,则创建一个Pending状态的Promise并同步执行block + * 如果value是Promise, 则直接返回Promise + * 如果vlaue是数组,则返回Promise.all封装的Promise + * 如果vlaue是NSError对象,则返回一个Rejected状态的Promise + * 如果vlaue是其它的对象,则返回一个Fulfilled状态的Promise + */ +FOUNDATION_EXPORT AYPromise *AYPromiseWith(_Nullable id value); +/** + * 创建Promise对象 + * + * 如果value是block,则创建一个Pending状态的Promise并异步执行block + * 其它同上 + */ +FOUNDATION_EXPORT AYPromise *AYPromiseAsyncWith(_Nullable id value); +/** + * 创建一个需要回调的Promise + */ +FOUNDATION_EXPORT AYPromise *AYPromiseWithResolve(void (^)(PSResolve resolve)); +NS_ASSUME_NONNULL_END + + diff --git a/AYPromise/Classes/AYPromise.m b/AYPromise/Classes/AYPromise.m new file mode 100644 index 0000000..f1a8e3f --- /dev/null +++ b/AYPromise/Classes/AYPromise.m @@ -0,0 +1,449 @@ +// +// AYPromise.m +// AYPromise +// +// Created by PoiSon on 16/2/21. +// Copyright © 2016年 PoiSon. All rights reserved. +// + +#import "AYPromise.h" +#import + +#define isError(obj) [obj isKindOfClass:[NSError class]] +#define isPromise(obj) [obj isKindOfClass:[AYPromise class]] +#define isBlock(obj) [obj isKindOfClass:NSClassFromString(@"NSBlock")] +#define isArray(obj) [obj isKindOfClass:[NSArray class]] + +NSString * const AYPromiseInternalErrorsKey = @"AYPromiseInternalErrorsKey"; +NSError *NSErrorMake(id _Nullable internalErrors, NSString *localizedDescription, ...){ + static NSString *domain = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + domain = [NSBundle mainBundle].infoDictionary[@"CFBundleName"]; + domain = domain ?: @"none domain"; + }); + + va_list desc_args; + va_start(desc_args, localizedDescription); + NSString *desc = [[NSString alloc] initWithFormat:localizedDescription arguments:desc_args]; + va_end(desc_args); + + return [NSError errorWithDomain:domain code:-1000 userInfo:@{ + NSLocalizedDescriptionKey: desc, + AYPromiseInternalErrorsKey: internalErrors ?: [NSNull null] + }]; +} + +/** + * @see CTObjectiveCRuntimeAdditions https://github.com/ebf/CTObjectiveCRuntimeAdditions + */ +struct PSBlockLiteral { + void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock + int flags; + int reserved; + void (*invoke)(void *, ...); + struct ps_block_descriptor { + unsigned long int reserved; // NULL + unsigned long int size; // sizeof(struct Block_literal_1) + // optional helper functions + void (*copy_helper)(void *dst, void *src); // IFF (1<<25) + void (*dispose_helper)(void *src); // IFF (1<<25) + // required ABI.2010.3.16 + const char *signature; // IFF (1<<30) + } *descriptor; + // imported variables +}; + +typedef NS_ENUM(NSUInteger, PSBlockDescriptionFlags) { + PSBlockDescriptionFlagsHasCopyDispose = (1 << 25), + PSBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code + PSBlockDescriptionFlagsIsGlobal = (1 << 28), + PSBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE + PSBlockDescriptionFlagsHasSignature = (1 << 30) +}; + +static NSMethodSignature *_signatureForBlock(id block) { + if (!block) + return nil; + + struct PSBlockLiteral *blockRef = (__bridge struct PSBlockLiteral *)block; + PSBlockDescriptionFlags flags = (PSBlockDescriptionFlags)blockRef->flags; + + if (flags & PSBlockDescriptionFlagsHasSignature) { + void *signatureLocation = blockRef->descriptor; + signatureLocation += sizeof(unsigned long int); + signatureLocation += sizeof(unsigned long int); + + if (flags & PSBlockDescriptionFlagsHasCopyDispose) { + signatureLocation += sizeof(void (*)(void *dst, void *src)); + signatureLocation += sizeof(void (*)(void *src)); + } + + const char *signature = (*(const char **)signatureLocation); + return [NSMethodSignature signatureWithObjCTypes:signature]; + } + return nil; +} + +static id _call_block(id block, id args){ + NSMethodSignature *signature = _signatureForBlock(block); + + const char returnType = signature.methodReturnType[0]; + if (returnType != '@' && returnType != 'v') { + [NSException raise:NSInvalidArgumentException format:@"AYPromise无法处理非对象返回值,block返回值必须是OC对象"]; + } + + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setTarget:[block copy]]; + if (args && signature.numberOfArguments > 1) { + [invocation setArgument:&args atIndex:1]; + } + + @try { + [invocation invoke]; + + if (returnType == 'v') { return nil; } + __unsafe_unretained id result; + [invocation getReturnValue:&result]; + return result; + } + @catch (NSError *error) {// just catch NSError + return error; + } +} + +@interface AYPromise() +@property (nonatomic) dispatch_queue_t barrier; +@property (nonatomic, strong) id value; +@property (nonatomic, strong) NSMutableArray *handlers; +@end + +@implementation AYPromise +- (dispatch_queue_t)barrier{ + return _barrier ?: (_barrier = dispatch_queue_create("cn.yerl.promise.barrier", DISPATCH_QUEUE_CONCURRENT)); +} + +- (NSMutableArray *)handlers{ + return _handlers ?: (_handlers = [NSMutableArray new]); +} + +/** + * 创建一个未执行的Promise + */ +- (instancetype)initWithResolver:(void (^)(PSResolve))resolver{ + if (self = [super init]) { + _state = AYPromiseStatePending; + + PSResolve __presolve = ^(id result){ + __block NSMutableArray *handlers; + //保证执行链的顺序执行 + dispatch_barrier_sync(self.barrier, ^{ + //race + if (self.state == AYPromiseStatePending) { + handlers = self.handlers; + + if (isError(result)) { + _state = AYPromiseStateRejected; + }else{ + _state = AYPromiseStateFulfilled; + } + self.value = result; + } + }); + for (PSResolve handler in handlers) { + handler(result); + } + }; + + PSResolve __resolve = ^(id result){ + if (self.state & AYPromiseStatePending) { + if (isPromise(result)) { + [result pipe:__presolve]; + }else{ + __presolve(result); + } + } + }; + //创建好之后,直接开始执行任务 + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + resolver(__resolve); + } + @catch (NSError *error) { + __resolve(error); + } + }); + } + return self; +} + +/** + * 创建一个已完成的Promise + * 如果Value是Promise对象,则直接返回 + * 如果Value是NSError对象,则返回一个Rejected状态的Promise + * 如果Vlaue是其它对象,则返回一个Fulfilled状态的Promise + */ +- (instancetype)initWithValue:(id)value{ + if (isPromise(value)) { + return value; + } + if (self = [super init]) { + if (isError(value)) { + _state = AYPromiseStateRejected; + self.value = value; + }else{ + _state = AYPromiseStateFulfilled; + self.value = value; + } + } + return self; +} +/** + * 拼接Promise + * 如果当前Promise还没有被执行,则接接在当前Promise的执行栈中 + * 如果当前Promise已经执行了,则直接将当前Promise的值传给下一个执行者 + */ +- (void)pipe:(PSResolve)resolve{ + if (self.state == AYPromiseStatePending) { + [self.handlers addObject:resolve]; + }else{ + resolve(self.value); + } +} + +/** + * 创建一个Promise,并拼接在Promise(self)的执行链中 + * + */ +static inline AYPromise *__pipe(AYPromise *self, void(^then)(id, PSResolve)){ + return [[AYPromise alloc] initWithResolver:^(PSResolve resolver) { + [self pipe:^(id result) { + then(result, resolver);//handle resule of previous promise + }]; + }]; +} + +/** + * 将Promise拼接在self之后,仅处理正确的逻辑 + */ +static inline AYPromise *__then(AYPromise *self, dispatch_queue_t queue, id block){ + return __pipe(self, ^(id result, PSResolve resolver) { + if (isError(result)) { + resolver(result); + }else{ + dispatch_async(queue, ^{ + resolver(_call_block(block, result)); + }); + } + }); +} +/** + * 将Promise接接在self之后,仅处理错误的逻辑 + */ +static inline AYPromise *__catch(AYPromise *self, dispatch_queue_t queue, id block){ + return __pipe(self, ^(id result, PSResolve resolver) { + if (isError(result)) { + dispatch_async(queue, ^{ + resolver(_call_block(block, result)); + }); + }else{ + resolver(result); + } + }); +} + +@end + +@implementation AYPromise (CommonJS) ++ (AYPromise *(^)(id))resolve{ + return ^(id value){ + return [[self alloc] initWithValue:value]; + }; +} + ++ (AYPromise *(^)(NSArray *))all{ + return ^(NSArray *promises){ + return [[AYPromise alloc] initWithResolver:^(PSResolve resolve) { + NSAssert(isArray(promises), @"all can only hand array"); + + __block int64_t totalCount = [promises count]; + for (__strong id promise in promises) { + if (!isPromise(promise)) { + promise = AYPromise.resolve(promise); + } + [promise pipe:^(id result) { + if (isError(result)) { + resolve([NSError errorWithDomain:@"cn.yerl.promise" + code:-1000 + userInfo:@{NSLocalizedDescriptionKey: [result localizedDescription], + AYPromiseInternalErrorsKey: result}]); + }else if (OSAtomicDecrement64(&totalCount) == 0){ + id results = [NSMutableArray new]; + for (AYPromise *promise in promises) { + id value = isPromise(promise) ? [promise value] : promise; + [results addObject:value ?: [NSNull null]]; + } + resolve(results); + } + }]; + } + }]; + }; +} + ++ (AYPromise *(^)(NSArray *))race{ + return ^(NSArray *promises){ + NSAssert(isArray(promises), @"race can only hand array"); + + return [[AYPromise alloc] initWithResolver:^(PSResolve resolve) { + __block int64_t totalCount = [promises count]; + for (__strong id promise in promises) { + if (!isPromise(promise)) { + promise = [[AYPromise alloc] initWithValue:promise]; + } + [promise pipe:^(id result) { + if (!isError(result)) { + resolve(result); + }else if (OSAtomicDecrement64(&totalCount) == 0){ + id errors = [NSMutableArray new]; + for (AYPromise *promise in promises) { + [errors addObject:isPromise(promise) ? [promise value] : promise]; + } + resolve([NSError errorWithDomain:@"cn.yerl.promise" + code:-1000 + userInfo:@{NSLocalizedDescriptionKey: @"all promise were rejected", + AYPromiseInternalErrorsKey: errors}]); + } + }]; + } + }]; + }; +} + +- (AYPromise *(^)(id))then{ + return ^id(id value){ + if (isBlock(value)) { + return __then(self, dispatch_get_main_queue(), value); + }else if (isPromise(value)){ + return __then(self, dispatch_get_main_queue(), ^{ + return value; + }); + }else{ + NSAssert(NO, @"then can only handle block and promise"); + return nil; + } + }; +} + +- (AYPromise *(^)(id))catch{ + return ^(id block){ + NSAssert(isBlock(block), @"catch can only handle block."); + return __catch(self, dispatch_get_main_queue(), block); + }; +} +@end + +@implementation AYPromise (Extension) +- (AYPromise *(^)(id))thenAsync{ + return ^(id block){ + NSAssert(isBlock(block), @"thenAsync can only handle block."); + return __then(self, dispatch_get_global_queue(0, 0), block); + }; +} + +- (AYPromise *(^)(NSTimeInterval, id))thenDelay{ + return ^(NSTimeInterval delaySecond, id block){ + NSAssert(isBlock(block), @"thenDelay can only handle block."); + return __pipe(self, ^(id result, PSResolve resolver) { + if (isError(result)) { + resolver(result); + }else{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySecond * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + resolver(_call_block(block, result)); + }); + }); + } + }); + }; +} + +- (AYPromise *(^)(dispatch_queue_t, id))thenOn{ + return ^(dispatch_queue_t queue, id block){ + NSAssert(isBlock(block), @"thenOn can only handle block."); + return __then(self, queue, block); + }; +} + +- (AYPromise * (^)(void (^)(id, PSResolve)))thenPromise{ + return ^(void (^resolver)(id, PSResolve)){ + return __pipe(self, ^(id result, PSResolve resolve) { + if (!isError(result)) { + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + resolver(result, resolve); + } + @catch (NSError *error) { + resolve(error); + } + }); + }else{ + resolve(result); + } + }); + }; +} + +- (AYPromise *(^)(id))catchAsync{ + return ^(id block){ + NSAssert(isBlock(block), @"catchAsync can only handle block."); + return __catch(self, dispatch_get_global_queue(0, 0), block); + }; +} + +- (AYPromise *(^)(dispatch_queue_t, id))catchOn{ + return ^(dispatch_queue_t queue, id block){ + NSAssert(isBlock(block), @"catchOn can only handle block."); + return __catch(self, queue, block); + }; +} + +- (AYPromise *(^)(id))always{ + return ^(id block){ + NSAssert(isBlock(block), @"always can only handle block."); + return __pipe(self, ^(id result, PSResolve resolver) { + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + resolver(_call_block(block, result)); + } + @catch (NSError *error) { + resolver(error); + } + }); + }); + }; +} +@end + +AYPromise *AYPromiseWith(id value){ + if (isBlock(value)) { + return AYPromise.resolve(nil).then(value); + }else if (isArray(value)){ + return AYPromise.all(value); + }else { + return [[AYPromise alloc] initWithValue:value]; + } +} + +AYPromise *AYPromiseAsyncWith(id value){ + if (isBlock(value)) { + return AYPromise.resolve(nil).thenAsync(value); + }else{ + return AYPromiseWith(value); + } +} + +AYPromise *AYPromiseWithResolve(void (^resolver)(PSResolve)){ + return [[AYPromise alloc] initWithResolver:resolver]; +} + diff --git a/Example/AYPromise.xcodeproj/project.pbxproj b/Example/AYPromise.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f12a16e --- /dev/null +++ b/Example/AYPromise.xcodeproj/project.pbxproj @@ -0,0 +1,627 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0F86ABB9D89AAE464DF7A7E0 /* Pods_AYPromise_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E7D6369F3C3E8D0E5CC5566 /* Pods_AYPromise_Tests.framework */; }; + 1D37FD491D42044700A690FD /* ErrorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D37FD481D42044700A690FD /* ErrorTest.m */; }; + 1D37FD4B1D42048800A690FD /* PromiseTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D37FD4A1D42048800A690FD /* PromiseTest.m */; }; + 1D37FD4D1D4204B400A690FD /* ThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D37FD4C1D4204B400A690FD /* ThreadTest.m */; }; + 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; + 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; + 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; + 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; + 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; + 6003F59E195388D20070C39A /* AYAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* AYAppDelegate.m */; }; + 6003F5A7195388D20070C39A /* AYViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* AYViewController.m */; }; + 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; + 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; + 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; + 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; + 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; + 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; + 968AB3FCCC170A3D20F2A4A0 /* Pods_AYPromise_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA3571645A959409DE7AEE84 /* Pods_AYPromise_Example.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6003F589195388D20070C39A; + remoteInfo = AYPromise; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 1D37FD481D42044700A690FD /* ErrorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ErrorTest.m; sourceTree = ""; }; + 1D37FD4A1D42048800A690FD /* PromiseTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PromiseTest.m; sourceTree = ""; }; + 1D37FD4C1D4204B400A690FD /* ThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadTest.m; sourceTree = ""; }; + 6003F58A195388D20070C39A /* AYPromise_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AYPromise_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 6003F595195388D20070C39A /* AYPromise-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "AYPromise-Info.plist"; sourceTree = ""; }; + 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 6003F59B195388D20070C39A /* AYPromise-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AYPromise-Prefix.pch"; sourceTree = ""; }; + 6003F59C195388D20070C39A /* AYAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AYAppDelegate.h; sourceTree = ""; }; + 6003F59D195388D20070C39A /* AYAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AYAppDelegate.m; sourceTree = ""; }; + 6003F5A5195388D20070C39A /* AYViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AYViewController.h; sourceTree = ""; }; + 6003F5A6195388D20070C39A /* AYViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AYViewController.m; sourceTree = ""; }; + 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 6003F5AE195388D20070C39A /* AYPromise_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AYPromise_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; + 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; + 7E7D6369F3C3E8D0E5CC5566 /* Pods_AYPromise_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AYPromise_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + 92DBDC6C0A07426C2C681F2D /* Pods-AYPromise_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AYPromise_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-AYPromise_Example/Pods-AYPromise_Example.release.xcconfig"; sourceTree = ""; }; + AA3571645A959409DE7AEE84 /* Pods_AYPromise_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AYPromise_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AFBF088C3C4B7C82143E70F9 /* Pods-AYPromise_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AYPromise_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AYPromise_Example/Pods-AYPromise_Example.debug.xcconfig"; sourceTree = ""; }; + B76630D489B91B9181464551 /* AYPromise.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = AYPromise.podspec; path = ../AYPromise.podspec; sourceTree = ""; }; + B8DA825C2BA96E8E9BFA2744 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + CFAB5D4D2D9C7911DA26774C /* Pods-AYPromise_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AYPromise_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AYPromise_Tests/Pods-AYPromise_Tests.debug.xcconfig"; sourceTree = ""; }; + DEA733E1CF3B4EB9D43D8FFA /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; + E762518FB19AF71D54D259C8 /* Pods-AYPromise_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AYPromise_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AYPromise_Tests/Pods-AYPromise_Tests.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6003F587195388D20070C39A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, + 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, + 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, + 968AB3FCCC170A3D20F2A4A0 /* Pods_AYPromise_Example.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6003F5AB195388D20070C39A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, + 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, + 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, + 0F86ABB9D89AAE464DF7A7E0 /* Pods_AYPromise_Tests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6003F581195388D10070C39A = { + isa = PBXGroup; + children = ( + 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, + 6003F593195388D20070C39A /* Example for AYPromise */, + 6003F5B5195388D20070C39A /* Tests */, + 6003F58C195388D20070C39A /* Frameworks */, + 6003F58B195388D20070C39A /* Products */, + F6D241D8C123E42D52F4E4A1 /* Pods */, + ); + sourceTree = ""; + }; + 6003F58B195388D20070C39A /* Products */ = { + isa = PBXGroup; + children = ( + 6003F58A195388D20070C39A /* AYPromise_Example.app */, + 6003F5AE195388D20070C39A /* AYPromise_Tests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 6003F58C195388D20070C39A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6003F58D195388D20070C39A /* Foundation.framework */, + 6003F58F195388D20070C39A /* CoreGraphics.framework */, + 6003F591195388D20070C39A /* UIKit.framework */, + 6003F5AF195388D20070C39A /* XCTest.framework */, + AA3571645A959409DE7AEE84 /* Pods_AYPromise_Example.framework */, + 7E7D6369F3C3E8D0E5CC5566 /* Pods_AYPromise_Tests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 6003F593195388D20070C39A /* Example for AYPromise */ = { + isa = PBXGroup; + children = ( + 6003F59C195388D20070C39A /* AYAppDelegate.h */, + 6003F59D195388D20070C39A /* AYAppDelegate.m */, + 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, + 6003F5A5195388D20070C39A /* AYViewController.h */, + 6003F5A6195388D20070C39A /* AYViewController.m */, + 6003F5A8195388D20070C39A /* Images.xcassets */, + 6003F594195388D20070C39A /* Supporting Files */, + ); + name = "Example for AYPromise"; + path = AYPromise; + sourceTree = ""; + }; + 6003F594195388D20070C39A /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 6003F595195388D20070C39A /* AYPromise-Info.plist */, + 6003F596195388D20070C39A /* InfoPlist.strings */, + 6003F599195388D20070C39A /* main.m */, + 6003F59B195388D20070C39A /* AYPromise-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 6003F5B5195388D20070C39A /* Tests */ = { + isa = PBXGroup; + children = ( + 6003F5B6195388D20070C39A /* Supporting Files */, + 1D37FD481D42044700A690FD /* ErrorTest.m */, + 1D37FD4A1D42048800A690FD /* PromiseTest.m */, + 1D37FD4C1D4204B400A690FD /* ThreadTest.m */, + ); + path = Tests; + sourceTree = ""; + }; + 6003F5B6195388D20070C39A /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 6003F5B7195388D20070C39A /* Tests-Info.plist */, + 6003F5B8195388D20070C39A /* InfoPlist.strings */, + 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { + isa = PBXGroup; + children = ( + B76630D489B91B9181464551 /* AYPromise.podspec */, + B8DA825C2BA96E8E9BFA2744 /* README.md */, + DEA733E1CF3B4EB9D43D8FFA /* LICENSE */, + ); + name = "Podspec Metadata"; + sourceTree = ""; + }; + F6D241D8C123E42D52F4E4A1 /* Pods */ = { + isa = PBXGroup; + children = ( + AFBF088C3C4B7C82143E70F9 /* Pods-AYPromise_Example.debug.xcconfig */, + 92DBDC6C0A07426C2C681F2D /* Pods-AYPromise_Example.release.xcconfig */, + CFAB5D4D2D9C7911DA26774C /* Pods-AYPromise_Tests.debug.xcconfig */, + E762518FB19AF71D54D259C8 /* Pods-AYPromise_Tests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6003F589195388D20070C39A /* AYPromise_Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "AYPromise_Example" */; + buildPhases = ( + 9D5360476AEE413E105BB188 /* [CP] Check Pods Manifest.lock */, + 6003F586195388D20070C39A /* Sources */, + 6003F587195388D20070C39A /* Frameworks */, + 6003F588195388D20070C39A /* Resources */, + 888AA59F9A4C6EBD8A2A9C0A /* [CP] Embed Pods Frameworks */, + 6AC1667516214076D76A5BDA /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AYPromise_Example; + productName = AYPromise; + productReference = 6003F58A195388D20070C39A /* AYPromise_Example.app */; + productType = "com.apple.product-type.application"; + }; + 6003F5AD195388D20070C39A /* AYPromise_Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "AYPromise_Tests" */; + buildPhases = ( + EFA78DA3FA0AA846AE8FE514 /* [CP] Check Pods Manifest.lock */, + 6003F5AA195388D20070C39A /* Sources */, + 6003F5AB195388D20070C39A /* Frameworks */, + 6003F5AC195388D20070C39A /* Resources */, + 741A6906523AAED248E6C62C /* [CP] Embed Pods Frameworks */, + 1626F3519461E1076E9D10A9 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6003F5B4195388D20070C39A /* PBXTargetDependency */, + ); + name = AYPromise_Tests; + productName = AYPromiseTests; + productReference = 6003F5AE195388D20070C39A /* AYPromise_Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6003F582195388D10070C39A /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = AY; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Alan Yeh"; + TargetAttributes = { + 6003F5AD195388D20070C39A = { + TestTargetID = 6003F589195388D20070C39A; + }; + }; + }; + buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "AYPromise" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6003F581195388D10070C39A; + productRefGroup = 6003F58B195388D20070C39A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6003F589195388D20070C39A /* AYPromise_Example */, + 6003F5AD195388D20070C39A /* AYPromise_Tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6003F588195388D20070C39A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, + 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, + 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6003F5AC195388D20070C39A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1626F3519461E1076E9D10A9 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AYPromise_Tests/Pods-AYPromise_Tests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6AC1667516214076D76A5BDA /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AYPromise_Example/Pods-AYPromise_Example-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 741A6906523AAED248E6C62C /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AYPromise_Tests/Pods-AYPromise_Tests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 888AA59F9A4C6EBD8A2A9C0A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AYPromise_Example/Pods-AYPromise_Example-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9D5360476AEE413E105BB188 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + EFA78DA3FA0AA846AE8FE514 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6003F586195388D20070C39A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6003F59E195388D20070C39A /* AYAppDelegate.m in Sources */, + 6003F5A7195388D20070C39A /* AYViewController.m in Sources */, + 6003F59A195388D20070C39A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6003F5AA195388D20070C39A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D37FD4D1D4204B400A690FD /* ThreadTest.m in Sources */, + 1D37FD491D42044700A690FD /* ErrorTest.m in Sources */, + 1D37FD4B1D42048800A690FD /* PromiseTest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6003F589195388D20070C39A /* AYPromise_Example */; + targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 6003F596195388D20070C39A /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 6003F597195388D20070C39A /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 6003F5B9195388D20070C39A /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 6003F5BD195388D20070C39A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6003F5BE195388D20070C39A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6003F5C0195388D20070C39A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AFBF088C3C4B7C82143E70F9 /* Pods-AYPromise_Example.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "AYPromise/AYPromise-Prefix.pch"; + INFOPLIST_FILE = "AYPromise/AYPromise-Info.plist"; + MODULE_NAME = ExampleApp; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 6003F5C1195388D20070C39A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 92DBDC6C0A07426C2C681F2D /* Pods-AYPromise_Example.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "AYPromise/AYPromise-Prefix.pch"; + INFOPLIST_FILE = "AYPromise/AYPromise-Info.plist"; + MODULE_NAME = ExampleApp; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + 6003F5C3195388D20070C39A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CFAB5D4D2D9C7911DA26774C /* Pods-AYPromise_Tests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AYPromise_Example.app/AYPromise_Example"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 6003F5C4195388D20070C39A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E762518FB19AF71D54D259C8 /* Pods-AYPromise_Tests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AYPromise_Example.app/AYPromise_Example"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6003F585195388D10070C39A /* Build configuration list for PBXProject "AYPromise" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6003F5BD195388D20070C39A /* Debug */, + 6003F5BE195388D20070C39A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "AYPromise_Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6003F5C0195388D20070C39A /* Debug */, + 6003F5C1195388D20070C39A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "AYPromise_Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6003F5C3195388D20070C39A /* Debug */, + 6003F5C4195388D20070C39A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6003F582195388D10070C39A /* Project object */; +} diff --git a/Example/AYPromise.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/AYPromise.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..6c84dca --- /dev/null +++ b/Example/AYPromise.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example/AYPromise.xcodeproj/xcshareddata/xcschemes/AYPromise-Example.xcscheme b/Example/AYPromise.xcodeproj/xcshareddata/xcschemes/AYPromise-Example.xcscheme new file mode 100644 index 0000000..4691766 --- /dev/null +++ b/Example/AYPromise.xcodeproj/xcshareddata/xcschemes/AYPromise-Example.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/AYPromise.xcworkspace/contents.xcworkspacedata b/Example/AYPromise.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..645df57 --- /dev/null +++ b/Example/AYPromise.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Example/AYPromise/AYAppDelegate.h b/Example/AYPromise/AYAppDelegate.h new file mode 100644 index 0000000..9e2c440 --- /dev/null +++ b/Example/AYPromise/AYAppDelegate.h @@ -0,0 +1,15 @@ +// +// AYAppDelegate.h +// AYPromise +// +// Created by Alan Yeh on 07/22/2016. +// Copyright (c) 2016 Alan Yeh. All rights reserved. +// + +@import UIKit; + +@interface AYAppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/Example/AYPromise/AYAppDelegate.m b/Example/AYPromise/AYAppDelegate.m new file mode 100644 index 0000000..4f81e69 --- /dev/null +++ b/Example/AYPromise/AYAppDelegate.m @@ -0,0 +1,46 @@ +// +// AYAppDelegate.m +// AYPromise +// +// Created by Alan Yeh on 07/22/2016. +// Copyright (c) 2016 Alan Yeh. All rights reserved. +// + +#import "AYAppDelegate.h" + +@implementation AYAppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + // 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. + // 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. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + // 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. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + // 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. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // 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. +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/Example/AYPromise/AYPromise-Info.plist b/Example/AYPromise/AYPromise-Info.plist new file mode 100644 index 0000000..b06b79c --- /dev/null +++ b/Example/AYPromise/AYPromise-Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Example/AYPromise/AYPromise-Prefix.pch b/Example/AYPromise/AYPromise-Prefix.pch new file mode 100644 index 0000000..7825372 --- /dev/null +++ b/Example/AYPromise/AYPromise-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_5_0 +#warning "This project uses features only available in iOS SDK 5.0 and later." +#endif + +#ifdef __OBJC__ + @import UIKit; + @import Foundation; +#endif diff --git a/Example/AYPromise/AYViewController.h b/Example/AYPromise/AYViewController.h new file mode 100644 index 0000000..7115c80 --- /dev/null +++ b/Example/AYPromise/AYViewController.h @@ -0,0 +1,13 @@ +// +// AYViewController.h +// AYPromise +// +// Created by Alan Yeh on 07/22/2016. +// Copyright (c) 2016 Alan Yeh. All rights reserved. +// + +@import UIKit; + +@interface AYViewController : UIViewController + +@end diff --git a/Example/AYPromise/AYViewController.m b/Example/AYPromise/AYViewController.m new file mode 100644 index 0000000..8ebc8ae --- /dev/null +++ b/Example/AYPromise/AYViewController.m @@ -0,0 +1,29 @@ +// +// AYViewController.m +// AYPromise +// +// Created by Alan Yeh on 07/22/2016. +// Copyright (c) 2016 Alan Yeh. All rights reserved. +// + +#import "AYViewController.h" + +@interface AYViewController () + +@end + +@implementation AYViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/Example/AYPromise/Images.xcassets/AppIcon.appiconset/Contents.json b/Example/AYPromise/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..f697f61 --- /dev/null +++ b/Example/AYPromise/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Example/AYPromise/Images.xcassets/LaunchImage.launchimage/Contents.json b/Example/AYPromise/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..4458b40 --- /dev/null +++ b/Example/AYPromise/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Example/AYPromise/Main.storyboard b/Example/AYPromise/Main.storyboard new file mode 100644 index 0000000..9bd255c --- /dev/null +++ b/Example/AYPromise/Main.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/AYPromise/en.lproj/InfoPlist.strings b/Example/AYPromise/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Example/AYPromise/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Example/AYPromise/main.m b/Example/AYPromise/main.m new file mode 100644 index 0000000..1c8f792 --- /dev/null +++ b/Example/AYPromise/main.m @@ -0,0 +1,17 @@ +// +// main.m +// AYPromise +// +// Created by Alan Yeh on 07/22/2016. +// Copyright (c) 2016 Alan Yeh. All rights reserved. +// + +@import UIKit; +#import "AYAppDelegate.h" + +int main(int argc, char * argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AYAppDelegate class])); + } +} diff --git a/Example/Podfile b/Example/Podfile new file mode 100644 index 0000000..0f8abcc --- /dev/null +++ b/Example/Podfile @@ -0,0 +1,11 @@ +use_frameworks! + +target 'AYPromise_Example' do + pod 'AYPromise', :path => '../' + + target 'AYPromise_Tests' do + inherit! :search_paths + + + end +end diff --git a/Example/Podfile.lock b/Example/Podfile.lock new file mode 100644 index 0000000..b5aa338 --- /dev/null +++ b/Example/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - AYPromise (1.0.0) + +DEPENDENCIES: + - AYPromise (from `../`) + +EXTERNAL SOURCES: + AYPromise: + :path: "../" + +SPEC CHECKSUMS: + AYPromise: 716ef1bbe2c6db978ceb3b1f2d8484f25df2cf12 + +PODFILE CHECKSUM: 6e0270353a9b3f48982dd61cbbf7e0e7648b456a + +COCOAPODS: 1.0.1 diff --git a/Example/Tests/ErrorTest.m b/Example/Tests/ErrorTest.m new file mode 100644 index 0000000..6374d07 --- /dev/null +++ b/Example/Tests/ErrorTest.m @@ -0,0 +1,178 @@ +// +// ErrorTest.m +// AYPromise +// +// Created by PoiSon on 16/7/22. +// Copyright © 2016年 Alan Yeh. All rights reserved. +// + +#import +#import + +#define TIME_OUT 1 + +@interface ErrorTest : XCTestCase + +@end + +@implementation ErrorTest + +- (void)testThen1 { + id ex = [self expectationWithDescription:@""]; + + AYPromiseWith(^{ + @throw NSErrorMake(nil, @"Error"); + }).then(^{ + XCTAssert(NO, @"这里不应该执行:%@", @"123"); + }).catch(^(NSError *error){ + XCTAssert([error.localizedDescription isEqualToString:@"Error"]); + }).catch(^{ + XCTAssert(NO, @"这里不应该执行"); + }).always(^{ + [ex fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testThen2{ + id ex = [self expectationWithDescription:@""]; + + AYPromiseWith(^{ + return @"aaa"; + }).thenAsync(^{ + @throw NSErrorMake(nil, @"Error"); + }).then(^{ + XCTAssert(NO, @"这里不应该执行"); + return nil; + }).catch(^(NSError *error) { + XCTAssert([error.localizedDescription isEqualToString:@"Error"]); + }).catch(^(NSError *error){ + XCTAssert(NO, @"这里不应该执行"); + }).always(^{ + [ex fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testThen3{ + id ex = [self expectationWithDescription:@""]; + + AYPromiseWith(^{ + return @"aaa"; + }).thenPromise(^(id result, PSResolve resolve){ + @throw NSErrorMake(nil, @"Error"); + }).then(^{ + XCTAssert(NO, @"这里不应该执行"); + return nil; + }).catch(^(NSError *error) { + XCTAssert([error.localizedDescription isEqualToString:@"Error"]); + }).catch(^(NSError *error){ + XCTAssert(NO, @"这里不应该执行"); + }).always(^{ + [ex fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testThen4{ + id ex = [self expectationWithDescription:@""]; + + AYPromiseWith(^{ + return @"aaa"; + }).thenOn(dispatch_get_global_queue(0, 0), ^{ + @throw NSErrorMake(nil, @"Error"); + }).then(^{ + XCTAssert(NO, @"这里不应该执行"); + return nil; + }).catch(^(NSError *error) { + XCTAssert([error.localizedDescription isEqualToString:@"Error"]); + }).catch(^(NSError *error){ + XCTAssert(NO, @"这里不应该执行"); + }).always(^{ + [ex fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testCatch1{ + id ex = [self expectationWithDescription:@""]; + + AYPromiseWith(^{ + @throw NSErrorMake(nil, @"abc"); + }).catch(^{ + @throw NSErrorMake(nil, @"Error"); + }).then(^{ + XCTAssert(NO, @"这里不应该执行"); + return nil; + }).catch(^(NSError *error) { + XCTAssert([error.localizedDescription isEqualToString:@"Error"]); + }).catch(^(NSError *error){ + XCTAssert(NO, @"这里不应该执行"); + }).always(^{ + [ex fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testCatch2{ + id ex = [self expectationWithDescription:@""]; + + AYPromiseWith(^{ + @throw NSErrorMake(nil, @"abc"); + }).catchAsync(^{ + @throw NSErrorMake(nil, @"Error"); + }).then(^{ + XCTAssert(NO, @"这里不应该执行"); + return nil; + }).catch(^(NSError *error) { + XCTAssert([error.localizedDescription isEqualToString:@"Error"]); + }).catch(^(NSError *error){ + XCTAssert(NO, @"这里不应该执行"); + }).always(^{ + [ex fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testCatch3{ + id ex = [self expectationWithDescription:@""]; + + AYPromiseWith(^{ + @throw NSErrorMake(nil, @"abc"); + }).catchOn(dispatch_get_global_queue(0, 0), ^{ + @throw NSErrorMake(nil, @"Error"); + }).then(^{ + XCTAssert(NO, @"这里不应该执行"); + return nil; + }).catch(^(NSError *error) { + XCTAssert([error.localizedDescription isEqualToString:@"Error"]); + }).catch(^(NSError *error){ + XCTAssert(NO, @"这里不应该执行"); + }).always(^{ + [ex fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testAlways{ + id ex = [self expectationWithDescription:@""]; + + AYPromiseWith(^{ + @throw NSErrorMake(nil, @"abc"); + }).always(^{ + @throw NSErrorMake(nil, @"Error"); + }).then(^{ + XCTAssert(NO, @"这里不应该执行"); + return nil; + }).catch(^(NSError *error) { + XCTAssert([error.localizedDescription isEqualToString:@"Error"]); + }).catch(^(NSError *error){ + XCTAssert(NO, @"这里不应该执行"); + }).always(^{ + [ex fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +@end diff --git a/Example/Tests/PromiseTest.m b/Example/Tests/PromiseTest.m new file mode 100644 index 0000000..35098a0 --- /dev/null +++ b/Example/Tests/PromiseTest.m @@ -0,0 +1,246 @@ +// +// PromiseTest.m +// AYPromise +// +// Created by PoiSon on 16/7/22. +// Copyright © 2016年 Alan Yeh. All rights reserved. +// + +#import +#import + +#define TIME_OUT 1 + +@interface PromiseTest : XCTestCase + +@end + +@implementation PromiseTest + +- (void)testPromiseBlock{ + id ex1 = [self expectationWithDescription:@""]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(@"AsyncTask completed"); + }); + }).then(^(NSString *result){ + XCTAssert([result isEqualToString:@"AsyncTask completed"]); + [ex1 fulfill]; + }).catch(^{ + XCTAssert(NO, @"这里不该执行"); + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testPromiseError{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(NSErrorMake(nil, @"发生错误了")); + }); + }).then(^{ + XCTAssert(NO, @"这里不该执行"); + }).catch(^(NSError *error){ + XCTAssert(error!= nil); + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testThen{ + id ex1 = [self expectationWithDescription:@""]; + AYPromise.resolve(@"123").then(^(NSString *result){ + XCTAssert([result isEqualToString:@"123"]); + }).then(^(NSString *result){ + XCTAssert(result == nil); + return @"123"; + }).then(^(NSString *result){ + return [result stringByAppendingString:@"123"]; + }).then(^(NSString *result){ + XCTAssert([result isEqualToString:@"123123"]); + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testThen2{ + id ex1 = [self expectationWithDescription:@""]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(@"AsyncTask completed"); + }); + }).then(^(NSString *result){ + XCTAssert([result isEqualToString:@"AsyncTask completed"]); + return AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + resolve([result stringByAppendingString:@"123"]); + }).then(^{ + return @"123"; + }); + }).then(^(NSString *result){ + XCTAssert([result isEqualToString:@"123"]); + }).catch(^{ + XCTAssert(NO, @"这里不该执行"); + }).always(^{ + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testFinally1{ + id ex1 = [self expectationWithDescription:@""]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(@"AsyncTask completed"); + }); + }).then(^(NSString *result){ + XCTAssert([result isEqualToString:@"AsyncTask completed"]); + }).catch(^{ + XCTAssert(NO, @"这里不该执行"); + }).always(^{ + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testFinally2{ + id ex1 = [self expectationWithDescription:@""]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(NSErrorMake(nil, @"发生错误了")); + }); + }).then(^{ + XCTAssert(NO, @"这里不该执行"); + }).catch(^(NSError *error){ + XCTAssert(error!= nil); + }).always(^{ + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testFinally3{ + id ex1 = [self expectationWithDescription:@""]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(@"AsyncTask completed"); + }); + }).then(^(NSString *result){ + XCTAssert([result isEqualToString:@"AsyncTask completed"]); + return AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + resolve([result stringByAppendingString:@"123"]); + }).then(^{ + return @"123"; + }); + }).catch(^{ + XCTAssert(NO, @"这里不该执行"); + }).always(^{ + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testPipe{ + id ex1 = [self expectationWithDescription:@"expectation"]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + resolve(@"123"); + }).then(^(NSString *result){ + return AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + resolve([result stringByAppendingString:@"123"]); + }); + }).then(^(NSString *result){ + XCTAssert([result isEqualToString:@"123123"]); + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testPromiseAll{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromise *p1 = AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + resolve(@"thread1"); + }); + + AYPromise *p2 = AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + resolve(@"thread2"); + }); + + AYPromise.all(@[@"1", p1, p2]).then(^(NSArray *result){ + BOOL isEqual = [result isEqualToArray:@[@"1", @"thread1", @"thread2"]]; + XCTAssert(isEqual); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testPromiseRace{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromise *p1 = AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + resolve(@"thread1"); + }); + + AYPromise *p2 = AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + resolve(@"thread2"); + }); + + AYPromise.race(@[@"1", p1, p2]).then(^(NSString *result){ + XCTAssert([result isKindOfClass:[NSString class]]); + XCTAssert([result isEqualToString:@"1"]); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testThenPromise{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + resolve(@"1"); + }).thenPromise(^(NSString *result, PSResolve resolve){ + XCTAssert([result isEqualToString:@"1"]); + resolve(@"2"); + }).then(^(NSString *result){ + XCTAssert([result isEqualToString:@"2"]); + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testDelayThen{ + id ex1 = [self expectationWithDescription:@""]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + NSLog(@"reutrn: 123"); + resolve(@"123"); + }).thenDelay(0.2, ^(NSString *result){ + NSLog(@"result: %@", result); + }).always(^{ + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)testDelay{ + id ex1 = [self expectationWithDescription:@""]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + resolve(NSErrorMake(nil, @"发生错误了")); + }); + resolve(@YES); + }).then(^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [ex1 fulfill]; + }); + }).catch(^(NSError *error){ + XCTAssert(NO, @"这里不应该执行"); + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} +@end \ No newline at end of file diff --git a/Example/Tests/Tests-Info.plist b/Example/Tests/Tests-Info.plist new file mode 100644 index 0000000..169b6f7 --- /dev/null +++ b/Example/Tests/Tests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Example/Tests/Tests-Prefix.pch b/Example/Tests/Tests-Prefix.pch new file mode 100644 index 0000000..0bfb741 --- /dev/null +++ b/Example/Tests/Tests-Prefix.pch @@ -0,0 +1,7 @@ +// The contents of this file are implicitly included at the beginning of every test case source file. + +#ifdef __OBJC__ + + + +#endif diff --git a/Example/Tests/ThreadTest.m b/Example/Tests/ThreadTest.m new file mode 100644 index 0000000..da20b7d --- /dev/null +++ b/Example/Tests/ThreadTest.m @@ -0,0 +1,185 @@ +// +// ThreadTest.m +// AYPromise +// +// Created by PoiSon on 16/7/22. +// Copyright © 2016年 Alan Yeh. All rights reserved. +// + +#import +#import + +#define TIME_OUT 1 +@interface ThreadTest : XCTestCase + +@end + +@implementation ThreadTest + + +- (void)test1{ + id ex1 = [self expectationWithDescription:@""]; + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + [ex1 fulfill]; + }); + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)test2{ + id ex1 = [self expectationWithDescription:@""]; + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + [ex1 fulfill]; + }); + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} +- (void)test3{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + resolve(@"abc"); + }).then(^(NSString *result){ + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} +- (void)test4{ + id ex1 = [self expectationWithDescription:@""]; + + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + resolve(@"abc"); + }).then(^(NSString *result){ + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + [ex1 fulfill]; + }); + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} +- (void)test5{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(@"success"); + }); + }).then(^(NSString *result){ + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)test6{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(NSErrorMake(nil, @"发生错误了")); + }); + }).catch(^(NSError *error){ + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} +- (void)test7{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(NSErrorMake(nil, @"发生错误了")); + }); + }).always(^{ + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} +- (void)test8{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(@"success"); + }); + }).always(^{ + XCTAssertEqual([NSThread currentThread].isMainThread, YES); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)test9{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(@"success"); + }); + }).thenAsync(^{ + XCTAssertEqual([NSThread currentThread].isMainThread, NO); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)test10{ + id ex1 = [self expectationWithDescription:@""]; + + dispatch_queue_t queue = dispatch_queue_create("dispatch_test", DISPATCH_QUEUE_PRIORITY_DEFAULT); + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + resolve(@"success"); + }); + }).thenOn(queue, ^{ + dispatch_queue_t current_queuet = dispatch_get_current_queue(); + XCTAssert(strcmp(dispatch_queue_get_label(current_queuet), "dispatch_test") == 0); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)test11{ + id ex1 = [self expectationWithDescription:@""]; + + dispatch_queue_t queue = dispatch_queue_create("dispatch_test", DISPATCH_QUEUE_PRIORITY_DEFAULT); + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + @throw NSErrorMake(nil, @"Error"); + }).catchOn(queue, ^{ + dispatch_queue_t current_queuet = dispatch_get_current_queue(); + XCTAssert(strcmp(dispatch_queue_get_label(current_queuet), "dispatch_test") == 0); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} + +- (void)test12{ + id ex1 = [self expectationWithDescription:@""]; + + AYPromiseWithResolve(^(PSResolve _Nonnull resolve) { + @throw NSErrorMake(nil, @"Error"); + }).catchAsync(^{ + XCTAssertEqual([NSThread currentThread].isMainThread, NO); + [ex1 fulfill]; + }); + + [self waitForExpectationsWithTimeout:TIME_OUT handler:nil]; +} +@end diff --git a/Example/Tests/en.lproj/InfoPlist.strings b/Example/Tests/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Example/Tests/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d9019a6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Alan Yeh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4bfed04 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# AYPromise + +[![CI Status](http://img.shields.io/travis/alan-yeh/AYPromise.svg?style=flat)](https://travis-ci.org/alan-yeh/AYPromise) +[![Version](https://img.shields.io/cocoapods/v/AYPromise.svg?style=flat)](http://cocoapods.org/pods/AYPromise) +[![License](https://img.shields.io/cocoapods/l/AYPromise.svg?style=flat)](http://cocoapods.org/pods/AYPromise) +[![Platform](https://img.shields.io/cocoapods/p/AYPromise.svg?style=flat)](http://cocoapods.org/pods/AYPromise) + +## Example + +To run the example project, clone the repo, and run `pod install` from the Example directory first. + +## Requirements + +## Installation + +AYPromise is available through [CocoaPods](http://cocoapods.org). To install +it, simply add the following line to your Podfile: + +```ruby +pod "AYPromise" +``` + +## Author + +Alan Yeh, alan@yerl.cn + +## License + +AYPromise is available under the MIT license. See the LICENSE file for more info. diff --git a/_Pods.xcodeproj b/_Pods.xcodeproj new file mode 120000 index 0000000..3c5a8e7 --- /dev/null +++ b/_Pods.xcodeproj @@ -0,0 +1 @@ +Example/Pods/Pods.xcodeproj \ No newline at end of file