From b607bb5ac468c11bf2befd76ba898b7765d86498 Mon Sep 17 00:00:00 2001 From: wangyuandong Date: Fri, 14 Oct 2022 11:34:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E7=A1=80YD=E5=BA=93=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Podfile | 24 + .../YDAuthorizationUtil.md | 13 + YDAuthorizationUtil/YDAuthorizationUtil.h | 60 + YDAuthorizationUtil/YDAuthorizationUtil.m | 368 ++++ YDBaseUI/YDBaseUIDefine.h | 4 +- YDBlockKit/BKDefines.h | 66 + YDBlockKit/Core/BKMacros.h | 93 + YDBlockKit/Core/NSArray+BlocksKit.h | 225 +++ YDBlockKit/Core/NSArray+BlocksKit.m | 173 ++ YDBlockKit/Core/NSDictionary+BlocksKit.h | 115 ++ YDBlockKit/Core/NSDictionary+BlocksKit.m | 102 + YDBlockKit/Core/NSIndexSet+BlocksKit.h | 124 ++ YDBlockKit/Core/NSIndexSet+BlocksKit.m | 99 + YDBlockKit/Core/NSInvocation+BlocksKit.h | 38 + YDBlockKit/Core/NSInvocation+BlocksKit.m | 48 + YDBlockKit/Core/NSMapTable+BlocksKit.h | 111 ++ YDBlockKit/Core/NSMapTable+BlocksKit.m | 149 ++ YDBlockKit/Core/NSMutableArray+BlocksKit.h | 55 + YDBlockKit/Core/NSMutableArray+BlocksKit.m | 42 + .../Core/NSMutableDictionary+BlocksKit.h | 51 + .../Core/NSMutableDictionary+BlocksKit.m | 44 + YDBlockKit/Core/NSMutableIndexSet+BlocksKit.h | 47 + YDBlockKit/Core/NSMutableIndexSet+BlocksKit.m | 44 + .../Core/NSMutableOrderedSet+BlocksKit.h | 55 + .../Core/NSMutableOrderedSet+BlocksKit.m | 44 + YDBlockKit/Core/NSMutableSet+BlocksKit.h | 55 + YDBlockKit/Core/NSMutableSet+BlocksKit.m | 41 + YDBlockKit/Core/NSNumber+BlocksKit.h | 27 + YDBlockKit/Core/NSNumber+BlocksKit.m | 19 + .../Core/NSObject+BKAssociatedObjects.h | 163 ++ .../Core/NSObject+BKAssociatedObjects.m | 115 ++ YDBlockKit/Core/NSObject+BKBlockExecution.h | 129 ++ YDBlockKit/Core/NSObject+BKBlockExecution.m | 151 ++ YDBlockKit/Core/NSObject+BKBlockObservation.h | 142 ++ YDBlockKit/Core/NSObject+BKBlockObservation.m | 343 ++++ YDBlockKit/Core/NSObject+YDAsyncBlock.h | 22 + YDBlockKit/Core/NSObject+YDAsyncBlock.m | 108 ++ YDBlockKit/Core/NSOrderedSet+BlocksKit.h | 173 ++ YDBlockKit/Core/NSOrderedSet+BlocksKit.m | 132 ++ YDBlockKit/Core/NSSet+BlocksKit.h | 138 ++ YDBlockKit/Core/NSSet+BlocksKit.m | 113 ++ YDBlockKit/Core/NSTimer+BlocksKit.h | 53 + YDBlockKit/Core/NSTimer+BlocksKit.m | 50 + .../DynamicDelegate/A2BlockInvocation.h | 103 + .../DynamicDelegate/A2BlockInvocation.m | 253 +++ .../DynamicDelegate/A2DynamicDelegate.h | 144 ++ .../DynamicDelegate/A2DynamicDelegate.m | 350 ++++ .../Foundation/NSCache+BlocksKit.h | 58 + .../Foundation/NSCache+BlocksKit.m | 60 + .../Foundation/NSURLConnection+BlocksKit.h | 140 ++ .../Foundation/NSURLConnection+BlocksKit.m | 413 ++++ .../NSObject+A2BlockDelegate.h | 121 ++ .../NSObject+A2BlockDelegate.m | 364 ++++ .../NSObject+A2DynamicDelegate.h | 71 + .../NSObject+A2DynamicDelegate.m | 89 + YDBlockKit/UIKit/UIActionSheet+BlocksKit.h | 129 ++ YDBlockKit/UIKit/UIActionSheet+BlocksKit.m | 186 ++ YDBlockKit/UIKit/UIAlertView+BlocksKit.h | 137 ++ YDBlockKit/UIKit/UIAlertView+BlocksKit.m | 230 +++ YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.h | 66 + YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.m | 65 + YDBlockKit/UIKit/UIControl+BlocksKit.h | 54 + YDBlockKit/UIKit/UIControl+BlocksKit.m | 111 ++ .../UIKit/UIGestureRecognizer+BlocksKit.h | 109 ++ .../UIKit/UIGestureRecognizer+BlocksKit.m | 103 + YDBlockKit/UIKit/UIImage+BlocksKit.h | 26 + YDBlockKit/UIKit/UIImage+BlocksKit.m | 36 + .../UIKit/UIImagePickerController+BlocksKit.h | 35 + .../UIKit/UIImagePickerController+BlocksKit.m | 59 + .../UIKit/UIPopoverController+BlocksKit.h | 27 + .../UIKit/UIPopoverController+BlocksKit.m | 59 + YDBlockKit/UIKit/UITextField+BlocksKit.h | 69 + YDBlockKit/UIKit/UITextField+BlocksKit.m | 120 ++ YDBlockKit/UIKit/UITextView+BlocksKit.h | 74 + YDBlockKit/UIKit/UITextView+BlocksKit.m | 158 ++ YDBlockKit/UIKit/UIView+BlocksKit.h | 74 + YDBlockKit/UIKit/UIView+BlocksKit.m | 55 + YDBlockKit/YDBlockKit.docc/YDBlockKit.md | 13 + YDBlockKit/YDBlockKit.h | 37 + YDEmptyView/UIViewController+YDMistakesShow.h | 71 + YDEmptyView/UIViewController+YDMistakesShow.m | 283 +++ YDEmptyView/YDEmptyView.docc/YDEmptyView.md | 13 + YDEmptyView/YDEmptyView.h | 38 + YDEmptyView/YDEmptyView.m | 291 +++ YDEmptyView/YDEmptyViewConfig.h | 42 + YDEmptyView/YDEmptyViewConfig.m | 49 + YDFoundation.podspec | 18 +- YDFoundation.xcodeproj/project.pbxproj | 1704 ++++++++++++++++- YDFoundationDemo/AppDelegate.m | 2 +- YDJPush/YDJPush.docc/YDJPush.md | 13 + YDJPush/YDJPush.h | 18 + YDJPush/YDJPushConfig.h | 18 + YDJPush/YDJPushConfig.m | 30 + YDJPush/YDJPushConfigProtocol.h | 23 + YDJPush/YDJPushManager.h | 60 + YDJPush/YDJPushManager.m | 258 +++ YDNetwrokManager/LICENSE | 21 + .../AFJSONResponseSerializer+YDAgent.h | 16 + .../AFJSONResponseSerializer+YDAgent.m | 42 + YDNetwrokManager/YDCommand/YDBatchCommand.h | 55 + YDNetwrokManager/YDCommand/YDBatchCommand.m | 171 ++ .../YDCommand/YDCommand+YDError.h | 18 + .../YDCommand/YDCommand+YDError.m | 37 + YDNetwrokManager/YDCommand/YDCommand.h | 110 ++ YDNetwrokManager/YDCommand/YDCommand.m | 501 +++++ YDNetwrokManager/YDCommandConfig.h | 80 + YDNetwrokManager/YDCommandConfig.m | 232 +++ .../YDNetwrokManager.docc/YDNetwrokManager.md | 13 + YDNetwrokManager/YDNetwrokManager.h | 18 + YDPreLoader/YDPreLoaderManager.m | 5 +- .../YDSafeThread.docc/YDSafeThread.md | 13 + 111 files changed, 12537 insertions(+), 92 deletions(-) create mode 100755 YDAuthorizationUtil/YDAuthorizationUtil.docc/YDAuthorizationUtil.md create mode 100644 YDAuthorizationUtil/YDAuthorizationUtil.h create mode 100644 YDAuthorizationUtil/YDAuthorizationUtil.m create mode 100644 YDBlockKit/BKDefines.h create mode 100644 YDBlockKit/Core/BKMacros.h create mode 100644 YDBlockKit/Core/NSArray+BlocksKit.h create mode 100644 YDBlockKit/Core/NSArray+BlocksKit.m create mode 100644 YDBlockKit/Core/NSDictionary+BlocksKit.h create mode 100644 YDBlockKit/Core/NSDictionary+BlocksKit.m create mode 100644 YDBlockKit/Core/NSIndexSet+BlocksKit.h create mode 100644 YDBlockKit/Core/NSIndexSet+BlocksKit.m create mode 100644 YDBlockKit/Core/NSInvocation+BlocksKit.h create mode 100644 YDBlockKit/Core/NSInvocation+BlocksKit.m create mode 100644 YDBlockKit/Core/NSMapTable+BlocksKit.h create mode 100644 YDBlockKit/Core/NSMapTable+BlocksKit.m create mode 100644 YDBlockKit/Core/NSMutableArray+BlocksKit.h create mode 100644 YDBlockKit/Core/NSMutableArray+BlocksKit.m create mode 100644 YDBlockKit/Core/NSMutableDictionary+BlocksKit.h create mode 100644 YDBlockKit/Core/NSMutableDictionary+BlocksKit.m create mode 100644 YDBlockKit/Core/NSMutableIndexSet+BlocksKit.h create mode 100644 YDBlockKit/Core/NSMutableIndexSet+BlocksKit.m create mode 100644 YDBlockKit/Core/NSMutableOrderedSet+BlocksKit.h create mode 100644 YDBlockKit/Core/NSMutableOrderedSet+BlocksKit.m create mode 100644 YDBlockKit/Core/NSMutableSet+BlocksKit.h create mode 100644 YDBlockKit/Core/NSMutableSet+BlocksKit.m create mode 100644 YDBlockKit/Core/NSNumber+BlocksKit.h create mode 100644 YDBlockKit/Core/NSNumber+BlocksKit.m create mode 100644 YDBlockKit/Core/NSObject+BKAssociatedObjects.h create mode 100644 YDBlockKit/Core/NSObject+BKAssociatedObjects.m create mode 100644 YDBlockKit/Core/NSObject+BKBlockExecution.h create mode 100644 YDBlockKit/Core/NSObject+BKBlockExecution.m create mode 100644 YDBlockKit/Core/NSObject+BKBlockObservation.h create mode 100644 YDBlockKit/Core/NSObject+BKBlockObservation.m create mode 100644 YDBlockKit/Core/NSObject+YDAsyncBlock.h create mode 100644 YDBlockKit/Core/NSObject+YDAsyncBlock.m create mode 100644 YDBlockKit/Core/NSOrderedSet+BlocksKit.h create mode 100644 YDBlockKit/Core/NSOrderedSet+BlocksKit.m create mode 100644 YDBlockKit/Core/NSSet+BlocksKit.h create mode 100644 YDBlockKit/Core/NSSet+BlocksKit.m create mode 100644 YDBlockKit/Core/NSTimer+BlocksKit.h create mode 100644 YDBlockKit/Core/NSTimer+BlocksKit.m create mode 100644 YDBlockKit/DynamicDelegate/A2BlockInvocation.h create mode 100644 YDBlockKit/DynamicDelegate/A2BlockInvocation.m create mode 100644 YDBlockKit/DynamicDelegate/A2DynamicDelegate.h create mode 100644 YDBlockKit/DynamicDelegate/A2DynamicDelegate.m create mode 100644 YDBlockKit/DynamicDelegate/Foundation/NSCache+BlocksKit.h create mode 100644 YDBlockKit/DynamicDelegate/Foundation/NSCache+BlocksKit.m create mode 100644 YDBlockKit/DynamicDelegate/Foundation/NSURLConnection+BlocksKit.h create mode 100644 YDBlockKit/DynamicDelegate/Foundation/NSURLConnection+BlocksKit.m create mode 100644 YDBlockKit/DynamicDelegate/NSObject+A2BlockDelegate.h create mode 100644 YDBlockKit/DynamicDelegate/NSObject+A2BlockDelegate.m create mode 100644 YDBlockKit/DynamicDelegate/NSObject+A2DynamicDelegate.h create mode 100644 YDBlockKit/DynamicDelegate/NSObject+A2DynamicDelegate.m create mode 100644 YDBlockKit/UIKit/UIActionSheet+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIActionSheet+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UIAlertView+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIAlertView+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UIControl+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIControl+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UIGestureRecognizer+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIGestureRecognizer+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UIImage+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIImage+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UIImagePickerController+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIImagePickerController+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UIPopoverController+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIPopoverController+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UITextField+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UITextField+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UITextView+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UITextView+BlocksKit.m create mode 100644 YDBlockKit/UIKit/UIView+BlocksKit.h create mode 100644 YDBlockKit/UIKit/UIView+BlocksKit.m create mode 100755 YDBlockKit/YDBlockKit.docc/YDBlockKit.md create mode 100644 YDBlockKit/YDBlockKit.h create mode 100644 YDEmptyView/UIViewController+YDMistakesShow.h create mode 100644 YDEmptyView/UIViewController+YDMistakesShow.m create mode 100755 YDEmptyView/YDEmptyView.docc/YDEmptyView.md create mode 100644 YDEmptyView/YDEmptyView.h create mode 100644 YDEmptyView/YDEmptyView.m create mode 100644 YDEmptyView/YDEmptyViewConfig.h create mode 100644 YDEmptyView/YDEmptyViewConfig.m create mode 100755 YDJPush/YDJPush.docc/YDJPush.md create mode 100644 YDJPush/YDJPush.h create mode 100644 YDJPush/YDJPushConfig.h create mode 100644 YDJPush/YDJPushConfig.m create mode 100644 YDJPush/YDJPushConfigProtocol.h create mode 100644 YDJPush/YDJPushManager.h create mode 100644 YDJPush/YDJPushManager.m create mode 100644 YDNetwrokManager/LICENSE create mode 100644 YDNetwrokManager/YDCommand/AFJSONResponseSerializer+YDAgent.h create mode 100644 YDNetwrokManager/YDCommand/AFJSONResponseSerializer+YDAgent.m create mode 100644 YDNetwrokManager/YDCommand/YDBatchCommand.h create mode 100644 YDNetwrokManager/YDCommand/YDBatchCommand.m create mode 100644 YDNetwrokManager/YDCommand/YDCommand+YDError.h create mode 100644 YDNetwrokManager/YDCommand/YDCommand+YDError.m create mode 100644 YDNetwrokManager/YDCommand/YDCommand.h create mode 100644 YDNetwrokManager/YDCommand/YDCommand.m create mode 100644 YDNetwrokManager/YDCommandConfig.h create mode 100644 YDNetwrokManager/YDCommandConfig.m create mode 100755 YDNetwrokManager/YDNetwrokManager.docc/YDNetwrokManager.md create mode 100644 YDNetwrokManager/YDNetwrokManager.h create mode 100755 YDSafeThread/YDSafeThread.docc/YDSafeThread.md diff --git a/Podfile b/Podfile index 7ca4d5d..6e673bc 100644 --- a/Podfile +++ b/Podfile @@ -58,11 +58,35 @@ abstract_target 'Demo' do end + target 'YDSafeThread' do + + end + + target 'YDEmptyView' do + + end + + target 'YDBlockKit' do + + end + + target 'YDAuthorizationUtil' do + + end + + target 'YDJPush' do + pod 'JPush' , '~> 3.2.4' + end + target 'YDImageService' do pod 'YYWebImage', '~> 1.0.5' pod 'YYImage', '~> 1.0.4' pod 'YDFoundation/YDSafeThread', :path => '.' end + + target 'YDNetwrokManager' do + pod 'YTKNetwork', '~> 3.0.6' + end end diff --git a/YDAuthorizationUtil/YDAuthorizationUtil.docc/YDAuthorizationUtil.md b/YDAuthorizationUtil/YDAuthorizationUtil.docc/YDAuthorizationUtil.md new file mode 100755 index 0000000..6d91098 --- /dev/null +++ b/YDAuthorizationUtil/YDAuthorizationUtil.docc/YDAuthorizationUtil.md @@ -0,0 +1,13 @@ +# ``YDAuthorizationUtil`` + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` \ No newline at end of file diff --git a/YDAuthorizationUtil/YDAuthorizationUtil.h b/YDAuthorizationUtil/YDAuthorizationUtil.h new file mode 100644 index 0000000..081caa4 --- /dev/null +++ b/YDAuthorizationUtil/YDAuthorizationUtil.h @@ -0,0 +1,60 @@ +// +// YDCameraPermissionUtil.h +// yd-general-ios-app +// +// Created by 王远东 on 2021/10/1. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, EYDAuthorization) { + EYDAuthorizationPhoto, + EYDAuthorizationCamera, + EYDAuthorizationMicrophone +}; + +typedef NS_ENUM(NSUInteger, EYDAuthorizationStatus) { + EYDAuthorizationStatusNotDetermined,//尚未决定 + EYDAuthorizationStatusRestricted,//限制 + EYDAuthorizationStatusDenied,//拒绝 + EYDAuthorizationStatusAuthorized,//已授权 定位时:使用AuthorizedAlways 或 AuthorizedWhenInUse + EYDAuthorizationStatusAuthorizedAlways,//定位使用 总是授权 + EYDAuthorizationStatusAuthorizedWhenInUse,//定位使用 使用时授权 +}; + +@interface YDAuthorizationUtil : NSObject + + ++ (EYDAuthorizationStatus)authorizedStatusWithType:(EYDAuthorization)authorizationType; + ++ (BOOL)authorizedWithType:(EYDAuthorization)authorizationType; + ++ (void)authorizeWithType:(EYDAuthorization)type completion:(void(^)(BOOL authorized))completion; + +////拍照并存储相册 需要请求相机、相册两个权限 ++ (void)authorizedForTakePhotoAndSaveWithCompletion:(void(^)(BOOL authorized))completion; + +//录制视频 需要相机、相册、麦克风三个权限 其中只要有一个权限无法获取,都会导致录制出错 ++ (void)authorizedForRecordVideoWithCompletion:(void(^)(BOOL authorized))completion; + +//语音 ++ (void)authorizedForRecordWithCompletion:(void(^)(BOOL authorized))completion; + +//相册 ++ (void)authorizedForPhotoWithCompletion:(void(^)(BOOL authorized))completion; + +//相机 ++ (void)authorizedForCameraWithCompletion:(void(^)(BOOL authorized))completion; + +//相机,是否弹窗 ++ (void)authorizedForCameraWithCompletion:(void(^)(BOOL authorized))completion needAlert:(BOOL)needAlert; + +//去授权 ++ (void)gotoSetting; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDAuthorizationUtil/YDAuthorizationUtil.m b/YDAuthorizationUtil/YDAuthorizationUtil.m new file mode 100644 index 0000000..6eff2ee --- /dev/null +++ b/YDAuthorizationUtil/YDAuthorizationUtil.m @@ -0,0 +1,368 @@ +// +// YDCameraPermissionUtil.m +// yd-general-ios-app +// +// Created by 王远东 on 2021/10/1. +// + +#import "YDAuthorizationUtil.h" +#import +#import + +#define kAuthorizationAppName ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]) + +@interface YDAuthorizationUtil () +@property (nonatomic, copy) void (^permissionCompletion)(BOOL granted); +@end + +@implementation YDAuthorizationUtil + ++ (BOOL)authorizedWithType:(EYDAuthorization)authorizationType +{ + BOOL authorized = NO; + switch (authorizationType) { + case EYDAuthorizationPhoto: + authorized = [self photoAuthorized]; + break; + case EYDAuthorizationCamera: + authorized = [self cameraAuthorized]; + break; + case EYDAuthorizationMicrophone: + authorized = [self microphoneAuthorized]; + break; + default: + authorized = NO; + break; + } + return authorized; +} + ++ (EYDAuthorizationStatus)authorizedStatusWithType:(EYDAuthorization)authorizationType +{ + EYDAuthorizationStatus status = EYDAuthorizationStatusNotDetermined; + switch (authorizationType) { + case EYDAuthorizationPhoto: + status = [self photoAuthorizationStatus]; + break; + case EYDAuthorizationCamera: + status = [self cameraAuthorizationStatus]; + break; + case EYDAuthorizationMicrophone: + status = [self microphoneAuthorizationStatus]; + break; + default: + break; + } + return status; +} + ++ (void)authorizeWithType:(EYDAuthorization)authorizationType completion:(void(^)(BOOL authorized))completion +{ + switch (authorizationType) { + case EYDAuthorizationPhoto: + [self photoAuthorizeWithCompletion:completion]; + break; + case EYDAuthorizationCamera: + [self cameraAuthorizedWithCompletion:completion]; + break; + case EYDAuthorizationMicrophone: + [self microphoneAuthorizedWithCompletion:completion]; + break; + default: + break; + } +} + ++ (UIViewController *)getRootViewController { + UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController; + while (rootViewController.presentedViewController) { +// if ([rootViewController.presentedViewController isKindOfClass:[UIAlertController class]]) { +// return rootViewController; +// } + rootViewController = rootViewController.presentedViewController; + } + return rootViewController; +} + +//MARK: Photo ++ (BOOL)photoAuthorized +{ + return [PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized; +} + ++ (EYDAuthorizationStatus)photoAuthorizationStatus +{ + return (EYDAuthorizationStatus)[PHPhotoLibrary authorizationStatus]; +} + ++ (void)photoAuthorizeWithCompletion:(void(^)(BOOL authorized))completion +{ + PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; + + switch (status) { + case PHAuthorizationStatusAuthorized: + { + if (completion) { + completion(YES); + } + } + break; + case PHAuthorizationStatusRestricted: + case PHAuthorizationStatusDenied: + { + if (completion) { + completion(NO); + } + } + break; + case PHAuthorizationStatusNotDetermined: + { + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(status == PHAuthorizationStatusAuthorized); + }); + } + }]; + } + break; + default: + { + dispatch_async(dispatch_get_main_queue(), ^{ + if (completion) { + completion(NO); + } + }); + } + break; + } +} +//MARK: Microphone ++ (BOOL)microphoneAuthorized +{ + return [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] == AVAuthorizationStatusAuthorized; +} + ++ (EYDAuthorizationStatus)microphoneAuthorizationStatus +{ + return (EYDAuthorizationStatus)[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; +} + ++ (void)microphoneAuthorizedWithCompletion:(void(^)(BOOL authorized))completion +{ + [self authorizedForMediaType:AVMediaTypeAudio completion:completion]; +} + +//MARK: Camera ++ (BOOL)cameraAuthorized +{ + return [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusAuthorized; +} + ++ (EYDAuthorizationStatus)cameraAuthorizationStatus +{ + return (EYDAuthorizationStatus)[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; +} + ++ (void)cameraAuthorizedWithCompletion:(void(^)(BOOL authorized))completion +{ + [self authorizedForMediaType:AVMediaTypeVideo completion:completion]; +} + ++ (void)authorizedForMediaType:(AVMediaType)type completion:(void(^)(BOOL authorized))completion +{ + AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:type]; + switch (status) { + case AVAuthorizationStatusAuthorized: + { + dispatch_async(dispatch_get_main_queue(), ^{ + if (completion) { + completion(YES); + } + }); + } + break; + case AVAuthorizationStatusRestricted: + case AVAuthorizationStatusDenied: + { + dispatch_async(dispatch_get_main_queue(), ^{ + if (completion) { + completion(NO); + } + }); + } + break; + case AVAuthorizationStatusNotDetermined: + { + //点击弹框授权 + [AVCaptureDevice requestAccessForMediaType:type completionHandler:^(BOOL granted) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (completion) { + completion(granted); + } + }); + }]; + } + break; + default: + { + dispatch_async(dispatch_get_main_queue(), ^{ + if (completion) { + completion(NO); + } + }); + } + break; + } +} + + +//MARK: 艺信 未授权有提示 ++ (void)authorizedForRecordVideoWithCompletion:(void(^)(BOOL authorized))completion +{ + __weak typeof(self) weakSelf = self; + [self authorizeWithType:EYDAuthorizationCamera completion:^(BOOL authorized) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!authorized) { + NSString *des = [NSString stringWithFormat:@"请在设备的\"设置-隐私-相机\"选项中允许%@访问你的相机",kAuthorizationAppName]; + [strongSelf alertActionTitle:@"无法打开相机" message:des completion:completion]; + return; + } + + [strongSelf authorizeWithType:EYDAuthorizationMicrophone completion:^(BOOL authorized) { + if (!authorized) { + NSString *des = [NSString stringWithFormat:@"请在设备的\"设置-隐私-麦克风\"选项中允许%@访问你的麦克风",kAuthorizationAppName]; + [strongSelf alertActionTitle:@"无法打开麦克风" message:des completion:completion]; + return; + } + if(completion){ + completion(YES); + } +// [strongSelf authorizeWithType:EYDAuthorizationPhoto completion:^(BOOL authorized) { +// if (!authorized) { +// NSString *des = [NSString stringWithFormat:@"请在设备的\"设置-隐私-相册\"选项中允许%@访问你的相册",kAuthorizationAppName]; +// [strongSelf alertActionTitle:@"无法打开相册" message:des completion:completion]; +// return; +// } +// }]; + }]; + }]; +} + ++ (void)authorizedForTakePhotoAndSaveWithCompletion:(void(^)(BOOL authorized))completion +{ + __weak typeof(self) weakSelf = self; + [self authorizeWithType:EYDAuthorizationCamera completion:^(BOOL authorized) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!authorized) { + NSString *des = [NSString stringWithFormat:@"请在设备的\"设置-隐私-相机\"选项中允许%@访问你的相机",kAuthorizationAppName]; + [strongSelf alertActionTitle:@"无法打开相机" message:des completion:completion]; + return; + } + [strongSelf authorizeWithType:EYDAuthorizationPhoto completion:^(BOOL authorized) { + if (!authorized) { + NSString *des = [NSString stringWithFormat:@"请在设备的\"设置-隐私-相册\"选项中允许%@访问你的相册",kAuthorizationAppName]; + [strongSelf alertActionTitle:@"无法打开相册" message:des completion:completion]; + return; + } + if(completion){ + completion(YES); + } + }]; + }]; +} + ++ (void)authorizedForRecordWithCompletion:(void(^)(BOOL authorized))completion { + __weak typeof(self) weakSelf = self; + [weakSelf authorizeWithType:EYDAuthorizationMicrophone completion:^(BOOL authorized) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!authorized) { + NSString *des = [NSString stringWithFormat:@"请在设备的\"设置-隐私-麦克风\"选项中允许%@访问你的麦克风",kAuthorizationAppName]; + [strongSelf alertActionTitle:@"无法打开麦克风" message:des completion:completion]; + return; + } + if(completion){ + completion(YES); + } + }]; +} + ++ (void)authorizedForCameraWithCompletion:(void(^)(BOOL authorized))completion +{ + [self authorizedForCameraWithCompletion:completion needAlert:YES]; +} ++ (void)authorizedForCameraWithCompletion:(void(^)(BOOL authorized))completion needAlert:(BOOL)needAlert +{ + __weak typeof(self) weakSelf = self; + [self authorizeWithType:EYDAuthorizationCamera completion:^(BOOL authorized) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!authorized) { + if (needAlert) { + NSString *des = [NSString stringWithFormat:@"请在设备的\"设置-隐私-相机\"选项中允许%@访问你的相机",kAuthorizationAppName]; + [strongSelf alertActionTitle:@"无法打开相机" message:des completion:completion]; + } + return; + } + if(completion){ + completion(YES); + } + }]; +} + ++ (void)authorizedForPhotoWithCompletion:(void(^)(BOOL authorized))completion +{ + __weak typeof(self) weakSelf = self; + [self authorizeWithType:EYDAuthorizationPhoto completion:^(BOOL authorized) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!authorized) { + NSString *des = [NSString stringWithFormat:@"请在设备的\"设置-隐私-相册\"选项中允许%@访问你的相册",kAuthorizationAppName]; + [strongSelf alertActionTitle:@"无法打开相册" message:des completion:completion]; + return; + } + if(completion){ + completion(YES); + } + }]; +} + + ++ (void)alertActionTitle:(NSString *)title message:(NSString *)message completion:(void(^)(BOOL authorized))completion { +// [YDAlertController showAlertWithTitle:title message:message cancelButtonTitle:@"去设置" otherButtonTitles:[YDSharedConfig shared].startConfig.ischeck ? @[@"取消"] : nil didSelectBlock:^(YDAlertAction *action, NSUInteger index) { +// if (completion) { +// completion(NO); +// } +// } didCancelBlock:^{ +// [self gotoSetting]; +// if (completion) { +// completion(NO); +// } +// }]; + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *enterAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + if(completion){ + completion(NO); + } + }]; + [alertController addAction:enterAction]; + + UIAlertAction *szAction = [UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self gotoSetting]; + if (completion) { + completion(NO); + } + }]; + [alertController addAction:szAction]; + + [[self getRootViewController] presentViewController:alertController animated:YES completion:nil]; +} + ++ (void)gotoSetting { + //跳转到定位权限页面 + NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; + if( [[UIApplication sharedApplication] canOpenURL:url] ) { + [[UIApplication sharedApplication] openURL:url]; + } +} + + +@end diff --git a/YDBaseUI/YDBaseUIDefine.h b/YDBaseUI/YDBaseUIDefine.h index fb89af8..3563644 100644 --- a/YDBaseUI/YDBaseUIDefine.h +++ b/YDBaseUI/YDBaseUIDefine.h @@ -61,9 +61,9 @@ alpha:alphaValue] #ifdef __cplusplus extern "C" { #endif - CGFloat getScreenWidth(); +CGFloat getScreenWidth(void); - CGFloat getScreenHeight(); +CGFloat getScreenHeight(void); // // 获取状态栏竖边高度 // CGFloat getStatusBarHeight(); diff --git a/YDBlockKit/BKDefines.h b/YDBlockKit/BKDefines.h new file mode 100644 index 0000000..5b90d64 --- /dev/null +++ b/YDBlockKit/BKDefines.h @@ -0,0 +1,66 @@ +// +// BKDefines.h +// BlocksKit + +#pragma once + +#import + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif +#ifndef __has_include +#define __has_include(x) 0 +#endif +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif +#ifndef __has_extension +#define __has_extension(x) 0 +#endif + +#if !defined(NS_ASSUME_NONNULL_BEGIN) +# if __has_feature(assume_nonnull) +# define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +# define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") +# else +# define NS_ASSUME_NONNULL_BEGIN +# define NS_ASSUME_NONNULL_END +# endif +#endif + +#if !__has_feature(nullability) +# define nonnull +# define nullable +# define null_unspecified +# define __nonnull +# define __nullable +# define __null_unspecified +#endif + +#if __has_feature(objc_generics) +# define __GENERICS(class, ...) class<__VA_ARGS__> +# define __GENERICS_TYPE(type) type +#else +# define __GENERICS(class, ...) class +# define __GENERICS_TYPE(type) id +#endif + +#if !defined(BK_INITIALIZER) +# if __has_attribute(objc_method_family) +# define BK_INITIALIZER __attribute__((objc_method_family(init))) +# else +# define BK_INITIALIZER +# endif +#endif + +#if !defined(BK_ALERT_CONTROLLER_DEPRECATED) +# define BK_ALERT_CONTROLLER_DEPRECATED(intro) NS_DEPRECATED_IOS(intro, 8_0, "The BlocksKit extensions for UIAlertView and UIActionSheet are deprecated. Use UIAlertController instead."); +#endif + +#if !defined(BK_URL_CONNECTION_DEPRECATED) +# define BK_URL_CONNECTION_DEPRECATED NS_DEPRECATED(10_5, 10_11, 2_0, 9_0, "The BlocksKit extensions for NSURLConnection are deprecated. Use NSURLSession instead."); +#endif diff --git a/YDBlockKit/Core/BKMacros.h b/YDBlockKit/Core/BKMacros.h new file mode 100644 index 0000000..d216d16 --- /dev/null +++ b/YDBlockKit/Core/BKMacros.h @@ -0,0 +1,93 @@ +// +// BKMacros.h +// BlocksKit +// +// Includes code by Michael Ash. . +// + +#import "BKDefines.h" +#import "NSArray+BlocksKit.h" +#import "NSSet+BlocksKit.h" +#import "NSDictionary+BlocksKit.h" +#import "NSIndexSet+BlocksKit.h" + +#ifndef __BLOCKSKIT_MACROS__ +#define __BLOCKSKIT_MACROS__ + +#define __BK_EACH_WRAPPER(...) (^{ __block CFMutableDictionaryRef BK_eachTable = nil; \ + (void)BK_eachTable; \ + __typeof__(__VA_ARGS__) BK_retval = __VA_ARGS__; \ + if(BK_eachTable) \ + CFRelease(BK_eachTable); \ + return BK_retval; \ + }()) + +#define __BK_EACH_WRAPPER_VOID(...) (^{ __block CFMutableDictionaryRef BK_eachTable = nil; \ + (void)BK_eachTable; \ + __VA_ARGS__; \ + if(BK_eachTable) \ + CFRelease(BK_eachTable); \ + }()) + +#define BK_EACH(collection, ...) __BK_EACH_WRAPPER_VOID([collection bk_each:^(id obj) { __VA_ARGS__ }]) +#define BK_MAP(collection, ...) __BK_EACH_WRAPPER([collection bk_map:^id(id obj) { return (__VA_ARGS__); }]) +#define BK_SELECT(collection, ...) __BK_EACH_WRAPPER([collection bk_select: ^BOOL (id obj) { return (__VA_ARGS__) != 0; }]) +#define BK_REJECT(collection, ...) __BK_EACH_WRAPPER([collection bk_select: ^BOOL (id obj) { return (__VA_ARGS__) == 0; }]) +#define BK_MATCH(collection, ...) __BK_EACH_WRAPPER([collection bk_match: ^BOOL (id obj) { return (__VA_ARGS__) != 0; }]) +#define BK_REDUCE(collection, initial, ...) __BK_EACH_WRAPPER([collection bk_reduce: (initial) withBlock: ^id (id a, id b) { return (__VA_ARGS__); }]) + +// BK_APPLY is not wrapped, because we don't guarantee that the order matches the current collection during parallel execution. +#define BK_APPLY(collection, ...) [collection bk_apply:^(id obj) { __VA_ARGS__ }] + +static inline id BKNextHelper(NSArray *array, CFMutableDictionaryRef *eachTablePtr) { + + if (!*eachTablePtr) { + CFDictionaryKeyCallBacks keycb = kCFTypeDictionaryKeyCallBacks; + keycb.equal = NULL; + keycb.hash = NULL; + *eachTablePtr = CFDictionaryCreateMutable(NULL, 0, &keycb, &kCFTypeDictionaryValueCallBacks); + } + + NSEnumerator *enumerator = (__bridge id)CFDictionaryGetValue(*eachTablePtr, (__bridge CFArrayRef)array); + if (!enumerator) { + enumerator = [array objectEnumerator]; + CFDictionarySetValue(*eachTablePtr, (__bridge CFArrayRef)array, (__bridge void *)enumerator); + } + return [enumerator nextObject]; +} + +#define BK_NEXT(array) BKNextHelper(array, &BK_eachTable) + +#ifndef EACH +#define EACH BK_EACH +#endif + +#ifndef APPLY +#define APPLY BK_APPLY +#endif + +#ifndef MAP +#define MAP BK_MAP +#endif + +#ifndef SELECT +#define SELECT BK_SELECT +#endif + +#ifndef REJECT +#define REJECT BK_REJECT +#endif + +#ifndef MATCH +#define MATCH BK_MATCH +#endif + +#ifndef REDUCE +#define REDUCE BK_REDUCE +#endif + +#ifndef NEXT +#define NEXT BK_NEXT +#endif + +#endif \ No newline at end of file diff --git a/YDBlockKit/Core/NSArray+BlocksKit.h b/YDBlockKit/Core/NSArray+BlocksKit.h new file mode 100644 index 0000000..3d9a858 --- /dev/null +++ b/YDBlockKit/Core/NSArray+BlocksKit.h @@ -0,0 +1,225 @@ +// +// NSArray+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import +#import // for CGFloat + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSArray. + + Both inspired by and resembling Smalltalk syntax, these utilities + allows for iteration of an array in a concise way that + saves quite a bit of boilerplate code for filtering or finding + objects or an object. + + Includes code by the following: + +- [Robin Lu](https://github.com/robin) +- [Michael Ash](https://github.com/mikeash) +- [Aleks Nesterow](https://github.com/nesterow) +- [Zach Waldowski](https://github.com/zwaldowski) + + @see NSDictionary(BlocksKit) + @see NSSet(BlocksKit) + */ +@interface __GENERICS(NSArray, ObjectType) (BlocksKit) + +/** Loops through an array and executes the given block with each object. + + @param block A single-argument, void-returning code block. + */ +- (void)bk_each:(void (^)(ObjectType obj))block; + +/** Enumerates through an array concurrently and executes + the given block once for each object. + + Enumeration will occur on appropriate background queues. This + will have a noticeable speed increase, especially on dual-core + devices, but you *must* be aware of the thread safety of the + objects you message from within the block. Be aware that the + order of objects is not necessarily the order each block will + be called in. + + @param block A single-argument, void-returning code block. + */ +- (void)bk_apply:(void (^)(ObjectType obj))block; + +/** Loops through an array to find the object matching the block. + + bk_match: is functionally identical to bk_select:, but will stop and return + on the first match. + + @param block A single-argument, `BOOL`-returning code block. + @return Returns the object, if found, or `nil`. + @see bk_select: + */ +- (nullable id)bk_match:(BOOL (^)(ObjectType obj))block; + +/** Loops through an array to find the objects matching the block. + + @param block A single-argument, BOOL-returning code block. + @return Returns an array of the objects found. + @see bk_match: + */ +- (NSArray *)bk_select:(BOOL (^)(ObjectType obj))block; + +/** Loops through an array to find the objects not matching the block. + + This selector performs *literally* the exact same function as bk_select: but in reverse. + + This is useful, as one may expect, for removing objects from an array. + NSArray *new = [computers bk_reject:^BOOL(id obj) { + return ([obj isUgly]); + }]; + + @param block A single-argument, BOOL-returning code block. + @return Returns an array of all objects not found. + */ +- (NSArray *)bk_reject:(BOOL (^)(ObjectType obj))block; + +/** Call the block once for each object and create an array of the return values. + + This is sometimes referred to as a transform, mutating one of each object: + NSArray *new = [stringArray bk_map:^id(id obj) { + return [obj stringByAppendingString:@".png"]); + }]; + + @param block A single-argument, object-returning code block. + @return Returns an array of the objects returned by the block. + */ +- (NSArray *)bk_map:(id (^)(ObjectType obj))block; + +/** Behaves like map, but doesn't add NSNull objects if you return nil in the block. + + @param block A single-argument, object-returning code block. + @return Returns an array of the objects returned by the block. + */ +- (NSArray *)bk_compact:(id (^)(ObjectType obj))block; + +/** Arbitrarily accumulate objects using a block. + + The concept of this selector is difficult to illustrate in words. The sum can + be any NSObject, including (but not limited to) a string, number, or value. + + For example, you can concentate the strings in an array: + NSString *concentrated = [stringArray bk_reduce:@"" withBlock:^id(id sum, id obj) { + return [sum stringByAppendingString:obj]; + }]; + + You can also do something like summing the lengths of strings in an array: + NSUInteger value = [[[stringArray bk_reduce:nil withBlock:^id(id sum, id obj) { + return @([sum integerValue] + obj.length); + }]] unsignedIntegerValue]; + + @param initial The value of the reduction at its start. + @param block A block that takes the current sum and the next object to return the new sum. + @return An accumulated value. + */ +- (nullable id)bk_reduce:(nullable id)initial withBlock:(__nullable id (^)(__nullable id sum, ObjectType obj))block; + +/** + Sometimes we just want to loop an objects list and reduce one property, where + each result is a primitive type. + + For example, say we want to calculate the total age of a list of people. + + Code without using block will be something like: + + NSArray *peoples = @[p1, p2, p3]; + NSInteger totalAge = 0; + for (People *people in peoples) { + totalAge += [people age]; + } + + We can use a block to make it simpler: + + NSArray *peoples = @[p1, p2, p3]; + NSInteger totalAge = [peoples reduceInteger:0 withBlock:^(NSInteger result, id obj, NSInteger index) { + return result + [obj age]; + }]; + + */ +- (NSInteger)bk_reduceInteger:(NSInteger)initial withBlock:(NSInteger(^)(NSInteger result, ObjectType obj))block; + +/** + Sometimes we just want to loop an objects list and reduce one property, where + each result is a primitive type. + + For instance, say we want to caculate the total balance from a list of people. + + Code without using a block will be something like: + + NSArray *peoples = @[p1, p2, p3]; + CGFloat totalBalance = 0; + for (People *people in peoples) { + totalBalance += [people balance]; + } + + We can use a block to make it simpler: + + NSArray *peoples = @[p1, p2, p3]; + CGFloat totalBalance = [peoples reduceFloat:.0f WithBlock:^CGFloat(CGFloat result, id obj, NSInteger index) { + return result + [obj balance]; + }]; + + */ + +- (CGFloat)bk_reduceFloat:(CGFloat)inital withBlock:(CGFloat(^)(CGFloat result, ObjectType obj))block; + +/** Loops through an array to find whether any object matches the block. + + This method is similar to the Scala list `exists`. It is functionally + identical to bk_match: but returns a `BOOL` instead. It is not recommended + to use bk_any: as a check condition before executing bk_match:, since it would + require two loops through the array. + + For example, you can find if a string in an array starts with a certain letter: + + NSString *letter = @"A"; + BOOL containsLetter = [stringArray bk_any:^(id obj) { + return [obj hasPrefix:@"A"]; + }]; + + @param block A single-argument, BOOL-returning code block. + @return YES for the first time the block returns YES for an object, NO otherwise. + */ +- (BOOL)bk_any:(BOOL (^)(ObjectType obj))block; + +/** Loops through an array to find whether no objects match the block. + + This selector performs *literally* the exact same function as bk_all: but in reverse. + + @param block A single-argument, BOOL-returning code block. + @return YES if the block returns NO for all objects in the array, NO otherwise. + */ +- (BOOL)bk_none:(BOOL (^)(ObjectType obj))block; + +/** Loops through an array to find whether all objects match the block. + + @param block A single-argument, BOOL-returning code block. + @return YES if the block returns YES for all objects in the array, NO otherwise. + */ +- (BOOL)bk_all:(BOOL (^)(ObjectType obj))block; + +/** Tests whether every element of this array relates to the corresponding element of another array according to match by block. + + For example, finding if a list of numbers corresponds to their sequenced string values; + NSArray *numbers = @[ @(1), @(2), @(3) ]; + NSArray *letters = @[ @"1", @"2", @"3" ]; + BOOL doesCorrespond = [numbers bk_corresponds:letters withBlock:^(id number, id letter) { + return [[number stringValue] isEqualToString:letter]; + }]; + + @param list An array of objects to compare with. + @param block A two-argument, BOOL-returning code block. + @return Returns a BOOL, true if every element of array relates to corresponding element in another array. + */ +- (BOOL)bk_corresponds:(NSArray *)list withBlock:(BOOL (^)(ObjectType obj1, id obj2))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSArray+BlocksKit.m b/YDBlockKit/Core/NSArray+BlocksKit.m new file mode 100644 index 0000000..629ce46 --- /dev/null +++ b/YDBlockKit/Core/NSArray+BlocksKit.m @@ -0,0 +1,173 @@ +// +// NSArray+BlocksKit.m +// BlocksKit +// + +#import "NSArray+BlocksKit.h" + +@implementation NSArray (BlocksKit) + +- (void)bk_each:(void (^)(id obj))block +{ + NSParameterAssert(block != nil); + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + block(obj); + }]; +} + +- (void)bk_apply:(void (^)(id obj))block +{ + NSParameterAssert(block != nil); + + [self enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + block(obj); + }]; +} + +- (id)bk_match:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + NSUInteger index = [self indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + return block(obj); + }]; + + if (index == NSNotFound) + return nil; + + return self[index]; +} + +- (NSArray *)bk_select:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + return [self objectsAtIndexes:[self indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + return block(obj); + }]]; +} + +- (NSArray *)bk_reject:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + return [self bk_select:^BOOL(id obj) { + return !block(obj); + }]; +} + +- (NSArray *)bk_map:(id (^)(id obj))block +{ + NSParameterAssert(block != nil); + + NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count]; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + id value = block(obj) ?: [NSNull null]; + [result addObject:value]; + }]; + + return result; +} + +- (NSArray *)bk_compact:(id (^)(id obj))block +{ + NSParameterAssert(block != nil); + + NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count]; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + id value = block(obj); + if(value) + { + [result addObject:value]; + } + }]; + + return result; +} + +- (id)bk_reduce:(id)initial withBlock:(id (^)(id sum, id obj))block +{ + NSParameterAssert(block != nil); + + __block id result = initial; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + result = block(result, obj); + }]; + + return result; +} + +- (NSInteger)bk_reduceInteger:(NSInteger)initial withBlock:(NSInteger (^)(NSInteger, id))block +{ + NSParameterAssert(block != nil); + + __block NSInteger result = initial; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + result = block(result, obj); + }]; + + return result; +} + +- (CGFloat)bk_reduceFloat:(CGFloat)inital withBlock:(CGFloat (^)(CGFloat, id))block +{ + NSParameterAssert(block != nil); + + __block CGFloat result = inital; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + result = block(result, obj); + }]; + + return result; +} + +- (BOOL)bk_any:(BOOL (^)(id obj))block +{ + return [self bk_match:block] != nil; +} + +- (BOOL)bk_none:(BOOL (^)(id obj))block +{ + return [self bk_match:block] == nil; +} + +- (BOOL)bk_all:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + __block BOOL result = YES; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (!block(obj)) { + result = NO; + *stop = YES; + } + }]; + + return result; +} + +- (BOOL)bk_corresponds:(NSArray *)list withBlock:(BOOL (^)(id obj1, id obj2))block +{ + NSParameterAssert(block != nil); + + __block BOOL result = NO; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx < list.count) { + id obj2 = list[idx]; + result = block(obj, obj2); + } else { + result = NO; + } + *stop = !result; + }]; + + return result; +} + +@end diff --git a/YDBlockKit/Core/NSDictionary+BlocksKit.h b/YDBlockKit/Core/NSDictionary+BlocksKit.h new file mode 100644 index 0000000..d47bc22 --- /dev/null +++ b/YDBlockKit/Core/NSDictionary+BlocksKit.h @@ -0,0 +1,115 @@ +// +// NSDictionary+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extension for NSDictionary. + + Both inspired by and resembling Smalltalk syntax, this utility + allows iteration of a dictionary in a concise way that + saves quite a bit of boilerplate code. + + Includes code by the following: + +- [Mirko Kiefer](https://github.com/mirkok) +- [Zach Waldowski](https://github.com/zwaldowski) + + @see NSArray(BlocksKit) + @see NSSet(BlocksKit) + */ +@interface __GENERICS(NSDictionary, KeyType, ObjectType) (BlocksKit) + +/** Loops through the dictionary and executes the given block using each item. + + @param block A block that performs an action using a key/value pair. + */ +- (void)bk_each:(void (^)(KeyType key, ObjectType obj))block; + +/** Enumerates through the dictionary concurrently and executes + the given block once for each pair. + + Enumeration will occur on appropriate background queues; + the system will spawn threads as need for execution. This + will have a noticeable speed increase, especially on dual-core + devices, but you *must* be aware of the thread safety of the + objects you message from within the block. + + @param block A block that performs an action using a key/value pair. + */ +- (void)bk_apply:(void (^)(KeyType key, ObjectType obj))block; + +/** Loops through a dictionary to find the first key/value pair matching the block. + + bk_match: is functionally identical to bk_select:, but will stop and return + the value on the first match. + + @param block A BOOL-returning code block for a key/value pair. + @return The value of the first pair found; + */ +- (nullable id)bk_match:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Loops through a dictionary to find the key/value pairs matching the block. + + @param block A BOOL-returning code block for a key/value pair. + @return Returns a dictionary of the objects found. + */ +- (NSDictionary *)bk_select:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Loops through a dictionary to find the key/value pairs not matching the block. + + This selector performs *literally* the exact same function as bk_select: but in reverse. + + This is useful, as one may expect, for filtering objects. + NSDictionary *strings = [userData bk_reject:^BOOL(id key, id value) { + return ([obj isKindOfClass:[NSString class]]); + }]; + + @param block A BOOL-returning code block for a key/value pair. + @return Returns a dictionary of all objects not found. + */ +- (NSDictionary *)bk_reject:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Call the block once for each object and create a dictionary with the same keys + and a new set of values. + + @param block A block that returns a new value for a key/value pair. + @return Returns a dictionary of the objects returned by the block. + */ +- (NSDictionary *)bk_map:(id (^)(KeyType key, ObjectType obj))block; + +/** Loops through a dictionary to find whether any key/value pair matches the block. + + This method is similar to the Scala list `exists`. It is functionally + identical to bk_match: but returns a `BOOL` instead. It is not recommended + to use bk_any: as a check condition before executing bk_match:, since it would + require two loops through the dictionary. + + @param block A two-argument, BOOL-returning code block. + @return YES for the first time the block returns YES for a key/value pair, NO otherwise. + */ +- (BOOL)bk_any:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Loops through a dictionary to find whether no key/value pairs match the block. + + This selector performs *literally* the exact same function as bk_all: but in reverse. + + @param block A two-argument, BOOL-returning code block. + @return YES if the block returns NO for all key/value pairs in the dictionary, NO otherwise. + */ +- (BOOL)bk_none:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Loops through a dictionary to find whether all key/value pairs match the block. + + @param block A two-argument, BOOL-returning code block. + @return YES if the block returns YES for all key/value pairs in the dictionary, NO otherwise. + */ +- (BOOL)bk_all:(BOOL (^)(KeyType key, ObjectType obj))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSDictionary+BlocksKit.m b/YDBlockKit/Core/NSDictionary+BlocksKit.m new file mode 100644 index 0000000..298546d --- /dev/null +++ b/YDBlockKit/Core/NSDictionary+BlocksKit.m @@ -0,0 +1,102 @@ +// +// NSDictionary+BlocksKit.m +// BlocksKit +// + +#import "NSDictionary+BlocksKit.h" + +@implementation NSDictionary (BlocksKit) + +- (void)bk_each:(void (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + block(key, obj); + }]; +} + +- (void)bk_apply:(void (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + [self enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id key, id obj, BOOL *stop) { + block(key, obj); + }]; +} + +- (id)bk_match:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + return self[[[self keysOfEntriesPassingTest:^(id key, id obj, BOOL *stop) { + if (block(key, obj)) { + *stop = YES; + return YES; + } + + return NO; + }] anyObject]]; +} + +- (NSDictionary *)bk_select:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + NSArray *keys = [[self keysOfEntriesPassingTest:^(id key, id obj, BOOL *stop) { + return block(key, obj); + }] allObjects]; + + NSArray *objects = [self objectsForKeys:keys notFoundMarker:[NSNull null]]; + return [NSDictionary dictionaryWithObjects:objects forKeys:keys]; +} + +- (NSDictionary *)bk_reject:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + return [self bk_select:^BOOL(id key, id obj) { + return !block(key, obj); + }]; +} + +- (NSDictionary *)bk_map:(id (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:self.count]; + + [self bk_each:^(id key, id obj) { + id value = block(key, obj) ?: [NSNull null]; + result[key] = value; + }]; + + return result; +} + +- (BOOL)bk_any:(BOOL (^)(id key, id obj))block +{ + return [self bk_match:block] != nil; +} + +- (BOOL)bk_none:(BOOL (^)(id key, id obj))block +{ + return [self bk_match:block] == nil; +} + +- (BOOL)bk_all:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + __block BOOL result = YES; + + [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if (!block(key, obj)) { + result = NO; + *stop = YES; + } + }]; + + return result; +} + +@end diff --git a/YDBlockKit/Core/NSIndexSet+BlocksKit.h b/YDBlockKit/Core/NSIndexSet+BlocksKit.h new file mode 100644 index 0000000..f29265c --- /dev/null +++ b/YDBlockKit/Core/NSIndexSet+BlocksKit.h @@ -0,0 +1,124 @@ +// +// NSIndexSet+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSIndexSet. + + Both inspired by and resembling Smalltalk syntax, these utilities + allows for iteration of an array in a concise way that + saves quite a bit of boilerplate code for filtering or finding + objects or an object. + + Includes code by the following: + +- [Robin Lu](https://github.com/robin) +- [Michael Ash](https://github.com/mikeash) +- [Zach Waldowski](https://github.com/zwaldowski) +- [Kaelin Colclasure] + + @see NSArray(BlocksKit) + @see NSDictionary(BlocksKit) + @see NSSet(BlocksKit) + */ +@interface NSIndexSet (BlocksKit) + +/** Loops through an index set and executes the given block at each index. + + @param block A single-argument, void-returning code block. + */ +- (void)bk_each:(void (^)(NSUInteger index))block; + +/** Enumerates each index in an index set concurrently and executes the + given block once per index. + + Enumeration will occur on appropriate background queues. + Be aware that the block will not necessarily be executed + in order for each index. + + @param block A single-argument, void-returning code block. + */ +- (void)bk_apply:(void (^)(NSUInteger index))block; + +/** Loops through an array and returns the index matching the block. + + @param block A single-argument, `BOOL`-returning code block. + @return Returns the index if found, `NSNotFound` otherwise. + @see bk_select: + */ +- (NSUInteger)bk_match:(BOOL (^)(NSUInteger index))block; + +/** Loops through an index set and returns an all indexes matching the block. + + @param block A single-argument, BOOL-returning code block. + @return Returns an index set of matching indexes found. + @see bk_match: + */ +- (NSIndexSet *)bk_select:(BOOL (^)(NSUInteger index))block; + +/** Loops through an index set and returns an all indexes but the ones matching the block. + + This selector performs *literally* the exact same function as bk_select: but in reverse. + + @param block A single-argument, BOOL-returning code block. + @return Returns an index set of all indexes but those matched. + */ +- (NSIndexSet *)bk_reject:(BOOL (^)(NSUInteger index))block; + +/** Call the block once for each index and create an index set with the new values. + + @param block A block that returns a new index for an index. + @return An index set of the indexes returned by the block. + */ +- (NSIndexSet *)bk_map:(NSUInteger (^)(NSUInteger index))block; + +/** Call the block once for each index and create an array of the return values. + + This method allows transforming indexes into objects: + int values[10] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }; + NSIndexSet *idxs = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 10)]; + NSArray *new = [idxs mapIndex:^id(NSUInteger index) { + return [NSNumber numberWithInt:values[index]]); + }]; + + @param block A block that returns an object for an index. + @return Returns an array of the objects returned by the block. + */ +- (NSArray *)bk_mapIndex:(id (^)(NSUInteger index))block; + +/** Loops through an index set to find whether any of the indexes matche the block. + + This method is similar to the Scala list `exists`. It is functionally + identical to bk_match: but returns a `BOOL` instead. It is not recommended + to use bk_any: as a check condition before executing bk_match:, since it would + require two loops through the index set. + + @param block A single-argument, BOOL-returning code block. + @return YES for the first time the block returns YES for an index, NO otherwise. + */ +- (BOOL)bk_any:(BOOL (^)(NSUInteger index))block; + +/** Loops through an index set to find whether all objects match the block. + + @param block A single-argument, BOOL-returning code block. + @return YES if the block returns YES for all indexes in the array, NO otherwise. + */ +- (BOOL)bk_all:(BOOL (^)(NSUInteger index))block; + +/** Loops through an index set to find whether no objects match the block. + + This selector performs *literally* the exact same function as bk_all: but in reverse. + + @param block A single-argument, BOOL-returning code block. + @return YES if the block returns NO for all indexes in the array, NO otherwise. + */ +- (BOOL)bk_none:(BOOL (^)(NSUInteger index))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSIndexSet+BlocksKit.m b/YDBlockKit/Core/NSIndexSet+BlocksKit.m new file mode 100644 index 0000000..56b4d6b --- /dev/null +++ b/YDBlockKit/Core/NSIndexSet+BlocksKit.m @@ -0,0 +1,99 @@ +// +// NSIndexSet+BlocksKit.m +// BlocksKit +// + +#import "NSIndexSet+BlocksKit.h" + +@implementation NSIndexSet (BlocksKit) + +- (void)bk_each:(void (^)(NSUInteger index))block { + NSParameterAssert(block != nil); + + [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + block(idx); + }]; +} + +- (void)bk_apply:(void (^)(NSUInteger index))block { + NSParameterAssert(block != nil); + + [self enumerateIndexesWithOptions:NSEnumerationConcurrent usingBlock:^(NSUInteger idx, BOOL *stop) { + block(idx); + }]; +} + +- (NSUInteger)bk_match:(BOOL (^)(NSUInteger index))block { + NSParameterAssert(block != nil); + + return [self indexPassingTest:^BOOL(NSUInteger idx, BOOL *stop) { + return block(idx); + }]; +} + +- (NSIndexSet *)bk_select:(BOOL (^)(NSUInteger index))block { + NSParameterAssert(block != nil); + + NSIndexSet *list = [self indexesPassingTest:^BOOL(NSUInteger idx, BOOL *stop) { + return block(idx); + }]; + + return list; +} + +- (NSIndexSet *)bk_reject:(BOOL (^)(NSUInteger index))block { + NSParameterAssert(block != nil); + return [self bk_select:^BOOL(NSUInteger idx) { + return !block(idx); + }]; +} + +- (NSIndexSet *)bk_map:(NSUInteger (^)(NSUInteger index))block { + NSParameterAssert(block != nil); + + NSMutableIndexSet *list = [NSMutableIndexSet indexSet]; + + [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + [list addIndex:block(idx)]; + }]; + + return list; +} + +- (NSArray *)bk_mapIndex:(id (^)(NSUInteger index))block { + NSParameterAssert(block != nil); + + NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count]; + + [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + id value = block(idx) ?: [NSNull null]; + [result addObject:value]; + }]; + + return result; +} + +- (BOOL)bk_any:(BOOL (^)(NSUInteger index))block { + return [self bk_match:block] != NSNotFound; +} + +- (BOOL)bk_none:(BOOL (^)(NSUInteger index))block { + return [self bk_match:block] == NSNotFound; +} + +- (BOOL)bk_all:(BOOL (^)(NSUInteger index))block { + NSParameterAssert(block != nil); + + __block BOOL result = YES; + + [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + if (!block(idx)) { + result = NO; + *stop = YES; + } + }]; + + return result; +} + +@end diff --git a/YDBlockKit/Core/NSInvocation+BlocksKit.h b/YDBlockKit/Core/NSInvocation+BlocksKit.h new file mode 100644 index 0000000..5ca4231 --- /dev/null +++ b/YDBlockKit/Core/NSInvocation+BlocksKit.h @@ -0,0 +1,38 @@ +// +// NSInvocation+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** BlocksKit extensions for NSInvocation. */ +@interface NSInvocation (BlocksKit) + +/** Generates a forwarding `NSInvocation` instance for a given method call + encapsulated by the given block. + + NSInvocation *invocation = [NSInvocation invocationWithTarget:target block:^(id myObject) { + [myObject someMethodWithArg:42.0]; + }]; + + This returns an invocation with the appropriate target, selector, and arguments + without creating the buffers yourself. It is only recommended to call a method + on the argument to the block only once. More complicated forwarding machinery + can be accomplished by the A2DynamicDelegate family of classes included in + BlocksKit. + + Created by [Jonathan Rentzch](https://github.com/rentzsch) as + `NSInvocation-blocks`. + + @param target The object to "grab" the block invocation from. + @param block A code block. + @return A fully-prepared instance of NSInvocation ready to be invoked. + */ ++ (NSInvocation *)bk_invocationWithTarget:(id)target block:(void (^)(id target))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSInvocation+BlocksKit.m b/YDBlockKit/Core/NSInvocation+BlocksKit.m new file mode 100644 index 0000000..4471ed7 --- /dev/null +++ b/YDBlockKit/Core/NSInvocation+BlocksKit.m @@ -0,0 +1,48 @@ +// +// NSInvocation+BlocksKit.m +// BlocksKit +// + +#import "NSInvocation+BlocksKit.h" + +@interface BKInvocationGrabber : NSProxy + ++ (BKInvocationGrabber *)grabberWithTarget:(id)target; + +@property (nonatomic, strong) id target; +@property (nonatomic, strong) NSInvocation *invocation; + +@end + +@implementation BKInvocationGrabber + ++ (BKInvocationGrabber *)grabberWithTarget:(id)target { + BKInvocationGrabber *instance = [BKInvocationGrabber alloc]; + instance.target = target; + return instance; +} + +- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { + return [self.target methodSignatureForSelector:selector]; +} + +- (void)forwardInvocation:(NSInvocation*)invocation { + [invocation setTarget:self.target]; + NSParameterAssert(self.invocation == nil); + self.invocation = invocation; +} + +@end + + +@implementation NSInvocation (BlocksKit) + ++ (NSInvocation *)bk_invocationWithTarget:(id)target block:(void (^)(id target))block +{ + NSParameterAssert(block != nil); + BKInvocationGrabber *grabber = [BKInvocationGrabber grabberWithTarget:target]; + block(grabber); + return grabber.invocation; +} + +@end diff --git a/YDBlockKit/Core/NSMapTable+BlocksKit.h b/YDBlockKit/Core/NSMapTable+BlocksKit.h new file mode 100644 index 0000000..79f9882 --- /dev/null +++ b/YDBlockKit/Core/NSMapTable+BlocksKit.h @@ -0,0 +1,111 @@ +// +// NSMapTable+BlocksKit.m +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface __GENERICS(NSMapTable, KeyType, ObjectType) (BlocksKit) + +/** Loops through the maptable and executes the given block using each item. + + @param block A block that performs an action using a key/value pair. + */ +- (void)bk_each:(void (^)(KeyType key, ObjectType obj))block; + +/** Loops through a maptable to find the first key/value pair matching the block. + + bk_match: is functionally identical to bk_select:, but will stop and return + the value on the first match. + + @param block A BOOL-returning code block for a key/value pair. + @return The value of the first pair found; + */ +- (nullable id)bk_match:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Loops through a maptable to find the key/value pairs matching the block. + + @param block A BOOL-returning code block for a key/value pair. + @return Returns a maptable of the objects found. + */ +- (NSMapTable *)bk_select:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Loops through a maptable to find the key/value pairs not matching the block. + + This selector performs *literally* the exact same function as bk_select: but in reverse. + + This is useful, as one may expect, for filtering objects. + NSMapTable *strings = [userData bk_reject:^BOOL(id key, id value) { + return ([obj isKindOfClass:[NSString class]]); + }]; + + @param block A BOOL-returning code block for a key/value pair. + @return Returns a maptable of all objects not found. + */ +- (NSMapTable *)bk_reject:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Call the block once for each object and create a maptable with the same keys + and a new set of values. + + @param block A block that returns a new value for a key/value pair. + @return Returns a maptable of the objects returned by the block. + */ +- (NSMapTable *)bk_map:(id (^)(KeyType key, ObjectType obj))block; + +/** Loops through a maptable to find whether any key/value pair matches the block. + + This method is similar to the Scala list `exists`. It is functionally + identical to bk_match: but returns a `BOOL` instead. It is not recommended + to use bk_any: as a check condition before executing bk_match:, since it would + require two loops through the maptable. + + @param block A two-argument, BOOL-returning code block. + @return YES for the first time the block returns YES for a key/value pair, NO otherwise. + */ +- (BOOL)bk_any:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Loops through a maptable to find whether no key/value pairs match the block. + + This selector performs *literally* the exact same function as bk_all: but in reverse. + + @param block A two-argument, BOOL-returning code block. + @return YES if the block returns NO for all key/value pairs in the maptable, NO otherwise. + */ +- (BOOL)bk_none:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Loops through a maptable to find whether all key/value pairs match the block. + + @param block A two-argument, BOOL-returning code block. + @return YES if the block returns YES for all key/value pairs in the maptable, NO otherwise. + */ +- (BOOL)bk_all:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Filters a mutable dictionary to the key/value pairs matching the block. + + @param block A BOOL-returning code block for a key/value pair. + @see bk_reject: + */ +- (void)bk_performSelect:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Filters a mutable dictionary to the key/value pairs not matching the block, + the logical inverse to bk_select:. + + @param block A BOOL-returning code block for a key/value pair. + @see bk_select: + */ +- (void)bk_performReject:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Transform each value of the dictionary to a new value, as returned by the + block. + + @param block A block that returns a new value for a given key/value pair. + @see bk_map: + */ +- (void)bk_performMap:(id (^)(KeyType key, ObjectType obj))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSMapTable+BlocksKit.m b/YDBlockKit/Core/NSMapTable+BlocksKit.m new file mode 100644 index 0000000..19cdcab --- /dev/null +++ b/YDBlockKit/Core/NSMapTable+BlocksKit.m @@ -0,0 +1,149 @@ +// +// NSMapTable+BlocksKit.h +// BlocksKit +// + +#import "NSMapTable+BlocksKit.h" + +@implementation NSMapTable (BlocksKit) + +- (void)bk_enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block { + BOOL stop = NO; + for(id key in self) { + id obj = [self objectForKey:key]; + block(key, obj, &stop); + if(stop) { + break; + } + } +} + +- (void)bk_each:(void (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + [self bk_enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + block(key, obj); + }]; +} + +- (id)bk_match:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + __block id match = nil; + [self bk_enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if(block(key, obj)) { + match = obj; + *stop = YES; + } + }]; + return match; +} + +- (NSMapTable *)bk_select:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + NSMapTable *result = [[NSMapTable alloc] initWithKeyPointerFunctions:self.keyPointerFunctions valuePointerFunctions:self.valuePointerFunctions capacity:self.count]; + + [self bk_each:^(id key, id obj) { + if(block(key, obj)) { + [result setObject:obj forKey:key]; + } + }]; + + return result; +} + +- (NSMapTable *)bk_reject:(BOOL (^)(id key, id obj))block +{ + return [self bk_select:^BOOL(id key, id obj) { + return !block(key, obj); + }]; +} + +- (NSMapTable *)bk_map:(id (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + NSMapTable *result = [[NSMapTable alloc] initWithKeyPointerFunctions:self.keyPointerFunctions valuePointerFunctions:self.valuePointerFunctions capacity:self.count]; + + [self bk_each:^(id key, id obj) { + id value = block(key, obj); + if (!value) + value = [NSNull null]; + + [result setObject:value forKey:key]; + }]; + + return result; +} + +- (BOOL)bk_any:(BOOL (^)(id key, id obj))block +{ + return [self bk_match:block] != nil; +} + +- (BOOL)bk_none:(BOOL (^)(id key, id obj))block +{ + return [self bk_match:block] == nil; +} + +- (BOOL)bk_all:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + __block BOOL result = YES; + + [self bk_enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if (!block(key, obj)) { + result = NO; + *stop = YES; + } + }]; + + return result; +} + +- (void)bk_performSelect:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + NSMutableArray *keys = [NSMutableArray arrayWithCapacity:self.count]; + + [self bk_each:^(id key, id obj) { + if(!block(key, obj)) { + [keys addObject:key]; + } + }]; + + for(id key in keys) { + [self removeObjectForKey:key]; + } +} + +- (void)bk_performReject:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + [self bk_performSelect:^BOOL(id key, id obj) { + return !block(key, obj); + }]; +} + +- (void)bk_performMap:(id (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + NSMutableDictionary *mapped = [NSMutableDictionary dictionaryWithCapacity:self.count]; + + [self bk_each:^(id key, id obj) { + mapped[key] = block(key, obj); + }]; + + [mapped enumerateKeysAndObjectsUsingBlock:^(id key, id mappedObject, BOOL *stop) { + [self setObject:mappedObject forKey:key]; + }]; +} + +@end diff --git a/YDBlockKit/Core/NSMutableArray+BlocksKit.h b/YDBlockKit/Core/NSMutableArray+BlocksKit.h new file mode 100644 index 0000000..bfef218 --- /dev/null +++ b/YDBlockKit/Core/NSMutableArray+BlocksKit.h @@ -0,0 +1,55 @@ +// +// NSMutableArray+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSMutableArray. + + These utilities expound upon the BlocksKit additions to the immutable + superclass by allowing certain utilities to work on an instance of the mutable + class, saving memory by not creating an immutable copy of the results. + + Includes code by the following: + + - [Martin Schürrer](https://github.com/MSch) + - [Zach Waldowski](https://github.com/zwaldowski) + + @see NSArray(BlocksKit) + */ +@interface __GENERICS(NSMutableArray, ObjectType) (BlocksKit) + +/** Filters a mutable array to the objects matching the block. + + @param block A single-argument, BOOL-returning code block. + @see bk_reject: + */ +- (void)bk_performSelect:(BOOL (^)(id ObjectType))block; + +/** Filters a mutable array to all objects but the ones matching the block, + the logical inverse to bk_select:. + + @param block A single-argument, BOOL-returning code block. + @see bk_select: + */ +- (void)bk_performReject:(BOOL (^)(id ObjectType))block; + +/** Transform the objects in the array to the results of the block. + + This is sometimes referred to as a transform, mutating one of each object: + [foo bk_performMap:^id(id obj) { + return [dateTransformer dateFromString:obj]; + }]; + + @param block A single-argument, object-returning code block. + @see bk_map: + */ +- (void)bk_performMap:(id (^)(id ObjectType))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSMutableArray+BlocksKit.m b/YDBlockKit/Core/NSMutableArray+BlocksKit.m new file mode 100644 index 0000000..4bd3013 --- /dev/null +++ b/YDBlockKit/Core/NSMutableArray+BlocksKit.m @@ -0,0 +1,42 @@ +// +// NSMutableArray+BlocksKit.m +// BlocksKit +// + +#import "NSMutableArray+BlocksKit.h" + +@implementation NSMutableArray (BlocksKit) + +- (void)bk_performSelect:(BOOL (^)(id obj))block { + NSParameterAssert(block != nil); + + NSIndexSet *list = [self indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + return !block(obj); + }]; + + if (!list.count) return; + [self removeObjectsAtIndexes:list]; +} + +- (void)bk_performReject:(BOOL (^)(id obj))block { + NSParameterAssert(block != nil); + return [self bk_performSelect:^BOOL(id obj) { + return !block(obj); + }]; +} + +- (void)bk_performMap:(id (^)(id obj))block { + NSParameterAssert(block != nil); + + NSMutableArray *new = [self mutableCopy]; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + id value = block(obj) ?: [NSNull null]; + if ([value isEqual:obj]) return; + new[idx] = value; + }]; + + [self setArray:new]; +} + +@end diff --git a/YDBlockKit/Core/NSMutableDictionary+BlocksKit.h b/YDBlockKit/Core/NSMutableDictionary+BlocksKit.h new file mode 100644 index 0000000..cf64dce --- /dev/null +++ b/YDBlockKit/Core/NSMutableDictionary+BlocksKit.h @@ -0,0 +1,51 @@ +// +// NSMutableDictionary+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSMutableDictionary. + + These utilities expound upon the BlocksKit additions to the immutable + superclass by allowing certain utilities to work on an instance of the mutable + class, saving memory by not creating an immutable copy of the results. + + Includes code by the following: + + - [Martin Schürrer](https://github.com/MSch) + - [Zach Waldowski](https://github.com/zwaldowski) + + @see NSDictionary(BlocksKit) + */ +@interface __GENERICS(NSMutableDictionary, KeyType, ObjectType) (BlocksKit) + +/** Filters a mutable dictionary to the key/value pairs matching the block. + + @param block A BOOL-returning code block for a key/value pair. + @see bk_reject: + */ +- (void)bk_performSelect:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Filters a mutable dictionary to the key/value pairs not matching the block, + the logical inverse to bk_select:. + + @param block A BOOL-returning code block for a key/value pair. + @see bk_select: + */ +- (void)bk_performReject:(BOOL (^)(KeyType key, ObjectType obj))block; + +/** Transform each value of the dictionary to a new value, as returned by the + block. + + @param block A block that returns a new value for a given key/value pair. + @see bk_map: + */ +- (void)bk_performMap:(id (^)(KeyType key, ObjectType obj))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSMutableDictionary+BlocksKit.m b/YDBlockKit/Core/NSMutableDictionary+BlocksKit.m new file mode 100644 index 0000000..9207b00 --- /dev/null +++ b/YDBlockKit/Core/NSMutableDictionary+BlocksKit.m @@ -0,0 +1,44 @@ +// +// NSMutableDictionary+BlocksKit.m +// BlocksKit +// + +#import "NSMutableDictionary+BlocksKit.h" + +@implementation NSMutableDictionary (BlocksKit) + +- (void)bk_performSelect:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + NSArray *keys = [[self keysOfEntriesWithOptions:NSEnumerationConcurrent passingTest:^BOOL(id key, id obj, BOOL *stop) { + return !block(key, obj); + }] allObjects]; + + [self removeObjectsForKeys:keys]; +} + +- (void)bk_performReject:(BOOL (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + [self bk_performSelect:^BOOL(id key, id obj) { + return !block(key, obj); + }]; +} + +- (void)bk_performMap:(id (^)(id key, id obj))block +{ + NSParameterAssert(block != nil); + + NSMutableDictionary *new = [self mutableCopy]; + + [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + id value = block(key, obj) ?: [NSNull null]; + if ([value isEqual:obj]) return; + new[key] = value; + }]; + + [self setDictionary:new]; +} + +@end diff --git a/YDBlockKit/Core/NSMutableIndexSet+BlocksKit.h b/YDBlockKit/Core/NSMutableIndexSet+BlocksKit.h new file mode 100644 index 0000000..66c4763 --- /dev/null +++ b/YDBlockKit/Core/NSMutableIndexSet+BlocksKit.h @@ -0,0 +1,47 @@ +// +// NSMutableIndexSet+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSMutableIndexSet. + + These utilities expound upon the BlocksKit additions to the immutable + superclass by allowing certain utilities to work on an instance of the mutable + class, saving memory by not creating an immutable copy of the results. + + @see NSIndexSet(BlocksKit) + */ +@interface NSMutableIndexSet (BlocksKit) + +/** Filters a mutable index set to the indexes matching the block. + + @param block A single-argument, BOOL-returning code block. + @see bk_reject: + */ +- (void)bk_performSelect:(BOOL (^)(NSUInteger index))block; + +/** Filters a mutable index set to all indexes but the ones matching the block, + the logical inverse to bk_select:. + + @param block A single-argument, BOOL-returning code block. + @see bk_select: + */ +- (void)bk_performReject:(BOOL (^)(NSUInteger index))block; + +/** Transform each index of the index set to a new index, as returned by the + block. + + @param block A block that returns a new index for a index. + @see bk_map: + */ +- (void)bk_performMap:(NSUInteger (^)(NSUInteger index))block; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSMutableIndexSet+BlocksKit.m b/YDBlockKit/Core/NSMutableIndexSet+BlocksKit.m new file mode 100644 index 0000000..ab70130 --- /dev/null +++ b/YDBlockKit/Core/NSMutableIndexSet+BlocksKit.m @@ -0,0 +1,44 @@ +// +// NSMutableIndexSet+BlocksKit.m +// BlocksKit +// + +#import "NSMutableIndexSet+BlocksKit.h" + +@implementation NSMutableIndexSet (BlocksKit) + +- (void)bk_performSelect:(BOOL (^)(NSUInteger index))block +{ + NSParameterAssert(block != nil); + + NSIndexSet *list = [self indexesPassingTest:^BOOL(NSUInteger idx, BOOL *stop) { + return !block(idx); + }]; + + if (!list.count) return; + [self removeIndexes:list]; +} + +- (void)bk_performReject:(BOOL (^)(NSUInteger index))block +{ + NSParameterAssert(block != nil); + return [self bk_performSelect:^BOOL(NSUInteger idx) { + return !block(idx); + }]; +} + +- (void)bk_performMap:(NSUInteger (^)(NSUInteger index))block +{ + NSParameterAssert(block != nil); + + NSMutableIndexSet *new = [self mutableCopy]; + + [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + [new addIndex:block(idx)]; + }]; + + [self removeAllIndexes]; + [self addIndexes:new]; +} + +@end diff --git a/YDBlockKit/Core/NSMutableOrderedSet+BlocksKit.h b/YDBlockKit/Core/NSMutableOrderedSet+BlocksKit.h new file mode 100644 index 0000000..80b237e --- /dev/null +++ b/YDBlockKit/Core/NSMutableOrderedSet+BlocksKit.h @@ -0,0 +1,55 @@ +// +// NSMutableOrderedSet+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSMutableOrderedSet. + + These utilities expound upon the BlocksKit additions to the immutable + superclass by allowing certain utilities to work on an instance of the mutable + class, saving memory by not creating an immutable copy of the results. + + Includes code by the following: + + - [Martin Schürrer](https://github.com/MSch) + - [Zach Waldowski](https://github.com/zwaldowski) + + @see NSOrderedSet(BlocksKit) + */ +@interface __GENERICS(NSMutableOrderedSet, ObjectType) (BlocksKit) + +/** Filters a mutable ordered set to the objects matching the block. + + @param block A single-argument, BOOL-returning code block. + @see bk_reject: + */ +- (void)bk_performSelect:(BOOL (^)(id ObjectType))block; + +/** Filters a mutable ordered set to all objects but the ones matching the + block, the logical inverse to bk_select:. + + @param block A single-argument, BOOL-returning code block. + @see bk_select: + */ +- (void)bk_performReject:(BOOL (^)(id ObjectType))block; + +/** Transform the objects in the ordered set to the results of the block. + + This is sometimes referred to as a transform, mutating one of each object: + [foo bk_performMap:^id(id obj) { + return [dateTransformer dateFromString:obj]; + }]; + + @param block A single-argument, object-returning code block. + @see bk_map: + */ +- (void)bk_performMap:(id (^)(id ObjectType))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSMutableOrderedSet+BlocksKit.m b/YDBlockKit/Core/NSMutableOrderedSet+BlocksKit.m new file mode 100644 index 0000000..0e2ff77 --- /dev/null +++ b/YDBlockKit/Core/NSMutableOrderedSet+BlocksKit.m @@ -0,0 +1,44 @@ +// +// NSMutableOrderedSet+BlocksKit.m +// BlocksKit +// + +#import "NSMutableOrderedSet+BlocksKit.h" + +@implementation NSMutableOrderedSet (BlocksKit) + +- (void)bk_performSelect:(BOOL (^)(id obj))block { + NSParameterAssert(block != nil); + + NSIndexSet *list = [self indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + return !block(obj); + }]; + + if (!list.count) return; + [self removeObjectsAtIndexes:list]; +} + +- (void)bk_performReject:(BOOL (^)(id obj))block { + NSParameterAssert(block != nil); + return [self bk_performSelect:^BOOL(id obj) { + return !block(obj); + }]; +} + +- (void)bk_performMap:(id (^)(id obj))block { + NSParameterAssert(block != nil); + + NSMutableIndexSet *newIndexes = [NSMutableIndexSet indexSet]; + NSMutableArray *newObjects = [NSMutableArray arrayWithCapacity:self.count]; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + id value = block(obj) ?: [NSNull null]; + if ([value isEqual:obj]) return; + [newIndexes addIndex:idx]; + [newObjects addObject:obj]; + }]; + + [self replaceObjectsAtIndexes:newIndexes withObjects:newObjects]; +} + +@end diff --git a/YDBlockKit/Core/NSMutableSet+BlocksKit.h b/YDBlockKit/Core/NSMutableSet+BlocksKit.h new file mode 100644 index 0000000..decb4fa --- /dev/null +++ b/YDBlockKit/Core/NSMutableSet+BlocksKit.h @@ -0,0 +1,55 @@ +// +// NSMutableSet+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSMutableSet. + + These utilities expound upon the BlocksKit additions to the immutable + superclass by allowing certain utilities to work on an instance of the mutable + class, saving memory by not creating an immutable copy of the results. + + Includes code by the following: + + - [Martin Schürrer](https://github.com/MSch) + - [Zach Waldowski](https://github.com/zwaldowski) + + @see NSSet(BlocksKit) + */ +@interface __GENERICS(NSMutableSet, ObjectType) (BlocksKit) + +/** Filters a mutable set to the objects matching the block. + + @param block A single-argument, BOOL-returning code block. + @see bk_reject: + */ +- (void)bk_performSelect:(BOOL (^)(id ObjectType))block; + +/** Filters a mutable set to all objects but the ones matching the block, + the logical inverse to bk_select:. + + @param block A single-argument, BOOL-returning code block. + @see bk_select: + */ +- (void)bk_performReject:(BOOL (^)(id ObjectType))block; + +/** Transform the objects in the set to the results of the block. + + This is sometimes referred to as a transform, mutating one of each object: + [controllers bk_map:^id(id obj) { + return [obj view]; + }]; + + @param block A single-argument, object-returning code block. + @see bk_map: + */ +- (void)bk_performMap:(id (^)(id ObjectType))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSMutableSet+BlocksKit.m b/YDBlockKit/Core/NSMutableSet+BlocksKit.m new file mode 100644 index 0000000..3ea155d --- /dev/null +++ b/YDBlockKit/Core/NSMutableSet+BlocksKit.m @@ -0,0 +1,41 @@ +// +// NSMutableSet+BlocksKit.m +// BlocksKit +// + +#import "NSMutableSet+BlocksKit.h" + +@implementation NSMutableSet (BlocksKit) + +- (void)bk_performSelect:(BOOL (^)(id obj))block { + NSParameterAssert(block != nil); + + NSSet *list = [self objectsPassingTest:^BOOL(id obj, BOOL *stop) { + return block(obj); + }]; + + [self setSet:list]; +} + +- (void)bk_performReject:(BOOL (^)(id obj))block { + NSParameterAssert(block != nil); + [self bk_performSelect:^BOOL(id obj) { + return !block(obj); + }]; +} + +- (void)bk_performMap:(id (^)(id obj))block { + NSParameterAssert(block != nil); + + NSMutableSet *new = [NSMutableSet setWithCapacity:self.count]; + + [self enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { + id value = block(obj); + if (!value) return; + [new addObject:value]; + }]; + + [self setSet:new]; +} + +@end diff --git a/YDBlockKit/Core/NSNumber+BlocksKit.h b/YDBlockKit/Core/NSNumber+BlocksKit.h new file mode 100644 index 0000000..3b258af --- /dev/null +++ b/YDBlockKit/Core/NSNumber+BlocksKit.h @@ -0,0 +1,27 @@ +// +// NSNumber+BlocksKit.h +// BlocksKit +// + +#import + +/** Block extensions for NSNumber. + + Both inspired by and resembling Smalltalk syntax, these utilities + allows for iteration of an array in a concise way that + saves quite a bit of boilerplate code for performing a task a fixed number of + times. + + Includes code by the following: + +- [Colin T.A. Gray](https://github.com/colinta) + */ +@interface NSNumber (BlocksKit) + +/** Performs a block `self` number of times + + @param block A void-returning code block that accepts no arguments. + */ +- (void)bk_times:(void (^)())block; + +@end diff --git a/YDBlockKit/Core/NSNumber+BlocksKit.m b/YDBlockKit/Core/NSNumber+BlocksKit.m new file mode 100644 index 0000000..e10ba04 --- /dev/null +++ b/YDBlockKit/Core/NSNumber+BlocksKit.m @@ -0,0 +1,19 @@ +// +// NSNumber+BlocksKit.m +// BlocksKit +// + +#import "NSNumber+BlocksKit.h" + +@implementation NSNumber (BlocksKit) + +- (void)bk_times:(void (^)())block +{ + NSParameterAssert(block != nil); + + for (NSInteger idx = 0 ; idx < self.integerValue ; ++idx ) { + block(); + } +} + +@end diff --git a/YDBlockKit/Core/NSObject+BKAssociatedObjects.h b/YDBlockKit/Core/NSObject+BKAssociatedObjects.h new file mode 100644 index 0000000..1216ced --- /dev/null +++ b/YDBlockKit/Core/NSObject+BKAssociatedObjects.h @@ -0,0 +1,163 @@ +// +// NSObject+BKAssociatedObjects.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Objective-C wrapper for 10.6+ associated object API. + + In Mac OS X Snow Leopard and iOS 3.0, Apple introduced an addition to the + Objective-C Runtime called associated objects. Associated objects allow for the + pairing of a random key and object pair to be saved on an instance. + + In BlocksKit, associated objects allow us to emulate instance variables in the + ategories we use. + + Class methods also exist for each association. These associations are unique to + each class, and exist for the lifetime of the application unless set to `nil`. + Each class is a unique meta-object; the ultimate singleton. + + Created by [Andy Matuschak](https://github.com/andymatuschak) as + `AMAssociatedObjects`. + */ +@interface NSObject (BKAssociatedObjects) + +/** Strongly associates an object with the reciever. + + The associated value is retained as if it were a property + synthesized with `nonatomic` and `retain`. + + Using retained association is strongly recommended for most + Objective-C object derivative of NSObject, particularly + when it is subject to being externally released or is in an + `NSAutoreleasePool`. + + @param value Any object. + @param key A unique key pointer. + */ +- (void)bk_associateValue:(nullable id)value withKey:(const void *)key; + +/** Strongly associates an object with the receiving class. + + @see associateValue:withKey: + @param value Any object. + @param key A unique key pointer. + */ ++ (void)bk_associateValue:(nullable id)value withKey:(const void *)key; + +/** Strongly, thread-safely associates an object with the reciever. + + The associated value is retained as if it were a property + synthesized with `atomic` and `retain`. + + Using retained association is strongly recommended for most + Objective-C object derivative of NSObject, particularly + when it is subject to being externally released or is in an + `NSAutoreleasePool`. + + @see associateValue:withKey: + @param value Any object. + @param key A unique key pointer. + */ +- (void)bk_atomicallyAssociateValue:(nullable id)value withKey:(const void *)key; + +/** Strongly, thread-safely associates an object with the receiving class. + + @see associateValue:withKey: + @param value Any object. + @param key A unique key pointer. + */ ++ (void)bk_atomicallyAssociateValue:(nullable id)value withKey:(const void *)key; + +/** Associates a copy of an object with the reciever. + + The associated value is copied as if it were a property + synthesized with `nonatomic` and `copy`. + + Using copied association is recommended for a block or + otherwise `NSCopying`-compliant instances like NSString. + + @param value Any object, pointer, or value. + @param key A unique key pointer. + */ +- (void)bk_associateCopyOfValue:(nullable id)value withKey:(const void *)key; + +/** Associates a copy of an object with the receiving class. + + @see associateCopyOfValue:withKey: + @param value Any object, pointer, or value. + @param key A unique key pointer. + */ ++ (void)bk_associateCopyOfValue:(nullable id)value withKey:(const void *)key; + +/** Thread-safely associates a copy of an object with the reciever. + + The associated value is copied as if it were a property + synthesized with `atomic` and `copy`. + + Using copied association is recommended for a block or + otherwise `NSCopying`-compliant instances like NSString. + + @see associateCopyOfValue:withKey: + @param value Any object, pointer, or value. + @param key A unique key pointer. + */ +- (void)bk_atomicallyAssociateCopyOfValue:(nullable id)value withKey:(const void *)key; + +/** Thread-safely associates a copy of an object with the receiving class. + + @see associateCopyOfValue:withKey: + @param value Any object, pointer, or value. + @param key A unique key pointer. + */ ++ (void)bk_atomicallyAssociateCopyOfValue:(nullable id)value withKey:(const void *)key; + +/** Weakly associates an object with the reciever. + + A weak association will cause the pointer to be set to zero + or nil upon the disappearance of what it references; + in other words, the associated object is not kept alive. + + @param value Any object. + @param key A unique key pointer. + */ +- (void)bk_weaklyAssociateValue:(nullable __autoreleasing id)value withKey:(const void *)key; + +/** Weakly associates an object with the receiving class. + + @see weaklyAssociateValue:withKey: + @param value Any object. + @param key A unique key pointer. + */ ++ (void)bk_weaklyAssociateValue:(nullable __autoreleasing id)value withKey:(const void *)key; + +/** Returns the associated value for a key on the reciever. + + @param key A unique key pointer. + @return The object associated with the key, or `nil` if not found. + */ +- (nullable id)bk_associatedValueForKey:(const void *)key; + +/** Returns the associated value for a key on the receiving class. + + @see associatedValueForKey: + @param key A unique key pointer. + @return The object associated with the key, or `nil` if not found. + */ ++ (nullable id)bk_associatedValueForKey:(const void *)key; + +/** Returns the reciever to a clean state by removing all + associated objects, releasing them if necessary. */ +- (void)bk_removeAllAssociatedObjects; + +/** Returns the recieving class to a clean state by removing + all associated objects, releasing them if necessary. */ ++ (void)bk_removeAllAssociatedObjects; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSObject+BKAssociatedObjects.m b/YDBlockKit/Core/NSObject+BKAssociatedObjects.m new file mode 100644 index 0000000..cf2e9f5 --- /dev/null +++ b/YDBlockKit/Core/NSObject+BKAssociatedObjects.m @@ -0,0 +1,115 @@ +// +// NSObject+BKAssociatedObjects.m +// BlocksKit +// + +#import "NSObject+BKAssociatedObjects.h" +@import ObjectiveC.runtime; + +#pragma mark - Weak support + +@interface _BKWeakAssociatedObject : NSObject + +@property (nonatomic, weak) id value; + +@end + +@implementation _BKWeakAssociatedObject + +@end + +@implementation NSObject (BKAssociatedObjects) + +#pragma mark - Instance Methods + +- (void)bk_associateValue:(id)value withKey:(const void *)key +{ + objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)bk_atomicallyAssociateValue:(id)value withKey:(const void *)key +{ + objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN); +} + +- (void)bk_associateCopyOfValue:(id)value withKey:(const void *)key +{ + objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (void)bk_atomicallyAssociateCopyOfValue:(id)value withKey:(const void *)key +{ + objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_COPY); +} + +- (void)bk_weaklyAssociateValue:(__autoreleasing id)value withKey:(const void *)key +{ + _BKWeakAssociatedObject *assoc = objc_getAssociatedObject(self, key); + if (!assoc) { + assoc = [_BKWeakAssociatedObject new]; + [self bk_associateValue:assoc withKey:key]; + } + assoc.value = value; +} + +- (id)bk_associatedValueForKey:(const void *)key +{ + id value = objc_getAssociatedObject(self, key); + if (value && [value isKindOfClass:[_BKWeakAssociatedObject class]]) { + return [(_BKWeakAssociatedObject *)value value]; + } + return value; +} + +- (void)bk_removeAllAssociatedObjects +{ + objc_removeAssociatedObjects(self); +} + +#pragma mark - Class Methods + ++ (void)bk_associateValue:(id)value withKey:(const void *)key +{ + objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + ++ (void)bk_atomicallyAssociateValue:(id)value withKey:(const void *)key +{ + objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN); +} + ++ (void)bk_associateCopyOfValue:(id)value withKey:(const void *)key +{ + objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + ++ (void)bk_atomicallyAssociateCopyOfValue:(id)value withKey:(const void *)key +{ + objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_COPY); +} + ++ (void)bk_weaklyAssociateValue:(__autoreleasing id)value withKey:(const void *)key +{ + _BKWeakAssociatedObject *assoc = objc_getAssociatedObject(self, key); + if (!assoc) { + assoc = [_BKWeakAssociatedObject new]; + [self bk_associateValue:assoc withKey:key]; + } + assoc.value = value; +} + ++ (id)bk_associatedValueForKey:(const void *)key +{ + id value = objc_getAssociatedObject(self, key); + if (value && [value isKindOfClass:[_BKWeakAssociatedObject class]]) { + return [(_BKWeakAssociatedObject *)value value]; + } + return value; +} + ++ (void)bk_removeAllAssociatedObjects +{ + objc_removeAssociatedObjects(self); +} + +@end diff --git a/YDBlockKit/Core/NSObject+BKBlockExecution.h b/YDBlockKit/Core/NSObject+BKBlockExecution.h new file mode 100644 index 0000000..afc12e1 --- /dev/null +++ b/YDBlockKit/Core/NSObject+BKBlockExecution.h @@ -0,0 +1,129 @@ +// +// NSObject+BKBlockExecution.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef __nonnull id BKCancellationToken; + +/** Block execution on *any* object. + + This category overhauls the `performSelector:` utilities on + NSObject to instead use blocks. Not only are the blocks performed + extremely speedily, thread-safely, and asynchronously using + Grand Central Dispatch, but each convenience method also returns + a pointer that can be used to cancel the execution before it happens! + + Includes code by the following: + + - [Peter Steinberger](https://github.com/steipete) + - [Zach Waldowski](https://github.com/zwaldowski) + + */ +@interface NSObject (BKBlockExecution) + +/** Executes a block after a given delay on the reciever. + + @warning *Important:* Use of the **self** reference in a block is discouraged. + The block argument @c obj should be used instead. + + @param delay A measure in seconds. + @param block A single-argument code block, where @c obj is the reciever. + @return An opaque, temporary token that may be used to cancel execution. + */ +- (BKCancellationToken)bk_performAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block; + +/** Executes a block after a given delay. + + @see bk_performAfterDelay:usingBlock: + @param delay A measure in seconds. + @param block A code block. + @return An opaque, temporary token that may be used to cancel execution. + */ ++ (BKCancellationToken)bk_performAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(void))block; + + +/** Executes a block in the background after a given delay on the receiver. + + This method is functionally identical to @c -bk_performAfterDelay:usingBlock: + except the block will be performed on a background queue. + + @warning *Important:* Use of the **self** reference in a block is discouraged. + The block argument @c obj should be used instead. + + @see bk_performAfterDelay:usingBlock: + @param delay A measure in seconds. + @param block A single-argument code block, where @c obj is the reciever. + @return An opaque, temporary token that may be used to cancel execution. + */ +- (BKCancellationToken)bk_performInBackgroundAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block; + +/** Executes a block in the background after a given delay. + + This method is functionally identical to @c +bk_performAfterDelay:usingBlock: + except the block will be performed on a background queue. + + @see bk_performAfterDelay:usingBlock: + @param delay A measure in seconds. + @param block A code block. + @return An opaque, temporary token that may be used to cancel execution. + */ ++ (BKCancellationToken)bk_performInBackgroundAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(void))block; + +/** Executes a block in the background after a given delay. + + This method is functionally identical to @c -bk_performAfterDelay:usingBlock: + except the block will be performed on a background queue. + + @warning *Important:* Use of the **self** reference in a block is discouraged. + The block argument @c obj should be used instead. + + @see bk_performAfterDelay:usingBlock: + @param queue A background queue. + @param delay A measure in seconds. + @param block A single-argument code block, where @c obj is the reciever. + @return An opaque, temporary token that may be used to cancel execution. + */ +- (BKCancellationToken)bk_performOnQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block; + +/** Executes a block in the background after a given delay. + + This method is functionally identical to @c +bk_performAfterDelay:usingBlock: + except the block will be performed on a background queue. + + @see bk_performAfterDelay:usingBlock: + @param queue A background queue. + @param delay A measure in seconds. + @param block A code block. + @return An opaque, temporary token that may be used to cancel execution. + */ ++ (BKCancellationToken)bk_performOnQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay usingBlock:(void (^)(void))block; + +/** Cancels the potential execution of a block. + + @warning *Important:* It is not recommended to cancel a block executed + with a delay of @c 0. + + @param token A cancellation token, as returned from one of the `bk_perform` + methods. + */ ++ (void)bk_cancelBlock:(BKCancellationToken)token; + +@end + +@interface NSObject (BKBlockExecution_Deprecated) + +- (BKCancellationToken)bk_performBlock:(void (^)(id obj))block afterDelay:(NSTimeInterval)delay DEPRECATED_MSG_ATTRIBUTE("Replaced with -bk_performAfterDelay:usingBlock:"); ++ (BKCancellationToken)bk_performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay DEPRECATED_MSG_ATTRIBUTE("Replaced with +bk_performAfterDelay:usingBlock:"); +- (BKCancellationToken)bk_performBlockInBackground:(void (^)(id obj))block afterDelay:(NSTimeInterval)delay DEPRECATED_MSG_ATTRIBUTE("Replaced with -bk_performInBackgroundAfterDelay:usingBlock:"); ++ (BKCancellationToken)bk_performBlockInBackground:(void (^)(void))block afterDelay:(NSTimeInterval)delay DEPRECATED_MSG_ATTRIBUTE("Replaced with +bk_performInBackgroundAfterDelay:usingBlock:"); ++ (BKCancellationToken)bk_performBlock:(void (^)(void))block onQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay DEPRECATED_MSG_ATTRIBUTE("Replaced with -bk_performOnQueue:afterDelay:usingBlock:"); +- (BKCancellationToken)bk_performBlock:(void (^)(id obj))block onQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay DEPRECATED_MSG_ATTRIBUTE("Replaced with -bk_performOnQueue:afterDelay:usingBlock:"); + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSObject+BKBlockExecution.m b/YDBlockKit/Core/NSObject+BKBlockExecution.m new file mode 100644 index 0000000..cab57de --- /dev/null +++ b/YDBlockKit/Core/NSObject+BKBlockExecution.m @@ -0,0 +1,151 @@ +// +// NSObject+BKBlockExecution.m +// BlocksKit +// + +#import "NSObject+BKBlockExecution.h" + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1010) +#define DISPATCH_CANCELLATION_SUPPORTED 1 +#else +#define DISPATCH_CANCELLATION_SUPPORTED 1 +#endif + +NS_INLINE dispatch_time_t BKTimeDelay(NSTimeInterval t) { + return dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(NSEC_PER_SEC * t)); +} + +NS_INLINE BOOL BKSupportsDispatchCancellation(void) { +#if DISPATCH_CANCELLATION_SUPPORTED + return (&dispatch_block_cancel != NULL); +#else + return NO; +#endif +} + +NS_INLINE dispatch_queue_t BKGetBackgroundQueue(void) { + return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); +} + +static id BKDispatchCancellableBlock(dispatch_queue_t queue, NSTimeInterval delay, void(^block)(void)) { + dispatch_time_t time = BKTimeDelay(delay); + +#if DISPATCH_CANCELLATION_SUPPORTED + if (BKSupportsDispatchCancellation()) { + dispatch_block_t ret = dispatch_block_create(0, block); + dispatch_after(time, queue, ret); + return ret; + } +#endif + + __block BOOL cancelled = NO; + void (^wrapper)(BOOL) = ^(BOOL cancel) { + if (cancel) { + cancelled = YES; + return; + } + if (!cancelled) block(); + }; + + dispatch_after(time, queue, ^{ + wrapper(NO); + }); + + return wrapper; +} + +@implementation NSObject (BlocksKit) + +- (id )bk_performAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block +{ + return [self bk_performOnQueue:dispatch_get_main_queue() afterDelay:delay usingBlock:block]; +} + ++ (id )bk_performAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(void))block +{ + return [NSObject bk_performOnQueue:dispatch_get_main_queue() afterDelay:delay usingBlock:block]; +} + +- (id )bk_performInBackgroundAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block +{ + return [self bk_performOnQueue:BKGetBackgroundQueue() afterDelay:delay usingBlock:block]; +} + ++ (id )bk_performInBackgroundAfterDelay:(NSTimeInterval)delay usingBlock:(void (^)(void))block +{ + return [NSObject bk_performOnQueue:BKGetBackgroundQueue() afterDelay:delay usingBlock:block]; +} + +- (id )bk_performOnQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay usingBlock:(void (^)(id obj))block +{ + NSParameterAssert(block != nil); + + return BKDispatchCancellableBlock(queue, delay, ^{ + block(self); + }); +} + ++ (id )bk_performOnQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay usingBlock:(void (^)(void))block +{ + NSParameterAssert(block != nil); + + return BKDispatchCancellableBlock(queue, delay, block); +} + ++ (void)bk_cancelBlock:(id )block +{ + NSParameterAssert(block != nil); + +#if DISPATCH_CANCELLATION_SUPPORTED + if (BKSupportsDispatchCancellation()) { + dispatch_block_cancel((dispatch_block_t)block); + return; + } +#endif + + void (^wrapper)(BOOL) = (void(^)(BOOL))block; + wrapper(YES); +} + +@end + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + +@implementation NSObject (BKBlockExecution_Deprecated) + +#pragma mark - Legacy verions + +- (id )bk_performBlock:(void (^)(id obj))block afterDelay:(NSTimeInterval)delay +{ + return [self bk_performAfterDelay:delay usingBlock:block]; +} + ++ (id )bk_performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay +{ + return [self bk_performAfterDelay:delay usingBlock:block]; +} + +- (id )bk_performBlockInBackground:(void (^)(id obj))block afterDelay:(NSTimeInterval)delay +{ + return [self bk_performInBackgroundAfterDelay:delay usingBlock:block]; +} + ++ (id )bk_performBlockInBackground:(void (^)(void))block afterDelay:(NSTimeInterval)delay +{ + return [self bk_performInBackgroundAfterDelay:delay usingBlock:block]; +} + +- (id )bk_performBlock:(void (^)(id obj))block onQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay +{ + return [self bk_performOnQueue:queue afterDelay:delay usingBlock:block]; +} + ++ (id )bk_performBlock:(void (^)(void))block onQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay +{ + return [self bk_performOnQueue:queue afterDelay:delay usingBlock:block]; +} + +@end + +#pragma clang diagnostic pop diff --git a/YDBlockKit/Core/NSObject+BKBlockObservation.h b/YDBlockKit/Core/NSObject+BKBlockObservation.h new file mode 100644 index 0000000..e4842ed --- /dev/null +++ b/YDBlockKit/Core/NSObject+BKBlockObservation.h @@ -0,0 +1,142 @@ +// +// NSObject+BKBlockObservation.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Blocks wrapper for key-value observation. + + In Mac OS X Panther, Apple introduced an API called "key-value + observing." It implements an [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern), + where an object will notify observers of any changes in state. + + NSNotification is a rudimentary form of this design style; + KVO, however, allows for the observation of any change in key-value state. + The API for key-value observation, however, is flawed, ugly, and lengthy. + + Like most of the other block abilities in BlocksKit, observation saves + and a bunch of code and a bunch of potential bugs. + + Includes code by the following: + + - [Andy Matuschak](https://github.com/andymatuschak) + - [Jon Sterling](https://github.com/jonsterling) + - [Zach Waldowski](https://github.com/zwaldowski) + - [Jonathan Wight](https://github.com/schwa) + */ + +@interface NSObject (BlockObservation) + +/** Adds an observer to an object conforming to NSKeyValueObserving. + + Adds a block observer that executes a block upon a state change. + + @param keyPath The property to observe, relative to the reciever. + @param task A block with no return argument, and a single parameter: the reciever. + @return Returns a globally unique process identifier for removing + observation with removeObserverWithBlockToken:. + @see addObserverForKeyPath:identifier:options:task: + */ +- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath task:(void (^)(id target))task; + +/** Adds an observer to an object conforming to NSKeyValueObserving. + + Adds a block observer that executes the same block upon + multiple state changes. + + @param keyPaths An array of properties to observe, relative to the reciever. + @param task A block with no return argument and two parameters: the + reciever and the key path of the value change. + @return A unique identifier for removing + observation with removeObserverWithBlockToken:. + @see addObserverForKeyPath:identifier:options:task: + */ +- (NSString *)bk_addObserverForKeyPaths:(NSArray *)keyPaths task:(void (^)(id obj, NSString *keyPath))task; + +/** Adds an observer to an object conforming to NSKeyValueObserving. + + Adds a block observer that executes a block upon a state change + with specific options. + + @param keyPath The property to observe, relative to the reciever. + @param options The NSKeyValueObservingOptions to use. + @param task A block with no return argument and two parameters: the + reciever and the change dictionary. + @return Returns a globally unique process identifier for removing + observation with removeObserverWithBlockToken:. + @see addObserverForKeyPath:identifier:options:task: + */ +- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSDictionary *change))task; + +/** Adds an observer to an object conforming to NSKeyValueObserving. + + Adds a block observer that executes the same block upon + multiple state changes with specific options. + + @param keyPaths An array of properties to observe, relative to the reciever. + @param options The NSKeyValueObservingOptions to use. + @param task A block with no return argument and three parameters: the + reciever, the key path of the value change, and the change dictionary. + @return A unique identifier for removing + observation with removeObserverWithBlockToken:. + @see addObserverForKeyPath:identifier:options:task: + */ +- (NSString *)bk_addObserverForKeyPaths:(NSArray *)keyPaths options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSString *keyPath, NSDictionary *change))task; + +/** Adds an observer to an object conforming to NSKeyValueObserving. + + Adds a block observer that executes the block upon a + state change. + + @param keyPath The property to observe, relative to the reciever. + @param token An identifier for the observation block. + @param options The NSKeyValueObservingOptions to use. + @param task A block responding to the reciever and the KVO change. + observation with removeObserverWithBlockToken:. + @see addObserverForKeyPath:task: + */ +- (void)bk_addObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)token options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSDictionary *change))task; + +/** Adds an observer to an object conforming to NSKeyValueObserving. + + Adds a block observer that executes the same block upon + multiple state changes. + + @param keyPaths An array of properties to observe, relative to the reciever. + @param token An identifier for the observation block. + @param options The NSKeyValueObservingOptions to use. + @param task A block responding to the reciever, the key path, and the KVO change. + observation with removeObserversWithIdentifier:. + @see addObserverForKeyPath:task: + */ +- (void)bk_addObserverForKeyPaths:(NSArray *)keyPaths identifier:(NSString *)token options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSString *keyPath, NSDictionary *change))task; + +/** Removes a block observer. + + @param keyPath The property to stop observing, relative to the reciever. + @param token The unique key returned by addObserverForKeyPath:task: + or the identifier given in addObserverForKeyPath:identifier:task:. + @see removeObserversWithIdentifier: + */ +- (void)bk_removeObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)token; + +/** Removes multiple block observers with a certain identifier. + + @param token A unique key returned by addObserverForKeyPath:task: + and addObserverForKeyPaths:task: or the identifier given in + addObserverForKeyPath:identifier:task: and + addObserverForKeyPaths:identifier:task:. + @see removeObserverForKeyPath:identifier: + */ +- (void)bk_removeObserversWithIdentifier:(NSString *)token; + +/** Remove all registered block observers. */ +- (void)bk_removeAllBlockObservers; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSObject+BKBlockObservation.m b/YDBlockKit/Core/NSObject+BKBlockObservation.m new file mode 100644 index 0000000..a114cdb --- /dev/null +++ b/YDBlockKit/Core/NSObject+BKBlockObservation.m @@ -0,0 +1,343 @@ +// +// NSObject+BKBlockObservation.m +// BlocksKit +// + +#import "NSObject+BKBlockObservation.h" +@import ObjectiveC.runtime; +@import ObjectiveC.message; +#import "NSArray+BlocksKit.h" +#import "NSDictionary+BlocksKit.h" +#import "NSSet+BlocksKit.h" +#import "NSObject+BKAssociatedObjects.h" + +typedef NS_ENUM(int, BKObserverContext) { + BKObserverContextKey, + BKObserverContextKeyWithChange, + BKObserverContextManyKeys, + BKObserverContextManyKeysWithChange +}; + +@interface _BKObserver : NSObject { + BOOL _isObserving; +} + +@property (nonatomic, readonly, unsafe_unretained) id observee; +@property (nonatomic, readonly) NSMutableArray *keyPaths; +@property (nonatomic, readonly) id task; +@property (nonatomic, readonly) BKObserverContext context; + +- (id)initWithObservee:(id)observee keyPaths:(NSArray *)keyPaths context:(BKObserverContext)context task:(id)task; + +@end + +static void *BKObserverBlocksKey = &BKObserverBlocksKey; +static void *BKBlockObservationContext = &BKBlockObservationContext; + +@implementation _BKObserver + +- (id)initWithObservee:(id)observee keyPaths:(NSArray *)keyPaths context:(BKObserverContext)context task:(id)task +{ + if ((self = [super init])) { + _observee = observee; + _keyPaths = [keyPaths mutableCopy]; + _context = context; + _task = [task copy]; + } + return self; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (context != BKBlockObservationContext) return; + + @synchronized(self) { + switch (self.context) { + case BKObserverContextKey: { + void (^task)(id) = self.task; + task(object); + break; + } + case BKObserverContextKeyWithChange: { + void (^task)(id, NSDictionary *) = self.task; + task(object, change); + break; + } + case BKObserverContextManyKeys: { + void (^task)(id, NSString *) = self.task; + task(object, keyPath); + break; + } + case BKObserverContextManyKeysWithChange: { + void (^task)(id, NSString *, NSDictionary *) = self.task; + task(object, keyPath, change); + break; + } + } + } +} + +- (void)startObservingWithOptions:(NSKeyValueObservingOptions)options +{ + @synchronized(self) { + if (_isObserving) return; + + [self.keyPaths bk_each:^(NSString *keyPath) { + [self.observee addObserver:self forKeyPath:keyPath options:options context:BKBlockObservationContext]; + }]; + + _isObserving = YES; + } +} + +- (void)stopObservingKeyPath:(NSString *)keyPath +{ + NSParameterAssert(keyPath); + + @synchronized (self) { + if (!_isObserving) return; + if (![self.keyPaths containsObject:keyPath]) return; + + NSObject *observee = self.observee; + if (!observee) return; + + [self.keyPaths removeObject: keyPath]; + keyPath = [keyPath copy]; + + if (!self.keyPaths.count) { + _task = nil; + _observee = nil; + _keyPaths = nil; + } + + [observee removeObserver:self forKeyPath:keyPath context:BKBlockObservationContext]; + } +} + +- (void)_stopObservingLocked +{ + if (!_isObserving) return; + + _task = nil; + + NSObject *observee = self.observee; + NSArray *keyPaths = [self.keyPaths copy]; + + _observee = nil; + _keyPaths = nil; + + [keyPaths bk_each:^(NSString *keyPath) { + [observee removeObserver:self forKeyPath:keyPath context:BKBlockObservationContext]; + }]; +} + +- (void)stopObserving +{ + if (_observee == nil) return; + + @synchronized (self) { + [self _stopObservingLocked]; + } +} + +- (void)dealloc +{ + if (self.keyPaths) { + [self _stopObservingLocked]; + } +} + +@end + +static const NSUInteger BKKeyValueObservingOptionWantsChangeDictionary = 0x1000; + +@implementation NSObject (BlockObservation) + +- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath task:(void (^)(id target))task +{ + NSString *token = [[NSProcessInfo processInfo] globallyUniqueString]; + [self bk_addObserverForKeyPaths:@[ keyPath ] identifier:token options:0 context:BKObserverContextKey task:task]; + return token; +} + +- (NSString *)bk_addObserverForKeyPaths:(NSArray *)keyPaths task:(void (^)(id obj, NSString *keyPath))task +{ + NSString *token = [[NSProcessInfo processInfo] globallyUniqueString]; + [self bk_addObserverForKeyPaths:keyPaths identifier:token options:0 context:BKObserverContextManyKeys task:task]; + return token; +} + +- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSDictionary *change))task +{ + NSString *token = [[NSProcessInfo processInfo] globallyUniqueString]; + options = options | BKKeyValueObservingOptionWantsChangeDictionary; + [self bk_addObserverForKeyPath:keyPath identifier:token options:options task:task]; + return token; +} + +- (NSString *)bk_addObserverForKeyPaths:(NSArray *)keyPaths options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSString *keyPath, NSDictionary *change))task +{ + NSString *token = [[NSProcessInfo processInfo] globallyUniqueString]; + options = options | BKKeyValueObservingOptionWantsChangeDictionary; + [self bk_addObserverForKeyPaths:keyPaths identifier:token options:options task:task]; + return token; +} + +- (void)bk_addObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)identifier options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSDictionary *change))task +{ + BKObserverContext context = (options == 0) ? BKObserverContextKey : BKObserverContextKeyWithChange; + options = options & (~BKKeyValueObservingOptionWantsChangeDictionary); + [self bk_addObserverForKeyPaths:@[keyPath] identifier:identifier options:options context:context task:task]; +} + +- (void)bk_addObserverForKeyPaths:(NSArray *)keyPaths identifier:(NSString *)identifier options:(NSKeyValueObservingOptions)options task:(void (^)(id obj, NSString *keyPath, NSDictionary *change))task +{ + BKObserverContext context = (options == 0) ? BKObserverContextManyKeys : BKObserverContextManyKeysWithChange; + options = options & (~BKKeyValueObservingOptionWantsChangeDictionary); + [self bk_addObserverForKeyPaths:keyPaths identifier:identifier options:options context:context task:task]; +} + +- (void)bk_removeObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)token +{ + NSParameterAssert(keyPath.length); + NSParameterAssert(token.length); + + NSMutableDictionary *dict; + + @synchronized (self) { + dict = [self bk_observerBlocks]; + if (!dict) return; + } + + _BKObserver *observer = dict[token]; + [observer stopObservingKeyPath:keyPath]; + + if (observer.keyPaths.count == 0) { + [dict removeObjectForKey:token]; + } + + if (dict.count == 0) [self bk_setObserverBlocks:nil]; +} + +- (void)bk_removeObserversWithIdentifier:(NSString *)token +{ + NSParameterAssert(token); + + NSMutableDictionary *dict; + + @synchronized (self) { + dict = [self bk_observerBlocks]; + if (!dict) return; + } + + _BKObserver *observer = dict[token]; + [observer stopObserving]; + + [dict removeObjectForKey:token]; + + if (dict.count == 0) [self bk_setObserverBlocks:nil]; +} + +- (void)bk_removeAllBlockObservers +{ + NSDictionary *dict; + + @synchronized (self) { + dict = [[self bk_observerBlocks] copy]; + [self bk_setObserverBlocks:nil]; + } + + [dict.allValues bk_each:^(_BKObserver *trampoline) { + [trampoline stopObserving]; + }]; +} + +#pragma mark - "Private"s + ++ (NSMutableSet *)bk_observedClassesHash +{ + static dispatch_once_t onceToken; + static NSMutableSet *swizzledClasses = nil; + dispatch_once(&onceToken, ^{ + swizzledClasses = [[NSMutableSet alloc] init]; + }); + + return swizzledClasses; +} + +- (void)bk_addObserverForKeyPaths:(NSArray *)keyPaths identifier:(NSString *)identifier options:(NSKeyValueObservingOptions)options context:(BKObserverContext)context task:(id)task +{ + NSParameterAssert(keyPaths.count); + NSParameterAssert(identifier.length); + NSParameterAssert(task); + + Class classToSwizzle = self.class; + NSMutableSet *classes = self.class.bk_observedClassesHash; + @synchronized (classes) { + NSString *className = NSStringFromClass(classToSwizzle); + if (![classes containsObject:className]) { + SEL deallocSelector = sel_registerName("dealloc"); + + __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL; + + id newDealloc = ^(__unsafe_unretained id objSelf) { + [objSelf bk_removeAllBlockObservers]; + + if (originalDealloc == NULL) { + struct objc_super superInfo = { + .receiver = objSelf, + .super_class = class_getSuperclass(classToSwizzle) + }; + + void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper; + msgSend(&superInfo, deallocSelector); + } else { + originalDealloc(objSelf, deallocSelector); + } + }; + + IMP newDeallocIMP = imp_implementationWithBlock(newDealloc); + + if (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:")) { + // The class already contains a method implementation. + Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector); + + // We need to store original implementation before setting new implementation + // in case method is called at the time of setting. + originalDealloc = (void(*)(__unsafe_unretained id, SEL))method_getImplementation(deallocMethod); + + // We need to store original implementation again, in case it just changed. + originalDealloc = (void(*)(__unsafe_unretained id, SEL))method_setImplementation(deallocMethod, newDeallocIMP); + } + + [classes addObject:className]; + } + } + + NSMutableDictionary *dict; + _BKObserver *observer = [[_BKObserver alloc] initWithObservee:self keyPaths:keyPaths context:context task:task]; + [observer startObservingWithOptions:options]; + + @synchronized (self) { + dict = [self bk_observerBlocks]; + + if (dict == nil) { + dict = [NSMutableDictionary dictionary]; + [self bk_setObserverBlocks:dict]; + } + } + + dict[identifier] = observer; +} + +- (void)bk_setObserverBlocks:(NSMutableDictionary *)dict +{ + [self bk_associateValue:dict withKey:BKObserverBlocksKey]; +} + +- (NSMutableDictionary *)bk_observerBlocks +{ + return [self bk_associatedValueForKey:BKObserverBlocksKey]; +} + +@end diff --git a/YDBlockKit/Core/NSObject+YDAsyncBlock.h b/YDBlockKit/Core/NSObject+YDAsyncBlock.h new file mode 100644 index 0000000..72e2cb9 --- /dev/null +++ b/YDBlockKit/Core/NSObject+YDAsyncBlock.h @@ -0,0 +1,22 @@ +// +// NSObject+YDAsyncBlock.h +// YDBlockKit +// +// Created by 王远东 on 2022/9/9. +// Copyright © 2022 10387577. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSObject (YDAsyncBlock) + ++ (void)yd_asyncBlock:(void (^)(void))block onQueue:(dispatch_queue_t)queue afterSecond:(double)second forKey:(NSString*)key; + ++ (void)yd_cancelBlockForKey:(NSString*)key; + ++ (BOOL)yd_hasAsyncBlockForKey:(NSString*)key; +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSObject+YDAsyncBlock.m b/YDBlockKit/Core/NSObject+YDAsyncBlock.m new file mode 100644 index 0000000..92942b1 --- /dev/null +++ b/YDBlockKit/Core/NSObject+YDAsyncBlock.m @@ -0,0 +1,108 @@ +// +// NSObject+YDAsyncBlock.m +// YDBlockKit +// +// Created by 王远东 on 2022/9/9. +// Copyright © 2022 10387577. All rights reserved. +// + +#import "NSObject+YDAsyncBlock.h" +#import + +@interface _YDAsyncBlockMap : NSObject +@property (nonatomic, assign) BOOL isNeedRemove; +@property (nonatomic, copy) void (^execBlock)(void); +@end + +static pthread_mutex_t kGlobalAsyncLock; +static NSMutableDictionary* kGlobalExcuteBlockMap; + +@implementation NSObject (YDAsyncBlock) ++ (void)yd_asyncBlock:(void (^)(void))block onQueue:(dispatch_queue_t)queue afterSecond:(double)second forKey:(NSString*)key +{ + if (block == nil) { + return; + } + + dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(second * NSEC_PER_SEC)); + + ///不可取消 + if (key == nil) { + dispatch_after(delayTime, queue, block); + return; + } + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + pthread_mutex_init(&kGlobalAsyncLock, NULL); + kGlobalExcuteBlockMap = [NSMutableDictionary dictionary]; + }); + + _YDAsyncBlockMap* blockMap = [_YDAsyncBlockMap new]; + blockMap.execBlock = block; + + pthread_mutex_lock(&kGlobalAsyncLock); + _YDAsyncBlockMap* removeObject = [kGlobalExcuteBlockMap objectForKey:key]; + if (removeObject) { + removeObject.isNeedRemove = YES; + removeObject.execBlock = nil; + } + [kGlobalExcuteBlockMap setObject:blockMap forKey:key]; + pthread_mutex_unlock(&kGlobalAsyncLock); + + __weak _YDAsyncBlockMap* weakBlockMap = blockMap; + dispatch_after(delayTime, queue, ^{ + void (^doExecBlock)(void) = nil; + pthread_mutex_lock(&kGlobalAsyncLock); + if (weakBlockMap && weakBlockMap.isNeedRemove == NO) { + weakBlockMap.isNeedRemove = YES; + doExecBlock = weakBlockMap.execBlock; + [kGlobalExcuteBlockMap removeObjectForKey:key]; + } + pthread_mutex_unlock(&kGlobalAsyncLock); + if (doExecBlock) { + doExecBlock(); + } + }); +} ++ (void)yd_cancelBlockForKey:(NSString*)key +{ + if (key == nil) { + return; + } + pthread_mutex_lock(&kGlobalAsyncLock); + _YDAsyncBlockMap* blockMap = [kGlobalExcuteBlockMap objectForKey:key]; + if (blockMap) { + blockMap.isNeedRemove = YES; + blockMap.execBlock = nil; + [kGlobalExcuteBlockMap removeObjectForKey:key]; + } + pthread_mutex_unlock(&kGlobalAsyncLock); +} ++ (BOOL)yd_hasAsyncBlockForKey:(NSString*)key +{ + if (key == nil) { + return NO; + } + pthread_mutex_lock(&kGlobalAsyncLock); + BOOL hasContain = NO; + _YDAsyncBlockMap* blockMap = [kGlobalExcuteBlockMap objectForKey:key]; + if (blockMap) { + hasContain = YES; + } + pthread_mutex_unlock(&kGlobalAsyncLock); + return hasContain; +} +@end + +@implementation _YDAsyncBlockMap ++ (NSMutableDictionary*)globalExcuteBlockMap +{ + return kGlobalExcuteBlockMap; +} ++ (pthread_mutex_t)globalAsyncLock +{ + return kGlobalAsyncLock; +} +@end + diff --git a/YDBlockKit/Core/NSOrderedSet+BlocksKit.h b/YDBlockKit/Core/NSOrderedSet+BlocksKit.h new file mode 100644 index 0000000..dba2f57 --- /dev/null +++ b/YDBlockKit/Core/NSOrderedSet+BlocksKit.h @@ -0,0 +1,173 @@ +// +// NSOrderedSet+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSOrderedSet. + + Both inspired by and resembling Smalltalk syntax, these utilities allow for + iteration through an ordered set (also known as a uniqued array) in a concise + way that saves a ton of boilerplate code for filtering or finding objects. + + Includes code by the following: + + - Robin Lu. . 2009. + - Michael Ash. . 2010. BSD. + - Aleks Nesterow. . 2010. BSD. + - Zach Waldowski. . 2011. + + @see NSArray(BlocksKit) + @see NSSet(BlocksKit) + */ +@interface __GENERICS(NSOrderedSet, ObjectType) (BlocksKit) + +/** Loops through an ordered set and executes the given block with each object. + + @param block A single-argument, void-returning code block. + */ +- (void)bk_each:(void (^)(ObjectType obj))block; + +/** Enumerates through an ordered set concurrently and executes the given block + once for each object. + + Enumeration will occur on appropriate background queues. This will have a + noticeable speed increase, especially on multi-core devices, but you *must* + be aware of the thread safety of the objects you message from within the block. + Be aware that the order of objects is not necessarily the order each block will + be called in. + + @param block A single-argument, void-returning code block. + */ +- (void)bk_apply:(void (^)(ObjectType obj))block; + +/** Loops through an ordered set to find the object matching the block. + + bk_match: is functionally identical to bk_select:, but will stop and return + on the first match. + + @param block A single-argument, `BOOL`-returning code block. + @return Returns the object, if found, or `nil`. + @see bk_select: + */ +- (nullable id)bk_match:(BOOL (^)(ObjectType obj))block; + +/** Loops through an ordered set to find the objects matching the block. + + @param block A single-argument, BOOL-returning code block. + @return Returns an ordered set of the objects found. + @see bk_match: + */ +- (NSOrderedSet *)bk_select:(BOOL (^)(ObjectType obj))block; + +/** Loops through an ordered set to to find the objects not matching the block. + + This selector performs *literally* the exact same function as bk_select: but in + reverse. + + This is useful, as one may expect, for removing objects from an ordered set to. + NSOrderedSet *new = [computers bk_reject:^BOOL(id obj) { + return ([obj isUgly]); + }]; + + @param block A single-argument, BOOL-returning code block. + @return Returns an ordered set of all objects not found. + */ +- (NSOrderedSet *)bk_reject:(BOOL (^)(ObjectType obj))block; + +/** Call the block once for each object and create an ordered set of the return + values. + + This is sometimes referred to as a transform, mutating one of each object: + NSOrderedSet *new = [stringArray bk_map:^id(id obj) { + return [obj stringByAppendingString:@".png"]); + }]; + + @param block A single-argument, object-returning code block. + @return Returns an ordered set of the objects returned by the block. + */ +- (NSOrderedSet *)bk_map:(id (^)(ObjectType obj))block; + +/** Arbitrarily accumulate objects using a block. + + The concept of this selector is difficult to illustrate in words. The sum can + be any NSObject, including (but not limited to) a string, number, or value. + + For example, you can concentate the strings in an ordered set: + NSString *concentrated = [stringArray bk_reduce:@"" withBlock:^id(id sum, id obj) { + return [sum stringByAppendingString:obj]; + }]; + + You can also do something like summing the lengths of strings in an ordered set: + NSUInteger value = [[[stringArray bk_reduce:nil withBlock:^id(id sum, id obj) { + return @([sum integerValue] + obj.length); + }]] unsignedIntegerValue]; + + @param initial The value of the reduction at its start. + @param block A block that takes the current sum and the next object to return the new sum. + @return An accumulated value. + */ +- (nullable id)bk_reduce:(nullable id)initial withBlock:(__nullable id (^)(__nullable id sum, ObjectType obj))block; + +/** Loops through an ordered set to find whether any object matches the block. + + This method is similar to the Scala list `exists`. It is functionally + identical to bk_match: but returns a `BOOL` instead. It is not recommended + to use bk_any: as a check condition before executing bk_match:, since it would + require two loops through the ordered set. + + For example, you can find if a string in an ordered set starts with a certain + letter: + + NSString *letter = @"A"; + BOOL containsLetter = [stringArray bk_any:^(id obj) { + return [obj hasPrefix:@"A"]; + }]; + + @param block A single-argument, BOOL-returning code block. + @return YES for the first time the block returns YES for an object, NO otherwise. + */ +- (BOOL)bk_any:(BOOL (^)(ObjectType obj))block; + +/** Loops through an ordered set to find whether no objects match the block. + + This selector performs *literally* the exact same function as bk_all: but in reverse. + + @param block A single-argument, BOOL-returning code block. + @return YES if the block returns NO for all objects in the ordered set, NO + otherwise. + */ +- (BOOL)bk_none:(BOOL (^)(ObjectType obj))block; + +/** Loops through an ordered set to find whether all objects match the block. + + @param block A single-argument, BOOL-returning code block. + @return YES if the block returns YES for all objects in the ordered set, NO + otherwise. + */ +- (BOOL)bk_all:(BOOL (^)(ObjectType obj))block; + +/** Tests whether every element of this ordered set relates to the corresponding + element of another array according to match by block. + + For example, finding if a list of numbers corresponds to their sequenced string values; + + NSArray *numbers = @[ @(1), @(2), @(3) ]; + NSArray *letters = @[ @"1", @"2", @"3" ]; + BOOL doesCorrespond = [numbers bk_corresponds:letters withBlock:^(id number, id letter) { + return [[number stringValue] isEqualToString:letter]; + }]; + + @param list An array of objects to compare with. + @param block A two-argument, BOOL-returning code block. + @return Returns a BOOL, true if every element of the ordered set relates to + corresponding element in another ordered set. + */ +- (BOOL)bk_corresponds:(NSOrderedSet *)list withBlock:(BOOL (^)(ObjectType obj1, id obj2))block; +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSOrderedSet+BlocksKit.m b/YDBlockKit/Core/NSOrderedSet+BlocksKit.m new file mode 100644 index 0000000..0464652 --- /dev/null +++ b/YDBlockKit/Core/NSOrderedSet+BlocksKit.m @@ -0,0 +1,132 @@ +// +// NSOrderedSet+BlocksKit.m +// BlocksKit +// + +#import "NSOrderedSet+BlocksKit.h" + +@implementation NSOrderedSet (BlocksKit) + +- (void)bk_each:(void (^)(id obj))block +{ + NSParameterAssert(block != nil); + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + block(obj); + }]; +} + +- (void)bk_apply:(void (^)(id obj))block +{ + NSParameterAssert(block != nil); + + [self enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + block(obj); + }]; +} + +- (id)bk_match:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + NSUInteger index = [self indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + return block(obj); + }]; + + if (index == NSNotFound) return nil; + return self[index]; +} + +- (NSOrderedSet *)bk_select:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + NSArray *objects = [self objectsAtIndexes:[self indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + return block(obj); + }]]; + + if (!objects.count) return [[self class] orderedSet]; + return [[self class] orderedSetWithArray:objects]; +} + +- (NSOrderedSet *)bk_reject:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + return [self bk_select:^BOOL(id obj) { + return !block(obj); + }]; +} + +- (NSOrderedSet *)bk_map:(id (^)(id obj))block +{ + NSParameterAssert(block != nil); + + NSMutableOrderedSet *result = [NSMutableOrderedSet orderedSetWithCapacity:self.count]; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + id value = block(obj) ?: [NSNull null]; + [result addObject:value]; + }]; + + return result; +} + +- (id)bk_reduce:(id)initial withBlock:(id (^)(id sum, id obj))block +{ + NSParameterAssert(block != nil); + + __block id result = initial; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + result = block(result, obj); + }]; + + return result; +} + +- (BOOL)bk_any:(BOOL (^)(id obj))block +{ + return [self bk_match:block] != nil; +} + +- (BOOL)bk_none:(BOOL (^)(id obj))block +{ + return [self bk_match:block] == nil; +} + +- (BOOL)bk_all:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + __block BOOL result = YES; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (!block(obj)) { + result = NO; + *stop = YES; + } + }]; + + return result; +} + +- (BOOL)bk_corresponds:(NSOrderedSet *)list withBlock:(BOOL (^)(id obj1, id obj2))block +{ + NSParameterAssert(block != nil); + + __block BOOL result = NO; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx < list.count) { + id obj2 = list[idx]; + result = block(obj, obj2); + } else { + result = NO; + } + *stop = !result; + }]; + + return result; +} + +@end diff --git a/YDBlockKit/Core/NSSet+BlocksKit.h b/YDBlockKit/Core/NSSet+BlocksKit.h new file mode 100644 index 0000000..016d4f6 --- /dev/null +++ b/YDBlockKit/Core/NSSet+BlocksKit.h @@ -0,0 +1,138 @@ +// +// NSSet+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block extensions for NSSet. + + Both inspired by and resembling Smalltalk syntax, these utilities allows for + iteration of a set in a logical way that saves quite a bit of boilerplate code + for filtering or finding objects or an object. + + Includes code by the following: + +- [Michael Ash](https://github.com/mikeash) +- [Corey Floyd](https://github.com/coreyfloyd) +- [Aleks Nesterow](https://github.com/nesterow) +- [Zach Waldowski](https://github.com/zwaldowski) + + @see NSArray(BlocksKit) + @see NSDictionary(BlocksKit) + */ +@interface __GENERICS(NSSet, ObjectType) (BlocksKit) + +/** Loops through a set and executes the given block with each object. + + @param block A single-argument, void-returning code block. + */ +- (void)bk_each:(void (^)(ObjectType obj))block; + +/** Enumerates through a set concurrently and executes + the given block once for each object. + + Enumeration will occur on appropriate background queues. This + will have a noticeable speed increase, especially on dual-core + devices, but you *must* be aware of the thread safety of the + objects you message from within the block. + + @param block A single-argument, void-returning code block. + */ +- (void)bk_apply:(void (^)(ObjectType obj))block; + +/** Loops through a set to find the object matching the block. + + bk_match: is functionally identical to bk_select:, but will stop and return + on the first match. + + @param block A single-argument, BOOL-returning code block. + @return Returns the object if found, `nil` otherwise. + @see bk_select: + */ +- (nullable id)bk_match:(BOOL (^)(ObjectType obj))block; + +/** Loops through a set to find the objects matching the block. + + @param block A single-argument, BOOL-returning code block. + @return Returns a set of the objects found. + @see bk_match: + */ +- (NSSet *)bk_select:(BOOL (^)(ObjectType obj))block; + +/** Loops through a set to find the objects not matching the block. + + This selector performs *literally* the exact same function as select, but in reverse. + + This is useful, as one may expect, for removing objects from a set: + NSSet *new = [reusableWebViews bk_reject:^BOOL(id obj) { + return ([obj isLoading]); + }]; + + @param block A single-argument, BOOL-returning code block. + @return Returns an array of all objects not found. + */ +- (NSSet *)bk_reject:(BOOL (^)(ObjectType obj))block; + +/** Call the block once for each object and create a set of the return values. + + This is sometimes referred to as a transform, mutating one of each object: + NSSet *new = [mimeTypes bk_map:^id(id obj) { + return [@"x-company-" stringByAppendingString:obj]); + }]; + + @param block A single-argument, object-returning code block. + @return Returns a set of the objects returned by the block. + */ +- (NSSet *)bk_map:(id (^)(ObjectType obj))block; + +/** Arbitrarily accumulate objects using a block. + + The concept of this selector is difficult to illustrate in words. The sum can + be any NSObject, including (but not limited to) a string, number, or value. + + You can also do something like summing the count of an item: + NSUInteger numberOfBodyParts = [[bodyList bk_reduce:nil withBlock:^id(id sum, id obj) { + return @([sum integerValue] + obj.numberOfAppendages); + }] unsignedIntegerValue]; + + @param initial The value of the reduction at its start. + @param block A block that takes the current sum and the next object to return the new sum. + @return An accumulated value. + */ +- (nullable id)bk_reduce:(nullable id)initial withBlock:(__nullable id (^)(__nullable id sum, ObjectType obj))block; + +/** Loops through a set to find whether any object matches the block. + + This method is similar to the Scala list `exists`. It is functionally + identical to bk_match: but returns a `BOOL` instead. It is not recommended + to use bk_any: as a check condition before executing bk_match:, since it would + require two loops through the array. + + @param block A single-argument, BOOL-returning code block. + @return YES for the first time the block returns YES for an object, NO otherwise. + */ +- (BOOL)bk_any:(BOOL (^)(ObjectType obj))block; + +/** Loops through a set to find whether no objects match the block. + + This selector performs *literally* the exact same function as bk_all: but in reverse. + + @param block A single-argument, BOOL-returning code block. + @return YES if the block returns NO for all objects in the set, NO otherwise. + */ +- (BOOL)bk_none:(BOOL (^)(ObjectType obj))block; + +/** Loops through a set to find whether all objects match the block. + + @param block A single-argument, BOOL-returning code block. + @return YES if the block returns YES for all objects in the set, NO otherwise. + */ +- (BOOL)bk_all:(BOOL (^)(ObjectType obj))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSSet+BlocksKit.m b/YDBlockKit/Core/NSSet+BlocksKit.m new file mode 100644 index 0000000..cb9d261 --- /dev/null +++ b/YDBlockKit/Core/NSSet+BlocksKit.m @@ -0,0 +1,113 @@ +// +// NSSet+BlocksKit.m +// BlocksKit +// + +#import "NSSet+BlocksKit.h" + +@implementation NSSet (BlocksKit) + +- (void)bk_each:(void (^)(id obj))block +{ + NSParameterAssert(block != nil); + + [self enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { + block(obj); + }]; +} + +- (void)bk_apply:(void (^)(id obj))block +{ + NSParameterAssert(block != nil); + + [self enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, BOOL *stop) { + block(obj); + }]; +} + +- (id)bk_match:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + return [[self objectsPassingTest:^BOOL(id obj, BOOL *stop) { + if (block(obj)) { + *stop = YES; + return YES; + } + + return NO; + }] anyObject]; +} + +- (NSSet *)bk_select:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + return [self objectsPassingTest:^BOOL(id obj, BOOL *stop) { + return block(obj); + }]; +} + +- (NSSet *)bk_reject:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + return [self objectsPassingTest:^BOOL(id obj, BOOL *stop) { + return !block(obj); + }]; +} + +- (NSSet *)bk_map:(id (^)(id obj))block +{ + NSParameterAssert(block != nil); + + NSMutableSet *result = [NSMutableSet setWithCapacity:self.count]; + + [self enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { + id value = block(obj) ?:[NSNull null]; + [result addObject:value]; + }]; + + return result; +} + +- (id)bk_reduce:(id)initial withBlock:(id (^)(id sum, id obj))block +{ + NSParameterAssert(block != nil); + + __block id result = initial; + + [self enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { + result = block(result, obj); + }]; + + return result; +} + +- (BOOL)bk_any:(BOOL (^)(id obj))block +{ + return [self bk_match:block] != nil; +} + +- (BOOL)bk_none:(BOOL (^)(id obj))block +{ + return [self bk_match:block] == nil; +} + +- (BOOL)bk_all:(BOOL (^)(id obj))block +{ + NSParameterAssert(block != nil); + + __block BOOL result = YES; + + [self enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { + if (!block(obj)) { + result = NO; + *stop = YES; + } + }]; + + return result; +} + +@end diff --git a/YDBlockKit/Core/NSTimer+BlocksKit.h b/YDBlockKit/Core/NSTimer+BlocksKit.h new file mode 100644 index 0000000..3e0c17a --- /dev/null +++ b/YDBlockKit/Core/NSTimer+BlocksKit.h @@ -0,0 +1,53 @@ +// +// NSTimer+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Simple category on NSTimer to give it blocks capability. + + Created by [Jiva DeVoe](https://github.com/jivadevoe) as `NSTimer-Blocks`. +*/ +@interface NSTimer (BlocksKit) + +/** Creates a new @c NSTimer object using the specified handler, schedules it on + * the current run loop, and returns it. + * + * @param seconds For a repeating timer, the seconds between firings of the + * timer. If seconds is less than or equal to @c 0.0, @c 0.1 is used instead. + * @param repeats If @c YES, the timer will repeatedly reschedule itself until + * invalidated. If @c NO, the timer will be invalidated after it fires. + * @param block The code unit to execute when the timer fires. + * @return A new @c NSTimer object, configured according to the specified parameters. + */ ++ (instancetype)bk_scheduleTimerWithTimeInterval:(NSTimeInterval)seconds repeats:(BOOL)repeats usingBlock:(void (^)(NSTimer *timer))block; + +/** Creates and returns a new @c NSTimer object using the specified handler. + * + * You must add the new timer to a run loop, using @c addTimer:forMode:. Upon + * firing, the timer fires the block. If the timer is configured to repeat, + * there is no need to subsequently re-add the timer to the run loop. + * + * @param seconds For a repeating timer, the seconds between firings of the + * timer. If seconds is less than or equal to @c 0.0, @c 0.1 is used instead. + * @param repeats If @c YES, the timer will repeatedly reschedule itself until + * invalidated. If @c NO, the timer will be invalidated after it fires. + * @param block The code unit to execute when the timer fires. + * @return A new @c NSTimer object, configured according to the specified parameters. + */ ++ (instancetype)bk_timerWithTimeInterval:(NSTimeInterval)seconds repeats:(BOOL)repeats usingBlock:(void (^)(NSTimer *timer))block; + +@end + +@interface NSTimer (BlocksKit_Deprecated) + ++ (instancetype)bk_scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats DEPRECATED_MSG_ATTRIBUTE("Replaced with -bk_performAfterDelay:usingBlock:"); ++ (instancetype)bk_timerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats DEPRECATED_MSG_ATTRIBUTE("Replaced with -bk_performAfterDelay:usingBlock:"); + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/Core/NSTimer+BlocksKit.m b/YDBlockKit/Core/NSTimer+BlocksKit.m new file mode 100644 index 0000000..6a504e7 --- /dev/null +++ b/YDBlockKit/Core/NSTimer+BlocksKit.m @@ -0,0 +1,50 @@ +// +// NSTimer+BlocksKit.m +// BlocksKit +// + +#import "NSTimer+BlocksKit.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" + +@implementation NSTimer (BlocksKit) + ++ (instancetype)bk_scheduleTimerWithTimeInterval:(NSTimeInterval)seconds repeats:(BOOL)repeats usingBlock:(void (^)(NSTimer *timer))block +{ + NSTimer *timer = [self bk_timerWithTimeInterval:seconds repeats:repeats usingBlock:block]; + [NSRunLoop.currentRunLoop addTimer:timer forMode:NSDefaultRunLoopMode]; + return timer; +} + ++ (instancetype)bk_timerWithTimeInterval:(NSTimeInterval)inSeconds repeats:(BOOL)repeats usingBlock:(void (^)(NSTimer *timer))block +{ + NSParameterAssert(block != nil); + CFAbsoluteTime seconds = fmax(inSeconds, 0.0001); + CFAbsoluteTime interval = repeats ? seconds : 0; + CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent() + seconds; + return (__bridge_transfer NSTimer *)CFRunLoopTimerCreateWithHandler(NULL, fireDate, interval, 0, 0, (void(^)(CFRunLoopTimerRef))block); +} + +@end + +#pragma clang diagnostic pop + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + +@implementation NSTimer (BlocksKit_Deprecated) + ++ (instancetype)bk_scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats +{ + return [self bk_scheduleTimerWithTimeInterval:seconds repeats:repeats usingBlock:block]; +} + ++ (instancetype)bk_timerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats +{ + return [self bk_timerWithTimeInterval:seconds repeats:repeats usingBlock:block]; +} + +@end + +#pragma clang diagnostic pop diff --git a/YDBlockKit/DynamicDelegate/A2BlockInvocation.h b/YDBlockKit/DynamicDelegate/A2BlockInvocation.h new file mode 100644 index 0000000..bce5293 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/A2BlockInvocation.h @@ -0,0 +1,103 @@ +// +// A2BlockInvocation.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/// If a block invocation is instiated with an invalid method signature, +/// an `NSInvalidArgumentException` is thrown containing this key in the +/// user info. +extern NSString *const A2IncompatibleMethodSignatureKey; + +/** An `A2BlockInvocation` is an Objective-C block call rendered static, that + is, it is an action turned into an object. `A2BlockInvocation` objects are used + to store and forward closure invocations between objects, primarily by the + `A2DynamicDelegate` system. + + A block invocation object can be repeatedly dispatched with multiple sets of + arguments with the same flexibility as NSInvocation. This design makes + `A2BlockInvocation` extremely useful, because blocks can be used to capture + scope and a block invocation can be used to save it for later. + + Like `NSInvocation`, `A2BlockInvocation` does not support invocations of + methods with variadic arguments or union arguments. + */ +@interface A2BlockInvocation : NSObject + +/** Inspects the given block literal and returns a compatible method signature. + + The returned method signature is suitable for use in the Foundation forwarding + system to link a method call to a block invocation. + + @param block An Objective-C block literal + @return A method signature matching the declared prototype for the block + */ ++ (nullable NSMethodSignature *)methodSignatureForBlock:(id)block; + +/** @name Creating A2BlockInvocation Objects */ + +/** Returns a block invocation object able to construct calls to a given block. + + This method synthesizes a compatible method signature for the given block. + + @param block A block literal + @return An initialized block invocation object + @see methodSignatureForBlock + */ +- (instancetype)initWithBlock:(id)block; + +/** Returns a block invocation object able to construct calls to a given block + using a given Objective-C method signature. + + The method signature given must be compatible with the signature of the block; + that is, equal to the block signature but with a `SEL` (`':'`) as the second + parameter. Passing in an incompatible method signature will raise an exception. + + An example method returning a string for an integer argument would have the + following properties: + - Block type signature of `NSString *(^)(int)` + - Block function definition of `NSString *(*)(id, int)` + - Block signature of `"@@?i"` + - Method signature of `"@:i"` or `"@i"` + + @param block An Objective-C block literal + @param methodSignature An method signature matching the block + @return An initialized block invocation object + */ +- (instancetype)initWithBlock:(id)block methodSignature:(NSMethodSignature *)methodSignature; + +/** @name Getting the Block and Method Signatures */ + +/// The receiver's method signature, reflecting the block with a selector. +/// Appropriate for use in `-methodSignatureForSelector:`. +@property (nonatomic, strong, readonly) NSMethodSignature *methodSignature; + +/// Returns the receiver's block. +@property (nonatomic, copy, readonly) id block; + +/** Calls the receiver's block with the arguments from the given invocation, + providing a buffer containing the block's return value upon return. + + @param inv An instance of NSInvocation with values for its arguments set. + @param returnValue On return, the block's return value, or `nil` for a void + return type. + @param NO if the buffer copies necessary for invocation failed, YES otherwise. + @see invokeWithInvocation:returnValue: + */ +- (BOOL)invokeWithInvocation:(NSInvocation *)inv returnValue:(out NSValue *__nullable *__nonnull)returnValue; + +/** Calls the receiver's block with the arguments from the given invocation + and sets the return value on the given invocation. + + @param inv An instance of NSInvocation with values for its arguments set. + @see invokeWithInvocation:returnValue: + */ +- (void)invokeWithInvocation:(NSInvocation *)inv; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/DynamicDelegate/A2BlockInvocation.m b/YDBlockKit/DynamicDelegate/A2BlockInvocation.m new file mode 100644 index 0000000..884fd85 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/A2BlockInvocation.m @@ -0,0 +1,253 @@ +// +// A2BlockInvocation.m +// BlocksKit +// + +#import "A2BlockInvocation.h" + +NSString *const A2IncompatibleMethodSignatureKey = @"incompatibleMethodSignature"; + +#pragma mark Block Internals + +typedef NS_OPTIONS(int, BKBlockFlags) { + BKBlockFlagsHasCopyDisposeHelpers = (1 << 25), + BKBlockFlagsHasSignature = (1 << 30) +}; + +typedef struct _BKBlock { + __unused Class isa; + BKBlockFlags flags; + __unused int reserved; + void (__unused *invoke)(struct _BKBlock *block, ...); + struct { + unsigned long int reserved; + unsigned long int size; + // requires BKBlockFlagsHasCopyDisposeHelpers + void (*copy)(void *dst, const void *src); + void (*dispose)(const void *); + // requires BKBlockFlagsHasSignature + const char *signature; + const char *layout; + } *descriptor; + // imported variables +} *BKBlockRef; + +NS_INLINE BOOL typesCompatible(const char *a, const char *b) { + if (a[0] == b[0]) { return YES; } + NSUInteger aSize, aAlign, bSize, bAlign; + NSGetSizeAndAlignment(a, &aSize, &aAlign); + NSGetSizeAndAlignment(a, &bSize, &bAlign); + if (aSize == bSize && aAlign == bAlign) { return YES; } + return !!strcmp(a, b); +} + +@interface A2BlockInvocation () + +@property (nonatomic, readonly) NSMethodSignature *blockSignature; + +@end + +@implementation A2BlockInvocation + +/** Determines if two given signatures (block or method) are compatible. + + A signature is compatible with another signature if their return types and + parameter types are equal, minus the parameter types dedicated to the Obj-C + method reciever and selector or a block literal argument. + + @param signatureA Any signature object reflecting a block or method signature + @param signatureB Any signature object reflecting a block or method signature + @return YES if the given signatures may be used to dispatch for one another + */ ++ (BOOL)isSignature:(NSMethodSignature *)signatureA compatibleWithSignature:(NSMethodSignature *)signatureB __attribute__((pure)) +{ + if (!signatureA || !signatureB) return NO; + if ([signatureA isEqual:signatureB]) return YES; + if (!typesCompatible(signatureA.methodReturnType, signatureB.methodReturnType)) return NO; + + NSMethodSignature *methodSignature = nil, *blockSignature = nil; + if (signatureA.numberOfArguments > signatureB.numberOfArguments) { + methodSignature = signatureA; + blockSignature = signatureB; + } else if (signatureB.numberOfArguments > signatureA.numberOfArguments) { + methodSignature = signatureB; + blockSignature = signatureA; + } else { + return NO; + } + + NSUInteger numberOfArguments = methodSignature.numberOfArguments; + for (NSUInteger i = 2; i < numberOfArguments; i++) { + if (!typesCompatible([methodSignature getArgumentTypeAtIndex:i], [blockSignature getArgumentTypeAtIndex:i - 1])) { + return NO; + } + } + + return YES; +} + +/** Inspects the given block literal and returns a compatible type signature. + + Unlike a typical method signature, a block type signature has no `self` (`'@'`) + or `_cmd` (`':'`) parameter, but instead just one parameter for the block itself + (`'@?'`). + + @param block An Objective-C block literal + @return A method signature matching the declared prototype for the block + */ ++ (NSMethodSignature *)typeSignatureForBlock:(id)block __attribute__((pure, nonnull(1))) +{ + BKBlockRef layout = (__bridge void *)block; + + if (!(layout->flags & BKBlockFlagsHasSignature)) + return nil; + + void *desc = layout->descriptor; + desc += 2 * sizeof(unsigned long int); + + if (layout->flags & BKBlockFlagsHasCopyDisposeHelpers) + desc += 2 * sizeof(void *); + + if (!desc) + return nil; + + const char *signature = (*(const char **)desc); + + return [NSMethodSignature signatureWithObjCTypes:signature]; +} + +/// Creates a method signature compatible with a given block signature. ++ (NSMethodSignature *)methodSignatureForBlockSignature:(NSMethodSignature *)original +{ + if (!original) return nil; + + if (original.numberOfArguments < 1) { + return nil; + } + + if (original.numberOfArguments >= 2 && strcmp(@encode(SEL), [original getArgumentTypeAtIndex:1]) == 0) { + return original; + } + + // initial capacity is num. arguments - 1 (@? -> @) + 1 (:) + 1 (ret type) + // optimistically assuming most signature components are char[1] + NSMutableString *signature = [[NSMutableString alloc] initWithCapacity:original.numberOfArguments + 1]; + + const char *retTypeStr = original.methodReturnType; + [signature appendFormat:@"%s%s%s", retTypeStr, @encode(id), @encode(SEL)]; + + for (NSUInteger i = 1; i < original.numberOfArguments; i++) { + const char *typeStr = [original getArgumentTypeAtIndex:i]; + NSString *type = [[NSString alloc] initWithBytesNoCopy:(void *)typeStr length:strlen(typeStr) encoding:NSUTF8StringEncoding freeWhenDone:NO]; + [signature appendString:type]; + } + + return [NSMethodSignature signatureWithObjCTypes:signature.UTF8String]; +} + ++ (NSMethodSignature *)methodSignatureForBlock:(id)block +{ + NSMethodSignature *original = [self typeSignatureForBlock:block]; + if (!original) return nil; + return [self methodSignatureForBlockSignature:original]; +} + +- (instancetype)initWithBlock:(id)block methodSignature:(NSMethodSignature *)methodSignature blockSignature:(NSMethodSignature *)blockSignature +{ + self = [super init]; + if (self) { + _block = [block copy]; + _methodSignature = methodSignature; + _blockSignature = blockSignature; + } + return self; +} + +- (instancetype)initWithBlock:(id)block +{ + NSParameterAssert(block); + NSMethodSignature *blockSignature = [[self class] typeSignatureForBlock:block]; + NSMethodSignature *methodSignature = [[self class] methodSignatureForBlockSignature:blockSignature]; + NSAssert(methodSignature, @"Incompatible block: %@", block); + return (self = [self initWithBlock:block methodSignature:methodSignature blockSignature:blockSignature]); +} + +- (instancetype)initWithBlock:(id)block methodSignature:(NSMethodSignature *)methodSignature +{ + NSParameterAssert(block); + NSMethodSignature *blockSignature = [[self class] typeSignatureForBlock:block]; + if (![[self class] isSignature:methodSignature compatibleWithSignature:blockSignature]) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Attempted to create block invocation with incompatible signatures" userInfo:@{A2IncompatibleMethodSignatureKey: methodSignature}]; + } + return (self = [self initWithBlock:block methodSignature:methodSignature blockSignature:blockSignature]); +} + +- (BOOL)invokeWithInvocation:(NSInvocation *)outerInv returnValue:(out NSValue **)outReturnValue setOnInvocation:(BOOL)setOnInvocation +{ + NSParameterAssert(outerInv); + + NSMethodSignature *sig = self.methodSignature; + + if (![outerInv.methodSignature isEqual:sig]) { + NSAssert(0, @"Attempted to invoke block invocation with incompatible frame"); + return NO; + } + + NSInvocation *innerInv = [NSInvocation invocationWithMethodSignature:self.blockSignature]; + + void *argBuf = NULL; + + for (NSUInteger i = 2; i < sig.numberOfArguments; i++) { + const char *type = [sig getArgumentTypeAtIndex:i]; + NSUInteger argSize; + NSGetSizeAndAlignment(type, &argSize, NULL); + + if (!(argBuf = reallocf(argBuf, argSize))) { + return NO; + } + + [outerInv getArgument:argBuf atIndex:i]; + [innerInv setArgument:argBuf atIndex:i - 1]; + } + + [innerInv invokeWithTarget:self.block]; + + NSUInteger retSize = sig.methodReturnLength; + if (retSize) { + if (outReturnValue || setOnInvocation) { + if (!(argBuf = reallocf(argBuf, retSize))) { + return NO; + } + + [innerInv getReturnValue:argBuf]; + + if (setOnInvocation) { + [outerInv setReturnValue:argBuf]; + } + + if (outReturnValue) { + *outReturnValue = [NSValue valueWithBytes:argBuf objCType:sig.methodReturnType]; + } + } + } else { + if (outReturnValue) { + *outReturnValue = nil; + } + } + + free(argBuf); + + return YES; +} + +- (void)invokeWithInvocation:(NSInvocation *)inv +{ + [self invokeWithInvocation:inv returnValue:NULL setOnInvocation:YES]; +} + +- (BOOL)invokeWithInvocation:(NSInvocation *)inv returnValue:(out NSValue **)returnValue +{ + return [self invokeWithInvocation:inv returnValue:returnValue setOnInvocation:NO]; +} + +@end diff --git a/YDBlockKit/DynamicDelegate/A2DynamicDelegate.h b/YDBlockKit/DynamicDelegate/A2DynamicDelegate.h new file mode 100644 index 0000000..24e2305 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/A2DynamicDelegate.h @@ -0,0 +1,144 @@ +// +// A2DynamicDelegate.h +// BlocksKit +// + +#import "BKDefines.h" +#import +#import "NSObject+A2BlockDelegate.h" +#import "NSObject+A2DynamicDelegate.h" + +NS_ASSUME_NONNULL_BEGIN + +/** A2DynamicDelegate implements a class's delegate, data source, or other + delegated protocol by associating protocol methods with a block implementation. + + - (IBAction) annoyUser + { + // Create an alert view + UIAlertView *alertView = [[UIAlertView alloc] + initWithTitle:@"Hello World!" + message:@"This alert's delegate is implemented using blocks. That's so cool!" + delegate:nil + cancelButtonTitle:@"Meh." + otherButtonTitles:@"Woo!", nil]; + + // Get the dynamic delegate + A2DynamicDelegate *dd = alertView.bk_dynamicDelegate; + + // Implement -alertViewShouldEnableFirstOtherButton: + [dd implementMethod:@selector(alertViewShouldEnableFirstOtherButton:) withBlock:^(UIAlertView *alertView) { + NSLog(@"Message: %@", alertView.message); + return YES; + }]; + + // Implement -alertView:willDismissWithButtonIndex: + [dd implementMethod:@selector(alertView:willDismissWithButtonIndex:) withBlock:^(UIAlertView *alertView, NSInteger buttonIndex) { + NSLog(@"You pushed button #%d (%@)", buttonIndex, [alertView buttonTitleAtIndex:buttonIndex]); + }]; + + // Set the delegate + alertView.delegate = dd; + + [alertView show]; + } + + A2DynamicDelegate is designed to be 'plug and play'. + */ +@interface A2DynamicDelegate : NSProxy + +/** + * The designated initializer for the A2DynamicDelegate proxy. + * + * An instance of A2DynamicDelegate should generally not be created by the user, + * but instead by calling a method in NSObject(A2DynamicDelegate). Since + * delegates are usually weak references on the part of the delegating object, a + * dynamic delegate would be deallocated immediately after its declaring scope + * ends. NSObject(A2DynamicDelegate) creates a strong reference. + * + * @param protocol A protocol to which the dynamic delegate should conform. + * @return An initialized delegate proxy. + */ +- (instancetype)initWithProtocol:(Protocol *)protocol; + +/** The protocol delegating the dynamic delegate. */ +@property (nonatomic, readonly) Protocol *protocol; + +/** A dictionary of custom handlers to be used by custom responders + in a A2Dynamic(Protocol Name) subclass of A2DynamicDelegate, like + `A2DynamicUIAlertViewDelegate`. */ +@property (nonatomic, strong, readonly) NSMutableDictionary *handlers; + +/** When replacing the delegate using the A2BlockDelegate extensions, the object + responding to classical delegate method implementations. */ +@property (nonatomic, weak, readonly, nullable) id realDelegate; + +/** @name Block Instance Method Implementations */ + +/** The block that is to be fired when the specified + selector is called on the reciever. + + @param selector An encoded selector. Must not be NULL. + @return A code block, or nil if no block is assigned. + */ +- (nullable id)blockImplementationForMethod:(SEL)selector; + +/** Assigns the given block to be fired when the specified + selector is called on the reciever. + + [tableView.dynamicDataSource implementMethod:@selector(numberOfSectionsInTableView:) + withBlock:NSInteger^(UITableView *tableView) { + return 2; + }]; + + @warning Starting with A2DynamicDelegate 2.0, a block will + not be checked for a matching signature. A block can have + less parameters than the original selector and will be + ignored, but cannot have more. + + @param selector An encoded selector. Must not be NULL. + @param block A code block with the same signature as selector. + */ +- (void)implementMethod:(SEL)selector withBlock:(nullable id)block; + +/** Disassociates any block so that nothing will be fired + when the specified selector is called on the reciever. + + @param selector An encoded selector. Must not be NULL. + */ +- (void)removeBlockImplementationForMethod:(SEL)selector; + +/** @name Block Class Method Implementations */ + +/** The block that is to be fired when the specified + selector is called on the delegating object's class. + + @param selector An encoded selector. Must not be NULL. + @return A code block, or nil if no block is assigned. + */ +- (id)blockImplementationForClassMethod:(SEL)selector; + +/** Assigns the given block to be fired when the specified + selector is called on the reciever. + + @warning Starting with A2DynamicDelegate 2.0, a block will + not be checked for a matching signature. A block can have + less parameters than the original selector and will be + ignored, but cannot have more. + + @param selector An encoded selector. Must not be NULL. + @param block A code block with the same signature as selector. + */ +- (void)implementClassMethod:(SEL)selector withBlock:(nullable id)block; + +/** Disassociates any blocks so that nothing will be fired + when the specified selector is called on the delegating + object's class. + + @param selector An encoded selector. Must not be NULL. + */ +- (void)removeBlockImplementationForClassMethod:(SEL)selector; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/DynamicDelegate/A2DynamicDelegate.m b/YDBlockKit/DynamicDelegate/A2DynamicDelegate.m new file mode 100644 index 0000000..83ced5f --- /dev/null +++ b/YDBlockKit/DynamicDelegate/A2DynamicDelegate.m @@ -0,0 +1,350 @@ +// +// A2DynamicDelegate.m +// BlocksKit +// + +#import "A2DynamicDelegate.h" +@import ObjectiveC.message; +@import ObjectiveC.runtime; +#import "A2BlockInvocation.h" + +Protocol *a2_dataSourceProtocol(Class cls); +Protocol *a2_delegateProtocol(Class cls); +Protocol *a2_protocolForDelegatingObject(id obj, Protocol *protocol); + +static BOOL selectorsEqual(const void *item1, const void *item2, NSUInteger(*__unused size)(const void __unused *item)) +{ + return sel_isEqual((SEL)item1, (SEL)item2); +} + +static NSString *selectorDescribe(const void *item1) +{ + return NSStringFromSelector((SEL)item1); +} + +static inline BOOL protocol_declaredSelector(Protocol *protocol, SEL selector) +{ + for (int i = 0; i < 4; i++) { + BOOL required = 1 & (i); + BOOL instance = 1 & (i >> 1); + + struct objc_method_description description = protocol_getMethodDescription(protocol, selector, required, instance); + if (description.name) { + return YES; + } + } + return NO; +} + +@interface NSMapTable (BKAdditions) + ++ (instancetype)bk_selectorsToStrongObjectsMapTable; +- (id)bk_objectForSelector:(SEL)aSEL; +- (void)bk_removeObjectForSelector:(SEL)aSEL; +- (void)bk_setObject:(id)anObject forSelector:(SEL)aSEL; + +@end + +@implementation NSMapTable (BKAdditions) + ++ (instancetype)bk_selectorsToStrongObjectsMapTable +{ + NSPointerFunctions *selectors = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsOpaqueMemory|NSPointerFunctionsOpaquePersonality]; + selectors.isEqualFunction = selectorsEqual; + selectors.descriptionFunction = selectorDescribe; + + NSPointerFunctions *strongObjects = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality]; + + return [[NSMapTable alloc] initWithKeyPointerFunctions:selectors valuePointerFunctions:strongObjects capacity:1]; +} + +- (id)bk_objectForSelector:(SEL)aSEL +{ + void *selAsPtr = aSEL; + return [self objectForKey:(__bridge id)selAsPtr]; +} + +- (void)bk_removeObjectForSelector:(SEL)aSEL +{ + void *selAsPtr = aSEL; + [self removeObjectForKey:(__bridge id)selAsPtr]; +} + +- (void)bk_setObject:(id)anObject forSelector:(SEL)aSEL +{ + void *selAsPtr = aSEL; + [self setObject:anObject forKey:(__bridge id)selAsPtr]; +} + + +@end + +@interface A2DynamicClassDelegate : A2DynamicDelegate + +@property (nonatomic) Class proxiedClass; + +@end + +#pragma mark - + +@interface A2DynamicDelegate () + +@property (nonatomic) A2DynamicClassDelegate *classProxy; +@property (nonatomic, readonly) NSMapTable *invocationsBySelectors; +@property (nonatomic, weak, readwrite) id realDelegate; + +- (BOOL) isClassProxy; + +@end + +@implementation A2DynamicDelegate + +- (A2DynamicClassDelegate *)classProxy +{ + if (!_classProxy) + { + _classProxy = [[A2DynamicClassDelegate alloc] initWithProtocol:self.protocol]; + _classProxy.proxiedClass = object_getClass(self); + } + + return _classProxy; +} + +- (BOOL)isClassProxy +{ + return NO; +} + +- (Class)class +{ + Class myClass = object_getClass(self); + if (myClass == [A2DynamicDelegate class] || [myClass superclass] == [A2DynamicDelegate class]) + return (Class)self.classProxy; + return [super class]; +} + +- (instancetype)initWithProtocol:(Protocol *)protocol +{ + _protocol = protocol; + _handlers = [NSMutableDictionary dictionary]; + _invocationsBySelectors = [NSMapTable bk_selectorsToStrongObjectsMapTable]; + return self; +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector +{ + A2BlockInvocation *invocation = nil; + if ((invocation = [self.invocationsBySelectors bk_objectForSelector:aSelector])) + return invocation.methodSignature; + else if ([self.realDelegate methodSignatureForSelector:aSelector]) + return [self.realDelegate methodSignatureForSelector:aSelector]; + else if (class_respondsToSelector(object_getClass(self), aSelector)) + return [object_getClass(self) methodSignatureForSelector:aSelector]; + return [[NSObject class] methodSignatureForSelector:aSelector]; +} + ++ (NSString *)description +{ + return @"A2DynamicDelegate"; +} +- (NSString *)description +{ + return [NSString stringWithFormat:@"", (__bridge void *)self, NSStringFromProtocol(self.protocol)]; +} + +- (void)forwardInvocation:(NSInvocation *)outerInv +{ + SEL selector = outerInv.selector; + A2BlockInvocation *innerInv = nil; + if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) { + [innerInv invokeWithInvocation:outerInv]; + } else if ([self.realDelegate respondsToSelector:selector]) { + [outerInv invokeWithTarget:self.realDelegate]; + } +} + +#pragma mark - + +- (BOOL)conformsToProtocol:(Protocol *)aProtocol +{ + return protocol_isEqual(aProtocol, self.protocol) || [super conformsToProtocol:aProtocol]; +} +- (BOOL)respondsToSelector:(SEL)selector +{ + return [self.invocationsBySelectors bk_objectForSelector:selector] || + class_respondsToSelector(object_getClass(self), selector) || + (protocol_declaredSelector(self.protocol, selector) && [self.realDelegate respondsToSelector:selector]); +} + +- (void)doesNotRecognizeSelector:(SEL)aSelector +{ + [NSException raise:NSInvalidArgumentException format:@"-[%s %@]: unrecognized selector sent to instance %p", object_getClassName(self), NSStringFromSelector(aSelector), (__bridge void *)self]; +} + +#pragma mark - Block Instance Method Implementations + +- (id)blockImplementationForMethod:(SEL)selector +{ + A2BlockInvocation *invocation = nil; + if ((invocation = [self.invocationsBySelectors bk_objectForSelector:selector])) + return invocation.block; + return NULL; +} + +- (void)implementMethod:(SEL)selector withBlock:(id)block +{ + NSCAssert(selector, @"Attempt to implement or remove NULL selector"); + BOOL isClassMethod = self.isClassProxy; + + if (!block) { + [self.invocationsBySelectors bk_removeObjectForSelector:selector]; + return; + } + + struct objc_method_description methodDescription = protocol_getMethodDescription(self.protocol, selector, YES, !isClassMethod); + if (!methodDescription.name) methodDescription = protocol_getMethodDescription(self.protocol, selector, NO, !isClassMethod); + + A2BlockInvocation *inv = nil; + if (methodDescription.name) { + NSMethodSignature *protoSig = [NSMethodSignature signatureWithObjCTypes:methodDescription.types]; + inv = [[A2BlockInvocation alloc] initWithBlock:block methodSignature:protoSig]; + } else { + inv = [[A2BlockInvocation alloc] initWithBlock:block]; + } + + [self.invocationsBySelectors bk_setObject:inv forSelector:selector]; +} +- (void)removeBlockImplementationForMethod:(SEL)selector __unused +{ + [self implementMethod:selector withBlock:nil]; +} + +#pragma mark - Block Class Method Implementations + +- (id)blockImplementationForClassMethod:(SEL)selector +{ + return [self.classProxy blockImplementationForMethod:selector]; +} + +- (void)implementClassMethod:(SEL)selector withBlock:(id)block +{ + [self.classProxy implementMethod:selector withBlock:block]; +} +- (void)removeBlockImplementationForClassMethod:(SEL)selector __unused +{ + [self.classProxy implementMethod:selector withBlock:nil]; +} + +@end + +#pragma mark - + +@implementation A2DynamicClassDelegate + +- (BOOL)isClassProxy +{ + return YES; +} +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] || [_proxiedClass isEqual:object]; +} +- (BOOL)respondsToSelector:(SEL)aSelector +{ + return [self.invocationsBySelectors bk_objectForSelector:aSelector] || [_proxiedClass respondsToSelector:aSelector]; +} + +- (Class)class +{ + return self.proxiedClass; +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector +{ + A2BlockInvocation *invocation = nil; + if ((invocation = [self.invocationsBySelectors bk_objectForSelector:aSelector])) + return invocation.methodSignature; + else if ([_proxiedClass methodSignatureForSelector:aSelector]) + return [_proxiedClass methodSignatureForSelector:aSelector]; + return [[NSObject class] methodSignatureForSelector:aSelector]; +} + +- (NSString *)description +{ + return [_proxiedClass description]; +} + +- (NSUInteger)hash +{ + return [_proxiedClass hash]; +} + +- (void)forwardInvocation:(NSInvocation *)outerInv +{ + SEL selector = outerInv.selector; + A2BlockInvocation *innerInv = nil; + if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) { + [innerInv invokeWithInvocation:outerInv]; + } else { + [outerInv invokeWithTarget:_proxiedClass]; + } +} + +#pragma mark - Unavailable Methods + +- (id)blockImplementationForClassMethod:(SEL)selector +{ + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (void)implementClassMethod:(SEL)selector withBlock:(id)block +{ + [self doesNotRecognizeSelector:_cmd]; +} +- (void)removeBlockImplementationForClassMethod:(SEL)selector +{ + [self doesNotRecognizeSelector:_cmd]; +} + +@end + +#pragma mark - Helper functions + +static Protocol *a2_classProtocol(Class _cls, NSString *suffix, NSString *description) +{ + Class cls = _cls; + while (cls) { + NSString *className = NSStringFromClass(cls); + NSString *protocolName = [className stringByAppendingString:suffix]; + Protocol *protocol = objc_getProtocol(protocolName.UTF8String); + if (protocol) return protocol; + + cls = class_getSuperclass(cls); + } + + NSCAssert(NO, @"Specify protocol explicitly: could not determine %@ protocol for class %@ (tried <%@>)", description, NSStringFromClass(_cls), [NSStringFromClass(_cls) stringByAppendingString:suffix]); + return nil; +} + +Protocol *a2_dataSourceProtocol(Class cls) +{ + return a2_classProtocol(cls, @"DataSource", @"data source"); +} +Protocol *a2_delegateProtocol(Class cls) +{ + return a2_classProtocol(cls, @"Delegate", @"delegate"); +} +Protocol *a2_protocolForDelegatingObject(id obj, Protocol *protocol) +{ + NSString *protocolName = NSStringFromProtocol(protocol); + if ([protocolName hasSuffix:@"Delegate"]) { + Protocol *p = a2_delegateProtocol([obj class]); + if (p) return p; + } else if ([protocolName hasSuffix:@"DataSource"]) { + Protocol *p = a2_dataSourceProtocol([obj class]); + if (p) return p; + } + + return protocol; +} diff --git a/YDBlockKit/DynamicDelegate/Foundation/NSCache+BlocksKit.h b/YDBlockKit/DynamicDelegate/Foundation/NSCache+BlocksKit.h new file mode 100644 index 0000000..fa4f142 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/Foundation/NSCache+BlocksKit.h @@ -0,0 +1,58 @@ +// +// NSCache+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** NSCache with block adding of objects + + This category allows you to conditionally add objects to + an instance of NSCache using blocks. Both the normal + delegation pattern and a block callback for NSCache's one + delegate method are allowed. + + These methods emulate Rails caching behavior. + + Created by [Igor Evsukov](https://github.com/evsukov89) and contributed to BlocksKit. + */ + +@interface NSCache (BlocksKit) + +/** Returns the value associated with a given key. If there is no + object for that key, it uses the result of the block, saves + that to the cache, and returns it. + + This mimics the cache behavior of Ruby on Rails. The following code: + + @products = Rails.cache.fetch('products') do + Product.all + end + + becomes: + + NSMutableArray *products = [cache objectForKey:@"products" withGetter:^id{ + return [Product all]; + }]; + + @return The value associated with *key*, or the object returned + by the block if no value is associated with *key*. + @param key An object identifying the value. + @param getterBlock A block used to get an object if there is no + value in the cache. + */ +- (id)bk_objectForKey:(id)key withGetter:(id (^)(void))getterBlock; + +/** Called when an object is about to be evicted from the cache. + + This block callback is an analog for the cache:willEviceObject: + method of NSCacheDelegate. + */ +@property (nonatomic, copy, setter = bk_setWillEvictBlock:, nullable) void (^bk_willEvictBlock)(NSCache *cache, id obj); + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/DynamicDelegate/Foundation/NSCache+BlocksKit.m b/YDBlockKit/DynamicDelegate/Foundation/NSCache+BlocksKit.m new file mode 100644 index 0000000..1967186 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/Foundation/NSCache+BlocksKit.m @@ -0,0 +1,60 @@ +// +// NSCache+BlocksKit.m +// BlocksKit +// + +#import "NSCache+BlocksKit.h" +#import "A2DynamicDelegate.h" +#import "NSObject+A2DynamicDelegate.h" +#import "NSObject+A2BlockDelegate.h" + +#pragma mark Custom delegate + +@interface A2DynamicNSCacheDelegate : A2DynamicDelegate + +@end + +@implementation A2DynamicNSCacheDelegate + +- (void)cache:(NSCache *)cache willEvictObject:(id)obj +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(cache:willEvictObject:)]) + [realDelegate cache:cache willEvictObject:obj]; + + void (^orig)(NSCache *, id) = [self blockImplementationForMethod:_cmd]; + if (orig) orig(cache, obj); +} + +@end + +#pragma mark Category + +@implementation NSCache (BlocksKit) + +@dynamic bk_willEvictBlock; + ++ (void)load +{ + @autoreleasepool { + [self bk_registerDynamicDelegate]; + [self bk_linkDelegateMethods:@{ @"bk_willEvictBlock": @"cache:willEvictObject:" }]; + } +} + +#pragma mark Methods + +- (id)bk_objectForKey:(id)key withGetter:(id (^)(void))block +{ + id object = [self objectForKey:key]; + if (object) return object; + + if (block) { + object = block(); + [self setObject:object forKey:key]; + } + + return object; +} + +@end diff --git a/YDBlockKit/DynamicDelegate/Foundation/NSURLConnection+BlocksKit.h b/YDBlockKit/DynamicDelegate/Foundation/NSURLConnection+BlocksKit.h new file mode 100644 index 0000000..25715a5 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/Foundation/NSURLConnection+BlocksKit.h @@ -0,0 +1,140 @@ +// +// NSURLConnection+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +/** NSURLConnection with both delegate and block callback support. + + It also adds useful block handlers for tracking upload and download progress. + + Here is a small example: + - (void)downloadImage:(id)sender { + self.downloadButton.enabled = NO; + self.progressView.progress = 0.0f; + NSURL *imageURL = [NSURL URLWithString:@"http://icanhascheezburger.files.wordpress.com/2011/06/funny-pictures-nyan-cat-wannabe1.jpg"]; + NSURLRequest *request = [NSURLRequest requestWithURL:imageURL]; + NSURLConnection *connection = [NSURLConnection connectionWithRequest:request]; + connection.delegate = self; + connection.failureBlock = ^(NSURLConnection *connection, NSError *error) { + [[UIAlertView alertViewWithTitle:@"Download error" message:[error localizedDescription]] show]; + + self.downloadButton.enabled = YES; + self.progressView.progress = 0.0f; + }; + connection.successBlock = ^(NSURLConnection *connection, NSURLResponse *response, NSData *responseData) { + self.imageView.image = [UIImage imageWithData:responseData]; + self.downloadButton.enabled = YES; + }; + connection.downloadBlock = ^(double progress) { + self.progressView.progress = progress; + }; + + [connection start]; + } + + //these methods will be called too! + - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { + NSLog(@"%s",__PRETTY_FUNCTION__); + } + + - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { + NSLog(@"%s",__PRETTY_FUNCTION__); + } + + Created by Igor Evsukov as + [IEURLConnection](https://github.com/evsukov89/IEURLConnection) and contributed + to BlocksKit. +*/ + +@interface NSURLConnection (BlocksKit) + +/** A mutable delegate that implements the NSURLConnectionDelegate protocol. + + This property allows for both use of block callbacks and delegate methods + in an instance of NSURLConnection. It only works on block-backed + NSURLConnection instances. + */ +@property (nonatomic, weak, setter = setDelegate:) id delegate BK_URL_CONNECTION_DEPRECATED; + +/** The block fired once the connection recieves a response from the server. + + This block corresponds to the connection:didReceiveResponse: method + of NSURLConnectionDataDelegate. */ +@property (nonatomic, copy, setter = bk_setResponseBlock:) void (^bk_responseBlock)(NSURLConnection *connection, NSURLResponse *response) BK_URL_CONNECTION_DEPRECATED; + +/** The block fired upon the failure of the connection. + + This block corresponds to the connection:didFailWithError: + method of NSURLConnectionDelegate. */ +@property (nonatomic, copy, setter = bk_setFailureBlock:) void (^bk_failureBlock)(NSURLConnection *connection, NSError *error) BK_URL_CONNECTION_DEPRECATED; + +/** The block that upon the successful completion of the connection. + + This block corresponds to the connectionDidFinishLoading: + method of NSURLConnectionDelegate. + + @warning If the delegate implements connection:didRecieveData:, then this + block will *not* include the data recieved by the connection and appending + the recieved data to an instance NSMutableData is left up to the user due + to the behavior of frameworks that use NSURLConnection. + */ +@property (nonatomic, copy, setter = bk_setSuccessBlock:) void (^bk_successBlock)(NSURLConnection *connection, NSURLResponse *response, NSData *responseData) BK_URL_CONNECTION_DEPRECATED; + +/** The block fired every time new data is sent to the server, + representing the current percentage of completion. + + This block corresponds to the + connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite: + method of NSURLConnectionDelegate. + */ +@property (nonatomic, copy, setter = bk_setUploadBlock:) void (^bk_uploadBlock)(double percent) BK_URL_CONNECTION_DEPRECATED; + +/** The block fired every time new data is recieved from the server, + representing the current percentage of completion. + + This block corresponds to the connection:didRecieveData: + method of NSURLConnectionDelegate. + */ +@property (nonatomic, copy, setter = bk_setDownloadBlock:) void (^bk_downloadBlock)(double percent) BK_URL_CONNECTION_DEPRECATED; + +/** Creates and returns an initialized block-backed URL connection that does not begin to load the data for the URL request. + + @param request The URL request to load. + @return An autoreleased NSURLConnection for the specified URL request. + */ ++ (NSURLConnection *)bk_connectionWithRequest:(NSURLRequest *)request BK_URL_CONNECTION_DEPRECATED; + +/** Creates, starts, and returns an asynchronous, block-backed URL connection + + @return New and running NSURLConnection with the specified properties. + @param request The URL request to load. + @param success A code block that acts on instances of NSURLResponse and NSData in the event of a successful connection. + @param failure A code block that acts on instances of NSURLResponse and NSError in the event of a failed connection. + */ ++ (NSURLConnection *)bk_startConnectionWithRequest:(NSURLRequest *)request successHandler:(void (^)(NSURLConnection *connection, NSURLResponse *response, NSData *responseData))success failureHandler:(void (^)(NSURLConnection *connection, NSError *error))failure BK_URL_CONNECTION_DEPRECATED; + +/** Returns an initialized block-backed URL connection. + + @return Newly initialized NSURLConnection with the specified properties. + @param request The URL request to load. + */ +- (instancetype)bk_initWithRequest:(NSURLRequest *)request BK_INITIALIZER BK_URL_CONNECTION_DEPRECATED; + +/** Returns an initialized URL connection with the specified completion handler. + + @return Newly initialized NSURLConnection with the specified properties. + @param request The URL request to load. + @param block A code block that acts on instances of NSURLResponse and NSData in the event of a successful connection. + */ +- (instancetype)bk_initWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLConnection *connection, NSURLResponse *response, NSData *responseData))block BK_INITIALIZER BK_URL_CONNECTION_DEPRECATED; + +/** Causes the connection to begin loading data, if it has not already, with the specified block to be fired on successful completion. + + @param block A code block that acts on instances of NSURLResponse and NSData in the event of a successful connection. + */ +- (void)bk_startWithCompletionBlock:(void (^)(NSURLConnection *connection, NSURLResponse *response, NSData *responseData))block BK_URL_CONNECTION_DEPRECATED; + +@end diff --git a/YDBlockKit/DynamicDelegate/Foundation/NSURLConnection+BlocksKit.m b/YDBlockKit/DynamicDelegate/Foundation/NSURLConnection+BlocksKit.m new file mode 100644 index 0000000..f207edb --- /dev/null +++ b/YDBlockKit/DynamicDelegate/Foundation/NSURLConnection+BlocksKit.m @@ -0,0 +1,413 @@ +// +// NSURLConnection+BlocksKit.m +// BlocksKit +// + +#import "NSURLConnection+BlocksKit.h" +@import ObjectiveC.runtime; +#import "A2DynamicDelegate.h" +#import "NSObject+A2DynamicDelegate.h" +#import "NSObject+A2BlockDelegate.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +#pragma mark Private + +static const void *BKResponseDataKey = &BKResponseDataKey; +static const void *BKResponseKey = &BKResponseKey; +static const void *BKResponseLengthKey = &BKResponseLengthKey; + +@interface NSURLConnection (BlocksKitPrivate) + +@property (nonatomic, retain, setter = bk_setResponseData:) NSMutableData *bk_responseData; +@property (nonatomic, retain, setter = bk_setResponse:) NSURLResponse *bk_response; +@property (nonatomic, setter = bk_setResponseLength:) NSUInteger bk_responseLength; + +@end + +@implementation NSURLConnection (BlocksKitPrivate) + +- (NSMutableData *)bk_responseData +{ + return objc_getAssociatedObject(self, BKResponseDataKey); +} + +- (void)bk_setResponseData:(NSMutableData *)responseData +{ + objc_setAssociatedObject(self, BKResponseDataKey, responseData, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSURLResponse *)bk_response +{ + return objc_getAssociatedObject(self, BKResponseKey); +} + +- (void)bk_setResponse:(NSURLResponse *)response +{ + objc_setAssociatedObject(self, BKResponseKey, response, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSUInteger)bk_responseLength +{ + return [objc_getAssociatedObject(self, BKResponseLengthKey) unsignedIntegerValue]; +} + +- (void)bk_setResponseLength:(NSUInteger)responseLength +{ + objc_setAssociatedObject(self, BKResponseLengthKey, @(responseLength), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - BKURLConnectionInformalDelegate - iOS 4.3 & Snow Leopard support + +@protocol BKURLConnectionInformalDelegate + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; +- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite; +- (void)connectionDidFinishLoading:(NSURLConnection *)connection; +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; +- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; +- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace; + +@end + +@interface A2DynamicBKURLConnectionInformalDelegate : A2DynamicDelegate + +@end + +@implementation A2DynamicBKURLConnectionInformalDelegate + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)]) + [realDelegate connection:connection didReceiveResponse:response]; + + connection.bk_responseLength = 0; + [connection.bk_responseData setLength:0]; + + connection.bk_response = response; + + void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd]; + if (block) block(connection, response); +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + connection.bk_responseLength += data.length; + + void (^block)(double) = connection.bk_downloadBlock; + if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength) + block((double)connection.bk_responseLength / (double)connection.bk_response.expectedContentLength); + + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) { + [realDelegate connection:connection didReceiveData:data]; + return; + } + + NSMutableData *responseData = connection.bk_responseData; + if (!responseData) { + responseData = [NSMutableData data]; + connection.bk_responseData = responseData; + } + + [responseData appendData:data]; +} + +- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)]) + [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; + + void (^block)(double) = connection.bk_uploadBlock; + if (block) block((double)totalBytesWritten/(double)totalBytesExpectedToWrite); +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)]) + [realDelegate connectionDidFinishLoading:connection]; + + if (!connection.bk_responseData.length) + connection.bk_responseData = nil; + + void (^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.bk_successBlock; + if (block) block(connection, connection.bk_response, connection.bk_responseData); +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)]) + [realDelegate connection:connection didFailWithError:error]; + + connection.bk_responseLength = 0; + [connection.bk_responseData setLength:0]; + + void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd]; + if (block) block(connection, error); +} + +- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveAuthenticationChallenge:)]) + [realDelegate connection:connection didReceiveAuthenticationChallenge:challenge]; +} + +- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:canAuthenticateAgainstProtectionSpace:)]) + return [realDelegate connection:connection canAuthenticateAgainstProtectionSpace:protectionSpace]; + + return NO; +} + +@end + +#pragma mark - NSURLConnectionDelegate - iOS 5.0 & Lion support + +@interface A2DynamicNSURLConnectionDelegate : A2DynamicDelegate + +@end + +@implementation A2DynamicNSURLConnectionDelegate + +- (BOOL)conformsToProtocol:(Protocol *)protocol +{ + Protocol *dataDelegateProtocol = objc_getProtocol("NSURLConnectionDataDelegate"); + return (protocol_isEqual(protocol, dataDelegateProtocol) || protocol_isEqual(protocol, self.protocol) || [super conformsToProtocol:protocol]); +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)]) + [realDelegate connection:connection didFailWithError:error]; + + connection.bk_responseLength = 0; + [connection.bk_responseData setLength:0]; + + void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd]; + if (block) block(connection, error); +} + +- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connectionShouldUseCredentialStorage:)]) + return [realDelegate connectionShouldUseCredentialStorage:connection]; + + return YES; +} + +- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)]) + [realDelegate connection:connection willSendRequestForAuthenticationChallenge:challenge]; +} + +#pragma mark - NSURLConnectionDataDelegate + +- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willSendRequest:redirectResponse:)]) + return [realDelegate connection:connection willSendRequest:request redirectResponse:response]; + + return request; +} + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)]) + [realDelegate connection:connection didReceiveResponse:response]; + + connection.bk_responseLength = 0; + + if (connection.bk_responseData) + [connection.bk_responseData setLength:0]; + + connection.bk_response = response; + + void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd]; + if (block) block(connection, response); +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + connection.bk_responseLength += data.length; + + void (^block)(double) = connection.bk_downloadBlock; + if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength) + block((double)connection.bk_responseLength / (double)connection.bk_response.expectedContentLength); + + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) { + [realDelegate connection:connection didReceiveData:data]; + return; + } + + NSMutableData *responseData = connection.bk_responseData; + if (!responseData) { + responseData = [NSMutableData data]; + connection.bk_responseData = responseData; + } + + [responseData appendData:data]; +} + +- (NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:needNewBodyStream:)]) + return [realDelegate connection:connection needNewBodyStream:request]; + + return nil; +} + +- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)]) + [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; + + void (^block)(double) = connection.bk_uploadBlock; + if (block) + block((double)totalBytesWritten/(double)totalBytesExpectedToWrite); +} + +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willCacheResponse:)]) + return [realDelegate connection:connection willCacheResponse:cachedResponse]; + + return cachedResponse; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)]) + [realDelegate connectionDidFinishLoading:connection]; + + if (!connection.bk_responseData.length) + connection.bk_responseData = nil; + + void (^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.bk_successBlock; + if (block) + block(connection, connection.bk_response, connection.bk_responseData); +} + +@end + +#pragma mark - Category + +static NSString *const kSuccessBlockKey = @"NSURLConnectionDidFinishLoading"; +static NSString *const kFailureBlockKey = @"NSURLConnectionDidFailWithError"; +static NSString *const kUploadBlockKey = @"NSURLConnectionDidSendData"; +static NSString *const kDownloadBlockKey = @"NSURLConnectionDidRecieveData"; + +@implementation NSURLConnection (BlocksKit) + +@dynamic delegate, bk_responseBlock, bk_failureBlock; + ++ (void)load +{ + @autoreleasepool { + [self bk_registerDynamicDelegate]; + [self bk_linkDelegateMethods:@{ @"bk_responseBlock": @"connection:didReceiveResponse:", @"bk_failureBlock": @"connection:didFailWithError:" }]; + } +} + +#pragma mark Initializers + ++ (NSURLConnection*)bk_connectionWithRequest:(NSURLRequest *)request +{ + return [[[self class] alloc] bk_initWithRequest:request]; +} + ++ (NSURLConnection *)bk_startConnectionWithRequest:(NSURLRequest *)request successHandler:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))success failureHandler:(void (^)(NSURLConnection *, NSError *))failure +{ + Protocol *delegateProtocol = objc_getProtocol("NSURLConnectionDelegate"); + if (!delegateProtocol) + delegateProtocol = @protocol(BKURLConnectionInformalDelegate); + NSURLConnection *ret = [[self class] alloc]; + A2DynamicDelegate *dd = [ret bk_dynamicDelegateForProtocol:delegateProtocol]; + if (success) + dd.handlers[kSuccessBlockKey] = [success copy]; + if (failure) + [dd implementMethod:@selector(connection:didFailWithError:) withBlock:[failure copy]]; + return [ret initWithRequest:request delegate:dd startImmediately:YES]; +} + +- (instancetype)bk_initWithRequest:(NSURLRequest *)request +{ + return (self = [self bk_initWithRequest:request completionHandler:nil]); +} + +- (instancetype)bk_initWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block +{ + Protocol *delegateProtocol = objc_getProtocol("NSURLConnectionDelegate"); + if (!delegateProtocol) + delegateProtocol = @protocol(BKURLConnectionInformalDelegate); + A2DynamicDelegate *dd = [self bk_dynamicDelegateForProtocol:delegateProtocol]; + if (block) + dd.handlers[kSuccessBlockKey] = [block copy]; + return (self = [self initWithRequest:request delegate:dd startImmediately:NO]); +} + +- (void)bk_startWithCompletionBlock:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block +{ + self.bk_successBlock = block; + [self start]; +} + +#pragma mark Properties + +- (void (^)(NSURLConnection *, NSURLResponse *, NSData *))bk_successBlock { + return [self.bk_dynamicDelegate handlers][kSuccessBlockKey]; +} + +- (void)bk_setSuccessBlock:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block { + if (block) + [self.bk_dynamicDelegate handlers][kSuccessBlockKey] = [block copy]; + else + [[self.bk_dynamicDelegate handlers] removeObjectForKey:kSuccessBlockKey]; +} + +- (void (^)(double))bk_uploadBlock { + return [self.bk_dynamicDelegate handlers][kUploadBlockKey]; +} + +- (void)bk_setUploadBlock:(void (^)(double))block { + if (block) + [self.bk_dynamicDelegate handlers][kUploadBlockKey] = [block copy]; + else + [[self.bk_dynamicDelegate handlers] removeObjectForKey:kUploadBlockKey]; +} + +- (void (^)(double))bk_downloadBlock { + return [self.bk_dynamicDelegate handlers][kDownloadBlockKey]; +} + +- (void)bk_setDownloadBlock:(void (^)(double))block { + if (block) + [self.bk_dynamicDelegate handlers][kDownloadBlockKey] = [block copy]; + else + [[self.bk_dynamicDelegate handlers] removeObjectForKey:kDownloadBlockKey]; +} + +@end + +#pragma clang diagnostic pop diff --git a/YDBlockKit/DynamicDelegate/NSObject+A2BlockDelegate.h b/YDBlockKit/DynamicDelegate/NSObject+A2BlockDelegate.h new file mode 100644 index 0000000..bdfb2a4 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/NSObject+A2BlockDelegate.h @@ -0,0 +1,121 @@ +// +// NSObject+A2BlockDelegate.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** The A2BlockDelegate category extends features provided by A2DynamicDelegate + to create custom block properties in a category on a delegating object and + dynamically map them to delegate (`UIAlertViewDelegate`), data source + (`UITableViewDataSource`), or other delegated protocol + (`NSErrorRecoveryAttempting`) methods. + + A2BlockDelegate also supports replacing the delegate of a given class with an + automatic - though optional - version of these block-based properties, while + still allowing for traditional method-based delegate implementations. + + Simply call one of the methods in the category on a class to add a block + property to that class, preferably during a `+load` method in a category. + + To interplay between classes that support delegation but are extended to have + block properties through delegate replacement extended to have block + properties, one should implement an `A2Dynamic` subclass of + `A2DynamicDelegate`. This behavior is documented in detail in the class + documentation for A2DynamicDelegate, and examples exist in BlocksKit. + */ +@interface NSObject (A2BlockDelegate) + +/** @name Linking Block Properties */ + +/** Synthesizes multiple properties and links them to the appropriate selector + in the data source protocol. + + A2DynamicDelegate assumes a protocol name `FooBarDataSource` for instances of + class `FooBar`. The method will generate appropriate `setHandler:` and + `handler` implementations for each property name and selector name string pair. + + @param selectorsForPropertyNames A dictionary with property names as keys and + selector strings as objects. + */ ++ (void)bk_linkDataSourceMethods:(NSDictionary *)selectorsForPropertyNames; + +/** Synthesizes multiple properties and links them to the appropriate selector + in the delegate protocol. + + A2DynamicDelegate assumes a protocol name `FooBarDelegate` for instances of + class `FooBar`. The method will generate appropriate `setHandler:` and + `handler` implementations for each property name and selector name string pair. + + @param selectorsForPropertyNames A dictionary with property names as keys and + selectors strings as objects. + */ ++ (void)bk_linkDelegateMethods:(NSDictionary *)selectorsForPropertyNames; + +/** Synthesizes multiple properties and links them to the appropriate selector + in the given protocol. + + The method will generate appropriate `setHandler:` and `handler` + implementations for each property name and selector name string pair. + + @param protocol A protocol that declares all of the given selectors. Must not + be NULL. + @param selectorsForPropertyNames A dictionary with property names as keys and + selector strings as objects. + */ ++ (void)bk_linkProtocol:(Protocol *)protocol methods:(NSDictionary *)selectorsForPropertyNames; + +/** @name Delegate replacement properties */ + +/** Registers a dynamic data source replacement using the property name + `dataSource` and the protocol name `FooBarDataSource` for an instance of + `FooBar`. */ ++ (void)bk_registerDynamicDataSource; + +/** Registers a dynamic delegate replacement using the property name `delegate` + and the protocol name `FooBarDelegate` for an instance of `FooBar`. */ ++ (void)bk_registerDynamicDelegate; + +/** Registers a dynamic data source replacement using the given property name + and the protocol name `FooBarDataSource` for an instance of `FooBar`. + + @param dataSourceName The name of the class' data source property. Must not be + nil. + */ ++ (void)bk_registerDynamicDataSourceNamed:(NSString *)dataSourceName; + +/** Registers a dynamic delegate replacement using the given property name and + the protocol name `FooBarDelegate` for an instance of `FooBar`. + + @param delegateName The name of the class' delegate property. Must not be nil. + */ ++ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName; + +/** Registers a dynamic protocol implementation replacement + using the given property name and the given protocol. + + @param delegateName The name of the class' delegation protocol property, such + as `safeDelegate`. Must not be nil. + @param protocol A properly encoded protocol. Must not be NULL. + */ ++ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName forProtocol:(Protocol *)protocol; + +/** Creates or gets a dynamic delegate, assuring that it is the delegate. + @see bk_dynamicDelegate: + @return A dynamic delegate. + */ +@property (NS_NONATOMIC_IOSONLY, readonly, strong) id bk_ensuredDynamicDelegate; + +/** Creates or gets a dynamic protocol implementation, assuring that it is + assigned to the delegate property correspending to that protocol + @see bk_dynamicDelegateForProtocol: + @return A dynamic delegate. + */ +- (id)bk_ensuredDynamicDelegateForProtocol:(Protocol *)protocol; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/DynamicDelegate/NSObject+A2BlockDelegate.m b/YDBlockKit/DynamicDelegate/NSObject+A2BlockDelegate.m new file mode 100644 index 0000000..d7238dd --- /dev/null +++ b/YDBlockKit/DynamicDelegate/NSObject+A2BlockDelegate.m @@ -0,0 +1,364 @@ +// +// NSObject+A2BlockDelegate.m +// BlocksKit +// + +#import "NSObject+A2BlockDelegate.h" +@import ObjectiveC.message; +#import "A2DynamicDelegate.h" +#import "NSObject+A2DynamicDelegate.h" + +#pragma mark - Declarations and macros + +extern Protocol *a2_dataSourceProtocol(Class cls); +extern Protocol *a2_delegateProtocol(Class cls); + +#pragma mark - Functions + +static BOOL bk_object_isKindOfClass(id obj, Class testClass) +{ + BOOL isKindOfClass = NO; + Class cls = object_getClass(obj); + while (cls && !isKindOfClass) { + isKindOfClass = (cls == testClass); + cls = class_getSuperclass(cls); + } + + return isKindOfClass; +} + +static Protocol *a2_protocolForDelegatingObject(id obj, Protocol *protocol) +{ + NSString *protocolName = NSStringFromProtocol(protocol); + if ([protocolName hasSuffix:@"Delegate"]) { + Protocol *p = a2_delegateProtocol([obj class]); + if (p) return p; + } else if ([protocolName hasSuffix:@"DataSource"]) { + Protocol *p = a2_dataSourceProtocol([obj class]); + if (p) return p; + } + + return protocol; +} + +static inline BOOL isValidIMP(IMP impl) { +#if defined(__arm64__) + if (impl == NULL || impl == _objc_msgForward) return NO; +#else + if (impl == NULL || impl == _objc_msgForward || impl == (IMP)_objc_msgForward_stret) return NO; +#endif + return YES; +} + +static BOOL addMethodWithIMP(Class cls, SEL oldSel, SEL newSel, IMP newIMP, const char *types, BOOL aggressive) { + if (!class_addMethod(cls, oldSel, newIMP, types)) { + return NO; + } + + // We just ended up implementing a method that doesn't exist + // (-[NSURLConnection setDelegate:]) or overrode a superclass + // version (-[UIImagePickerController setDelegate:]). + IMP parentIMP = NULL; + Class superclass = class_getSuperclass(cls); + while (superclass && !isValidIMP(parentIMP)) { + parentIMP = class_getMethodImplementation(superclass, oldSel); + if (isValidIMP(parentIMP)) { + break; + } else { + parentIMP = NULL; + } + + superclass = class_getSuperclass(superclass); + } + + if (parentIMP) { + if (aggressive) { + return class_addMethod(cls, newSel, parentIMP, types); + } + + class_replaceMethod(cls, newSel, newIMP, types); + class_replaceMethod(cls, oldSel, parentIMP, types); + } + + return YES; +} + +static BOOL swizzleWithIMP(Class cls, SEL oldSel, SEL newSel, IMP newIMP, const char *types, BOOL aggressive) { + Method origMethod = class_getInstanceMethod(cls, oldSel); + + if (addMethodWithIMP(cls, oldSel, newSel, newIMP, types, aggressive)) { + return YES; + } + + // common case, actual swap + BOOL ret = class_addMethod(cls, newSel, newIMP, types); + Method newMethod = class_getInstanceMethod(cls, newSel); + method_exchangeImplementations(origMethod, newMethod); + return ret; +} + +static SEL selectorWithPattern(const char *prefix, const char *key, const char *suffix) { + size_t prefixLength = prefix ? strlen(prefix) : 0; + size_t suffixLength = suffix ? strlen(suffix) : 0; + + char initial = key[0]; + if (prefixLength) initial = (char)toupper(initial); + size_t initialLength = 1; + + const char *rest = key + initialLength; + size_t restLength = strlen(rest); + + char selector[prefixLength + initialLength + restLength + suffixLength + 1]; + memcpy(selector, prefix, prefixLength); + selector[prefixLength] = initial; + memcpy(selector + prefixLength + initialLength, rest, restLength); + memcpy(selector + prefixLength + initialLength + restLength, suffix, suffixLength); + selector[prefixLength + initialLength + restLength + suffixLength] = '\0'; + + return sel_registerName(selector); +} + +static SEL getterForProperty(objc_property_t property, const char *name) +{ + if (property) { + char *getterName = property_copyAttributeValue(property, "G"); + if (getterName) { + SEL getter = sel_getUid(getterName); + free(getterName); + if (getter) return getter; + } + } + + const char *propertyName = property ? property_getName(property) : name; + return sel_registerName(propertyName); +} + +static SEL setterForProperty(objc_property_t property, const char *name) +{ + if (property) { + char *setterName = property_copyAttributeValue(property, "S"); + if (setterName) { + SEL setter = sel_getUid(setterName); + free(setterName); + if (setter) return setter; + } + } + + const char *propertyName = property ? property_getName(property) : name; + return selectorWithPattern("set", propertyName, ":"); +} + +static inline SEL prefixedSelector(SEL original) { + return selectorWithPattern("a2_", sel_getName(original), NULL); +} + +#pragma mark - + +typedef struct { + SEL setter; + SEL a2_setter; + SEL getter; +} A2BlockDelegateInfo; + +static NSUInteger A2BlockDelegateInfoSize(const void *__unused item) { + return sizeof(A2BlockDelegateInfo); +} + +static NSString *A2BlockDelegateInfoDescribe(const void *__unused item) { + if (!item) { return nil; } + const A2BlockDelegateInfo *info = item; + return [NSString stringWithFormat:@"(setter: %s, getter: %s)", sel_getName(info->setter), sel_getName(info->getter)]; +} + +static inline A2DynamicDelegate *getDynamicDelegate(NSObject *delegatingObject, Protocol *protocol, const A2BlockDelegateInfo *info, BOOL ensuring) { + A2DynamicDelegate *dynamicDelegate = [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)]; + + if (!info || !info->setter || !info->getter) { + return dynamicDelegate; + } + + if (!info->a2_setter && !info->setter) { return dynamicDelegate; } + + id (*getterDispatch)(id, SEL) = (id (*)(id, SEL)) objc_msgSend; + id originalDelegate = getterDispatch(delegatingObject, info->getter); + + if (bk_object_isKindOfClass(originalDelegate, A2DynamicDelegate.class)) { return dynamicDelegate; } + + void (*setterDispatch)(id, SEL, id) = (void (*)(id, SEL, id)) objc_msgSend; + setterDispatch(delegatingObject, info->a2_setter ?: info->setter, dynamicDelegate); + + return dynamicDelegate; +} + +typedef A2DynamicDelegate *(^A2GetDynamicDelegateBlock)(NSObject *, BOOL); + +@interface A2DynamicDelegate () + +@property (nonatomic, weak, readwrite) id realDelegate; + +@end + +#pragma mark - + +@implementation NSObject (A2BlockDelegate) + +#pragma mark Helpers + ++ (NSMapTable *)bk_delegateInfoByProtocol:(BOOL)createIfNeeded +{ + NSMapTable *delegateInfo = objc_getAssociatedObject(self, _cmd); + if (delegateInfo || !createIfNeeded) { return delegateInfo; } + + NSPointerFunctions *protocols = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsOpaqueMemory|NSPointerFunctionsObjectPointerPersonality]; + NSPointerFunctions *infoStruct = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsMallocMemory|NSPointerFunctionsStructPersonality|NSPointerFunctionsCopyIn]; + infoStruct.sizeFunction = A2BlockDelegateInfoSize; + infoStruct.descriptionFunction = A2BlockDelegateInfoDescribe; + + delegateInfo = [[NSMapTable alloc] initWithKeyPointerFunctions:protocols valuePointerFunctions:infoStruct capacity:0]; + objc_setAssociatedObject(self, _cmd, delegateInfo, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + return delegateInfo; +} + ++ (const A2BlockDelegateInfo *)bk_delegateInfoForProtocol:(Protocol *)protocol +{ + A2BlockDelegateInfo *infoAsPtr = NULL; + Class cls = self; + while ((infoAsPtr == NULL || infoAsPtr->getter == NULL) && cls != nil && cls != NSObject.class) { + NSMapTable *map = [cls bk_delegateInfoByProtocol:NO]; + infoAsPtr = (__bridge void *)[map objectForKey:protocol]; + cls = [cls superclass]; + } + NSCAssert(infoAsPtr != NULL, @"Class %@ not assigned dynamic delegate for protocol %@", NSStringFromClass(self), NSStringFromProtocol(protocol)); + return infoAsPtr; +} + +#pragma mark Linking block properties + ++ (void)bk_linkDataSourceMethods:(NSDictionary *)dictionary +{ + [self bk_linkProtocol:a2_dataSourceProtocol(self) methods:dictionary]; +} + ++ (void)bk_linkDelegateMethods:(NSDictionary *)dictionary +{ + [self bk_linkProtocol:a2_delegateProtocol(self) methods:dictionary]; +} + ++ (void)bk_linkProtocol:(Protocol *)protocol methods:(NSDictionary *)dictionary +{ + [dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *selectorName, BOOL *stop) { + const char *name = propertyName.UTF8String; + objc_property_t property = class_getProperty(self, name); + NSCAssert(property, @"Property \"%@\" does not exist on class %s", propertyName, class_getName(self)); + + char *dynamic = property_copyAttributeValue(property, "D"); + NSCAssert2(dynamic, @"Property \"%@\" on class %s must be backed with \"@dynamic\"", propertyName, class_getName(self)); + free(dynamic); + + char *copy = property_copyAttributeValue(property, "C"); + NSCAssert2(copy, @"Property \"%@\" on class %s must be defined with the \"copy\" attribute", propertyName, class_getName(self)); + free(copy); + + SEL selector = NSSelectorFromString(selectorName); + SEL getter = getterForProperty(property, name); + SEL setter = setterForProperty(property, name); + + if (class_respondsToSelector(self, setter) || class_respondsToSelector(self, getter)) { return; } + + const A2BlockDelegateInfo *info = [self bk_delegateInfoForProtocol:protocol]; + + IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) { + A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject, protocol, info, NO); + return [delegate blockImplementationForMethod:selector]; + }); + + if (!class_addMethod(self, getter, getterImplementation, "@@:")) { + NSCAssert(NO, @"Could not implement getter for \"%@\" property.", propertyName); + } + + IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id block) { + A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject, protocol, info, YES); + [delegate implementMethod:selector withBlock:block]; + }); + + if (!class_addMethod(self, setter, setterImplementation, "v@:@")) { + NSCAssert(NO, @"Could not implement setter for \"%@\" property.", propertyName); + } + }]; +} + +#pragma mark Dynamic Delegate Replacement + ++ (void)bk_registerDynamicDataSource +{ + [self bk_registerDynamicDelegateNamed:@"dataSource" forProtocol:a2_dataSourceProtocol(self)]; +} ++ (void)bk_registerDynamicDelegate +{ + [self bk_registerDynamicDelegateNamed:@"delegate" forProtocol:a2_delegateProtocol(self)]; +} + ++ (void)bk_registerDynamicDataSourceNamed:(NSString *)dataSourceName +{ + [self bk_registerDynamicDelegateNamed:dataSourceName forProtocol:a2_dataSourceProtocol(self)]; +} ++ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName +{ + [self bk_registerDynamicDelegateNamed:delegateName forProtocol:a2_delegateProtocol(self)]; +} + ++ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName forProtocol:(Protocol *)protocol +{ + NSMapTable *propertyMap = [self bk_delegateInfoByProtocol:YES]; + A2BlockDelegateInfo *infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol]; + if (infoAsPtr != NULL) { return; } + + const char *name = delegateName.UTF8String; + objc_property_t property = class_getProperty(self, name); + SEL setter = setterForProperty(property, name); + SEL a2_setter = prefixedSelector(setter); + SEL getter = getterForProperty(property, name); + + A2BlockDelegateInfo info = { + setter, a2_setter, getter + }; + + [propertyMap setObject:(__bridge id)&info forKey:protocol]; + infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol]; + + IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id delegate) { + A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject, protocol, infoAsPtr, YES); + if ([delegate isEqual:dynamicDelegate]) { + delegate = nil; + } + dynamicDelegate.realDelegate = delegate; + }); + + if (!swizzleWithIMP(self, setter, a2_setter, setterImplementation, "v@:@", YES)) { + bzero(infoAsPtr, sizeof(A2BlockDelegateInfo)); + return; + } + + if (![self instancesRespondToSelector:getter]) { + IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) { + return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)]; + }); + + addMethodWithIMP(self, getter, NULL, getterImplementation, "@@:", NO); + } +} + +- (id)bk_ensuredDynamicDelegate +{ + Protocol *protocol = a2_delegateProtocol(self.class); + return [self bk_ensuredDynamicDelegateForProtocol:protocol]; +} + +- (id)bk_ensuredDynamicDelegateForProtocol:(Protocol *)protocol +{ + const A2BlockDelegateInfo *info = [self.class bk_delegateInfoForProtocol:protocol]; + return getDynamicDelegate(self, protocol, info, YES); +} + +@end diff --git a/YDBlockKit/DynamicDelegate/NSObject+A2DynamicDelegate.h b/YDBlockKit/DynamicDelegate/NSObject+A2DynamicDelegate.h new file mode 100644 index 0000000..b09c339 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/NSObject+A2DynamicDelegate.h @@ -0,0 +1,71 @@ +// +// NSObject+A2DynamicDelegate.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** The A2DynamicDelegate category to NSObject provides the primary interface + by which dynamic delegates are generated for a given object. */ +@interface NSObject (A2DynamicDelegate) + +/** Creates or gets a dynamic data source for the reciever. + + A2DynamicDelegate assumes a protocol name `FooBarDataSource` + for instances of class `FooBar`. The object is given a strong + attachment to the reciever, and is automatically deallocated + when the reciever is released. + + If the user implements a `A2DynamicFooBarDataSource` subclass + of A2DynamicDelegate, its implementation of any method + will be used over the block. If the block needs to be used, + it can be called from within the custom + implementation using blockImplementationForMethod:. + + @see blockImplementationForMethod: + @return A dynamic data source. + */ +@property (readonly, strong) id bk_dynamicDataSource; + +/** Creates or gets a dynamic delegate for the reciever. + + A2DynamicDelegate assumes a protocol name `FooBarDelegate` + for instances of class `FooBar`. The object is given a strong + attachment to the reciever, and is automatically deallocated + when the reciever is released. + + If the user implements a `A2DynamicFooBarDelegate` subclass + of A2DynamicDelegate, its implementation of any method + will be used over the block. If the block needs to be used, + it can be called from within the custom + implementation using blockImplementationForMethod:. + + @see blockImplementationForMethod: + @return A dynamic delegate. + */ +@property (readonly, strong) id bk_dynamicDelegate; + +/** Creates or gets a dynamic protocol implementation for + the reciever. The designated initializer. + + The object is given a strong attachment to the reciever, + and is automatically deallocated when the reciever is released. + + If the user implements a subclass of A2DynamicDelegate prepended + with `A2Dynamic`, such as `A2DynamicFooProvider`, its + implementation of any method will be used over the block. + If the block needs to be used, it can be called from within the + custom implementation using blockImplementationForMethod:. + + @param protocol A custom protocol. + @return A dynamic protocol implementation. + @see blockImplementationForMethod: + */ +- (id)bk_dynamicDelegateForProtocol:(Protocol *)protocol; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/DynamicDelegate/NSObject+A2DynamicDelegate.m b/YDBlockKit/DynamicDelegate/NSObject+A2DynamicDelegate.m new file mode 100644 index 0000000..17854a4 --- /dev/null +++ b/YDBlockKit/DynamicDelegate/NSObject+A2DynamicDelegate.m @@ -0,0 +1,89 @@ +// +// NSObject+A2DynamicDelegate.m +// BlocksKit +// + +#import "NSObject+A2DynamicDelegate.h" +@import ObjectiveC.runtime; +#import "A2DynamicDelegate.h" + +extern Protocol *a2_dataSourceProtocol(Class cls); +extern Protocol *a2_delegateProtocol(Class cls); + +static Class a2_dynamicDelegateClass(Class cls, NSString *suffix) +{ + while (cls) { + NSString *className = [NSString stringWithFormat:@"A2Dynamic%@%@", NSStringFromClass(cls), suffix]; + Class ddClass = NSClassFromString(className); + if (ddClass) return ddClass; + + cls = class_getSuperclass(cls); + } + + return [A2DynamicDelegate class]; +} + +static dispatch_queue_t a2_backgroundQueue(void) +{ + static dispatch_once_t onceToken; + static dispatch_queue_t backgroundQueue = nil; + dispatch_once(&onceToken, ^{ + backgroundQueue = dispatch_queue_create("BlocksKit.DynamicDelegate.Queue", DISPATCH_QUEUE_SERIAL); + }); + return backgroundQueue; +} + +@implementation NSObject (A2DynamicDelegate) + +- (id)bk_dynamicDataSource +{ + Protocol *protocol = a2_dataSourceProtocol([self class]); + Class class = a2_dynamicDelegateClass([self class], @"DataSource"); + return [self bk_dynamicDelegateWithClass:class forProtocol:protocol]; +} +- (id)bk_dynamicDelegate +{ + Protocol *protocol = a2_delegateProtocol([self class]); + Class class = a2_dynamicDelegateClass([self class], @"Delegate"); + return [self bk_dynamicDelegateWithClass:class forProtocol:protocol]; +} +- (id)bk_dynamicDelegateForProtocol:(Protocol *)protocol +{ + Class class = [A2DynamicDelegate class]; + NSString *protocolName = NSStringFromProtocol(protocol); + if ([protocolName hasSuffix:@"Delegate"]) { + class = a2_dynamicDelegateClass([self class], @"Delegate"); + } else if ([protocolName hasSuffix:@"DataSource"]) { + class = a2_dynamicDelegateClass([self class], @"DataSource"); + } + + return [self bk_dynamicDelegateWithClass:class forProtocol:protocol]; +} +- (id)bk_dynamicDelegateWithClass:(Class)cls forProtocol:(Protocol *)protocol +{ + /** + * Storing the dynamic delegate as an associated object of the delegating + * object not only allows us to later retrieve the delegate, but it also + * creates a strong relationship to the delegate. Since delegates are weak + * references on the part of the delegating object, a dynamic delegate + * would be deallocated immediately after its declaring scope ends. + * Therefore, this strong relationship is required to ensure that the + * delegate's lifetime is at least as long as that of the delegating object. + **/ + + __block A2DynamicDelegate *dynamicDelegate; + + dispatch_sync(a2_backgroundQueue(), ^{ + dynamicDelegate = objc_getAssociatedObject(self, (__bridge const void *)protocol); + + if (!dynamicDelegate) + { + dynamicDelegate = [[cls alloc] initWithProtocol:protocol]; + objc_setAssociatedObject(self, (__bridge const void *)protocol, dynamicDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + }); + + return dynamicDelegate; +} + +@end diff --git a/YDBlockKit/UIKit/UIActionSheet+BlocksKit.h b/YDBlockKit/UIKit/UIActionSheet+BlocksKit.h new file mode 100644 index 0000000..f04abe3 --- /dev/null +++ b/YDBlockKit/UIKit/UIActionSheet+BlocksKit.h @@ -0,0 +1,129 @@ +// +// UIActionSheet+BlocksKit.h +// BlocksKit +// + +#import +#import "BKDefines.h" + +/** UIActionSheet without delegates! + + This set of extensions and convenience classes allows + for an instance of UIActionSheet without the implementation + of a delegate. Any time you instantiate a UIActionSheet + using the methods here, you must add buttons using + addButtonWithTitle:handler: to make sure nothing breaks. + + A typical invocation might go like this: + UIActionSheet *testSheet = [UIActionSheet actionSheetWithTitle:@"Please select one."]; + [testSheet addButtonWithTitle:@"Zip" handler:^{ NSLog(@"Zip!"); }]; + [testSheet addButtonWithTitle:@"Zap" handler:^{ NSLog(@"Zap!"); }]; + [testSheet addButtonWithTitle:@"Zop" handler:^{ NSLog(@"Zop!"); }]; + [testSheet setDestructiveButtonWithTitle:@"No!" handler:^{ NSLog(@"Fine!"); }]; + [testSheet setCancelButtonWithTitle:nil handler:^{ NSLog(@"Never mind, then!"); }]; + [testSheet showInView:self.view]; + + Includes code by the following: + + - [Landon Fuller](http://landonf.bikemonkey.org), "Using Blocks". + - [Peter Steinberger](https://github.com/steipete) + - [Zach Waldowski](https://github.com/zwaldowski) + + @warning UIActionSheet is only available on a platform with UIKit. + */ +@interface UIActionSheet (BlocksKit) + +///----------------------------------- +/// @name Creating action sheets +///----------------------------------- + +/** Creates and returns a new action sheet with only a title and cancel button. + + @param title The header of the action sheet. + @return A newly created action sheet. + */ ++ (instancetype)bk_actionSheetWithTitle:(NSString *)title BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** Returns a configured action sheet with only a title and cancel button. + + @param title The header of the action sheet. + @return An instantiated actionSheet. + */ +- (instancetype)bk_initWithTitle:(NSString *)title BK_INITIALIZER BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +///----------------------------------- +/// @name Adding buttons +///----------------------------------- + +/** Add a new button with an associated code block. + + @param title The text of the button. + @param block A block of code. + */ +- (NSInteger)bk_addButtonWithTitle:(NSString *)title handler:(void (^)(void))block BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** Set the destructive (red) button with an associated code block. + + @warning Because buttons cannot be removed from an action sheet, + be aware that the effects of calling this method are cumulative. + Previously added destructive buttons will become normal buttons. + + @param title The text of the button. + @param block A block of code. + */ +- (NSInteger)bk_setDestructiveButtonWithTitle:(NSString *)title handler:(void (^)(void))block BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** Set the title and trigger of the cancel button. + + `block` can be set to `nil`, but this is generally useless as + the cancel button is configured already to do nothing. + + iPhone users will have the button shown regardless; if the title is + set to `nil`, it will automatically be localized. + + @param title The text of the button. + @param block A block of code. + */ +- (NSInteger)bk_setCancelButtonWithTitle:(NSString *)title handler:(void (^)(void))block BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +///----------------------------------- +/// @name Altering actions +///----------------------------------- + +/** Sets the block that is to be fired when a button is pressed. + + @param block A code block, or nil to set no response. + @param index The index of a button already added to the action sheet. +*/ +- (void)bk_setHandler:(void (^)(void))block forButtonAtIndex:(NSInteger)index BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block that is to be fired when a button is pressed. + + @param index The index of a button already added to the action sheet. + @return A code block, or nil if no block is assigned. + */ +- (void (^)(void))bk_handlerForButtonAtIndex:(NSInteger)index BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired when the action sheet is dismissed with the cancel + button and/or action. + + This property performs the same action as setCancelButtonWithTitle:handler: + but with `title` set to nil. Contrary to setCancelButtonWithTitle:handler:, + you can set this property multiple times and multiple cancel buttons will + not be generated. + */ +@property (nonatomic, copy, setter = bk_setCancelBlock:) void (^bk_cancelBlock)(void) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired before the action sheet will show. */ +@property (nonatomic, copy, setter = bk_setWillShowBlock:) void (^bk_willShowBlock)(UIActionSheet *actionSheet) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired when the action sheet shows. */ +@property (nonatomic, copy, setter = bk_setDidShowBlock:) void (^bk_didShowBlock)(UIActionSheet *actionSheet) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired before the action sheet will dismiss. */ +@property (nonatomic, copy, setter = bk_setWillDismissBlock:) void (^bk_willDismissBlock)(UIActionSheet *actionSheet, NSInteger buttonIndex) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired after the action sheet dismisses. */ +@property (nonatomic, copy, setter = bk_setDidDismissBlock:) void (^bk_didDismissBlock)(UIActionSheet *actionSheet, NSInteger buttonIndex) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +@end diff --git a/YDBlockKit/UIKit/UIActionSheet+BlocksKit.m b/YDBlockKit/UIKit/UIActionSheet+BlocksKit.m new file mode 100644 index 0000000..6cb54e5 --- /dev/null +++ b/YDBlockKit/UIKit/UIActionSheet+BlocksKit.m @@ -0,0 +1,186 @@ +// +// UIActionSheet+BlocksKit.m +// BlocksKit +// + +#import "UIActionSheet+BlocksKit.h" +#import "A2DynamicDelegate.h" +#import "NSObject+A2DynamicDelegate.h" +#import "NSObject+A2BlockDelegate.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +#pragma mark Custom delegate + +@interface A2DynamicUIActionSheetDelegate : A2DynamicDelegate + +@property (nonatomic, assign) BOOL didHandleButtonClick; + +@end + +@implementation A2DynamicUIActionSheetDelegate + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(actionSheet:clickedButtonAtIndex:)]) + [realDelegate actionSheet:actionSheet clickedButtonAtIndex:buttonIndex]; + + void (^handler)(void) = self.handlers[@(buttonIndex)]; + + // Note: On iPad with iOS 8 GM seed, `actionSheet:clickedButtonAtIndex:` always gets called twice if you tap any button other than Cancel; + // In other words, assume you have two buttons: OK and Cancel; if you tap OK, this method will be called once for the OK button and once + // for the Cancel button. This could result in some really obscure bugs, so adding `didHandleButtonClick` property maintains iOS 7 compatibility. + if (handler && self.didHandleButtonClick == NO) { + self.didHandleButtonClick = YES; + + // Presenting view controllers from within action sheet delegate does not work on iPad running iOS 8 GM seed, without delay + dispatch_async(dispatch_get_main_queue(), handler); + } +} + +- (void)willPresentActionSheet:(UIActionSheet *)actionSheet +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(willPresentActionSheet:)]) + [realDelegate willPresentActionSheet:actionSheet]; + + void (^block)(UIActionSheet *) = [self blockImplementationForMethod:_cmd]; + if (block) block(actionSheet); +} + +- (void)didPresentActionSheet:(UIActionSheet *)actionSheet +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(didPresentActionSheet:)]) + [realDelegate didPresentActionSheet:actionSheet]; + + void (^block)(UIActionSheet *) = [self blockImplementationForMethod:_cmd]; + if (block) block(actionSheet); +} + +- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(actionSheet:willDismissWithButtonIndex:)]) + [realDelegate actionSheet:actionSheet willDismissWithButtonIndex:buttonIndex]; + + void (^block)(UIActionSheet *, NSInteger) = [self blockImplementationForMethod:_cmd]; + if (block) block(actionSheet, buttonIndex); +} + +- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(actionSheet:didDismissWithButtonIndex:)]) + [realDelegate actionSheet:actionSheet didDismissWithButtonIndex:buttonIndex]; + + void (^block)(UIActionSheet *, NSInteger) = [self blockImplementationForMethod:_cmd]; + if (block) block(actionSheet, buttonIndex); + self.didHandleButtonClick = NO; +} + +- (void)actionSheetCancel:(UIActionSheet *)actionSheet +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(actionSheetCancel:)]) + [realDelegate actionSheetCancel:actionSheet]; + + void (^block)(void) = actionSheet.bk_cancelBlock; + if (block) block(); +} + +@end + +#pragma mark - Category + +@implementation UIActionSheet (BlocksKit) + +@dynamic bk_willShowBlock, bk_didShowBlock, bk_willDismissBlock, bk_didDismissBlock; + ++ (void)load +{ + @autoreleasepool { + [self bk_registerDynamicDelegate]; + [self bk_linkDelegateMethods:@{ + @"bk_willShowBlock": @"willPresentActionSheet:", + @"bk_didShowBlock": @"didPresentActionSheet:", + @"bk_willDismissBlock": @"actionSheet:willDismissWithButtonIndex:", + @"bk_didDismissBlock": @"actionSheet:didDismissWithButtonIndex:" + }]; + } +} + +#pragma mark Initializers + ++ (instancetype)bk_actionSheetWithTitle:(NSString *)title { + return [[[self class] alloc] bk_initWithTitle:title]; +} + +- (instancetype)bk_initWithTitle:(NSString *)title { + self = [self initWithTitle:title delegate:self.bk_dynamicDelegate cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil]; + if (!self) { return nil; } + self.delegate = self.bk_dynamicDelegate; + return self; +} + +#pragma mark Actions + +- (NSInteger)bk_addButtonWithTitle:(NSString *)title handler:(void (^)(void))block { + NSAssert(title.length, @"A button without a title cannot be added to an action sheet."); + NSInteger index = [self addButtonWithTitle:title]; + [self bk_setHandler:block forButtonAtIndex:index]; + return index; +} + +- (NSInteger)bk_setDestructiveButtonWithTitle:(NSString *)title handler:(void (^)(void))block { + NSInteger index = [self bk_addButtonWithTitle:title handler:block]; + self.destructiveButtonIndex = index; + return index; +} + +- (NSInteger)bk_setCancelButtonWithTitle:(NSString *)title handler:(void (^)(void))block { + NSInteger cancelButtonIndex = self.cancelButtonIndex; + + if ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) && !title.length) + title = NSLocalizedString(@"Cancel", nil); + + if (title.length) + cancelButtonIndex = [self addButtonWithTitle:title]; + + [self bk_setHandler:block forButtonAtIndex:cancelButtonIndex]; + self.cancelButtonIndex = cancelButtonIndex; + return cancelButtonIndex; +} + +#pragma mark Properties + +- (void)bk_setHandler:(void (^)(void))block forButtonAtIndex:(NSInteger)index { + A2DynamicUIActionSheetDelegate *delegate = self.bk_ensuredDynamicDelegate; + + if (block) { + delegate.handlers[@(index)] = [block copy]; + } else { + [delegate.handlers removeObjectForKey:@(index)]; + } +} + +- (void (^)(void))bk_handlerForButtonAtIndex:(NSInteger)index +{ + return [self.bk_dynamicDelegate handlers][@(index)]; +} + +- (void (^)(void))bk_cancelBlock +{ + return [self bk_handlerForButtonAtIndex:self.cancelButtonIndex]; +} + +- (void)bk_setCancelBlock:(void (^)(void))block +{ + [self bk_setHandler:block forButtonAtIndex:self.cancelButtonIndex]; +} + +@end + +#pragma clang diagnostic pop diff --git a/YDBlockKit/UIKit/UIAlertView+BlocksKit.h b/YDBlockKit/UIKit/UIAlertView+BlocksKit.h new file mode 100644 index 0000000..8bc8557 --- /dev/null +++ b/YDBlockKit/UIKit/UIAlertView+BlocksKit.h @@ -0,0 +1,137 @@ +// +// UIAlertView+BlocksKit.h +// BlocksKit +// + +#import +#import "BKDefines.h" + +/** UIAlertView without delegates! + + This set of extensions and convenience classes allows + for an instance of UIAlertView without the implementation + of a delegate. Any time you instantiate a UIAlertView + using the methods here, you must add buttons using + addButtonWithTitle:handler:otherwise nothing will happen. + + A typical invocation will go like this: + UIAlertView *testView = [UIAlertView alertViewWithTitle:@"Application Alert" message:@"This app will explode in 42 seconds."]; + [testView setCancelButtonWithTitle:@"Oh No!" handler:^{ NSLog(@"Boom!"); }]; + [testView show]; + + A more traditional, and more useful, modal dialog looks like so: + UIAlertView *testView = [UIAlertView alertViewWithTitle:@"Very important!" message:@"Do you like chocolate?"]; + [testView addButtonWithTitle:@"Yes" handler:^{ NSLog(@"Yay!"); }]; + [testView addButtonWithTitle:@"No" handler:^{ NSLog(@"We hate you."); }]; + [testView show]; + + Includes code by the following: + + - [Landon Fuller](http://landonf.bikemonkey.org), "Using Blocks". + - [Peter Steinberger](https://github.com/steipete) + - [Zach Waldowski](https://github.com/zwaldowski) + + @warning UIAlertView is only available on a platform with UIKit. + */ +@interface UIAlertView (BlocksKit) + +///----------------------------------- +/// @name Creating alert views +///----------------------------------- + +/** Creates and shows a new alert view with only a title, message, and cancel button. + + @param title The title of the alert view. + @param message The message content of the alert. + @param cancelButtonTitle The title of the cancel button. If both cancelButtonTitle and otherButtonTitles are empty or nil, defaults to a + @param otherButtonTitles Titles of additional buttons to add to the receiver. + @param block A block of code to be fired on the dismissal of the alert view. + + @return The UIAlertView. + */ ++ (instancetype)bk_showAlertViewWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles handler:(void (^)(UIAlertView *alertView, NSInteger buttonIndex))block BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** Creates and returns a new alert view with only a title and cancel button. + + @param title The title of the alert view. + @return A newly created alert view. + */ ++ (instancetype)bk_alertViewWithTitle:(NSString *)title BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** Creates and returns a new alert view with only a title, message, and cancel button. + + @param title The title of the alert view. + @param message The message content of the alert. + @return A newly created alert view. + */ ++ (instancetype)bk_alertViewWithTitle:(NSString *)title message:(NSString *)message BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** Returns a configured alert view with only a title, message, and cancel button. + + @param title The title of the alert view. + @param message The message content of the alert. + @return An instantiated alert view. + */ +- (instancetype)bk_initWithTitle:(NSString *)title message:(NSString *)message BK_INITIALIZER BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +///----------------------------------- +/// @name Adding buttons +///----------------------------------- + +/** Add a new button with an associated code block. + + @param title The text of the button. + @param block A block of code. + */ +- (NSInteger)bk_addButtonWithTitle:(NSString *)title handler:(void (^)(void))block BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** Set the title and trigger of the cancel button. + + @param title The text of the button. + @param block A block of code. + */ +- (NSInteger)bk_setCancelButtonWithTitle:(NSString *)title handler:(void (^)(void))block BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +///----------------------------------- +/// @name Altering actions +///----------------------------------- + +/** Sets the block that is to be fired when a button is pressed. + + @param block A code block, or nil to set no response. + @param index The index of a button already added to the action sheet. + */ +- (void)bk_setHandler:(void (^)(void))block forButtonAtIndex:(NSInteger)index BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block that is to be fired when a button is pressed. + + @param index The index of the button already added to the alert view. + @return A code block, or nil if no block yet assigned. + */ +- (void (^)(void))bk_handlerForButtonAtIndex:(NSInteger)index BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired when the action sheet is dismissed with the cancel + button. + + Contrary to setCancelButtonWithTitle:handler:, you can set this + property multiple times but multiple cancel buttons will + not be generated. + */ +@property (nonatomic, copy, setter = bk_setCancelBlock:) void (^bk_cancelBlock)(void) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired before the alert view will show. */ +@property (nonatomic, copy, setter = bk_setWillShowBlock:) void (^bk_willShowBlock)(UIAlertView *alertView) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired when the alert view shows. */ +@property (nonatomic, copy, setter = bk_setDidShowBlock:) void (^bk_didShowBlock)(UIAlertView *alertView) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired before the alert view will dismiss. */ +@property (nonatomic, copy, setter = bk_setWillDismissBlock:) void (^bk_willDismissBlock)(UIAlertView *alertView, NSInteger buttonIndex) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired after the alert view dismisses. */ +@property (nonatomic, copy, setter = bk_setDidDismissBlock:) void (^bk_didDismissBlock)(UIAlertView *alertView, NSInteger buttonIndex) BK_ALERT_CONTROLLER_DEPRECATED(2_0); + +/** The block to be fired to determine whether the first non-cancel should be enabled */ +@property (nonatomic, copy, setter = bk_SetShouldEnableFirstOtherButtonBlock:) BOOL (^bk_shouldEnableFirstOtherButtonBlock)(UIAlertView *alertView) BK_ALERT_CONTROLLER_DEPRECATED(5_0); + +@end diff --git a/YDBlockKit/UIKit/UIAlertView+BlocksKit.m b/YDBlockKit/UIKit/UIAlertView+BlocksKit.m new file mode 100644 index 0000000..0262c36 --- /dev/null +++ b/YDBlockKit/UIKit/UIAlertView+BlocksKit.m @@ -0,0 +1,230 @@ +// +// UIAlertView+BlocksKit.m +// BlocksKit +// + +#import "UIAlertView+BlocksKit.h" +#import "A2DynamicDelegate.h" +#import "NSObject+A2BlockDelegate.h" +#import "NSObject+A2DynamicDelegate.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +#pragma mark Delegate + +@interface A2DynamicUIAlertViewDelegate : A2DynamicDelegate + +@end + +@implementation A2DynamicUIAlertViewDelegate + +- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView +{ + BOOL should = YES; + + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(alertViewShouldEnableFirstOtherButton:)]) + should &= [realDelegate alertViewShouldEnableFirstOtherButton:alertView]; + + BOOL (^block)(UIAlertView *) = [self blockImplementationForMethod:_cmd]; + if (block) + should &= block(alertView); + + return should; +} + +- (void)alertViewCancel:(UIAlertView *)alertView +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(alertViewCancel:)]) + [realDelegate alertViewCancel:alertView]; + + id key = @(alertView.cancelButtonIndex); + void (^cancelBlock)(void) = (self.handlers)[key]; + if (cancelBlock) + cancelBlock(); +} + +- (void)willPresentAlertView:(UIAlertView *)alertView +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(willPresentAlertView:)]) + [realDelegate willPresentAlertView:alertView]; + + void (^block)(UIAlertView *) = [self blockImplementationForMethod:_cmd]; + if (block) + block(alertView); +} + +- (void)didPresentAlertView:(UIAlertView *)alertView +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(didPresentAlertView:)]) + [realDelegate didPresentAlertView:alertView]; + + void (^block)(UIAlertView *) = [self blockImplementationForMethod:_cmd]; + if (block) + block(alertView); +} + +- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(alertView:willDismissWithButtonIndex:)]) + [realDelegate alertView:alertView willDismissWithButtonIndex:buttonIndex]; + + void (^block)(UIAlertView *, NSInteger) = [self blockImplementationForMethod:_cmd]; + if (block) + block(alertView, buttonIndex); +} + +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(alertView:didDismissWithButtonIndex:)]) + [realDelegate alertView:alertView didDismissWithButtonIndex:buttonIndex]; + + void (^block)(UIAlertView *, NSInteger) = [self blockImplementationForMethod:_cmd]; + if (block) + block(alertView, buttonIndex); +} + +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(alertView:clickedButtonAtIndex:)]) + [realDelegate alertView:alertView clickedButtonAtIndex:buttonIndex]; + + void (^block)(UIAlertView *, NSInteger) = [self blockImplementationForMethod:_cmd]; + if (block) + block(alertView, buttonIndex); + + id key = @(buttonIndex); + void (^buttonBlock)(void) = (self.handlers)[key]; + if (buttonBlock) + buttonBlock(); +} + +@end + +#pragma mark - Category + +@implementation UIAlertView (BlocksKit) + +@dynamic bk_willShowBlock, bk_didShowBlock, bk_willDismissBlock, bk_didDismissBlock, bk_shouldEnableFirstOtherButtonBlock; + ++ (void)load +{ + @autoreleasepool { + [self bk_registerDynamicDelegate]; + [self bk_linkDelegateMethods:@{ + @"bk_willShowBlock": @"willPresentAlertView:", + @"bk_didShowBlock": @"didPresentAlertView:", + @"bk_willDismissBlock": @"alertView:willDismissWithButtonIndex:", + @"bk_didDismissBlock": @"alertView:didDismissWithButtonIndex:", + @"bk_shouldEnableFirstOtherButtonBlock": @"alertViewShouldEnableFirstOtherButton:" + }]; + } +} + +#pragma mark Convenience + ++ (instancetype)bk_showAlertViewWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSArray *)otherButtonTitles handler:(void (^)(UIAlertView *alertView, NSInteger buttonIndex))block +{ + // If no buttons were specified, cancel button becomes "Dismiss" + if (!cancelButtonTitle.length && !otherButtonTitles.count) + cancelButtonTitle = NSLocalizedString(@"Dismiss", nil); + + UIAlertView *alertView = [[[self class] alloc] initWithTitle:title message:message delegate:self.bk_dynamicDelegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil]; + + // Set other buttons + [otherButtonTitles enumerateObjectsUsingBlock:^(NSString *button, NSUInteger idx, BOOL *stop) { + [alertView addButtonWithTitle:button]; + }]; + + // Set `didDismissBlock` + if (block) alertView.bk_didDismissBlock = block; + + // Show alert view + [alertView show]; + + return alertView; +} + +#pragma mark Initializers + ++ (instancetype)bk_alertViewWithTitle:(NSString *)title +{ + return [self bk_alertViewWithTitle:title message:nil]; +} + ++ (instancetype)bk_alertViewWithTitle:(NSString *)title message:(NSString *)message +{ + return [[[self class] alloc] bk_initWithTitle:title message:message]; +} + +- (instancetype)bk_initWithTitle:(NSString *)title message:(NSString *)message +{ + self = [self initWithTitle:title message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil]; + if (!self) return nil; + + self.delegate = self.bk_dynamicDelegate; + + return self; +} + +#pragma Actions + +- (NSInteger)bk_addButtonWithTitle:(NSString *)title handler:(void (^)(void))block +{ + NSAssert(title.length, @"A button without a title cannot be added to the alert view."); + NSInteger index = [self addButtonWithTitle:title]; + [self bk_setHandler:block forButtonAtIndex:index]; + return index; +} + +- (NSInteger)bk_setCancelButtonWithTitle:(NSString *)title handler:(void (^)(void))block +{ + if (!title.length) + title = NSLocalizedString(@"Cancel", nil); + NSInteger cancelButtonIndex = [self addButtonWithTitle:title]; + self.cancelButtonIndex = cancelButtonIndex; + [self bk_setHandler:block forButtonAtIndex:cancelButtonIndex]; + return cancelButtonIndex; +} + +#pragma mark Properties + +- (void)bk_setHandler:(void (^)(void))block forButtonAtIndex:(NSInteger)index +{ + id key = @(index); + if (block) + [self.bk_dynamicDelegate handlers][key] = [block copy]; + else + [[self.bk_dynamicDelegate handlers] removeObjectForKey:key]; +} + +- (void (^)(void))bk_handlerForButtonAtIndex:(NSInteger)index +{ + return [self.bk_dynamicDelegate handlers][@(index)]; +} + +- (void (^)(void))bk_cancelBlock +{ + return [self bk_handlerForButtonAtIndex:self.cancelButtonIndex]; +} + +- (void)bk_setCancelBlock:(void (^)(void))block +{ + if (block && self.cancelButtonIndex == -1) { + [self bk_setCancelButtonWithTitle:nil handler:block]; + return; + } + + [self bk_setHandler:block forButtonAtIndex:self.cancelButtonIndex]; +} + +@end + +#pragma clang diagnostic pop diff --git a/YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.h b/YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.h new file mode 100644 index 0000000..2718f3d --- /dev/null +++ b/YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.h @@ -0,0 +1,66 @@ +// +// UIBarButtonItem+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block event initialization for UIBarButtonItem. + + This set of extensions has near-drop-in replacements + for the standard set of UIBarButton item initializations, + using a block handler instead of a target/selector. + + Includes code by the following: + + - [Kevin O'Neill](https://github.com/kevinoneill) + - [Zach Waldowski](https://github.com/zwaldowski) + + @warning UIBarButtonItem is only available on a platform with UIKit. + */ +@interface UIBarButtonItem (BlocksKit) + +/** Creates and returns a configured item containing the specified system item. + + @return Newly initialized item with the specified properties. + @param systemItem The system item to use as the item representation. One of the constants defined in UIBarButtonSystemItem. + @param action The block that gets fired on the button press. + */ +- (instancetype)bk_initWithBarButtonSystemItem:(UIBarButtonSystemItem)systemItem handler:(void (^)(id sender))action BK_INITIALIZER; + +/** Creates and returns a configured item using the specified image and style. + + @return Newly initialized item with the specified properties. + @param image The item’s image. If nil an image is not displayed. + If this image is too large to fit on the bar, it is scaled to fit + The size of a toolbar and navigation bar image is 20 x 20 points. + @param style The style of the item. One of the constants defined in UIBarButtonItemStyle. + @param action The block that gets fired on the button press. + */ +- (instancetype)bk_initWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style handler:(void (^)(id sender))action BK_INITIALIZER; + +/** Creates and returns a configured item using the specified image and style. + + @return Newly initialized item with the specified properties. + @param image The item’s image. If nil an image is not displayed. + @param landscapeImagePhone The image to be used for the item in landscape bars in the UIUserInterfaceIdiomPhone idiom. + @param style The style of the item. One of the constants defined in UIBarButtonItemStyle. + @param action The block that gets fired on the button press. + */ +- (instancetype)bk_initWithImage:(UIImage *)image landscapeImagePhone:(UIImage *)landscapeImagePhone style:(UIBarButtonItemStyle)style handler:(void (^)(id sender))action BK_INITIALIZER; + +/** Creates and returns a configured item using the specified text and style. + + @return Newly initialized item with the specified properties. + @param title The text displayed on the button item. + @param style The style of the item. One of the constants defined in UIBarButtonItemStyle. + @param action The block that gets fired on the button press. + */ +- (instancetype)bk_initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style handler:(void (^)(id sender))action BK_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.m b/YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.m new file mode 100644 index 0000000..3f7e58e --- /dev/null +++ b/YDBlockKit/UIKit/UIBarButtonItem+BlocksKit.m @@ -0,0 +1,65 @@ +// +// UIBarButtonItem+BlocksKit.m +// BlocksKit +// + +#import "UIBarButtonItem+BlocksKit.h" +@import ObjectiveC.runtime; + +static const void *BKBarButtonItemBlockKey = &BKBarButtonItemBlockKey; + +@interface UIBarButtonItem (BlocksKitPrivate) + +- (void)bk_handleAction:(UIBarButtonItem *)sender; + +@end + +@implementation UIBarButtonItem (BlocksKit) + +- (instancetype)bk_initWithBarButtonSystemItem:(UIBarButtonSystemItem)systemItem handler:(void (^)(id sender))action +{ + self = [self initWithBarButtonSystemItem:systemItem target:self action:@selector(bk_handleAction:)]; + if (!self) return nil; + + objc_setAssociatedObject(self, BKBarButtonItemBlockKey, action, OBJC_ASSOCIATION_COPY_NONATOMIC); + + return self; +} + +- (instancetype)bk_initWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style handler:(void (^)(id sender))action +{ + self = [self initWithImage:image style:style target:self action:@selector(bk_handleAction:)]; + if (!self) return nil; + + objc_setAssociatedObject(self, BKBarButtonItemBlockKey, action, OBJC_ASSOCIATION_COPY_NONATOMIC); + + return self; +} + +- (instancetype)bk_initWithImage:(UIImage *)image landscapeImagePhone:(UIImage *)landscapeImagePhone style:(UIBarButtonItemStyle)style handler:(void (^)(id sender))action +{ + self = [self initWithImage:image landscapeImagePhone:landscapeImagePhone style:style target:self action:@selector(bk_handleAction:)]; + if (!self) return nil; + + objc_setAssociatedObject(self, BKBarButtonItemBlockKey, action, OBJC_ASSOCIATION_COPY_NONATOMIC); + + return self; +} + +- (instancetype)bk_initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style handler:(void (^)(id sender))action +{ + self = [self initWithTitle:title style:style target:self action:@selector(bk_handleAction:)]; + if (!self) return nil; + + objc_setAssociatedObject(self, BKBarButtonItemBlockKey, action, OBJC_ASSOCIATION_COPY_NONATOMIC); + + return self; +} + +- (void)bk_handleAction:(UIBarButtonItem *)sender +{ + void (^block)(id) = objc_getAssociatedObject(self, BKBarButtonItemBlockKey); + if (block) block(sender); +} + +@end diff --git a/YDBlockKit/UIKit/UIControl+BlocksKit.h b/YDBlockKit/UIKit/UIControl+BlocksKit.h new file mode 100644 index 0000000..05e3c19 --- /dev/null +++ b/YDBlockKit/UIKit/UIControl+BlocksKit.h @@ -0,0 +1,54 @@ +// +// UIControl+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block control event handling for UIControl. + + Includes code by the following: + + - [Kevin O'Neill](https://github.com/kevinoneill) + - [Zach Waldowski](https://github.com/zwaldowski) + + @warning UIControl is only available on a platform with UIKit. + */ +@interface UIControl (BlocksKit) + +///----------------------------------- +/// @name Block event handling +///----------------------------------- + +/** Adds a block for a particular event to an internal dispatch table. + + @param handler A block representing an action message, with an argument for the sender. + @param controlEvents A bitmask specifying the control events for which the action message is sent. + @see removeEventHandlersForControlEvents: + */ +- (void)bk_addEventHandler:(void (^)(id sender))handler forControlEvents:(UIControlEvents)controlEvents; + +/** Removes all control event blocks associated with the given mask of control + * events. + * + * Do note that, like @c UIControl, this method will not decompose control + * events; thus, only a handler matching an exact given bitmask will be removed. + * + * @param controlEvents A bitmask specifying the control events for which the block will be removed. + * @see addEventHandler:forControlEvents: + */ +- (void)bk_removeEventHandlersForControlEvents:(UIControlEvents)controlEvents; + +/** Checks to see if the control has any blocks for a particular event combination. + @param controlEvents A bitmask specifying the control events for which to check for blocks. + @see addEventHandler:forControlEvents: + @return Returns YES if there are blocks for these control events, NO otherwise. + */ +- (BOOL)bk_hasEventHandlersForControlEvents:(UIControlEvents)controlEvents; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/UIKit/UIControl+BlocksKit.m b/YDBlockKit/UIKit/UIControl+BlocksKit.m new file mode 100644 index 0000000..674356f --- /dev/null +++ b/YDBlockKit/UIKit/UIControl+BlocksKit.m @@ -0,0 +1,111 @@ +// +// UIControl+BlocksKit.m +// BlocksKit +// + +#import "UIControl+BlocksKit.h" +@import ObjectiveC.runtime; + +static const void *BKControlHandlersKey = &BKControlHandlersKey; + +#pragma mark Private + +@interface BKControlWrapper : NSObject + +- (id)initWithHandler:(void (^)(id sender))handler forControlEvents:(UIControlEvents)controlEvents; + +@property (nonatomic) UIControlEvents controlEvents; +@property (nonatomic, copy) void (^handler)(id sender); + +@end + +@implementation BKControlWrapper + +- (id)initWithHandler:(void (^)(id sender))handler forControlEvents:(UIControlEvents)controlEvents +{ + self = [super init]; + if (!self) return nil; + + self.handler = handler; + self.controlEvents = controlEvents; + + return self; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return [[BKControlWrapper alloc] initWithHandler:self.handler forControlEvents:self.controlEvents]; +} + +- (void)invoke:(id)sender +{ + self.handler(sender); +} + +@end + +#pragma mark Category + +@implementation UIControl (BlocksKit) + +- (void)bk_addEventHandler:(void (^)(id sender))handler forControlEvents:(UIControlEvents)controlEvents +{ + NSParameterAssert(handler); + + NSMutableDictionary *events = objc_getAssociatedObject(self, BKControlHandlersKey); + if (!events) { + events = [NSMutableDictionary dictionary]; + objc_setAssociatedObject(self, BKControlHandlersKey, events, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + NSNumber *key = @(controlEvents); + NSMutableSet *handlers = events[key]; + if (!handlers) { + handlers = [NSMutableSet set]; + events[key] = handlers; + } + + BKControlWrapper *target = [[BKControlWrapper alloc] initWithHandler:handler forControlEvents:controlEvents]; + [handlers addObject:target]; + [self addTarget:target action:@selector(invoke:) forControlEvents:controlEvents]; +} + +- (void)bk_removeEventHandlersForControlEvents:(UIControlEvents)controlEvents +{ + NSMutableDictionary *events = objc_getAssociatedObject(self, BKControlHandlersKey); + if (!events) { + events = [NSMutableDictionary dictionary]; + objc_setAssociatedObject(self, BKControlHandlersKey, events, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + NSNumber *key = @(controlEvents); + NSSet *handlers = events[key]; + + if (!handlers) + return; + + [handlers enumerateObjectsUsingBlock:^(id sender, BOOL *stop) { + [self removeTarget:sender action:NULL forControlEvents:controlEvents]; + }]; + + [events removeObjectForKey:key]; +} + +- (BOOL)bk_hasEventHandlersForControlEvents:(UIControlEvents)controlEvents +{ + NSMutableDictionary *events = objc_getAssociatedObject(self, BKControlHandlersKey); + if (!events) { + events = [NSMutableDictionary dictionary]; + objc_setAssociatedObject(self, BKControlHandlersKey, events, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + NSNumber *key = @(controlEvents); + NSSet *handlers = events[key]; + + if (!handlers) + return NO; + + return !!handlers.count; +} + +@end diff --git a/YDBlockKit/UIKit/UIGestureRecognizer+BlocksKit.h b/YDBlockKit/UIKit/UIGestureRecognizer+BlocksKit.h new file mode 100644 index 0000000..2fb9fcf --- /dev/null +++ b/YDBlockKit/UIKit/UIGestureRecognizer+BlocksKit.h @@ -0,0 +1,109 @@ +// +// UIGestureRecognizer+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block functionality for UIGestureRecognizer. + + Use of the delay property is pretty straightforward, although + cancellation might be a little harder to swallow. An example + follows: + UITapGestureRecognizer *singleTap = [UITapGestureRecognizer recognizerWithHandler:^(id sender) { + NSLog(@"Single tap."); + } delay:0.18]; + [self addGestureRecognizer:singleTap]; + + UITapGestureRecognizer *doubleTap = [UITapGestureRecognizer recognizerWithHandler:^(id sender) { + [singleTap cancel]; + NSLog(@"Double tap."); + }]; + doubleTap.numberOfTapsRequired = 2; + [self addGestureRecognizer:doubleTap]; + + Believe it or not, the above code is fully memory-safe and efficient. Eagle-eyed coders + will notice that this setup emulates UIGestureRecognizer's requireGestureRecognizerToFail:, + and, yes, it totally apes it. Not only is this setup much faster on the user's end of + things, it is more flexible and allows for much more complicated setups. + + Includes code by the following: + + - [Kevin O'Neill](https://github.com/kevinoneill) + - [Zach Waldowski](https://github.com/zwaldowski) + + @warning UIGestureRecognizer is only available on a platform with UIKit. + + @warning It is not recommended to use the Apple-supplied locationInView and state + methods on a *delayed* block-backed gesture recognizer, as these properties are + likely to have been cleared by the time by the block fires. It is instead recommended + to use the arguments provided to the block. + */ + +@interface UIGestureRecognizer (BlocksKit) + +/** An autoreleased gesture recognizer that will, on firing, call + the given block asynchronously after a number of seconds. + + @return An autoreleased instance of a concrete UIGestureRecognizer subclass, or `nil`. + @param block The block which handles an executed gesture. + @param delay A number of seconds after which the block will fire. + */ ++ (instancetype)bk_recognizerWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block delay:(NSTimeInterval)delay; + +/** Initializes an allocated gesture recognizer that will call the given block + after a given delay. + + An alternative to the designated initializer. + + @return An initialized instance of a concrete UIGestureRecognizer subclass or `nil`. + @param block The block which handles an executed gesture. + @param delay A number of seconds after which the block will fire. + */ +- (instancetype)bk_initWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block delay:(NSTimeInterval)delay BK_INITIALIZER; + +/** An autoreleased gesture recognizer that will call the given block. + + For convenience and compatibility reasons, this method is indentical + to using recognizerWithHandler:delay: with a delay of 0.0. + + @return An initialized and autoreleased instance of a concrete UIGestureRecognizer + subclass, or `nil`. + @param block The block which handles an executed gesture. + */ ++ (instancetype)bk_recognizerWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block; + +/** Initializes an allocated gesture recognizer that will call the given block. + + This method is indentical to calling initWithHandler:delay: with a delay of 0.0. + + @return An initialized instance of a concrete UIGestureRecognizer subclass or `nil`. + @param block The block which handles an executed gesture. + */ +- (instancetype)bk_initWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block BK_INITIALIZER; + +/** Allows the block that will be fired by the gesture recognizer + to be modified after the fact. + */ +@property (nonatomic, copy, setter = bk_setHandler:, nullable) void (^bk_handler)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location); + +/** Allows the length of the delay after which the gesture + recognizer will be fired to modify. */ +@property (nonatomic, setter = bk_setHandlerDelay:) NSTimeInterval bk_handlerDelay; + +/** If the recognizer happens to be fired, calling this method + will stop it from firing, but only if a delay is set. + + @warning This method is not for arbitrarily canceling the + firing of a recognizer, but will only function for a block + handler *after the recognizer has already been fired*. Be + sure to make your delay times accomodate this likelihood. + */ +- (void)bk_cancel; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/UIKit/UIGestureRecognizer+BlocksKit.m b/YDBlockKit/UIKit/UIGestureRecognizer+BlocksKit.m new file mode 100644 index 0000000..73f344a --- /dev/null +++ b/YDBlockKit/UIKit/UIGestureRecognizer+BlocksKit.m @@ -0,0 +1,103 @@ +// +// UIGestureRecognizer+BlocksKit.m +// BlocksKit +// + +#import "UIGestureRecognizer+BlocksKit.h" +#import "NSObject+BKBlockExecution.h" +@import ObjectiveC.runtime; + +static const void *BKGestureRecognizerBlockKey = &BKGestureRecognizerBlockKey; +static const void *BKGestureRecognizerDelayKey = &BKGestureRecognizerDelayKey; +static const void *BKGestureRecognizerShouldHandleActionKey = &BKGestureRecognizerShouldHandleActionKey; + +@interface UIGestureRecognizer (BlocksKitInternal) + +@property (nonatomic, setter = bk_setShouldHandleAction:) BOOL bk_shouldHandleAction; + +- (void)bk_handleAction:(UIGestureRecognizer *)recognizer; + +@end + +@implementation UIGestureRecognizer (BlocksKit) + ++ (instancetype)bk_recognizerWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block delay:(NSTimeInterval)delay +{ + return [[[self class] alloc] bk_initWithHandler:block delay:delay]; +} + +- (instancetype)bk_initWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block delay:(NSTimeInterval)delay +{ + self = [self initWithTarget:self action:@selector(bk_handleAction:)]; + if (!self) return nil; + + self.bk_handler = block; + self.bk_handlerDelay = delay; + + return self; +} + ++ (instancetype)bk_recognizerWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block +{ + return [self bk_recognizerWithHandler:block delay:0.0]; +} + +- (instancetype)bk_initWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block +{ + return (self = [self bk_initWithHandler:block delay:0.0]); +} + +- (void)bk_handleAction:(UIGestureRecognizer *)recognizer +{ + void (^handler)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) = recognizer.bk_handler; + if (!handler) return; + + NSTimeInterval delay = self.bk_handlerDelay; + CGPoint location = [self locationInView:self.view]; + void (^block)(void) = ^{ + if (!self.bk_shouldHandleAction) return; + handler(self, self.state, location); + }; + + self.bk_shouldHandleAction = YES; + + [NSObject bk_performAfterDelay:delay usingBlock:block]; +} + +- (void)bk_setHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))handler +{ + objc_setAssociatedObject(self, BKGestureRecognizerBlockKey, handler, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))bk_handler +{ + return objc_getAssociatedObject(self, BKGestureRecognizerBlockKey); +} + +- (void)bk_setHandlerDelay:(NSTimeInterval)delay +{ + NSNumber *delayValue = delay ? @(delay) : nil; + objc_setAssociatedObject(self, BKGestureRecognizerDelayKey, delayValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSTimeInterval)bk_handlerDelay +{ + return [objc_getAssociatedObject(self, BKGestureRecognizerDelayKey) doubleValue]; +} + +- (void)bk_setShouldHandleAction:(BOOL)flag +{ + objc_setAssociatedObject(self, BKGestureRecognizerShouldHandleActionKey, @(flag), OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (BOOL)bk_shouldHandleAction +{ + return [objc_getAssociatedObject(self, BKGestureRecognizerShouldHandleActionKey) boolValue]; +} + +- (void)bk_cancel +{ + self.bk_shouldHandleAction = NO; +} + +@end diff --git a/YDBlockKit/UIKit/UIImage+BlocksKit.h b/YDBlockKit/UIKit/UIImage+BlocksKit.h new file mode 100644 index 0000000..eda81f1 --- /dev/null +++ b/YDBlockKit/UIKit/UIImage+BlocksKit.h @@ -0,0 +1,26 @@ +// +// UIImage+Blockskit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Camera Roll export without selectors. + + Includes code by the following: + + - [Yusuke Murata](https://github.com/muratayusuke) + + @warning UIImage is only available on a platform with UIKit. + */ + +// Adds a photo to the saved photos album. +UIKIT_EXTERN void BKImageWriteToSavedPhotosAlbum(UIImage *image, void(^completionBlock)(NSError *)); + +// Adds a video to the saved photos album. +UIKIT_EXTERN void BKSaveVideoAtURLToSavedPhotosAlbum(NSURL *videoURL, void(^completionBlock)(NSError *)); + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/UIKit/UIImage+BlocksKit.m b/YDBlockKit/UIKit/UIImage+BlocksKit.m new file mode 100644 index 0000000..2e2210f --- /dev/null +++ b/YDBlockKit/UIKit/UIImage+BlocksKit.m @@ -0,0 +1,36 @@ +// +// UIImage+Blockskit.m +// BlocksKit +// + +#import "UIImage+BlocksKit.h" + +@implementation UIImage (BKPhotoLibraryExport) + ++ (void)bk_image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo +{ + void(^block)(NSError *) = (__bridge_transfer id)contextInfo; + if (!block) { return; } + block(error); +} + ++ (void)bk_videoAtPath:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo; +{ + void(^block)(NSError *) = (__bridge_transfer id)contextInfo; + if (!block) { return; } + block(error); +} + +@end + +void BKImageWriteToSavedPhotosAlbum(UIImage *image, void(^completionBlock)(NSError *)) +{ + void *blockAsContext = (__bridge_retained void *)[completionBlock copy]; + UIImageWriteToSavedPhotosAlbum(image, UIImage.class, @selector(bk_image:didFinishSavingWithError:contextInfo:), blockAsContext); +} + +void BKSaveVideoAtURLToSavedPhotosAlbum(NSURL *videoURL, void(^completionBlock)(NSError *)) +{ + void *blockAsContext = (__bridge_retained void *)[completionBlock copy]; + UISaveVideoAtPathToSavedPhotosAlbum(videoURL.path, UIImage.class, @selector(bk_videoAtPath:didFinishSavingWithError:contextInfo:), blockAsContext); +} diff --git a/YDBlockKit/UIKit/UIImagePickerController+BlocksKit.h b/YDBlockKit/UIKit/UIImagePickerController+BlocksKit.h new file mode 100644 index 0000000..8539457 --- /dev/null +++ b/YDBlockKit/UIKit/UIImagePickerController+BlocksKit.h @@ -0,0 +1,35 @@ +// +// UIImagePickerController+BlocksKit.h +// BlocksKit +// +// Contributed by Yas Kuraishi. +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** UIImagePickerController with block callback. + + Created by [Yas Kuraishi](https://github.com/YasKuraishi) and contributed to + BlocksKit. + + @warning UIImagePickerController is only available on a platform with + UIKit. +*/ +@interface UIImagePickerController (BlocksKit) + +/** + * The block that fires after the receiver finished picking up an image + */ +@property (nonatomic, copy, nullable) void(^bk_didFinishPickingMediaBlock)(UIImagePickerController *, NSDictionary *); + +/** + * The block that fires after the user cancels out of picker + */ +@property (nonatomic, copy, nullable) void(^bk_didCancelBlock)(UIImagePickerController *); + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/UIKit/UIImagePickerController+BlocksKit.m b/YDBlockKit/UIKit/UIImagePickerController+BlocksKit.m new file mode 100644 index 0000000..125081d --- /dev/null +++ b/YDBlockKit/UIKit/UIImagePickerController+BlocksKit.m @@ -0,0 +1,59 @@ +// +// UIImagePickerController+BlocksKit.m +// BlocksKit +// + +#import "UIImagePickerController+BlocksKit.h" +#import "A2DynamicDelegate.h" +#import "NSObject+A2DynamicDelegate.h" +#import "NSObject+A2BlockDelegate.h" + +#pragma mark Custom delegate + +@interface A2DynamicUIImagePickerControllerDelegate : A2DynamicDelegate + +@end + +@implementation A2DynamicUIImagePickerControllerDelegate + +- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingMediaWithInfo:)]) + [realDelegate imagePickerController:picker didFinishPickingMediaWithInfo:info]; + + void (^block)(UIImagePickerController *, NSDictionary *) = [self blockImplementationForMethod:_cmd]; + if (block) block(picker, info); +} + +- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(imagePickerControllerDidCancel:)]) + [realDelegate imagePickerControllerDidCancel:picker]; + + void (^block)(UIImagePickerController *) = [self blockImplementationForMethod:_cmd]; + if (block) block(picker); +} +- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(navigationController:willShowViewController:animated:)]) + [realDelegate navigationController:navigationController willShowViewController:viewController animated:animated]; +} +@end + +#pragma mark Category + +@implementation UIImagePickerController (BlocksKit) + +@dynamic bk_didFinishPickingMediaBlock; +@dynamic bk_didCancelBlock; + ++ (void)load +{ + @autoreleasepool { +// [self bk_registerDynamicDelegate]; +// [self bk_linkDelegateMethods:@{ @"bk_didFinishPickingMediaBlock": @"imagePickerController:didFinishPickingMediaWithInfo:", +// @"bk_didCancelBlock": @"imagePickerControllerDidCancel:" }]; + } +} + +@end diff --git a/YDBlockKit/UIKit/UIPopoverController+BlocksKit.h b/YDBlockKit/UIKit/UIPopoverController+BlocksKit.h new file mode 100644 index 0000000..838a525 --- /dev/null +++ b/YDBlockKit/UIKit/UIPopoverController+BlocksKit.h @@ -0,0 +1,27 @@ +// +// UIPopoverController+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block functionality for UIPopoverController. + + Created by [Alexsander Akers](https://github.com/a2) and contributed to BlocksKit. + + @warning UIPopovercontroller is only available on a platform with UIKit. + */ +@interface UIPopoverController (BlocksKit) + +/** The block to be called when the popover controller will dismiss the popover. Return NO to prevent the dismissal of the view. */ +@property (nonatomic, copy, setter = bk_setShouldDismissBlock:, nullable) BOOL (^bk_shouldDismissBlock)(UIPopoverController *popoverController); + +/** The block to be called when the user has taken action to dismiss the popover. This is not called when -dismissPopoverAnimated: is called directly. */ +@property (nonatomic, copy, setter = bk_setDidDismissBlock:, nullable) void (^bk_didDismissBlock)(UIPopoverController *popoverController); + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/UIKit/UIPopoverController+BlocksKit.m b/YDBlockKit/UIKit/UIPopoverController+BlocksKit.m new file mode 100644 index 0000000..7ed35cf --- /dev/null +++ b/YDBlockKit/UIKit/UIPopoverController+BlocksKit.m @@ -0,0 +1,59 @@ +// +// UIPopoverController+BlocksKit.m +// BlocksKit +// + +#import "UIPopoverController+BlocksKit.h" +#import "A2DynamicDelegate.h" +#import "NSObject+A2DynamicDelegate.h" +#import "NSObject+A2BlockDelegate.h" + +#pragma mark - Delegate + +@interface A2DynamicUIPopoverControllerDelegate : A2DynamicDelegate + +@end + +@implementation A2DynamicUIPopoverControllerDelegate + +- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController +{ + BOOL should = YES; + + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(popoverControllerShouldDismissPopover:)]) + should &= [realDelegate popoverControllerShouldDismissPopover:popoverController]; + + BOOL (^block)(UIPopoverController *) = [self blockImplementationForMethod:_cmd]; + if (block) should &= block(popoverController); + + return should; +} + +- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(popoverControllerDidDismissPopover:)]) + [realDelegate popoverControllerDidDismissPopover:popoverController]; + + void (^block)(UIPopoverController *) = [self blockImplementationForMethod:_cmd]; + if (block) block(popoverController); +} + +@end + +#pragma mark - Category + +@implementation UIPopoverController (BlocksKit) + +@dynamic bk_didDismissBlock, bk_shouldDismissBlock; + ++ (void)load +{ + @autoreleasepool { + [self bk_registerDynamicDelegate]; + [self bk_linkDelegateMethods:@{ @"bk_didDismissBlock": @"popoverControllerDidDismissPopover:", @"bk_shouldDismissBlock": @"popoverControllerShouldDismissPopover:" }]; + } +} + +@end diff --git a/YDBlockKit/UIKit/UITextField+BlocksKit.h b/YDBlockKit/UIKit/UITextField+BlocksKit.h new file mode 100644 index 0000000..b5d0cb1 --- /dev/null +++ b/YDBlockKit/UIKit/UITextField+BlocksKit.h @@ -0,0 +1,69 @@ +// +// UITextField+BlocksKit.h +// BlocksKit +// +// Contributed by Samuel E. Giddins. +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Block callbacks for UITextField. + + @warning UITextField is only available on a platform with UIKit. + + Created by [Samuel E. Giddins](https://github.com/segiddins) and + contributed to BlocksKit. + */ +@interface UITextField (BlocksKit) + +/** + * The block that fires before the receiver begins editing + * + * The return value indicates whether the receiver should begin editing + */ +@property (nonatomic, copy, nullable) BOOL(^bk_shouldBeginEditingBlock)(UITextField *textField); + +/** + * The block that fires after the receiver begins editing + */ +@property (nonatomic, copy, nullable) void(^bk_didBeginEditingBlock)(UITextField *textField); + +/** + * The block that fires before the receiver ends editing + * + * The return value indicates whether the receiver should end editing + */ +@property (nonatomic, copy, nullable) BOOL(^bk_shouldEndEditingBlock)(UITextField *textField); + +/** + * The block that fires after the receiver ends editing + */ +@property (nonatomic, copy, nullable) void(^bk_didEndEditingBlock)(UITextField *textField); + +/** + * The block that fires when the receiver's text will change + * + * The return value indicates whether the receiver should replace the characters in the given range with the replacement string + */ +@property (nonatomic, copy, nullable) BOOL(^bk_shouldChangeCharactersInRangeWithReplacementStringBlock)(UITextField *textField, NSRange range, NSString *string); + +/** + * The block that fires when the receiver's clear button is pressed + * + * The return value indicates whether the receiver should clear its contents + */ +@property (nonatomic, copy, nullable) BOOL(^bk_shouldClearBlock)(UITextField *textField); + +/** + * The block that fires when the keyboard's return button is pressed and the receiver is the first responder + * + * The return value indicates whether the receiver should return + */ +@property (nonatomic, copy, nullable) BOOL(^bk_shouldReturnBlock)(UITextField *textField); + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/UIKit/UITextField+BlocksKit.m b/YDBlockKit/UIKit/UITextField+BlocksKit.m new file mode 100644 index 0000000..ea936cd --- /dev/null +++ b/YDBlockKit/UIKit/UITextField+BlocksKit.m @@ -0,0 +1,120 @@ +// +// UITextField+BlocksKit.m +// BlocksKit +// + +#import "UITextField+BlocksKit.h" +#import "A2DynamicDelegate.h" +#import "NSObject+A2DynamicDelegate.h" +#import "NSObject+A2BlockDelegate.h" + +#pragma mark Delegate + +@interface A2DynamicUITextFieldDelegate : A2DynamicDelegate + +@end + +@implementation A2DynamicUITextFieldDelegate + +- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) + ret = [realDelegate textFieldShouldBeginEditing:textField]; + BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textField); + return ret; +} + +- (void)textFieldDidBeginEditing:(UITextField *)textField +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) + [realDelegate textFieldDidBeginEditing:textField]; + void (^block)(UITextField *) = [self blockImplementationForMethod:_cmd]; + if (block) + block(textField); +} + +- (BOOL)textFieldShouldEndEditing:(UITextField *)textField +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)]) + ret = [realDelegate textFieldShouldEndEditing:textField]; + BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textField); + return ret; +} + +- (void)textFieldDidEndEditing:(UITextField *)textField +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldDidEndEditing:)]) + [realDelegate textFieldDidEndEditing:textField]; + void (^block)(UITextField *) = [self blockImplementationForMethod:_cmd]; + if (block) + block(textField); +} + +- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) + ret = [realDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string]; + BOOL (^block)(UITextField *, NSRange, NSString *) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textField, range, string); + return ret; +} + +- (BOOL)textFieldShouldClear:(UITextField *)textField +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldClear:)]) + ret = [realDelegate textFieldShouldClear:textField]; + BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textField); + return ret; +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldReturn:)]) + ret = [realDelegate textFieldShouldReturn:textField]; + BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textField); + return ret; +} + +@end + +#pragma mark - Category + +@implementation UITextField (BlocksKit) + +@dynamic bk_shouldBeginEditingBlock, bk_didBeginEditingBlock, bk_shouldEndEditingBlock, bk_didEndEditingBlock, bk_shouldChangeCharactersInRangeWithReplacementStringBlock, bk_shouldClearBlock, bk_shouldReturnBlock; + ++ (void)load { + [self bk_registerDynamicDelegate]; + [self bk_linkDelegateMethods: @{ + @"bk_shouldBeginEditingBlock": @"textFieldShouldBeginEditing:", + @"bk_didBeginEditingBlock": @"textFieldDidBeginEditing:", + @"bk_shouldEndEditingBlock": @"textFieldShouldEndEditing:", + @"bk_didEndEditingBlock" : @"textFieldDidEndEditing:", + @"bk_shouldChangeCharactersInRangeWithReplacementStringBlock" : @"textField:shouldChangeCharactersInRange:replacementString:", + @"bk_shouldClearBlock" : @"textFieldShouldClear:", + @"bk_shouldReturnBlock" : @"textFieldShouldReturn:", + }]; +} + +@end diff --git a/YDBlockKit/UIKit/UITextView+BlocksKit.h b/YDBlockKit/UIKit/UITextView+BlocksKit.h new file mode 100644 index 0000000..06447ba --- /dev/null +++ b/YDBlockKit/UIKit/UITextView+BlocksKit.h @@ -0,0 +1,74 @@ +// +// UITextView+BlocksKit.h +// BlocksKit +// +// Contributed by Su, Hsing-Yu +// + +#import + +/** Block callbacks for UITextView. + + @warning UITextView is only available on a platform with UIKit. + + Created by Su, Hsing-Yu (https://github.com/aipeople) and + contributed to BlocksKit. + */ +@interface UITextView (BlocksKit) + +/** + * The block that fires before the receiver begins editing + * + * The return value indicates whether the receiver should begin editing + */ +@property (nonatomic, copy) BOOL(^bk_shouldBeginEditingBlock)(__kindof UITextView *textView); + +/** + * The block that fires after the receiver begins editing + */ +@property (nonatomic, copy) void(^bk_didBeginEditingBlock)(__kindof UITextView *textView); + +/** + * The block that fires before the receiver ends editing + * + * The return value indicates whether the receiver should end editing + */ +@property (nonatomic, copy) BOOL(^bk_shouldEndEditingBlock)(__kindof UITextView *textView); + +/** + * The block that fires after the receiver ends editing + */ +@property (nonatomic, copy) void(^bk_didEndEditingBlock)(__kindof UITextView *textView); + +/** + * The block that fires when the receiver's text will change + * + * The return value indicates whether the receiver should replace the characters in the given range with the replacement string + */ +@property (nonatomic, copy) BOOL(^bk_shouldChangeCharactersInRangeWithReplacementTextBlock)(__kindof UITextView *textView, NSRange range, NSString *text); + +/** + * The block that fires after the receiver's text changed + */ +@property (nonatomic, copy) void(^bk_didChangeBlock)(__kindof UITextView *textView); + +/** + * The block that fires after the receiver changes selection + */ +@property (nonatomic, copy) void(^bk_didChangeSelecionBlock)(__kindof UITextView *textView); + +/** + * The block that fires when the user tring to interact with the receiver's text attachment + * + * The return value indicates whether the user should interact with the text attachment + */ +@property (nonatomic, copy) BOOL(^bk_shouldInteractWithTextAttachmentInRangeBlock)(__kindof UITextView *textView, NSTextAttachment *attachment, NSRange range); + +/** + * The block that fires when the user tring to interact with an URL in the given range + * + * The return value indicates whether the receiver should response to the interaction or not + */ +@property (nonatomic, copy) BOOL(^bk_shouldInteractWithURLInRangeBlock)(__kindof UITextView *textView, NSURL *url, NSRange range); + +@end diff --git a/YDBlockKit/UIKit/UITextView+BlocksKit.m b/YDBlockKit/UIKit/UITextView+BlocksKit.m new file mode 100644 index 0000000..f7ce438 --- /dev/null +++ b/YDBlockKit/UIKit/UITextView+BlocksKit.m @@ -0,0 +1,158 @@ +// +// UITextView+BlocksKit.h +// BlocksKit +// + +#import "UITextView+BlocksKit.h" +#import "A2DynamicDelegate.h" +#import "NSObject+A2BlockDelegate.h" + +#pragma mark Delegate + +@interface A2DynamicUITextViewDelegate : A2DynamicDelegate + +@end + +@implementation A2DynamicUITextViewDelegate + +- (BOOL)textViewShouldBeginEditing:(UITextView *)textView +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textViewShouldBeginEditing:)]) + ret = [realDelegate textViewShouldBeginEditing:textView]; + BOOL (^block)(UITextView *) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textView); + return ret; +} + +- (void)textViewDidBeginEditing:(UITextView *)textView +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textViewDidBeginEditing:)]) + [realDelegate textViewDidBeginEditing:textView]; + void (^block)(UITextView *) = [self blockImplementationForMethod:_cmd]; + if (block) + block(textView); +} + +- (BOOL)textViewShouldEndEditing:(UITextView *)textView +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textViewShouldEndEditing:)]) + ret = [realDelegate textViewShouldEndEditing:textView]; + BOOL (^block)(UITextView *) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textView); + return ret; +} + +- (void)textViewDidEndEditing:(UITextView *)textView +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textViewDidEndEditing:)]) + [realDelegate textViewDidEndEditing:textView]; + void (^block)(UITextView *) = [self blockImplementationForMethod:_cmd]; + if (block) + block(textView); +} + +- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textView:shouldChangeTextInRange:replacementText:)]) + ret = [realDelegate textView:textView shouldChangeTextInRange:range replacementText:text]; + BOOL (^block)(UITextView *, NSRange, NSString *) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textView, range, text); + return ret; +} + +- (void)textViewDidChange:(UITextView *)textView +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textViewDidChange:)]) + [realDelegate textViewDidChange:textView]; + void (^block)(UITextView *) = [self blockImplementationForMethod:_cmd]; + if (block) + block(textView); +} + +- (void)textViewDidChangeSelection:(UITextView *)textView +{ + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textViewDidChangeSelection:)]) + [realDelegate textViewDidChangeSelection:textView]; + void (^block)(UITextView *) = [self blockImplementationForMethod:_cmd]; + if (block) + block(textView); +} + +- (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textView:shouldInteractWithTextAttachment:inRange:)]) + ret = [realDelegate textView:textView shouldInteractWithTextAttachment:textAttachment inRange:characterRange]; + BOOL (^block)(UITextView *, NSTextAttachment *, NSRange) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textView, textAttachment, characterRange); + return ret; +} + +- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange +{ + BOOL ret = YES; + id realDelegate = self.realDelegate; + if (realDelegate && [realDelegate respondsToSelector:@selector(textView:shouldInteractWithURL:inRange:)]) + ret = [realDelegate textView:textView shouldInteractWithURL:URL inRange:characterRange]; + BOOL (^block)(UITextView *, NSURL *, NSRange) = [self blockImplementationForMethod:_cmd]; + if (block) + ret &= block(textView, URL, characterRange); + return ret; +} + +@end + +#pragma mark - Category + +@implementation UITextView (BlocksKit) + +@dynamic bk_shouldBeginEditingBlock, bk_didBeginEditingBlock, bk_shouldEndEditingBlock, bk_didEndEditingBlock, bk_shouldChangeCharactersInRangeWithReplacementTextBlock, bk_didChangeBlock, bk_didChangeSelecionBlock, bk_shouldInteractWithTextAttachmentInRangeBlock, bk_shouldInteractWithURLInRangeBlock; + ++ (void)load { + [self bk_registerDynamicDelegate]; + [self bk_linkDelegateMethods: @{ + @"bk_shouldBeginEditingBlock": + @"textViewShouldBeginEditing:", + + @"bk_didBeginEditingBlock": + @"textViewDidBeginEditing:", + + @"bk_shouldEndEditingBlock": + @"textViewDidBeginEditing:", + + @"bk_didEndEditingBlock" : + @"textViewDidEndEditing:", + + @"bk_shouldChangeCharactersInRangeWithReplacementTextBlock" : + @"textView:shouldChangeTextInRange:replacementText:", + + @"bk_didChangeBlock" : + @"textViewDidChange:", + + @"bk_didChangeSelecionBlock" : + @"textViewDidChangeSelection:", + + @"bk_shouldInteractWithTextAttachmentInRangeBlock" : + @"textView:shouldInteractWithTextAttachment:inRange:", + + @"bk_shouldInteractWithURLInRangeBlock" : + @"textView:shouldInteractWithURL:inRange:", + }]; +} + +@end diff --git a/YDBlockKit/UIKit/UIView+BlocksKit.h b/YDBlockKit/UIKit/UIView+BlocksKit.h new file mode 100644 index 0000000..e9f04ca --- /dev/null +++ b/YDBlockKit/UIKit/UIView+BlocksKit.h @@ -0,0 +1,74 @@ +// +// UIView+BlocksKit.h +// BlocksKit +// + +#import "BKDefines.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** Convenience on-touch methods for UIView. + + Includes code by the following: + + - Kevin O'Neill. . 2011. BSD. + - Jake Marsh. . 2011. + - Zach Waldowski. . 2011. + + @warning UIView is only available on a platform with UIKit. + */ +@interface UIView (BlocksKit) + +/** Abstract creation of a block-backed UITapGestureRecognizer. + + This method allows for the recognition of any arbitrary number + of fingers tapping any number of times on a view. An instance + of UITapGesture recognizer is allocated for the block and added + to the recieving view. + + @warning This method has an _additive_ effect. Do not call it multiple + times to set-up or tear-down. The view will discard the gesture recognizer + on release. + + @param numberOfTouches The number of fingers tapping that will trigger the block. + @param numberOfTaps The number of taps required to trigger the block. + @param block The handler for the UITapGestureRecognizer + @see whenTapped: + @see whenDoubleTapped: + */ +- (void)bk_whenTouches:(NSUInteger)numberOfTouches tapped:(NSUInteger)numberOfTaps handler:(void (^)(void))block; + +/** Adds a recognizer for one finger tapping once. + + @warning This method has an _additive_ effect. Do not call it multiple + times to set-up or tear-down. The view will discard the gesture recognizer + on release. + + @param block The handler for the tap recognizer + @see whenDoubleTapped: + @see whenTouches:tapped:handler: + */ +- (void)bk_whenTapped:(void (^)(void))block; + +/** Adds a recognizer for one finger tapping twice. + + @warning This method has an _additive_ effect. Do not call it multiple + times to set-up or tear-down. The view will discard the gesture recognizer + on release. + + @param block The handler for the tap recognizer + @see whenTapped: + @see whenTouches:tapped:handler: + */ +- (void)bk_whenDoubleTapped:(void (^)(void))block; + +/** A convenience wrapper that non-recursively loops through the subviews of a view. + + @param block A code block that interacts with a UIView sender. + */ +- (void)bk_eachSubview:(void (^)(UIView *subview))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDBlockKit/UIKit/UIView+BlocksKit.m b/YDBlockKit/UIKit/UIView+BlocksKit.m new file mode 100644 index 0000000..d80c94b --- /dev/null +++ b/YDBlockKit/UIKit/UIView+BlocksKit.m @@ -0,0 +1,55 @@ +// +// UIView+BlocksKit.m +// BlocksKit +// + +#import "UIView+BlocksKit.h" +#import "UIGestureRecognizer+BlocksKit.h" + +@implementation UIView (BlocksKit) + +- (void)bk_whenTouches:(NSUInteger)numberOfTouches tapped:(NSUInteger)numberOfTaps handler:(void (^)(void))block +{ + if (!block) return; + + UITapGestureRecognizer *gesture = [UITapGestureRecognizer bk_recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) { + if (state == UIGestureRecognizerStateRecognized) block(); + }]; + + gesture.numberOfTouchesRequired = numberOfTouches; + gesture.numberOfTapsRequired = numberOfTaps; + + [self.gestureRecognizers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (![obj isKindOfClass:[UITapGestureRecognizer class]]) return; + + UITapGestureRecognizer *tap = obj; + BOOL rightTouches = (tap.numberOfTouchesRequired == numberOfTouches); + BOOL rightTaps = (tap.numberOfTapsRequired == numberOfTaps); + if (rightTouches && rightTaps) { + [gesture requireGestureRecognizerToFail:tap]; + } + }]; + + [self addGestureRecognizer:gesture]; +} + +- (void)bk_whenTapped:(void (^)(void))block +{ + [self bk_whenTouches:1 tapped:1 handler:block]; +} + +- (void)bk_whenDoubleTapped:(void (^)(void))block +{ + [self bk_whenTouches:1 tapped:2 handler:block]; +} + +- (void)bk_eachSubview:(void (^)(UIView *subview))block +{ + NSParameterAssert(block != nil); + + [self.subviews enumerateObjectsUsingBlock:^(UIView *subview, NSUInteger idx, BOOL *stop) { + block(subview); + }]; +} + +@end diff --git a/YDBlockKit/YDBlockKit.docc/YDBlockKit.md b/YDBlockKit/YDBlockKit.docc/YDBlockKit.md new file mode 100755 index 0000000..c55dcfe --- /dev/null +++ b/YDBlockKit/YDBlockKit.docc/YDBlockKit.md @@ -0,0 +1,13 @@ +# ``YDBlockKit`` + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` \ No newline at end of file diff --git a/YDBlockKit/YDBlockKit.h b/YDBlockKit/YDBlockKit.h new file mode 100644 index 0000000..72f57fa --- /dev/null +++ b/YDBlockKit/YDBlockKit.h @@ -0,0 +1,37 @@ +// +// YDBlockKit.h +// YDBlockKit +// +// Created by 王远东 on 2022/10/14. +// + +#import + +//! Project version number for YDBlockKit. +FOUNDATION_EXPORT double YDBlockKitVersionNumber; + +//! Project version string for YDBlockKit. +FOUNDATION_EXPORT const unsigned char YDBlockKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import "BKDefines.h" +#import "BKMacros.h" +#import "NSArray+BlocksKit.h" +#import "NSDictionary+BlocksKit.h" +#import "NSIndexSet+BlocksKit.h" +#import "NSInvocation+BlocksKit.h" +#import "NSNumber+BlocksKit.h" +#import "NSMapTable+BlocksKit.h" +#import "NSMutableArray+BlocksKit.h" +#import "NSMutableDictionary+BlocksKit.h" +#import "NSMutableIndexSet+BlocksKit.h" +#import "NSMutableOrderedSet+BlocksKit.h" +#import "NSMutableSet+BlocksKit.h" +#import "NSObject+BKAssociatedObjects.h" +#import "NSObject+BKBlockExecution.h" +#import "NSObject+BKBlockObservation.h" +#import "NSOrderedSet+BlocksKit.h" +#import "NSSet+BlocksKit.h" +#import "NSTimer+BlocksKit.h" +#import "NSObject+YDAsyncBlock.h" diff --git a/YDEmptyView/UIViewController+YDMistakesShow.h b/YDEmptyView/UIViewController+YDMistakesShow.h new file mode 100644 index 0000000..312de73 --- /dev/null +++ b/YDEmptyView/UIViewController+YDMistakesShow.h @@ -0,0 +1,71 @@ +// +// UIViewController+YDMistakesShow.h +// YDEmptyView +// +// Created by 王远东 on 2022/8/22. +// Copyright © 2022 wangyuandong. All rights reserved. +// + +#import +#import "YDEmptyView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIViewController (YDMistakesShow) + +@property (nonatomic, copy) void(^retryBlock)(void); +@property (nonatomic, strong, readonly) YDEmptyView *emptyView; + +- (void)hideRetryView; + +// 显示隐藏 RetryView +- (void)showEmptyViewType:(EYDEmptyType)aEmptyType; + +/** + * 显示自定义的retryView + * @param aTitle 黑色字体 + * + */ +- (void)showEmptyView:(NSString *)aImageString title:(NSString *)aTitle tipBtnTitle:(NSString *)tipBtnTitle subTipBtnTitle:(NSString *)subTipBtnTitle; + +/** + * 显示自定义的retryView + * @param aTitle 黑色字体 + * + */ +- (void)showEmptyView:(NSString *)aImageString title:(NSString *)aTitle tipBtnTitle:(NSString *)tipBtnTitle subTipBtnTitle:(NSString *)subTipBtnTitle isRetryTap:(BOOL)isRetryTap; + +/// 展示自定义的emptyView +/// @param aImageString image +/// @param aTitle title +/// @param tipBtnTitle button title +/// @param subTipBtnTitle sub tip button title +/// @param isrefresh reload emptyView +- (void)showEmptyView:(NSString *)aImageString title:(NSString *)aTitle tipBtnTitle:(NSString *)tipBtnTitle subTipBtnTitle:(NSString *)subTipBtnTitle refresh:(BOOL)isrefresh; + +// 重写该方法,该方法在被点击时执行 +- (void)handleRetry; +- (void)handleEmptyTipBtn:(id)sender; +- (void)handleEmptySubTipBtn:(id)sender; + +#pragma mark - UI处理 kRACUINoData kRACUIError +/** + * 只支持 kRACUINoData kRACUIError 的显示 + * @param aHiddenBlock 要将self 上的部分view 显示隐藏 + * @param ashowErrorBlock 错误判断 是否在界面上显示错误信息 + */ +- (void)sendNext:(NSDictionary *)aDic hiddenUI:(void(^)(BOOL hidden))aHiddenBlock showError:(BOOL (^)(NSError *error))ashowErrorBlock ; +/** + * 只支持 kRACUINoData kRACUIError 的显示 + * @param aHiddenBlock 要将self 上的部分view 显示隐藏 + * @param isRefresh 是否重新加载控件 + * @param ashowErrorBlock 错误判断 是否在界面上显示错误信息 + */ +- (void)sendNext:(NSDictionary *)aDic hiddenUI:(void(^)(BOOL hidden))aHiddenBlock refresh:(BOOL)isRefresh showError:(BOOL (^)(NSError *error))ashowErrorBlock; + +// SplitViewController +- (void)sendSplitViewControllerNext:(NSDictionary *)aDic showError:(BOOL (^)(NSError *error))ashowErrorBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDEmptyView/UIViewController+YDMistakesShow.m b/YDEmptyView/UIViewController+YDMistakesShow.m new file mode 100644 index 0000000..8d99809 --- /dev/null +++ b/YDEmptyView/UIViewController+YDMistakesShow.m @@ -0,0 +1,283 @@ +// +// UIViewController+YDMistakesShow.m +// YDEmptyView +// +// Created by 王远东 on 2022/8/22. +// Copyright © 2022 wangyuandong. All rights reserved. +// + +#import "UIViewController+YDMistakesShow.h" +#import +#import + +#define kRACUINoData @"kRACUINoData" // BOOL 是否 网络请求成功 无数据 +#define kRACUIError @"kRACUIError" // NSError 网络请求错误进行UI处理 +// 发送 kRACUINoData kRACUIError 时可加上 自定义 图片 文字 +#define kShowImgeString @"kShowImgeString" // NSString 自定义图片 +#define kShowTipString @"kShowTipString" // NSString NSAttributedString 自定义文字 14 +#define kShowTipBtnString @"kShowTipBtnString" // NSString NSAttributedString 自定义文字 14 +#define kShowSubTipBtnString @"kShowSubTipBtnString" // NSString NSAttributedString 自定义文字 12 +#define kRACUIIsRetryTap @"kRACUIIsRetryTap" // BOOL 是否添加tap手势 + +@interface UIViewController () +@property (nonatomic, strong) YDEmptyView *emptyView; +@property (nonatomic, strong) UITapGestureRecognizer *retryTap; +@end + +@implementation UIViewController (YDMistakesShow) + +- (void)hideRetryView { + if (self.emptyView) { + [self.emptyView removeFromSuperview]; + self.emptyView = nil; + } + + if(self.retryTap) { + [self.view removeGestureRecognizer:self.retryTap]; + self.retryTap = nil; + } +} +- (void)showEmptyView:(NSString *)aImageString title:(NSString *)aTitle tipBtnTitle:(NSString *)tipBtnTitle subTipBtnTitle:(NSString *)subTipBtnTitle refresh:(BOOL)isrefresh { + if (isrefresh) { + if (self.emptyView) { + [self.emptyView removeFromSuperview]; + self.emptyView = nil; + } + } + [self showEmptyView:aImageString title:aTitle tipBtnTitle:tipBtnTitle subTipBtnTitle:subTipBtnTitle type:EYDEmptyTypeCustom isRetryTap:YES]; +} + + +- (void)showEmptyView:(NSString *)aImageString title:(NSString *)aTitle tipBtnTitle:(NSString *)tipBtnTitle subTipBtnTitle:(NSString *)subTipBtnTitle type:(EYDEmptyType)aEmptyType isRetryTap:(BOOL)isRetryTap +{ + if (self.emptyView) { + [self.emptyView removeFromSuperview]; + self.emptyView = nil; + } + self.emptyView = [[YDEmptyView alloc] init]; + [self.view addSubview:self.emptyView]; + [self.view sendSubviewToBack:self.emptyView]; + [self.emptyView mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.width.equalTo(self.view); + }]; + self.emptyView.emptyType = aEmptyType; + if (aEmptyType == EYDEmptyTypeCustom || aEmptyType == EYDEmptyTypeNetwork) { + __weak typeof(self) ws = self; + [self.emptyView configureEmptyView:aImageString title:aTitle tipBtnTitle:tipBtnTitle subTipBtnTitle:subTipBtnTitle handleAction:^(EYDEmptyActionType actionType) { + switch (actionType) { + case EYDEmptyActionTypeCustomTip: + [ws handleEmptyTipBtn:ws.emptyView.tipBtn]; + break; + case EYDEmptyActionTypeCustomSubTip: + [ws handleEmptySubTipBtn:ws.emptyView.subTipBtn]; + break; + case EYDEmptyActionTypeNetworkRefresh: + [ws retry]; + break; + case EYDEmptyActionTypeNetworkSet: { +// [YDUtil openSystemSetting]; xwx 方法实现 + NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; + if ([[UIApplication sharedApplication] canOpenURL:url]) { + //打开该应用的通知设置界面 + // NSURL *url = [NSURL URLWithString:@"prefs:root=NOTIFICATIONS_ID&path=com.msb.YDbox"]; + //打开该应用的设置界面(包含地理位置、通知、隐私...) + [[UIApplication sharedApplication] openURL:url]; + } + } + break; + default: + break; + } + }]; + } + self.emptyView.hidden = NO; + if (isRetryTap == YES) { + if (!self.retryTap) { + self.retryTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(retry)]; + [self.view addGestureRecognizer:self.retryTap]; + } + } + + +} + +- (void)showEmptyView:(NSString *)aImageString title:(NSString *)aTitle tipBtnTitle:(NSString *)tipBtnTitle subTipBtnTitle:(NSString *)subTipBtnTitle +{ + [self showEmptyView:aImageString title:aTitle tipBtnTitle:tipBtnTitle subTipBtnTitle:subTipBtnTitle type:EYDEmptyTypeCustom isRetryTap:YES]; +} + +- (void)showEmptyView:(NSString *)aImageString title:(NSString *)aTitle tipBtnTitle:(NSString *)tipBtnTitle subTipBtnTitle:(NSString *)subTipBtnTitle isRetryTap:(BOOL)isRetryTap +{ + [self showEmptyView:aImageString title:aTitle tipBtnTitle:tipBtnTitle subTipBtnTitle:subTipBtnTitle type:EYDEmptyTypeCustom isRetryTap:isRetryTap]; +} + +- (void)showEmptyViewType:(EYDEmptyType)aEmptyType { + [self showEmptyView:nil title:nil tipBtnTitle:nil subTipBtnTitle:nil type:aEmptyType isRetryTap:YES]; +} + +- (void)retry +{ + //数据为空的情况不需要重新请求 + [self hideRetryView]; + if (self.retryBlock) { + self.retryBlock(); + } + [self handleRetry]; +} + +// 重写该方法,该方法在被点击时执行 +- (void)handleRetry { + +} + +// 重写该方法,该方法在被点击时执行 +- (void)handleEmptyTipBtn:(id)sender { + +} + +// 重写该方法,该方法在被点击时执行 +- (void)handleEmptySubTipBtn:(id)sender { + +} + +// 为普通调用准备 +- (void)sendNext:(NSDictionary *)aDic hiddenUI:(void(^)(BOOL hidden))aHiddenBlock showError:(BOOL (^)(NSError *error))ashowErrorBlock { + + if (aHiddenBlock) { + aHiddenBlock(NO); + } + + __weak typeof(self) ws = self; + self.retryBlock = ^{ + if (aHiddenBlock) { + aHiddenBlock(NO); + } + ws.retryBlock = nil; + }; + + if (aDic[kRACUINoData]) { + if (aHiddenBlock) { + aHiddenBlock(YES); + } + if(aDic[kShowImgeString] || aDic[kShowTipString] || aDic[kShowTipBtnString] || aDic[kShowSubTipBtnString]) { + [self showEmptyView:aDic[kShowImgeString] title:aDic[kShowTipString] tipBtnTitle:aDic[kShowTipBtnString] subTipBtnTitle:aDic[kShowSubTipBtnString] type:EYDEmptyTypeCustom isRetryTap:aDic[kRACUIIsRetryTap]?[aDic[kRACUIIsRetryTap] boolValue]:YES]; + } else { + [self showEmptyViewType:EYDEmptyTypeData]; + } + } + + NSError *error = aDic[kRACUIError]; + if (error != nil) { + NSLog(@"加载错误,进行错误的UI展现处理"); + BOOL showError = NO; + if (aHiddenBlock) { + showError = ashowErrorBlock(error); + } + if (showError) { + + if (aHiddenBlock) { + aHiddenBlock(YES); + } + if(aDic[kShowImgeString] || aDic[kShowTipString] || aDic[kShowTipBtnString] || aDic[kShowSubTipBtnString]) { + [self showEmptyView:aDic[kShowImgeString] title:aDic[kShowTipString] tipBtnTitle:aDic[kShowTipBtnString] subTipBtnTitle:aDic[kShowSubTipBtnString] type:EYDEmptyTypeCustom isRetryTap:aDic[kRACUIIsRetryTap]?[aDic[kRACUIIsRetryTap] boolValue]:YES]; + } else { + if(error.code == NSURLErrorNetworkConnectionLost || error.code == NSURLErrorNotConnectedToInternet) { + [self showEmptyViewType:EYDEmptyTypeNetwork]; + } else { + [self showEmptyViewType:EYDEmptyTypeRequest]; + } + } + } + } +} + +- (void)sendNext:(NSDictionary *)aDic hiddenUI:(void (^)(BOOL))aHiddenBlock refresh:(BOOL)isRefresh showError:(BOOL (^)(NSError *))ashowErrorBlock{ + if (aHiddenBlock) { + aHiddenBlock(NO); + } + + __weak typeof(self) ws = self; + self.retryBlock = ^{ + if (aHiddenBlock) { + aHiddenBlock(NO); + } + ws.retryBlock = nil; + }; + + if (aDic[kRACUINoData]) { + if (aHiddenBlock) { + aHiddenBlock(YES); + } + if(aDic[kShowImgeString] || aDic[kShowTipString] || aDic[kShowTipBtnString] || aDic[kShowSubTipBtnString]) { + [self showEmptyView:aDic[kShowImgeString] title:aDic[kShowTipString] tipBtnTitle:aDic[kShowTipBtnString] subTipBtnTitle:aDic[kShowSubTipBtnString] refresh:isRefresh]; + } else { + [self showEmptyViewType:EYDEmptyTypeData]; + } + } + + NSError *error = aDic[kRACUIError]; + if (error != nil) { + NSLog(@"加载错误,进行错误的UI展现处理"); + BOOL showError = NO; + if (aHiddenBlock) { + showError = ashowErrorBlock(error); + } + if (showError) { + + if (aHiddenBlock) { + aHiddenBlock(YES); + } + if(aDic[kShowImgeString] || aDic[kShowTipString] || aDic[kShowTipBtnString] || aDic[kShowSubTipBtnString]) { + [self showEmptyView:aDic[kShowImgeString] title:aDic[kShowTipString] tipBtnTitle:aDic[kShowTipBtnString] subTipBtnTitle:aDic[kShowSubTipBtnString] refresh:isRefresh]; + //[self showEmptyView:aDic[kShowImgeString] title:aDic[kShowTipString] tipBtnTitle:aDic[kShowTipBtnString] subTipBtnTitle:aDic[kShowSubTipBtnString] type:EYDEmptyTypeCustom]; + } else { + if(error.code == NSURLErrorNetworkConnectionLost || error.code == NSURLErrorNotConnectedToInternet) { + [self showEmptyViewType:EYDEmptyTypeNetwork]; + } else { + [self showEmptyViewType:EYDEmptyTypeRequest]; + } + } + } + } +} + +- (void)sendSplitViewControllerNext:(NSDictionary *)aDic showError:(BOOL (^)(NSError *error))ashowErrorBlock { + NSAssert(self.splitViewController != nil, @"调用该方法时请保证他有 self.splitViewController"); + __weak typeof(self) ws = self; + [self.splitViewController sendNext:aDic hiddenUI:^(BOOL hidden) { + [ws.splitViewController.viewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + obj.view.hidden = hidden; + }]; + } showError:ashowErrorBlock]; +} + +#pragma mark - 属性绑定 +- (void)setEmptyView:(YDEmptyView *)emptyView +{ + objc_setAssociatedObject(self, @selector(emptyView), emptyView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (YDEmptyView *)emptyView +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setRetryTap:(UITapGestureRecognizer *)retryTap +{ + objc_setAssociatedObject(self, @selector(retryTap), retryTap, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (UITapGestureRecognizer *)retryTap +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setRetryBlock:(void (^)(void))retryBlock { + objc_setAssociatedObject(self, @selector(retryBlock), retryBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void (^)(void))retryBlock { + return objc_getAssociatedObject(self, _cmd); +} + +@end diff --git a/YDEmptyView/YDEmptyView.docc/YDEmptyView.md b/YDEmptyView/YDEmptyView.docc/YDEmptyView.md new file mode 100755 index 0000000..ad19322 --- /dev/null +++ b/YDEmptyView/YDEmptyView.docc/YDEmptyView.md @@ -0,0 +1,13 @@ +# ``YDEmptyView`` + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` \ No newline at end of file diff --git a/YDEmptyView/YDEmptyView.h b/YDEmptyView/YDEmptyView.h new file mode 100644 index 0000000..ddb67f6 --- /dev/null +++ b/YDEmptyView/YDEmptyView.h @@ -0,0 +1,38 @@ +// +// YDEmptyView.h +// YDEmptyView +// +// Created by 王远东 on 2022/8/22. +// Copyright © 2022 wangyuandong. All rights reserved. +// + +#import + +typedef NS_ENUM(NSUInteger, EYDEmptyType) { + EYDEmptyTypeNone, + EYDEmptyTypeData, //数据空 + EYDEmptyTypeNetwork, //网络连接出错 + EYDEmptyTypeRequest, //请求失败 + EYDEmptyTypeCustom, //自定义 +}; +typedef NS_ENUM(NSUInteger, EYDEmptyActionType) { + EYDEmptyActionTypeNone, + EYDEmptyActionTypeCustomTip, //自定义的tipBtn点击 + EYDEmptyActionTypeCustomSubTip, //自定义的tipSubBtn点击 + EYDEmptyActionTypeNetworkRefresh, //网络连接出错 刷新 + EYDEmptyActionTypeNetworkSet, //网络连接出错 打开网络设置 +}; + +@interface YDEmptyView : UIView + +@property (nonatomic, assign) EYDEmptyType emptyType; + +@property (nonatomic, strong, readonly) UIButton *tipBtn; +@property (nonatomic, strong, readonly) UIButton *subTipBtn; +@property (nonatomic, strong) UILabel *tipLabel; + +//不是自定义 设置无效 +- (void)configureEmptyView:(NSString *)aImageString title:(id)aTitle tipBtnTitle:(id)tipBtnTitle subTipBtnTitle:(id)subTipBtnTitle handleAction:(void (^)(EYDEmptyActionType actionType))handleActionBlock; + +@end + diff --git a/YDEmptyView/YDEmptyView.m b/YDEmptyView/YDEmptyView.m new file mode 100644 index 0000000..1b45f7b --- /dev/null +++ b/YDEmptyView/YDEmptyView.m @@ -0,0 +1,291 @@ +// +// YDEmptyView.m +// YDEmptyView +// +// Created by 王远东 on 2022/8/22. +// Copyright © 2022 wangyuandong. All rights reserved. +// + +#import "YDEmptyView.h" +#import "YDEmptyViewConfig.h" +#import + +@interface YDEmptyView () + +@property (nonatomic, strong) UIImageView *cYDoonImageView; + +@property (nonatomic, strong) UIButton *tipBtn; +@property (nonatomic, strong) UIButton *subTipBtn; + +@property (nonatomic, copy) NSString *imageName; +@property (nonatomic, strong) NSString *tipTitle; +@property (nonatomic, copy) NSString *tipBtnTitle; +@property (nonatomic, copy) NSString *subTipBtnTitle; +@property (nonatomic, strong) NSAttributedString *tipAttributedTitle; +@property (nonatomic, copy) NSAttributedString *tipBtnAttributedTitle; +@property (nonatomic, copy) NSAttributedString *subTipBtnAttributedTitle; +@property (nonatomic, copy) void (^handleActionBlock)(EYDEmptyActionType); + +@end + +@implementation YDEmptyView + +- (instancetype) init +{ + self = [super init]; + if (self) { + [self makeUI]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self makeUI]; + } + return self; +} +- (void)setEmptyType:(EYDEmptyType)emptyType +{ + _emptyType = emptyType; + self.hidden = emptyType == EYDEmptyTypeNone; + if (self.hidden) { + return; + } + self.cYDoonImageView.hidden = YES; + self.tipLabel.hidden = YES; + self.tipBtn.hidden = YES; + self.subTipBtn.hidden = YES; + + UIImage *img = nil; + switch (self.emptyType) { + case EYDEmptyTypeData: + if (!self.imageName) { + img = [YDEmptyViewConfig emptyImage][EmptyNoDataImage]; + } + if (!self.tipTitle) { + self.tipTitle = @"暂无数据"; + } + break; + case EYDEmptyTypeNetwork: + if (!self.imageName) { + img = [YDEmptyViewConfig emptyImage][EmptyNetworkExceptionImage]; + } + if (!self.tipTitle) { + self.tipTitle = @"哇哦,网络出错了,刷新试试"; + } + if (!self.tipBtnTitle) { + self.tipBtnTitle = @"刷新"; + } + if (!self.subTipBtnTitle) { + self.subTipBtnTitle = @"打开网络设置"; + } + break; + case EYDEmptyTypeRequest: + if (!self.imageName) { + img = [YDEmptyViewConfig emptyImage][EmptyRequestExceptionImage]; + } + if (!self.tipTitle) { + self.tipTitle = @"数据获取异常\n请稍后点击页面刷新"; + } + break; + case EYDEmptyTypeCustom: + if (self.imageName) { + img = [UIImage imageNamed:self.imageName inBundle:[NSBundle mainBundle] compatibleWithTraitCollection:nil]; + } + break; + default: + break; + } + UIView *lastTopView = self; + __block MASConstraint *bottomConstraint; + if (img) { + self.cYDoonImageView.image = img; + self.cYDoonImageView.hidden = NO; + if (bottomConstraint) [bottomConstraint deactivate]; + [self.cYDoonImageView mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(lastTopView); + make.centerX.equalTo(lastTopView); + bottomConstraint = make.bottom.equalTo(self); + }]; + lastTopView = self.cYDoonImageView; + } + if (self.tipTitle.length > 0 || self.tipAttributedTitle.length > 0) { + if (self.tipAttributedTitle.length > 0) { + self.tipLabel.attributedText = self.tipAttributedTitle; + } else if (self.tipTitle.length > 0) { + self.tipLabel.text = self.tipTitle; + } + self.tipLabel.hidden = NO; + if (bottomConstraint) [bottomConstraint deactivate]; + [self.tipLabel mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(lastTopView.mas_bottom).offset(12.5); + make.centerX.equalTo(lastTopView); + bottomConstraint = make.bottom.equalTo(self); + }]; + lastTopView = self.tipLabel; + } + if (self.tipBtnTitle.length > 0 || self.tipBtnAttributedTitle.length > 0) { + if (self.tipBtnAttributedTitle.length > 0) { + [self.tipBtn setAttributedTitle:self.tipAttributedTitle forState:UIControlStateNormal]; + } else if (self.tipBtnTitle.length > 0) { + [self.tipBtn setTitle:self.tipBtnTitle forState:UIControlStateNormal]; + } + self.tipBtn.hidden = NO; + if (bottomConstraint) [bottomConstraint deactivate];; + [self.tipBtn mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(lastTopView.mas_bottom).offset(23.5); + make.centerX.equalTo(lastTopView); + make.size.mas_equalTo(CGSizeMake(136., 40.)); + bottomConstraint = make.bottom.equalTo(self); + }]; + lastTopView = self.tipBtn; + } + if (self.subTipBtnTitle.length > 0 || self.subTipBtnAttributedTitle.length > 0) { + if (self.subTipBtnAttributedTitle.length > 0) { + [self.subTipBtn setAttributedTitle:self.subTipBtnAttributedTitle forState:UIControlStateNormal]; + } else if (self.subTipBtnTitle.length > 0) { + [self.subTipBtn setTitle:self.subTipBtnTitle forState:UIControlStateNormal]; + } + self.subTipBtn.hidden = NO; + if (bottomConstraint) [bottomConstraint deactivate];; + [self.subTipBtn mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(lastTopView.mas_bottom).offset(13.); + make.centerX.equalTo(lastTopView); + make.bottom.equalTo(self); + bottomConstraint = make.bottom.equalTo(self); + }]; + lastTopView = self.subTipBtn; + } +} + +- (void)configureEmptyView:(NSString *)aImageString title:(id)aTitle tipBtnTitle:(id)tipBtnTitle subTipBtnTitle:(id)subTipBtnTitle handleAction:(void (^)(EYDEmptyActionType actionType))handleActionBlock +{ + if (self.emptyType == EYDEmptyTypeCustom) { + self.imageName = aImageString; + if ([aTitle isKindOfClass:[NSString class]]) { + self.tipTitle = aTitle; + } else if ([aTitle isKindOfClass:[NSAttributedString class]]) { + self.tipAttributedTitle = aTitle; + } else { + self.tipTitle = nil; + self.tipAttributedTitle = nil; + } + if ([tipBtnTitle isKindOfClass:[NSString class]]) { + self.tipBtnTitle = tipBtnTitle; + } else if ([aTitle isKindOfClass:[NSAttributedString class]]) { + self.tipBtnAttributedTitle = tipBtnTitle; + } else { + self.tipBtnTitle = nil; + self.tipBtnAttributedTitle = nil; + } + if ([subTipBtnTitle isKindOfClass:[NSString class]]) { + self.subTipBtnTitle = subTipBtnTitle; + } else if ([aTitle isKindOfClass:[NSAttributedString class]]) { + self.subTipBtnAttributedTitle = subTipBtnTitle; + }else { + self.subTipBtnTitle = nil; + self.subTipBtnAttributedTitle = nil; + } + self.emptyType = EYDEmptyTypeCustom; + } + if (handleActionBlock) { + self.handleActionBlock = handleActionBlock; + } +} +- (void)makeUI +{ + self.hidden = YES; + self.backgroundColor = [UIColor clearColor]; + [self addSubview:self.tipBtn]; + [self addSubview:self.subTipBtn]; + [self addSubview:self.cYDoonImageView]; + [self addSubview:self.tipLabel]; + [self.tipBtn addTarget:self action:@selector(handleTipBtnClick:) forControlEvents:UIControlEventTouchUpInside]; + [self.subTipBtn addTarget:self action:@selector(handleSubTipBtnClick:) forControlEvents:UIControlEventTouchUpInside]; +} + +- (void)handleTipBtnClick:(id)sender { + if (self.handleActionBlock) { + if (self.emptyType == EYDEmptyTypeCustom) { + self.handleActionBlock(EYDEmptyActionTypeCustomTip); + } + else if (self.emptyType == EYDEmptyTypeNetwork) { + self.handleActionBlock(EYDEmptyActionTypeNetworkRefresh); + } + } +} + +- (void)handleSubTipBtnClick:(id)sender { + if (self.handleActionBlock) { + if (self.emptyType == EYDEmptyTypeCustom) { + self.handleActionBlock(EYDEmptyActionTypeCustomSubTip); + } + else if (self.emptyType == EYDEmptyTypeNetwork) { + self.handleActionBlock(EYDEmptyActionTypeNetworkSet); + } + } +} +//MARK: lazy +- (UIButton *)tipBtn { + if (!_tipBtn) { + UIButton *resetTipBtn = [YDEmptyViewConfig resetTipBtn]; + if (resetTipBtn) { + _tipBtn = resetTipBtn; + } else { + _tipBtn = [[UIButton alloc] init]; + // fixme 有backgroudImage 为啥要设置颜色? +// [_tipBtn setBackgroundColor:[UIColor colorWithRGBHex:0xF24F4F]]; + [_tipBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [_tipBtn.titleLabel setFont:[UIFont systemFontOfSize:14.]]; + _tipBtn.layer.masksToBounds = YES; +// _tipBtn.layer.cornerRadius = 20.f; + _tipBtn.layer.cornerRadius = [YDEmptyViewConfig tipBtnCornerRadius]; + [_tipBtn setBackgroundImage:[YDEmptyViewConfig buttonBackgroundImage][EmptyButtonNormalImage] forState:UIControlStateNormal]; + [_tipBtn setBackgroundImage:[YDEmptyViewConfig buttonBackgroundImage][EmptyButtonHighlightedImage] forState:UIControlStateHighlighted]; + [_tipBtn setBackgroundImage:[YDEmptyViewConfig buttonBackgroundImage][EmptyButtonDisabledImage] forState:UIControlStateDisabled]; + } + } + return _tipBtn; +} + +- (UIButton *)subTipBtn { + if (!_subTipBtn) { + UIButton *resetSubTipBtn = [YDEmptyViewConfig resetSubTipBtn]; + if (resetSubTipBtn) { + _subTipBtn = resetSubTipBtn; + } else { + _subTipBtn = [[UIButton alloc] init]; + [_subTipBtn setTitleColor:[YDEmptyViewConfig subButtonTitleColor] forState:UIControlStateNormal]; + [_subTipBtn.titleLabel setFont:[UIFont systemFontOfSize:12.]]; + } + } + return _subTipBtn; +} + +- (UIImageView *)cYDoonImageView +{ + if(!_cYDoonImageView){ + _cYDoonImageView = [[UIImageView alloc] init]; + _cYDoonImageView.image = [YDEmptyViewConfig defaultEmptyImage]; + _cYDoonImageView.backgroundColor = [UIColor clearColor]; + } + return _cYDoonImageView; +} + +- (UILabel *)tipLabel +{ + if(!_tipLabel){ + _tipLabel = [[UILabel alloc] init]; + _tipLabel.backgroundColor = [UIColor clearColor]; + _tipLabel.font = [UIFont systemFontOfSize:15.]; + _tipLabel.textColor = [YDEmptyViewConfig tipTextColor]; + _tipLabel.textAlignment = NSTextAlignmentCenter; + _tipLabel.numberOfLines = 0; + } + return _tipLabel; +} + +@end diff --git a/YDEmptyView/YDEmptyViewConfig.h b/YDEmptyView/YDEmptyViewConfig.h new file mode 100644 index 0000000..65924e5 --- /dev/null +++ b/YDEmptyView/YDEmptyViewConfig.h @@ -0,0 +1,42 @@ +// +// YDEmptyViewConfig.h +// YDEmptyView +// +// Created by 王远东 on 2022/8/22. +// Copyright © 2022 wangyuandong. All rights reserved. +// + +#import + +#define EmptyNoDataImage @"noDataImage" +#define EmptyNetworkExceptionImage @"networkExceptionImage" +#define EmptyRequestExceptionImage @"requestExceptionImage" + +#define EmptyButtonNormalImage @"buttonNormalImage" +#define EmptyButtonHighlightedImage @"buttonHighlightedImage" +#define EmptyButtonDisabledImage @"buttonDisabledImage" + +NS_ASSUME_NONNULL_BEGIN + +///工程使用的时候用分类覆盖即可 +@interface YDEmptyViewConfig : NSObject + ++ (NSDictionary *)emptyImage; + ++ (UIImage *)defaultEmptyImage; + ++ (NSDictionary *)buttonBackgroundImage; + ++ (UIColor *)subButtonTitleColor; + ++ (UIColor *)tipTextColor; +// tipBtn 圆角 ++ (CGFloat)tipBtnCornerRadius; +// 可选 重新覆盖tipBtn ++ (UIButton *)resetTipBtn; +// 可选 重新覆盖subTipBtn ++ (UIButton *)resetSubTipBtn; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDEmptyView/YDEmptyViewConfig.m b/YDEmptyView/YDEmptyViewConfig.m new file mode 100644 index 0000000..882b33a --- /dev/null +++ b/YDEmptyView/YDEmptyViewConfig.m @@ -0,0 +1,49 @@ +// +// YDEmptyViewConfig.m +// YDEmptyView +// +// Created by 王远东 on 2022/8/22. +// Copyright © 2022 wangyuandong. All rights reserved. +// + +#import "YDEmptyViewConfig.h" + +@implementation YDEmptyViewConfig + ++ (NSDictionary *)emptyImage { + return [NSDictionary new]; +} + ++ (UIImage *)defaultEmptyImage { + return [UIImage new]; +} + ++ (NSDictionary *)buttonBackgroundImage { + return [NSDictionary new]; +} + ++ (UIColor *)subButtonTitleColor { + return [UIColor blueColor]; +} + ++ (UIColor *)tipTextColor { + return [UIColor grayColor]; +} + +// tipBtn 圆角 ++ (CGFloat)tipBtnCornerRadius{ + return 20.f; +} + ++ (UIButton *)resetTipBtn +{ + return nil; +} + ++ (UIButton *)resetSubTipBtn +{ + return nil; +} + + +@end diff --git a/YDFoundation.podspec b/YDFoundation.podspec index 62cb7f0..f13caba 100644 --- a/YDFoundation.podspec +++ b/YDFoundation.podspec @@ -77,11 +77,25 @@ Pod::Spec.new do |s| _YDPreLoader = { :spec_name => "YDPreLoader", :source_files => ['YDPreLoader/**/*.{h,m}'], :dependency => [{:name => "KTVHTTPCache", :version => "2.0.1"}] } # YDImageService - _YDImageService = { :spec_name => "YDImageService", :source_files => ['YDImageService/**/*.{h,m}'], :sub_dependency => [_YDSafeThread], :dependency => [{:name => "YYImage", :version => "1.0.4"}, {:name => "YYWebImage", :version => "1.0.5"}] } + _YDImageService = { :spec_name => "YDImageService", :source_files => ['YDImageService/**/*.{h,m}'], :dependency => [{:name => "YYImage", :version => "1.0.4"}, {:name => "YYWebImage", :version => "1.0.5"}], :sub_dependency => [_YDSafeThread] } + # YDEmptyView + _YDEmptyView = { :spec_name => "YDEmptyView", :source_files => ['YDEmptyView/**/*.{h,m}'], :dependency => [{:name => "Masonry", :version => "1.1.0"}] } + + # YDBlockKit + _YDBlockKit = { :spec_name => "YDBlockKit", :source_files => ['YDBlockKit/**/*.{h,m}'] } + + # YDAuthorizationUtil + _YDAuthorizationUtil = { :spec_name => "YDAuthorizationUtil", :source_files => ['YDAuthorizationUtil/**/*.{h,m}'] } + + # YDJPush + _YDJPush = { :spec_name => "YDJPush", :source_files => ['YDJPush/**/*.{h,m}'], :dependency => [{:name => "JPush", :version => "3.2.4"}] } + + # YDNetworkManager + _YDNetworkManager = { :spec_name => "YDNetworkManager", :source_files => ['YDNetworkManager/**/*.{h,m}'], :dependency => [{:name => "YTKNetwork", :version => "3.0.6"}] } all_subspec = [ _YDRouter, _YDWebp, _YDUtilKit, _YDFuncKit, _YDBaseUI, _YDUIKit, _YDTools, _YDSVProgressHUD, _YDAvoidCrashKit, _YDSafeThread, _YDLogger, _YDLoggerUI, _YDAvoidCrash, _YDMonitor, _YDTimer, _YDAlertAction, _YDActionSheet, _YDActionAlert, _YDFileManager, - _YDPreLoader, _YDMediator, _YDClearCacheService, _YDImageService ] + _YDPreLoader, _YDMediator, _YDClearCacheService, _YDImageService, _YDEmptyView, _YDBlockKit, _YDAuthorizationUtil, _YDJPush, _YDNetworkManager ] all_subspec.each do |spec| diff --git a/YDFoundation.xcodeproj/project.pbxproj b/YDFoundation.xcodeproj/project.pbxproj index dfcde4f..c436f7c 100644 --- a/YDFoundation.xcodeproj/project.pbxproj +++ b/YDFoundation.xcodeproj/project.pbxproj @@ -223,12 +223,147 @@ 15F893BE28F81F9C003B9519 /* YDImageConfigProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893B628F81F9B003B9519 /* YDImageConfigProtocol.h */; }; 15F893BF28F81F9C003B9519 /* YYWebImageOperation+YDNetworkThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893B728F81F9B003B9519 /* YYWebImageOperation+YDNetworkThread.h */; }; 15F893C028F81F9C003B9519 /* YYWebImageOperation+YDNetworkThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F893B828F81F9B003B9519 /* YYWebImageOperation+YDNetworkThread.m */; }; + 15F893D028F900AE003B9519 /* YDSafeThread.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F893C828F900AD003B9519 /* YDSafeThread.framework */; }; + 15F893D128F900AE003B9519 /* YDSafeThread.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 15F893C828F900AD003B9519 /* YDSafeThread.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 15F893E528F90237003B9519 /* YDThreadSafeMutableSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F893D728F90237003B9519 /* YDThreadSafeMutableSet.m */; }; + 15F893E628F90237003B9519 /* YDThreadSafeMutableArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F893D828F90237003B9519 /* YDThreadSafeMutableArray.m */; }; + 15F893E728F90237003B9519 /* YDLoopThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893D928F90237003B9519 /* YDLoopThread.h */; }; + 15F893E828F90237003B9519 /* YDThreadSafeMutableDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F893DA28F90237003B9519 /* YDThreadSafeMutableDictionary.m */; }; + 15F893E928F90237003B9519 /* YDMainThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893DB28F90237003B9519 /* YDMainThread.h */; }; + 15F893EA28F90237003B9519 /* YDSafeThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893DC28F90237003B9519 /* YDSafeThreadPool.h */; }; + 15F893EB28F90237003B9519 /* YDSafeThread.docc in Sources */ = {isa = PBXBuildFile; fileRef = 15F893DD28F90237003B9519 /* YDSafeThread.docc */; }; + 15F893EC28F90237003B9519 /* YDLoopThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F893DE28F90237003B9519 /* YDLoopThread.m */; }; + 15F893ED28F90237003B9519 /* YDThreadSafeMutableArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893DF28F90237003B9519 /* YDThreadSafeMutableArray.h */; }; + 15F893EE28F90237003B9519 /* YDThreadSafeMutableSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893E028F90237003B9519 /* YDThreadSafeMutableSet.h */; }; + 15F893EF28F90237003B9519 /* YDMainThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F893E128F90237003B9519 /* YDMainThread.m */; }; + 15F893F028F90237003B9519 /* YDThreadSafeMutableDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893E228F90237003B9519 /* YDThreadSafeMutableDictionary.h */; }; + 15F893F128F90237003B9519 /* YDSafeThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893E328F90237003B9519 /* YDSafeThread.h */; }; + 15F893F228F90237003B9519 /* YDSafeThreadPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F893E428F90237003B9519 /* YDSafeThreadPool.m */; }; + 15F893FC28F90511003B9519 /* YDEmptyView.docc in Sources */ = {isa = PBXBuildFile; fileRef = 15F893FB28F90511003B9519 /* YDEmptyView.docc */; }; + 15F893FD28F90511003B9519 /* YDEmptyView.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F893FA28F90511003B9519 /* YDEmptyView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 15F8940028F90511003B9519 /* YDEmptyView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F893F828F90510003B9519 /* YDEmptyView.framework */; }; + 15F8940128F90511003B9519 /* YDEmptyView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 15F893F828F90510003B9519 /* YDEmptyView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 15F8940E28F90527003B9519 /* YDBlockKit.docc in Sources */ = {isa = PBXBuildFile; fileRef = 15F8940D28F90527003B9519 /* YDBlockKit.docc */; }; + 15F8940F28F90527003B9519 /* YDBlockKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8940C28F90527003B9519 /* YDBlockKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 15F8941228F90527003B9519 /* YDBlockKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F8940A28F90526003B9519 /* YDBlockKit.framework */; }; + 15F8941328F90527003B9519 /* YDBlockKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 15F8940A28F90526003B9519 /* YDBlockKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 15F8942028F90542003B9519 /* YDAuthorizationUtil.docc in Sources */ = {isa = PBXBuildFile; fileRef = 15F8941F28F90542003B9519 /* YDAuthorizationUtil.docc */; }; + 15F8942128F90542003B9519 /* YDAuthorizationUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8941E28F90542003B9519 /* YDAuthorizationUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 15F8942428F90542003B9519 /* YDAuthorizationUtil.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F8941C28F90541003B9519 /* YDAuthorizationUtil.framework */; }; + 15F8942528F90542003B9519 /* YDAuthorizationUtil.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 15F8941C28F90541003B9519 /* YDAuthorizationUtil.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 15F8943228F90552003B9519 /* YDJPush.docc in Sources */ = {isa = PBXBuildFile; fileRef = 15F8943128F90552003B9519 /* YDJPush.docc */; }; + 15F8943328F90552003B9519 /* YDJPush.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8943028F90552003B9519 /* YDJPush.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 15F8943628F90552003B9519 /* YDJPush.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F8942E28F90551003B9519 /* YDJPush.framework */; }; + 15F8943728F90552003B9519 /* YDJPush.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 15F8942E28F90551003B9519 /* YDJPush.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 15F8944428F90575003B9519 /* YDNetwrokManager.docc in Sources */ = {isa = PBXBuildFile; fileRef = 15F8944328F90575003B9519 /* YDNetwrokManager.docc */; }; + 15F8944528F90575003B9519 /* YDNetwrokManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8944228F90575003B9519 /* YDNetwrokManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 15F8944828F90575003B9519 /* YDNetwrokManager.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F8944028F90574003B9519 /* YDNetwrokManager.framework */; }; + 15F8944928F90575003B9519 /* YDNetwrokManager.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 15F8944028F90574003B9519 /* YDNetwrokManager.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 15F8945228F90962003B9519 /* YDEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8944D28F90961003B9519 /* YDEmptyView.m */; }; + 15F8945328F90962003B9519 /* YDEmptyViewConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8944E28F90961003B9519 /* YDEmptyViewConfig.m */; }; + 15F8945428F90962003B9519 /* UIViewController+YDMistakesShow.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8944F28F90961003B9519 /* UIViewController+YDMistakesShow.h */; }; + 15F8945528F90962003B9519 /* YDEmptyViewConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8945028F90962003B9519 /* YDEmptyViewConfig.h */; }; + 15F8945628F90962003B9519 /* UIViewController+YDMistakesShow.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8945128F90962003B9519 /* UIViewController+YDMistakesShow.m */; }; + 15F894A328F90A2C003B9519 /* A2BlockInvocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8945828F90A2B003B9519 /* A2BlockInvocation.m */; }; + 15F894A428F90A2C003B9519 /* NSObject+A2DynamicDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8945928F90A2B003B9519 /* NSObject+A2DynamicDelegate.h */; }; + 15F894A528F90A2C003B9519 /* A2DynamicDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8945A28F90A2B003B9519 /* A2DynamicDelegate.h */; }; + 15F894A628F90A2C003B9519 /* NSURLConnection+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8945C28F90A2B003B9519 /* NSURLConnection+BlocksKit.h */; }; + 15F894A728F90A2C003B9519 /* NSCache+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8945D28F90A2B003B9519 /* NSCache+BlocksKit.m */; }; + 15F894A828F90A2C003B9519 /* NSCache+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8945E28F90A2B003B9519 /* NSCache+BlocksKit.h */; }; + 15F894A928F90A2C003B9519 /* NSURLConnection+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8945F28F90A2B003B9519 /* NSURLConnection+BlocksKit.m */; }; + 15F894AA28F90A2C003B9519 /* NSObject+A2BlockDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8946028F90A2B003B9519 /* NSObject+A2BlockDelegate.m */; }; + 15F894AB28F90A2C003B9519 /* A2BlockInvocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8946128F90A2B003B9519 /* A2BlockInvocation.h */; }; + 15F894AC28F90A2C003B9519 /* A2DynamicDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8946228F90A2B003B9519 /* A2DynamicDelegate.m */; }; + 15F894AD28F90A2C003B9519 /* NSObject+A2DynamicDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8946328F90A2B003B9519 /* NSObject+A2DynamicDelegate.m */; }; + 15F894AE28F90A2C003B9519 /* NSObject+A2BlockDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8946428F90A2B003B9519 /* NSObject+A2BlockDelegate.h */; }; + 15F894AF28F90A2C003B9519 /* NSDictionary+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8946628F90A2B003B9519 /* NSDictionary+BlocksKit.m */; }; + 15F894B028F90A2C003B9519 /* NSOrderedSet+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8946728F90A2B003B9519 /* NSOrderedSet+BlocksKit.h */; }; + 15F894B128F90A2C003B9519 /* NSMutableArray+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8946828F90A2B003B9519 /* NSMutableArray+BlocksKit.m */; }; + 15F894B228F90A2C003B9519 /* NSMutableIndexSet+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8946928F90A2B003B9519 /* NSMutableIndexSet+BlocksKit.m */; }; + 15F894B328F90A2C003B9519 /* NSObject+BKBlockExecution.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8946A28F90A2B003B9519 /* NSObject+BKBlockExecution.h */; }; + 15F894B428F90A2C003B9519 /* NSArray+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8946B28F90A2B003B9519 /* NSArray+BlocksKit.h */; }; + 15F894B528F90A2C003B9519 /* NSObject+BKBlockObservation.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8946C28F90A2B003B9519 /* NSObject+BKBlockObservation.m */; }; + 15F894B628F90A2C003B9519 /* NSTimer+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8946D28F90A2B003B9519 /* NSTimer+BlocksKit.m */; }; + 15F894B728F90A2C003B9519 /* NSMapTable+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8946E28F90A2B003B9519 /* NSMapTable+BlocksKit.h */; }; + 15F894B828F90A2C003B9519 /* BKMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8946F28F90A2B003B9519 /* BKMacros.h */; }; + 15F894B928F90A2C003B9519 /* NSObject+BKAssociatedObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947028F90A2B003B9519 /* NSObject+BKAssociatedObjects.h */; }; + 15F894BA28F90A2C003B9519 /* NSObject+YDAsyncBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947128F90A2B003B9519 /* NSObject+YDAsyncBlock.h */; }; + 15F894BB28F90A2C003B9519 /* NSNumber+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947228F90A2B003B9519 /* NSNumber+BlocksKit.h */; }; + 15F894BC28F90A2C003B9519 /* NSInvocation+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8947328F90A2B003B9519 /* NSInvocation+BlocksKit.m */; }; + 15F894BD28F90A2C003B9519 /* NSSet+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8947428F90A2B003B9519 /* NSSet+BlocksKit.m */; }; + 15F894BE28F90A2C003B9519 /* NSIndexSet+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947528F90A2B003B9519 /* NSIndexSet+BlocksKit.h */; }; + 15F894BF28F90A2C003B9519 /* NSMutableSet+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947628F90A2B003B9519 /* NSMutableSet+BlocksKit.h */; }; + 15F894C028F90A2C003B9519 /* NSMutableDictionary+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947728F90A2B003B9519 /* NSMutableDictionary+BlocksKit.h */; }; + 15F894C128F90A2C003B9519 /* NSMutableOrderedSet+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8947828F90A2B003B9519 /* NSMutableOrderedSet+BlocksKit.m */; }; + 15F894C228F90A2C003B9519 /* NSOrderedSet+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8947928F90A2B003B9519 /* NSOrderedSet+BlocksKit.m */; }; + 15F894C328F90A2C003B9519 /* NSDictionary+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947A28F90A2C003B9519 /* NSDictionary+BlocksKit.h */; }; + 15F894C428F90A2C003B9519 /* NSObject+BKAssociatedObjects.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8947B28F90A2C003B9519 /* NSObject+BKAssociatedObjects.m */; }; + 15F894C528F90A2C003B9519 /* NSMapTable+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8947C28F90A2C003B9519 /* NSMapTable+BlocksKit.m */; }; + 15F894C628F90A2C003B9519 /* NSTimer+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947D28F90A2C003B9519 /* NSTimer+BlocksKit.h */; }; + 15F894C728F90A2C003B9519 /* NSObject+BKBlockObservation.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8947E28F90A2C003B9519 /* NSObject+BKBlockObservation.h */; }; + 15F894C828F90A2C003B9519 /* NSArray+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8947F28F90A2C003B9519 /* NSArray+BlocksKit.m */; }; + 15F894C928F90A2C003B9519 /* NSObject+BKBlockExecution.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8948028F90A2C003B9519 /* NSObject+BKBlockExecution.m */; }; + 15F894CA28F90A2C003B9519 /* NSMutableIndexSet+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948128F90A2C003B9519 /* NSMutableIndexSet+BlocksKit.h */; }; + 15F894CB28F90A2C003B9519 /* NSMutableArray+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948228F90A2C003B9519 /* NSMutableArray+BlocksKit.h */; }; + 15F894CC28F90A2C003B9519 /* NSIndexSet+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8948328F90A2C003B9519 /* NSIndexSet+BlocksKit.m */; }; + 15F894CD28F90A2C003B9519 /* NSInvocation+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948428F90A2C003B9519 /* NSInvocation+BlocksKit.h */; }; + 15F894CE28F90A2C003B9519 /* NSSet+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948528F90A2C003B9519 /* NSSet+BlocksKit.h */; }; + 15F894CF28F90A2C003B9519 /* NSNumber+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8948628F90A2C003B9519 /* NSNumber+BlocksKit.m */; }; + 15F894D028F90A2C003B9519 /* NSObject+YDAsyncBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8948728F90A2C003B9519 /* NSObject+YDAsyncBlock.m */; }; + 15F894D128F90A2C003B9519 /* NSMutableOrderedSet+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948828F90A2C003B9519 /* NSMutableOrderedSet+BlocksKit.h */; }; + 15F894D228F90A2C003B9519 /* NSMutableDictionary+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8948928F90A2C003B9519 /* NSMutableDictionary+BlocksKit.m */; }; + 15F894D328F90A2C003B9519 /* NSMutableSet+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8948A28F90A2C003B9519 /* NSMutableSet+BlocksKit.m */; }; + 15F894D428F90A2C003B9519 /* BKDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948B28F90A2C003B9519 /* BKDefines.h */; }; + 15F894D528F90A2C003B9519 /* UIImagePickerController+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948D28F90A2C003B9519 /* UIImagePickerController+BlocksKit.h */; }; + 15F894D628F90A2C003B9519 /* UIActionSheet+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948E28F90A2C003B9519 /* UIActionSheet+BlocksKit.h */; }; + 15F894D728F90A2C003B9519 /* UIPopoverController+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8948F28F90A2C003B9519 /* UIPopoverController+BlocksKit.h */; }; + 15F894D828F90A2C003B9519 /* UITextView+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949028F90A2C003B9519 /* UITextView+BlocksKit.m */; }; + 15F894D928F90A2C003B9519 /* UIControl+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8949128F90A2C003B9519 /* UIControl+BlocksKit.h */; }; + 15F894DA28F90A2C003B9519 /* UITextField+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8949228F90A2C003B9519 /* UITextField+BlocksKit.h */; }; + 15F894DB28F90A2C003B9519 /* UIBarButtonItem+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949328F90A2C003B9519 /* UIBarButtonItem+BlocksKit.m */; }; + 15F894DC28F90A2C003B9519 /* UIImage+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8949428F90A2C003B9519 /* UIImage+BlocksKit.h */; }; + 15F894DD28F90A2C003B9519 /* UIAlertView+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949528F90A2C003B9519 /* UIAlertView+BlocksKit.m */; }; + 15F894DE28F90A2C003B9519 /* UIView+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949628F90A2C003B9519 /* UIView+BlocksKit.m */; }; + 15F894DF28F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949728F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.m */; }; + 15F894E028F90A2C003B9519 /* UIPopoverController+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949828F90A2C003B9519 /* UIPopoverController+BlocksKit.m */; }; + 15F894E128F90A2C003B9519 /* UITextView+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8949928F90A2C003B9519 /* UITextView+BlocksKit.h */; }; + 15F894E228F90A2C003B9519 /* UIActionSheet+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949A28F90A2C003B9519 /* UIActionSheet+BlocksKit.m */; }; + 15F894E328F90A2C003B9519 /* UIImagePickerController+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949B28F90A2C003B9519 /* UIImagePickerController+BlocksKit.m */; }; + 15F894E428F90A2C003B9519 /* UITextField+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949C28F90A2C003B9519 /* UITextField+BlocksKit.m */; }; + 15F894E528F90A2C003B9519 /* UIControl+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8949D28F90A2C003B9519 /* UIControl+BlocksKit.m */; }; + 15F894E628F90A2C003B9519 /* UIView+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8949E28F90A2C003B9519 /* UIView+BlocksKit.h */; }; + 15F894E728F90A2C003B9519 /* UIAlertView+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8949F28F90A2C003B9519 /* UIAlertView+BlocksKit.h */; }; + 15F894E828F90A2C003B9519 /* UIImage+BlocksKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F894A028F90A2C003B9519 /* UIImage+BlocksKit.m */; }; + 15F894E928F90A2C003B9519 /* UIBarButtonItem+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894A128F90A2C003B9519 /* UIBarButtonItem+BlocksKit.h */; }; + 15F894EA28F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894A228F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.h */; }; + 15F894EC28F90AC2003B9519 /* YDAuthorizationUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F894EB28F90AC2003B9519 /* YDAuthorizationUtil.m */; }; + 15F894F228F90BBA003B9519 /* YDJPushManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894ED28F90BB9003B9519 /* YDJPushManager.h */; }; + 15F894F328F90BBA003B9519 /* YDJPushConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F894EE28F90BB9003B9519 /* YDJPushConfig.m */; }; + 15F894F428F90BBA003B9519 /* YDJPushManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F894EF28F90BB9003B9519 /* YDJPushManager.m */; }; + 15F894F528F90BBA003B9519 /* YDJPushConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894F028F90BB9003B9519 /* YDJPushConfig.h */; }; + 15F894F628F90BBA003B9519 /* YDJPushConfigProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894F128F90BBA003B9519 /* YDJPushConfigProtocol.h */; }; + 15F8950228F90E84003B9519 /* YDBatchCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894F828F90E84003B9519 /* YDBatchCommand.h */; }; + 15F8950328F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F894F928F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.m */; }; + 15F8950428F90E84003B9519 /* YDCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894FA28F90E84003B9519 /* YDCommand.h */; }; + 15F8950528F90E84003B9519 /* YDCommand+YDError.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894FB28F90E84003B9519 /* YDCommand+YDError.h */; }; + 15F8950628F90E84003B9519 /* YDBatchCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F894FC28F90E84003B9519 /* YDBatchCommand.m */; }; + 15F8950728F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F894FD28F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.h */; }; + 15F8950828F90E84003B9519 /* YDCommand+YDError.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F894FE28F90E84003B9519 /* YDCommand+YDError.m */; }; + 15F8950928F90E84003B9519 /* YDCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F894FF28F90E84003B9519 /* YDCommand.m */; }; + 15F8950A28F90E84003B9519 /* YDCommandConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 15F8950028F90E84003B9519 /* YDCommandConfig.h */; }; + 15F8950B28F90E84003B9519 /* YDCommandConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 15F8950128F90E84003B9519 /* YDCommandConfig.m */; }; 2B04A132DCCBE9C94D44BBFD /* libPods-Demo-YDPreLoader.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 70DD7948A038F1AC88184EBB /* libPods-Demo-YDPreLoader.a */; }; 2B9FC333C49B365554FFC807 /* libPods-Demo-YDRouter.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 70F7A59B27218BCEC43795C5 /* libPods-Demo-YDRouter.a */; }; + 413100D1EB5883109D2B337C /* libPods-Demo-YDSafeThread.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 221D1D890F55E5C802E4DE90 /* libPods-Demo-YDSafeThread.a */; }; 49A96DEC46BFF2D5AFA66BFC /* libPods-Demo-YDFileManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A69EAE8978E57DACCD29B04 /* libPods-Demo-YDFileManager.a */; }; + 58E0D1ED915450D5259B8A9E /* libPods-Demo-YDEmptyView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AE57CFC9FB3339BD4593366B /* libPods-Demo-YDEmptyView.a */; }; + 9630AD7386E98034D169316A /* libPods-Demo-YDJPush.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E8996D8640022BBC9EA1DAFA /* libPods-Demo-YDJPush.a */; }; + 968C43A74226EA5A513F74A3 /* libPods-Demo-YDNetwrokManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A2093B3507ECC428C15A72F7 /* libPods-Demo-YDNetwrokManager.a */; }; A0F741EA3A65644D59E3607F /* libPods-Demo-YDTimer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5849D7C289E1CFE43AD7F61 /* libPods-Demo-YDTimer.a */; }; A10CD66930D32ACC639D170D /* libPods-Demo-YDMediator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD7C91BDF79B17073B1266C7 /* libPods-Demo-YDMediator.a */; }; A9036DD9EF6727CA11F17B96 /* libPods-Demo-YDAvoidCrashKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E467EF421851A330EB5AFD6C /* libPods-Demo-YDAvoidCrashKit.a */; }; + B1F7953AB883F2E01E41AABD /* libPods-Demo-YDBlockKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9391046E5D5AC8699671FCC4 /* libPods-Demo-YDBlockKit.a */; }; + B790C432EBBAF609B0F5C66A /* libPods-Demo-YDAuthorizationUtil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 261A4D2FF1A35CB9C4D1E593 /* libPods-Demo-YDAuthorizationUtil.a */; }; C8537A37B83567073648B848 /* libPods-Demo-YDImageService.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 05384258C7C4DD2C52B7A969 /* libPods-Demo-YDImageService.a */; }; CA2693DC8412977F45A9DDAA /* libPods-Demo-YDAlertAction.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 685820F8FBE650638E970CE7 /* libPods-Demo-YDAlertAction.a */; }; D93C41CB675E3BF7C4459A79 /* libPods-Demo-YDClearCacheService.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B0D26888CA0999E4B59D6B46 /* libPods-Demo-YDClearCacheService.a */; }; @@ -345,6 +480,48 @@ remoteGlobalIDString = 15F893A328F81F46003B9519; remoteInfo = YDImageService; }; + 15F893CE28F900AE003B9519 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 155FF8B428F6636B00057B87 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 15F893C728F900AC003B9519; + remoteInfo = YDSafeThread; + }; + 15F893FE28F90511003B9519 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 155FF8B428F6636B00057B87 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 15F893F728F90510003B9519; + remoteInfo = YDEmptyView; + }; + 15F8941028F90527003B9519 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 155FF8B428F6636B00057B87 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 15F8940928F90526003B9519; + remoteInfo = YDBlockKit; + }; + 15F8942228F90542003B9519 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 155FF8B428F6636B00057B87 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 15F8941B28F90541003B9519; + remoteInfo = YDAuthorizationUtil; + }; + 15F8943428F90552003B9519 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 155FF8B428F6636B00057B87 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 15F8942D28F90551003B9519; + remoteInfo = YDJPush; + }; + 15F8944628F90575003B9519 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 155FF8B428F6636B00057B87 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 15F8943F28F90574003B9519; + remoteInfo = YDNetwrokManager; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -354,8 +531,12 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 15F8941328F90527003B9519 /* YDBlockKit.framework in Embed Frameworks */, + 15F893D128F900AE003B9519 /* YDSafeThread.framework in Embed Frameworks */, + 15F8940128F90511003B9519 /* YDEmptyView.framework in Embed Frameworks */, 150B9CF828F6CF6E00113B28 /* YDSVProgressHUD.framework in Embed Frameworks */, 15F893AD28F81F47003B9519 /* YDImageService.framework in Embed Frameworks */, + 15F8944928F90575003B9519 /* YDNetwrokManager.framework in Embed Frameworks */, 15652EB428F6BB680050EEB3 /* YDFoundation.framework in Embed Frameworks */, 150204CB28F7B21000025BA4 /* YDAvoidCrashKit.framework in Embed Frameworks */, 155FF96A28F6A2B300057B87 /* YDRouter.framework in Embed Frameworks */, @@ -363,7 +544,9 @@ 150205A228F7EEEA00025BA4 /* YDFileManager.framework in Embed Frameworks */, 15652EEA28F6C24F0050EEB3 /* YDUtilKit.framework in Embed Frameworks */, 155FF94828F6A21C00057B87 /* YDWebp.framework in Embed Frameworks */, + 15F8943728F90552003B9519 /* YDJPush.framework in Embed Frameworks */, 1502057228F7EAB000025BA4 /* YDAlertAction.framework in Embed Frameworks */, + 15F8942528F90542003B9519 /* YDAuthorizationUtil.framework in Embed Frameworks */, 1502055C28F7BF7B00025BA4 /* YDTimer.framework in Embed Frameworks */, 15F8938828F81272003B9519 /* YDClearCacheService.framework in Embed Frameworks */, 15F8936928F81164003B9519 /* YDMediator.framework in Embed Frameworks */, @@ -537,22 +720,6 @@ 155FF98828F6A3D000057B87 /* UIImage+YDWebp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImage+YDWebp.h"; sourceTree = ""; }; 15652EAD28F6BB680050EEB3 /* YDFoundation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YDFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 15652EAF28F6BB680050EEB3 /* YDFoundation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDFoundation.h; sourceTree = ""; }; - 15652EB928F6BCAE0050EEB3 /* YDWebp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDWebp.h; sourceTree = ""; }; - 15652EBA28F6BCAE0050EEB3 /* CGImage+YDWebp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CGImage+YDWebp.h"; sourceTree = ""; }; - 15652EBB28F6BCAE0050EEB3 /* UIImage+YDWebp.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIImage+YDWebp.m"; sourceTree = ""; }; - 15652EBC28F6BCAE0050EEB3 /* CGImage+YDWebp.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CGImage+YDWebp.m"; sourceTree = ""; }; - 15652EBD28F6BCAE0050EEB3 /* YDWebp.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = YDWebp.docc; sourceTree = ""; }; - 15652EBE28F6BCAE0050EEB3 /* UIImage+YDWebp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImage+YDWebp.h"; sourceTree = ""; }; - 15652EC028F6BCAE0050EEB3 /* MGJRouter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGJRouter.h; sourceTree = ""; }; - 15652EC128F6BCAE0050EEB3 /* YDURLHandle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDURLHandle.h; sourceTree = ""; }; - 15652EC228F6BCAE0050EEB3 /* YDRouter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDRouter.h; sourceTree = ""; }; - 15652EC328F6BCAE0050EEB3 /* YDURLHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YDURLHelper.m; sourceTree = ""; }; - 15652EC428F6BCAE0050EEB3 /* YDRouter.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = YDRouter.docc; sourceTree = ""; }; - 15652EC528F6BCAE0050EEB3 /* YDRouter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YDRouter.m; sourceTree = ""; }; - 15652EC628F6BCAE0050EEB3 /* YDURLHandle.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YDURLHandle.m; sourceTree = ""; }; - 15652EC728F6BCAE0050EEB3 /* MGJRouter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGJRouter.m; sourceTree = ""; }; - 15652EC828F6BCAE0050EEB3 /* YDURLHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDURLHelper.h; sourceTree = ""; }; - 15652EC928F6BCAE0050EEB3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 15652EE128F6C24E0050EEB3 /* YDUtilKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YDUtilKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 15652EE328F6C24F0050EEB3 /* YDUtilKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDUtilKit.h; sourceTree = ""; }; 15652EEF28F6C2B70050EEB3 /* NSFileManager+YDCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileManager+YDCommon.h"; sourceTree = ""; }; @@ -653,40 +820,181 @@ 15F893B628F81F9B003B9519 /* YDImageConfigProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDImageConfigProtocol.h; sourceTree = ""; }; 15F893B728F81F9B003B9519 /* YYWebImageOperation+YDNetworkThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "YYWebImageOperation+YDNetworkThread.h"; sourceTree = ""; }; 15F893B828F81F9B003B9519 /* YYWebImageOperation+YDNetworkThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "YYWebImageOperation+YDNetworkThread.m"; sourceTree = ""; }; + 15F893C828F900AD003B9519 /* YDSafeThread.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YDSafeThread.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 15F893D728F90237003B9519 /* YDThreadSafeMutableSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDThreadSafeMutableSet.m; sourceTree = ""; }; + 15F893D828F90237003B9519 /* YDThreadSafeMutableArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDThreadSafeMutableArray.m; sourceTree = ""; }; + 15F893D928F90237003B9519 /* YDLoopThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDLoopThread.h; sourceTree = ""; }; + 15F893DA28F90237003B9519 /* YDThreadSafeMutableDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDThreadSafeMutableDictionary.m; sourceTree = ""; }; + 15F893DB28F90237003B9519 /* YDMainThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDMainThread.h; sourceTree = ""; }; + 15F893DC28F90237003B9519 /* YDSafeThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDSafeThreadPool.h; sourceTree = ""; }; + 15F893DD28F90237003B9519 /* YDSafeThread.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = YDSafeThread.docc; sourceTree = ""; }; + 15F893DE28F90237003B9519 /* YDLoopThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDLoopThread.m; sourceTree = ""; }; + 15F893DF28F90237003B9519 /* YDThreadSafeMutableArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDThreadSafeMutableArray.h; sourceTree = ""; }; + 15F893E028F90237003B9519 /* YDThreadSafeMutableSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDThreadSafeMutableSet.h; sourceTree = ""; }; + 15F893E128F90237003B9519 /* YDMainThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDMainThread.m; sourceTree = ""; }; + 15F893E228F90237003B9519 /* YDThreadSafeMutableDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDThreadSafeMutableDictionary.h; sourceTree = ""; }; + 15F893E328F90237003B9519 /* YDSafeThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDSafeThread.h; sourceTree = ""; }; + 15F893E428F90237003B9519 /* YDSafeThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDSafeThreadPool.m; sourceTree = ""; }; + 15F893F828F90510003B9519 /* YDEmptyView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YDEmptyView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 15F893FA28F90511003B9519 /* YDEmptyView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDEmptyView.h; sourceTree = ""; }; + 15F893FB28F90511003B9519 /* YDEmptyView.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = YDEmptyView.docc; sourceTree = ""; }; + 15F8940A28F90526003B9519 /* YDBlockKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YDBlockKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 15F8940C28F90527003B9519 /* YDBlockKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDBlockKit.h; sourceTree = ""; }; + 15F8940D28F90527003B9519 /* YDBlockKit.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = YDBlockKit.docc; sourceTree = ""; }; + 15F8941C28F90541003B9519 /* YDAuthorizationUtil.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YDAuthorizationUtil.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 15F8941E28F90542003B9519 /* YDAuthorizationUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDAuthorizationUtil.h; sourceTree = ""; }; + 15F8941F28F90542003B9519 /* YDAuthorizationUtil.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = YDAuthorizationUtil.docc; sourceTree = ""; }; + 15F8942E28F90551003B9519 /* YDJPush.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YDJPush.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 15F8943028F90552003B9519 /* YDJPush.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDJPush.h; sourceTree = ""; }; + 15F8943128F90552003B9519 /* YDJPush.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = YDJPush.docc; sourceTree = ""; }; + 15F8944028F90574003B9519 /* YDNetwrokManager.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YDNetwrokManager.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 15F8944228F90575003B9519 /* YDNetwrokManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YDNetwrokManager.h; sourceTree = ""; }; + 15F8944328F90575003B9519 /* YDNetwrokManager.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = YDNetwrokManager.docc; sourceTree = ""; }; + 15F8944D28F90961003B9519 /* YDEmptyView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDEmptyView.m; sourceTree = ""; }; + 15F8944E28F90961003B9519 /* YDEmptyViewConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDEmptyViewConfig.m; sourceTree = ""; }; + 15F8944F28F90961003B9519 /* UIViewController+YDMistakesShow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+YDMistakesShow.h"; sourceTree = ""; }; + 15F8945028F90962003B9519 /* YDEmptyViewConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDEmptyViewConfig.h; sourceTree = ""; }; + 15F8945128F90962003B9519 /* UIViewController+YDMistakesShow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+YDMistakesShow.m"; sourceTree = ""; }; + 15F8945828F90A2B003B9519 /* A2BlockInvocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = A2BlockInvocation.m; sourceTree = ""; }; + 15F8945928F90A2B003B9519 /* NSObject+A2DynamicDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+A2DynamicDelegate.h"; sourceTree = ""; }; + 15F8945A28F90A2B003B9519 /* A2DynamicDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = A2DynamicDelegate.h; sourceTree = ""; }; + 15F8945C28F90A2B003B9519 /* NSURLConnection+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLConnection+BlocksKit.h"; sourceTree = ""; }; + 15F8945D28F90A2B003B9519 /* NSCache+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSCache+BlocksKit.m"; sourceTree = ""; }; + 15F8945E28F90A2B003B9519 /* NSCache+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCache+BlocksKit.h"; sourceTree = ""; }; + 15F8945F28F90A2B003B9519 /* NSURLConnection+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLConnection+BlocksKit.m"; sourceTree = ""; }; + 15F8946028F90A2B003B9519 /* NSObject+A2BlockDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+A2BlockDelegate.m"; sourceTree = ""; }; + 15F8946128F90A2B003B9519 /* A2BlockInvocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = A2BlockInvocation.h; sourceTree = ""; }; + 15F8946228F90A2B003B9519 /* A2DynamicDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = A2DynamicDelegate.m; sourceTree = ""; }; + 15F8946328F90A2B003B9519 /* NSObject+A2DynamicDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+A2DynamicDelegate.m"; sourceTree = ""; }; + 15F8946428F90A2B003B9519 /* NSObject+A2BlockDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+A2BlockDelegate.h"; sourceTree = ""; }; + 15F8946628F90A2B003B9519 /* NSDictionary+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+BlocksKit.m"; sourceTree = ""; }; + 15F8946728F90A2B003B9519 /* NSOrderedSet+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSOrderedSet+BlocksKit.h"; sourceTree = ""; }; + 15F8946828F90A2B003B9519 /* NSMutableArray+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+BlocksKit.m"; sourceTree = ""; }; + 15F8946928F90A2B003B9519 /* NSMutableIndexSet+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableIndexSet+BlocksKit.m"; sourceTree = ""; }; + 15F8946A28F90A2B003B9519 /* NSObject+BKBlockExecution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+BKBlockExecution.h"; sourceTree = ""; }; + 15F8946B28F90A2B003B9519 /* NSArray+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+BlocksKit.h"; sourceTree = ""; }; + 15F8946C28F90A2B003B9519 /* NSObject+BKBlockObservation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+BKBlockObservation.m"; sourceTree = ""; }; + 15F8946D28F90A2B003B9519 /* NSTimer+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+BlocksKit.m"; sourceTree = ""; }; + 15F8946E28F90A2B003B9519 /* NSMapTable+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMapTable+BlocksKit.h"; sourceTree = ""; }; + 15F8946F28F90A2B003B9519 /* BKMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKMacros.h; sourceTree = ""; }; + 15F8947028F90A2B003B9519 /* NSObject+BKAssociatedObjects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+BKAssociatedObjects.h"; sourceTree = ""; }; + 15F8947128F90A2B003B9519 /* NSObject+YDAsyncBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+YDAsyncBlock.h"; sourceTree = ""; }; + 15F8947228F90A2B003B9519 /* NSNumber+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNumber+BlocksKit.h"; sourceTree = ""; }; + 15F8947328F90A2B003B9519 /* NSInvocation+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+BlocksKit.m"; sourceTree = ""; }; + 15F8947428F90A2B003B9519 /* NSSet+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSet+BlocksKit.m"; sourceTree = ""; }; + 15F8947528F90A2B003B9519 /* NSIndexSet+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+BlocksKit.h"; sourceTree = ""; }; + 15F8947628F90A2B003B9519 /* NSMutableSet+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableSet+BlocksKit.h"; sourceTree = ""; }; + 15F8947728F90A2B003B9519 /* NSMutableDictionary+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableDictionary+BlocksKit.h"; sourceTree = ""; }; + 15F8947828F90A2B003B9519 /* NSMutableOrderedSet+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableOrderedSet+BlocksKit.m"; sourceTree = ""; }; + 15F8947928F90A2B003B9519 /* NSOrderedSet+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSOrderedSet+BlocksKit.m"; sourceTree = ""; }; + 15F8947A28F90A2C003B9519 /* NSDictionary+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+BlocksKit.h"; sourceTree = ""; }; + 15F8947B28F90A2C003B9519 /* NSObject+BKAssociatedObjects.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+BKAssociatedObjects.m"; sourceTree = ""; }; + 15F8947C28F90A2C003B9519 /* NSMapTable+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMapTable+BlocksKit.m"; sourceTree = ""; }; + 15F8947D28F90A2C003B9519 /* NSTimer+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTimer+BlocksKit.h"; sourceTree = ""; }; + 15F8947E28F90A2C003B9519 /* NSObject+BKBlockObservation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+BKBlockObservation.h"; sourceTree = ""; }; + 15F8947F28F90A2C003B9519 /* NSArray+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+BlocksKit.m"; sourceTree = ""; }; + 15F8948028F90A2C003B9519 /* NSObject+BKBlockExecution.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+BKBlockExecution.m"; sourceTree = ""; }; + 15F8948128F90A2C003B9519 /* NSMutableIndexSet+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableIndexSet+BlocksKit.h"; sourceTree = ""; }; + 15F8948228F90A2C003B9519 /* NSMutableArray+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+BlocksKit.h"; sourceTree = ""; }; + 15F8948328F90A2C003B9519 /* NSIndexSet+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+BlocksKit.m"; sourceTree = ""; }; + 15F8948428F90A2C003B9519 /* NSInvocation+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+BlocksKit.h"; sourceTree = ""; }; + 15F8948528F90A2C003B9519 /* NSSet+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+BlocksKit.h"; sourceTree = ""; }; + 15F8948628F90A2C003B9519 /* NSNumber+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNumber+BlocksKit.m"; sourceTree = ""; }; + 15F8948728F90A2C003B9519 /* NSObject+YDAsyncBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+YDAsyncBlock.m"; sourceTree = ""; }; + 15F8948828F90A2C003B9519 /* NSMutableOrderedSet+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableOrderedSet+BlocksKit.h"; sourceTree = ""; }; + 15F8948928F90A2C003B9519 /* NSMutableDictionary+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableDictionary+BlocksKit.m"; sourceTree = ""; }; + 15F8948A28F90A2C003B9519 /* NSMutableSet+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableSet+BlocksKit.m"; sourceTree = ""; }; + 15F8948B28F90A2C003B9519 /* BKDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKDefines.h; sourceTree = ""; }; + 15F8948D28F90A2C003B9519 /* UIImagePickerController+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImagePickerController+BlocksKit.h"; sourceTree = ""; }; + 15F8948E28F90A2C003B9519 /* UIActionSheet+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIActionSheet+BlocksKit.h"; sourceTree = ""; }; + 15F8948F28F90A2C003B9519 /* UIPopoverController+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIPopoverController+BlocksKit.h"; sourceTree = ""; }; + 15F8949028F90A2C003B9519 /* UITextView+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+BlocksKit.m"; sourceTree = ""; }; + 15F8949128F90A2C003B9519 /* UIControl+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIControl+BlocksKit.h"; sourceTree = ""; }; + 15F8949228F90A2C003B9519 /* UITextField+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextField+BlocksKit.h"; sourceTree = ""; }; + 15F8949328F90A2C003B9519 /* UIBarButtonItem+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+BlocksKit.m"; sourceTree = ""; }; + 15F8949428F90A2C003B9519 /* UIImage+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+BlocksKit.h"; sourceTree = ""; }; + 15F8949528F90A2C003B9519 /* UIAlertView+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIAlertView+BlocksKit.m"; sourceTree = ""; }; + 15F8949628F90A2C003B9519 /* UIView+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+BlocksKit.m"; sourceTree = ""; }; + 15F8949728F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIGestureRecognizer+BlocksKit.m"; sourceTree = ""; }; + 15F8949828F90A2C003B9519 /* UIPopoverController+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIPopoverController+BlocksKit.m"; sourceTree = ""; }; + 15F8949928F90A2C003B9519 /* UITextView+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+BlocksKit.h"; sourceTree = ""; }; + 15F8949A28F90A2C003B9519 /* UIActionSheet+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIActionSheet+BlocksKit.m"; sourceTree = ""; }; + 15F8949B28F90A2C003B9519 /* UIImagePickerController+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImagePickerController+BlocksKit.m"; sourceTree = ""; }; + 15F8949C28F90A2C003B9519 /* UITextField+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextField+BlocksKit.m"; sourceTree = ""; }; + 15F8949D28F90A2C003B9519 /* UIControl+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIControl+BlocksKit.m"; sourceTree = ""; }; + 15F8949E28F90A2C003B9519 /* UIView+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+BlocksKit.h"; sourceTree = ""; }; + 15F8949F28F90A2C003B9519 /* UIAlertView+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIAlertView+BlocksKit.h"; sourceTree = ""; }; + 15F894A028F90A2C003B9519 /* UIImage+BlocksKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+BlocksKit.m"; sourceTree = ""; }; + 15F894A128F90A2C003B9519 /* UIBarButtonItem+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+BlocksKit.h"; sourceTree = ""; }; + 15F894A228F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIGestureRecognizer+BlocksKit.h"; sourceTree = ""; }; + 15F894EB28F90AC2003B9519 /* YDAuthorizationUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDAuthorizationUtil.m; sourceTree = ""; }; + 15F894ED28F90BB9003B9519 /* YDJPushManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDJPushManager.h; sourceTree = ""; }; + 15F894EE28F90BB9003B9519 /* YDJPushConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDJPushConfig.m; sourceTree = ""; }; + 15F894EF28F90BB9003B9519 /* YDJPushManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDJPushManager.m; sourceTree = ""; }; + 15F894F028F90BB9003B9519 /* YDJPushConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDJPushConfig.h; sourceTree = ""; }; + 15F894F128F90BBA003B9519 /* YDJPushConfigProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDJPushConfigProtocol.h; sourceTree = ""; }; + 15F894F828F90E84003B9519 /* YDBatchCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDBatchCommand.h; sourceTree = ""; }; + 15F894F928F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AFJSONResponseSerializer+YDAgent.m"; sourceTree = ""; }; + 15F894FA28F90E84003B9519 /* YDCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDCommand.h; sourceTree = ""; }; + 15F894FB28F90E84003B9519 /* YDCommand+YDError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "YDCommand+YDError.h"; sourceTree = ""; }; + 15F894FC28F90E84003B9519 /* YDBatchCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDBatchCommand.m; sourceTree = ""; }; + 15F894FD28F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AFJSONResponseSerializer+YDAgent.h"; sourceTree = ""; }; + 15F894FE28F90E84003B9519 /* YDCommand+YDError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "YDCommand+YDError.m"; sourceTree = ""; }; + 15F894FF28F90E84003B9519 /* YDCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDCommand.m; sourceTree = ""; }; + 15F8950028F90E84003B9519 /* YDCommandConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YDCommandConfig.h; sourceTree = ""; }; + 15F8950128F90E84003B9519 /* YDCommandConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YDCommandConfig.m; sourceTree = ""; }; 16D085D6B4C507F245BC7ABE /* Pods-Demo-YDSVProgressHUD.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDSVProgressHUD.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDSVProgressHUD/Pods-Demo-YDSVProgressHUD.debug.xcconfig"; sourceTree = ""; }; 1D505EBECB0D92AD672BFA87 /* Pods-Demo-YDAlertAction.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDAlertAction.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDAlertAction/Pods-Demo-YDAlertAction.release.xcconfig"; sourceTree = ""; }; 1F2A14A9359009CC35E7351A /* Pods-Demo-YDAvoidCrashKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDAvoidCrashKit.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDAvoidCrashKit/Pods-Demo-YDAvoidCrashKit.release.xcconfig"; sourceTree = ""; }; + 221D1D890F55E5C802E4DE90 /* libPods-Demo-YDSafeThread.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDSafeThread.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 261A4D2FF1A35CB9C4D1E593 /* libPods-Demo-YDAuthorizationUtil.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDAuthorizationUtil.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 2A8B173399F58939D5094029 /* Pods-Demo-YDWebp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDWebp.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDWebp/Pods-Demo-YDWebp.debug.xcconfig"; sourceTree = ""; }; 2B6D4C3EC07A80BE5E2095E0 /* Pods-Demo-YDUtilKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDUtilKit.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDUtilKit/Pods-Demo-YDUtilKit.release.xcconfig"; sourceTree = ""; }; 33AA69E105D5010A3CD352D3 /* Pods-Demo-YDFoundation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDFoundation.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDFoundation/Pods-Demo-YDFoundation.release.xcconfig"; sourceTree = ""; }; 3F3FC6915E58BDD1410C6387 /* Pods-Demo-YDFileManager.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDFileManager.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDFileManager/Pods-Demo-YDFileManager.debug.xcconfig"; sourceTree = ""; }; + 3F7E9A61095E8C747A896F27 /* Pods-Demo-YDBlockKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDBlockKit.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDBlockKit/Pods-Demo-YDBlockKit.release.xcconfig"; sourceTree = ""; }; + 44C8EB858C8DD40502E6DDEF /* Pods-Demo-YDSafeThread.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDSafeThread.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDSafeThread/Pods-Demo-YDSafeThread.release.xcconfig"; sourceTree = ""; }; + 4A1891D1BC49B726E8532901 /* Pods-Demo-YDNetwrokManager.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDNetwrokManager.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDNetwrokManager/Pods-Demo-YDNetwrokManager.debug.xcconfig"; sourceTree = ""; }; 4F530558963CE0840CE3E418 /* Pods-Demo-YDImageService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDImageService.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDImageService/Pods-Demo-YDImageService.debug.xcconfig"; sourceTree = ""; }; 56AC9569087FA81F6D0F431E /* Pods-Demo-YDFileManager.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDFileManager.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDFileManager/Pods-Demo-YDFileManager.release.xcconfig"; sourceTree = ""; }; 5C12888EBE845B1DC79D33E8 /* Pods-Demo-YDTimer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDTimer.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDTimer/Pods-Demo-YDTimer.debug.xcconfig"; sourceTree = ""; }; + 6304683E8D2BCA770F960101 /* Pods-Demo-YDBlockKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDBlockKit.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDBlockKit/Pods-Demo-YDBlockKit.debug.xcconfig"; sourceTree = ""; }; 657CC907A183A1D6BAD60E56 /* Pods-Demo-YDFoundationDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDFoundationDemo.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDFoundationDemo/Pods-Demo-YDFoundationDemo.debug.xcconfig"; sourceTree = ""; }; 685820F8FBE650638E970CE7 /* libPods-Demo-YDAlertAction.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDAlertAction.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 6C8173001B4BC2837AA2615C /* Pods-Demo-YDAlertAction.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDAlertAction.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDAlertAction/Pods-Demo-YDAlertAction.debug.xcconfig"; sourceTree = ""; }; 6EE44FADB374F00A1C921640 /* Pods-Demo-YDImageService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDImageService.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDImageService/Pods-Demo-YDImageService.release.xcconfig"; sourceTree = ""; }; 70DD7948A038F1AC88184EBB /* libPods-Demo-YDPreLoader.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDPreLoader.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 70F7A59B27218BCEC43795C5 /* libPods-Demo-YDRouter.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDRouter.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7409A637F6BE705FCAFD0909 /* Pods-Demo-YDAuthorizationUtil.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDAuthorizationUtil.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDAuthorizationUtil/Pods-Demo-YDAuthorizationUtil.release.xcconfig"; sourceTree = ""; }; 740AF606F6F4F09C67240080 /* Pods-Demo-YDTimer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDTimer.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDTimer/Pods-Demo-YDTimer.release.xcconfig"; sourceTree = ""; }; + 763FF49E40ABB2F7D7FB84ED /* Pods-Demo-YDEmptyView.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDEmptyView.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDEmptyView/Pods-Demo-YDEmptyView.release.xcconfig"; sourceTree = ""; }; + 8855BF7F12FFD2C8A8499E80 /* Pods-Demo-YDAuthorizationUtil.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDAuthorizationUtil.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDAuthorizationUtil/Pods-Demo-YDAuthorizationUtil.debug.xcconfig"; sourceTree = ""; }; 89CA84E3FA1E7C4E1F261A36 /* Pods-Demo-YDRouter.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDRouter.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDRouter/Pods-Demo-YDRouter.debug.xcconfig"; sourceTree = ""; }; 928FF33EFADD2F910D07030D /* Pods-Demo-YDMediator.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDMediator.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDMediator/Pods-Demo-YDMediator.release.xcconfig"; sourceTree = ""; }; + 9391046E5D5AC8699671FCC4 /* libPods-Demo-YDBlockKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDBlockKit.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 9513DEE60F752745915B1CE2 /* libPods-Demo-YDUtilKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDUtilKit.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 985067D1ED3234F3F335C399 /* Pods-Demo-YDUtilKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDUtilKit.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDUtilKit/Pods-Demo-YDUtilKit.debug.xcconfig"; sourceTree = ""; }; + A2093B3507ECC428C15A72F7 /* libPods-Demo-YDNetwrokManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDNetwrokManager.a"; sourceTree = BUILT_PRODUCTS_DIR; }; A8844756FF2296FEB377D462 /* libPods-Demo-YDWebp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDWebp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + AE57CFC9FB3339BD4593366B /* libPods-Demo-YDEmptyView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDEmptyView.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B0D26888CA0999E4B59D6B46 /* libPods-Demo-YDClearCacheService.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDClearCacheService.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BB1AC91FD504924B289B50BE /* libPods-Demo-YDFoundationDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDFoundationDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + C05B3C218AFA4B96B3DB8308 /* Pods-Demo-YDEmptyView.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDEmptyView.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDEmptyView/Pods-Demo-YDEmptyView.debug.xcconfig"; sourceTree = ""; }; C1555FC5ACC4F0DA083EDD8C /* Pods-Demo-YDFoundationDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDFoundationDemo.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDFoundationDemo/Pods-Demo-YDFoundationDemo.release.xcconfig"; sourceTree = ""; }; C5D5B567F8944D6ED096A435 /* Pods-Demo-YDAvoidCrashKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDAvoidCrashKit.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDAvoidCrashKit/Pods-Demo-YDAvoidCrashKit.debug.xcconfig"; sourceTree = ""; }; CB490BF21CE2694DA417A94D /* Pods-Demo-YDPreLoader.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDPreLoader.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDPreLoader/Pods-Demo-YDPreLoader.debug.xcconfig"; sourceTree = ""; }; CD7C91BDF79B17073B1266C7 /* libPods-Demo-YDMediator.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDMediator.a"; sourceTree = BUILT_PRODUCTS_DIR; }; D0C5DF6EE72A9AC8EAC8CEAD /* Pods-Demo-YDMediator.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDMediator.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDMediator/Pods-Demo-YDMediator.debug.xcconfig"; sourceTree = ""; }; D0FBB82BF33A16110550D3F2 /* Pods-Demo-YDSVProgressHUD.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDSVProgressHUD.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDSVProgressHUD/Pods-Demo-YDSVProgressHUD.release.xcconfig"; sourceTree = ""; }; + D6A1A33CBE5A4512CA515E43 /* Pods-Demo-YDJPush.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDJPush.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDJPush/Pods-Demo-YDJPush.debug.xcconfig"; sourceTree = ""; }; + DCCC1DCB022DF68DA763F14B /* Pods-Demo-YDNetwrokManager.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDNetwrokManager.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDNetwrokManager/Pods-Demo-YDNetwrokManager.release.xcconfig"; sourceTree = ""; }; + DD75EF7AAE74C527603EE635 /* Pods-Demo-YDJPush.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDJPush.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDJPush/Pods-Demo-YDJPush.release.xcconfig"; sourceTree = ""; }; DE2C472930C4DC39287966E3 /* Pods-Demo-YDWebp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDWebp.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDWebp/Pods-Demo-YDWebp.release.xcconfig"; sourceTree = ""; }; E467EF421851A330EB5AFD6C /* libPods-Demo-YDAvoidCrashKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDAvoidCrashKit.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E5849D7C289E1CFE43AD7F61 /* libPods-Demo-YDTimer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDTimer.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + E8996D8640022BBC9EA1DAFA /* libPods-Demo-YDJPush.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDJPush.a"; sourceTree = BUILT_PRODUCTS_DIR; }; EB2C18A9FE25B410E1DCCCBE /* Pods-Demo-YDPreLoader.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDPreLoader.release.xcconfig"; path = "Target Support Files/Pods-Demo-YDPreLoader/Pods-Demo-YDPreLoader.release.xcconfig"; sourceTree = ""; }; + EB531028C1ED0AD4B3B72BD2 /* Pods-Demo-YDSafeThread.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDSafeThread.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDSafeThread/Pods-Demo-YDSafeThread.debug.xcconfig"; sourceTree = ""; }; F11DCF586C38A097B709FE02 /* libPods-Demo-YDFoundation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDFoundation.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F4B083465D8AA9FDEA93E157 /* libPods-Demo-YDSVProgressHUD.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Demo-YDSVProgressHUD.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FE8A79B5C1CF2644056A754B /* Pods-Demo-YDClearCacheService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo-YDClearCacheService.debug.xcconfig"; path = "Target Support Files/Pods-Demo-YDClearCacheService/Pods-Demo-YDClearCacheService.debug.xcconfig"; sourceTree = ""; }; @@ -738,20 +1046,26 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 15F8941228F90527003B9519 /* YDBlockKit.framework in Frameworks */, 15F8934E28F80C1D003B9519 /* YDPreLoader.framework in Frameworks */, 155FF96928F6A2B300057B87 /* YDRouter.framework in Frameworks */, 150B9CF728F6CF6E00113B28 /* YDSVProgressHUD.framework in Frameworks */, 15F893AC28F81F47003B9519 /* YDImageService.framework in Frameworks */, 155FF94728F6A21C00057B87 /* YDWebp.framework in Frameworks */, 1502055B28F7BF7B00025BA4 /* YDTimer.framework in Frameworks */, + 15F893D028F900AE003B9519 /* YDSafeThread.framework in Frameworks */, 15652EE928F6C24F0050EEB3 /* YDUtilKit.framework in Frameworks */, + 15F8940028F90511003B9519 /* YDEmptyView.framework in Frameworks */, 15F8936828F81164003B9519 /* YDMediator.framework in Frameworks */, + 15F8942428F90542003B9519 /* YDAuthorizationUtil.framework in Frameworks */, + 15F8944828F90575003B9519 /* YDNetwrokManager.framework in Frameworks */, 150205A128F7EEEA00025BA4 /* YDFileManager.framework in Frameworks */, 1502057128F7EAB000025BA4 /* YDAlertAction.framework in Frameworks */, 15652EB328F6BB680050EEB3 /* YDFoundation.framework in Frameworks */, F87DFE70EE6E1B32A4544D85 /* libPods-Demo-YDFoundationDemo.a in Frameworks */, 150204CA28F7B21000025BA4 /* YDAvoidCrashKit.framework in Frameworks */, 15F8938728F81272003B9519 /* YDClearCacheService.framework in Frameworks */, + 15F8943628F90552003B9519 /* YDJPush.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -833,6 +1147,54 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 15F893C528F900AC003B9519 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 413100D1EB5883109D2B337C /* libPods-Demo-YDSafeThread.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F893F528F90510003B9519 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 58E0D1ED915450D5259B8A9E /* libPods-Demo-YDEmptyView.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8940728F90526003B9519 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B1F7953AB883F2E01E41AABD /* libPods-Demo-YDBlockKit.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8941928F90541003B9519 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B790C432EBBAF609B0F5C66A /* libPods-Demo-YDAuthorizationUtil.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8942B28F90551003B9519 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9630AD7386E98034D169316A /* libPods-Demo-YDJPush.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8943D28F90574003B9519 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 968C43A74226EA5A513F74A3 /* libPods-Demo-YDNetwrokManager.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -1058,6 +1420,12 @@ isa = PBXGroup; children = ( 15652EAE28F6BB680050EEB3 /* YDFoundation */, + 15F8944128F90575003B9519 /* YDNetwrokManager */, + 15F8942F28F90552003B9519 /* YDJPush */, + 15F8941D28F90542003B9519 /* YDAuthorizationUtil */, + 15F8940B28F90527003B9519 /* YDBlockKit */, + 15F893F928F90511003B9519 /* YDEmptyView */, + 15F893D628F90237003B9519 /* YDSafeThread */, 15F893A528F81F47003B9519 /* YDImageService */, 15F8938028F81272003B9519 /* YDClearCacheService */, 15F8936128F81164003B9519 /* YDMediator */, @@ -1098,6 +1466,12 @@ 15F8936028F81163003B9519 /* YDMediator.framework */, 15F8937F28F81272003B9519 /* YDClearCacheService.framework */, 15F893A428F81F46003B9519 /* YDImageService.framework */, + 15F893C828F900AD003B9519 /* YDSafeThread.framework */, + 15F893F828F90510003B9519 /* YDEmptyView.framework */, + 15F8940A28F90526003B9519 /* YDBlockKit.framework */, + 15F8941C28F90541003B9519 /* YDAuthorizationUtil.framework */, + 15F8942E28F90551003B9519 /* YDJPush.framework */, + 15F8944028F90574003B9519 /* YDNetwrokManager.framework */, ); name = Products; sourceTree = ""; @@ -1170,43 +1544,11 @@ 15652EAE28F6BB680050EEB3 /* YDFoundation */ = { isa = PBXGroup; children = ( - 15652EBF28F6BCAE0050EEB3 /* YDRouter */, - 15652EB828F6BCAE0050EEB3 /* YDWebp */, 15652EAF28F6BB680050EEB3 /* YDFoundation.h */, ); path = YDFoundation; sourceTree = ""; }; - 15652EB828F6BCAE0050EEB3 /* YDWebp */ = { - isa = PBXGroup; - children = ( - 15652EB928F6BCAE0050EEB3 /* YDWebp.h */, - 15652EBA28F6BCAE0050EEB3 /* CGImage+YDWebp.h */, - 15652EBB28F6BCAE0050EEB3 /* UIImage+YDWebp.m */, - 15652EBC28F6BCAE0050EEB3 /* CGImage+YDWebp.m */, - 15652EBD28F6BCAE0050EEB3 /* YDWebp.docc */, - 15652EBE28F6BCAE0050EEB3 /* UIImage+YDWebp.h */, - ); - path = YDWebp; - sourceTree = SOURCE_ROOT; - }; - 15652EBF28F6BCAE0050EEB3 /* YDRouter */ = { - isa = PBXGroup; - children = ( - 15652EC028F6BCAE0050EEB3 /* MGJRouter.h */, - 15652EC128F6BCAE0050EEB3 /* YDURLHandle.h */, - 15652EC228F6BCAE0050EEB3 /* YDRouter.h */, - 15652EC328F6BCAE0050EEB3 /* YDURLHelper.m */, - 15652EC428F6BCAE0050EEB3 /* YDRouter.docc */, - 15652EC528F6BCAE0050EEB3 /* YDRouter.m */, - 15652EC628F6BCAE0050EEB3 /* YDURLHandle.m */, - 15652EC728F6BCAE0050EEB3 /* MGJRouter.m */, - 15652EC828F6BCAE0050EEB3 /* YDURLHelper.h */, - 15652EC928F6BCAE0050EEB3 /* Info.plist */, - ); - path = YDRouter; - sourceTree = SOURCE_ROOT; - }; 15652EE228F6C24F0050EEB3 /* YDUtilKit */ = { isa = PBXGroup; children = ( @@ -1383,6 +1725,205 @@ path = YDImageService; sourceTree = ""; }; + 15F893D628F90237003B9519 /* YDSafeThread */ = { + isa = PBXGroup; + children = ( + 15F893D728F90237003B9519 /* YDThreadSafeMutableSet.m */, + 15F893D828F90237003B9519 /* YDThreadSafeMutableArray.m */, + 15F893D928F90237003B9519 /* YDLoopThread.h */, + 15F893DA28F90237003B9519 /* YDThreadSafeMutableDictionary.m */, + 15F893DB28F90237003B9519 /* YDMainThread.h */, + 15F893DC28F90237003B9519 /* YDSafeThreadPool.h */, + 15F893DD28F90237003B9519 /* YDSafeThread.docc */, + 15F893DE28F90237003B9519 /* YDLoopThread.m */, + 15F893DF28F90237003B9519 /* YDThreadSafeMutableArray.h */, + 15F893E028F90237003B9519 /* YDThreadSafeMutableSet.h */, + 15F893E128F90237003B9519 /* YDMainThread.m */, + 15F893E228F90237003B9519 /* YDThreadSafeMutableDictionary.h */, + 15F893E328F90237003B9519 /* YDSafeThread.h */, + 15F893E428F90237003B9519 /* YDSafeThreadPool.m */, + ); + path = YDSafeThread; + sourceTree = ""; + }; + 15F893F928F90511003B9519 /* YDEmptyView */ = { + isa = PBXGroup; + children = ( + 15F893FA28F90511003B9519 /* YDEmptyView.h */, + 15F8944F28F90961003B9519 /* UIViewController+YDMistakesShow.h */, + 15F8945128F90962003B9519 /* UIViewController+YDMistakesShow.m */, + 15F8944D28F90961003B9519 /* YDEmptyView.m */, + 15F8945028F90962003B9519 /* YDEmptyViewConfig.h */, + 15F8944E28F90961003B9519 /* YDEmptyViewConfig.m */, + 15F893FB28F90511003B9519 /* YDEmptyView.docc */, + ); + path = YDEmptyView; + sourceTree = ""; + }; + 15F8940B28F90527003B9519 /* YDBlockKit */ = { + isa = PBXGroup; + children = ( + 15F8940C28F90527003B9519 /* YDBlockKit.h */, + 15F8948B28F90A2C003B9519 /* BKDefines.h */, + 15F8946528F90A2B003B9519 /* Core */, + 15F8945728F90A2B003B9519 /* DynamicDelegate */, + 15F8948C28F90A2C003B9519 /* UIKit */, + 15F8940D28F90527003B9519 /* YDBlockKit.docc */, + ); + path = YDBlockKit; + sourceTree = ""; + }; + 15F8941D28F90542003B9519 /* YDAuthorizationUtil */ = { + isa = PBXGroup; + children = ( + 15F8941E28F90542003B9519 /* YDAuthorizationUtil.h */, + 15F894EB28F90AC2003B9519 /* YDAuthorizationUtil.m */, + 15F8941F28F90542003B9519 /* YDAuthorizationUtil.docc */, + ); + path = YDAuthorizationUtil; + sourceTree = ""; + }; + 15F8942F28F90552003B9519 /* YDJPush */ = { + isa = PBXGroup; + children = ( + 15F8943028F90552003B9519 /* YDJPush.h */, + 15F894F028F90BB9003B9519 /* YDJPushConfig.h */, + 15F894EE28F90BB9003B9519 /* YDJPushConfig.m */, + 15F894F128F90BBA003B9519 /* YDJPushConfigProtocol.h */, + 15F894ED28F90BB9003B9519 /* YDJPushManager.h */, + 15F894EF28F90BB9003B9519 /* YDJPushManager.m */, + 15F8943128F90552003B9519 /* YDJPush.docc */, + ); + path = YDJPush; + sourceTree = ""; + }; + 15F8944128F90575003B9519 /* YDNetwrokManager */ = { + isa = PBXGroup; + children = ( + 15F8944228F90575003B9519 /* YDNetwrokManager.h */, + 15F894F728F90E84003B9519 /* YDCommand */, + 15F8950028F90E84003B9519 /* YDCommandConfig.h */, + 15F8950128F90E84003B9519 /* YDCommandConfig.m */, + 15F8944328F90575003B9519 /* YDNetwrokManager.docc */, + ); + path = YDNetwrokManager; + sourceTree = ""; + }; + 15F8945728F90A2B003B9519 /* DynamicDelegate */ = { + isa = PBXGroup; + children = ( + 15F8945828F90A2B003B9519 /* A2BlockInvocation.m */, + 15F8945928F90A2B003B9519 /* NSObject+A2DynamicDelegate.h */, + 15F8945A28F90A2B003B9519 /* A2DynamicDelegate.h */, + 15F8945B28F90A2B003B9519 /* Foundation */, + 15F8946028F90A2B003B9519 /* NSObject+A2BlockDelegate.m */, + 15F8946128F90A2B003B9519 /* A2BlockInvocation.h */, + 15F8946228F90A2B003B9519 /* A2DynamicDelegate.m */, + 15F8946328F90A2B003B9519 /* NSObject+A2DynamicDelegate.m */, + 15F8946428F90A2B003B9519 /* NSObject+A2BlockDelegate.h */, + ); + path = DynamicDelegate; + sourceTree = ""; + }; + 15F8945B28F90A2B003B9519 /* Foundation */ = { + isa = PBXGroup; + children = ( + 15F8945C28F90A2B003B9519 /* NSURLConnection+BlocksKit.h */, + 15F8945D28F90A2B003B9519 /* NSCache+BlocksKit.m */, + 15F8945E28F90A2B003B9519 /* NSCache+BlocksKit.h */, + 15F8945F28F90A2B003B9519 /* NSURLConnection+BlocksKit.m */, + ); + path = Foundation; + sourceTree = ""; + }; + 15F8946528F90A2B003B9519 /* Core */ = { + isa = PBXGroup; + children = ( + 15F8946628F90A2B003B9519 /* NSDictionary+BlocksKit.m */, + 15F8946728F90A2B003B9519 /* NSOrderedSet+BlocksKit.h */, + 15F8946828F90A2B003B9519 /* NSMutableArray+BlocksKit.m */, + 15F8946928F90A2B003B9519 /* NSMutableIndexSet+BlocksKit.m */, + 15F8946A28F90A2B003B9519 /* NSObject+BKBlockExecution.h */, + 15F8946B28F90A2B003B9519 /* NSArray+BlocksKit.h */, + 15F8946C28F90A2B003B9519 /* NSObject+BKBlockObservation.m */, + 15F8946D28F90A2B003B9519 /* NSTimer+BlocksKit.m */, + 15F8946E28F90A2B003B9519 /* NSMapTable+BlocksKit.h */, + 15F8946F28F90A2B003B9519 /* BKMacros.h */, + 15F8947028F90A2B003B9519 /* NSObject+BKAssociatedObjects.h */, + 15F8947128F90A2B003B9519 /* NSObject+YDAsyncBlock.h */, + 15F8947228F90A2B003B9519 /* NSNumber+BlocksKit.h */, + 15F8947328F90A2B003B9519 /* NSInvocation+BlocksKit.m */, + 15F8947428F90A2B003B9519 /* NSSet+BlocksKit.m */, + 15F8947528F90A2B003B9519 /* NSIndexSet+BlocksKit.h */, + 15F8947628F90A2B003B9519 /* NSMutableSet+BlocksKit.h */, + 15F8947728F90A2B003B9519 /* NSMutableDictionary+BlocksKit.h */, + 15F8947828F90A2B003B9519 /* NSMutableOrderedSet+BlocksKit.m */, + 15F8947928F90A2B003B9519 /* NSOrderedSet+BlocksKit.m */, + 15F8947A28F90A2C003B9519 /* NSDictionary+BlocksKit.h */, + 15F8947B28F90A2C003B9519 /* NSObject+BKAssociatedObjects.m */, + 15F8947C28F90A2C003B9519 /* NSMapTable+BlocksKit.m */, + 15F8947D28F90A2C003B9519 /* NSTimer+BlocksKit.h */, + 15F8947E28F90A2C003B9519 /* NSObject+BKBlockObservation.h */, + 15F8947F28F90A2C003B9519 /* NSArray+BlocksKit.m */, + 15F8948028F90A2C003B9519 /* NSObject+BKBlockExecution.m */, + 15F8948128F90A2C003B9519 /* NSMutableIndexSet+BlocksKit.h */, + 15F8948228F90A2C003B9519 /* NSMutableArray+BlocksKit.h */, + 15F8948328F90A2C003B9519 /* NSIndexSet+BlocksKit.m */, + 15F8948428F90A2C003B9519 /* NSInvocation+BlocksKit.h */, + 15F8948528F90A2C003B9519 /* NSSet+BlocksKit.h */, + 15F8948628F90A2C003B9519 /* NSNumber+BlocksKit.m */, + 15F8948728F90A2C003B9519 /* NSObject+YDAsyncBlock.m */, + 15F8948828F90A2C003B9519 /* NSMutableOrderedSet+BlocksKit.h */, + 15F8948928F90A2C003B9519 /* NSMutableDictionary+BlocksKit.m */, + 15F8948A28F90A2C003B9519 /* NSMutableSet+BlocksKit.m */, + ); + path = Core; + sourceTree = ""; + }; + 15F8948C28F90A2C003B9519 /* UIKit */ = { + isa = PBXGroup; + children = ( + 15F8948D28F90A2C003B9519 /* UIImagePickerController+BlocksKit.h */, + 15F8948E28F90A2C003B9519 /* UIActionSheet+BlocksKit.h */, + 15F8948F28F90A2C003B9519 /* UIPopoverController+BlocksKit.h */, + 15F8949028F90A2C003B9519 /* UITextView+BlocksKit.m */, + 15F8949128F90A2C003B9519 /* UIControl+BlocksKit.h */, + 15F8949228F90A2C003B9519 /* UITextField+BlocksKit.h */, + 15F8949328F90A2C003B9519 /* UIBarButtonItem+BlocksKit.m */, + 15F8949428F90A2C003B9519 /* UIImage+BlocksKit.h */, + 15F8949528F90A2C003B9519 /* UIAlertView+BlocksKit.m */, + 15F8949628F90A2C003B9519 /* UIView+BlocksKit.m */, + 15F8949728F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.m */, + 15F8949828F90A2C003B9519 /* UIPopoverController+BlocksKit.m */, + 15F8949928F90A2C003B9519 /* UITextView+BlocksKit.h */, + 15F8949A28F90A2C003B9519 /* UIActionSheet+BlocksKit.m */, + 15F8949B28F90A2C003B9519 /* UIImagePickerController+BlocksKit.m */, + 15F8949C28F90A2C003B9519 /* UITextField+BlocksKit.m */, + 15F8949D28F90A2C003B9519 /* UIControl+BlocksKit.m */, + 15F8949E28F90A2C003B9519 /* UIView+BlocksKit.h */, + 15F8949F28F90A2C003B9519 /* UIAlertView+BlocksKit.h */, + 15F894A028F90A2C003B9519 /* UIImage+BlocksKit.m */, + 15F894A128F90A2C003B9519 /* UIBarButtonItem+BlocksKit.h */, + 15F894A228F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.h */, + ); + path = UIKit; + sourceTree = ""; + }; + 15F894F728F90E84003B9519 /* YDCommand */ = { + isa = PBXGroup; + children = ( + 15F894F828F90E84003B9519 /* YDBatchCommand.h */, + 15F894FC28F90E84003B9519 /* YDBatchCommand.m */, + 15F894FD28F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.h */, + 15F894F928F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.m */, + 15F894FA28F90E84003B9519 /* YDCommand.h */, + 15F894FF28F90E84003B9519 /* YDCommand.m */, + 15F894FB28F90E84003B9519 /* YDCommand+YDError.h */, + 15F894FE28F90E84003B9519 /* YDCommand+YDError.m */, + ); + path = YDCommand; + sourceTree = ""; + }; 2D2731C3336908C7B084B141 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1400,6 +1941,12 @@ B0D26888CA0999E4B59D6B46 /* libPods-Demo-YDClearCacheService.a */, CD7C91BDF79B17073B1266C7 /* libPods-Demo-YDMediator.a */, 05384258C7C4DD2C52B7A969 /* libPods-Demo-YDImageService.a */, + 221D1D890F55E5C802E4DE90 /* libPods-Demo-YDSafeThread.a */, + 261A4D2FF1A35CB9C4D1E593 /* libPods-Demo-YDAuthorizationUtil.a */, + 9391046E5D5AC8699671FCC4 /* libPods-Demo-YDBlockKit.a */, + AE57CFC9FB3339BD4593366B /* libPods-Demo-YDEmptyView.a */, + E8996D8640022BBC9EA1DAFA /* libPods-Demo-YDJPush.a */, + A2093B3507ECC428C15A72F7 /* libPods-Demo-YDNetwrokManager.a */, ); name = Frameworks; sourceTree = ""; @@ -1435,6 +1982,18 @@ 928FF33EFADD2F910D07030D /* Pods-Demo-YDMediator.release.xcconfig */, 4F530558963CE0840CE3E418 /* Pods-Demo-YDImageService.debug.xcconfig */, 6EE44FADB374F00A1C921640 /* Pods-Demo-YDImageService.release.xcconfig */, + EB531028C1ED0AD4B3B72BD2 /* Pods-Demo-YDSafeThread.debug.xcconfig */, + 44C8EB858C8DD40502E6DDEF /* Pods-Demo-YDSafeThread.release.xcconfig */, + 8855BF7F12FFD2C8A8499E80 /* Pods-Demo-YDAuthorizationUtil.debug.xcconfig */, + 7409A637F6BE705FCAFD0909 /* Pods-Demo-YDAuthorizationUtil.release.xcconfig */, + 6304683E8D2BCA770F960101 /* Pods-Demo-YDBlockKit.debug.xcconfig */, + 3F7E9A61095E8C747A896F27 /* Pods-Demo-YDBlockKit.release.xcconfig */, + C05B3C218AFA4B96B3DB8308 /* Pods-Demo-YDEmptyView.debug.xcconfig */, + 763FF49E40ABB2F7D7FB84ED /* Pods-Demo-YDEmptyView.release.xcconfig */, + D6A1A33CBE5A4512CA515E43 /* Pods-Demo-YDJPush.debug.xcconfig */, + DD75EF7AAE74C527603EE635 /* Pods-Demo-YDJPush.release.xcconfig */, + 4A1891D1BC49B726E8532901 /* Pods-Demo-YDNetwrokManager.debug.xcconfig */, + DCCC1DCB022DF68DA763F14B /* Pods-Demo-YDNetwrokManager.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -1626,6 +2185,107 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 15F893C328F900AC003B9519 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F893F128F90237003B9519 /* YDSafeThread.h in Headers */, + 15F893EA28F90237003B9519 /* YDSafeThreadPool.h in Headers */, + 15F893ED28F90237003B9519 /* YDThreadSafeMutableArray.h in Headers */, + 15F893E928F90237003B9519 /* YDMainThread.h in Headers */, + 15F893EE28F90237003B9519 /* YDThreadSafeMutableSet.h in Headers */, + 15F893E728F90237003B9519 /* YDLoopThread.h in Headers */, + 15F893F028F90237003B9519 /* YDThreadSafeMutableDictionary.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F893F328F90510003B9519 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F8945428F90962003B9519 /* UIViewController+YDMistakesShow.h in Headers */, + 15F893FD28F90511003B9519 /* YDEmptyView.h in Headers */, + 15F8945528F90962003B9519 /* YDEmptyViewConfig.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8940528F90526003B9519 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F894C728F90A2C003B9519 /* NSObject+BKBlockObservation.h in Headers */, + 15F894D528F90A2C003B9519 /* UIImagePickerController+BlocksKit.h in Headers */, + 15F894D428F90A2C003B9519 /* BKDefines.h in Headers */, + 15F894EA28F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.h in Headers */, + 15F894BB28F90A2C003B9519 /* NSNumber+BlocksKit.h in Headers */, + 15F894A428F90A2C003B9519 /* NSObject+A2DynamicDelegate.h in Headers */, + 15F894DA28F90A2C003B9519 /* UITextField+BlocksKit.h in Headers */, + 15F894BE28F90A2C003B9519 /* NSIndexSet+BlocksKit.h in Headers */, + 15F894E728F90A2C003B9519 /* UIAlertView+BlocksKit.h in Headers */, + 15F894B028F90A2C003B9519 /* NSOrderedSet+BlocksKit.h in Headers */, + 15F894CB28F90A2C003B9519 /* NSMutableArray+BlocksKit.h in Headers */, + 15F894B328F90A2C003B9519 /* NSObject+BKBlockExecution.h in Headers */, + 15F894A528F90A2C003B9519 /* A2DynamicDelegate.h in Headers */, + 15F894BF28F90A2C003B9519 /* NSMutableSet+BlocksKit.h in Headers */, + 15F894DC28F90A2C003B9519 /* UIImage+BlocksKit.h in Headers */, + 15F894BA28F90A2C003B9519 /* NSObject+YDAsyncBlock.h in Headers */, + 15F894C628F90A2C003B9519 /* NSTimer+BlocksKit.h in Headers */, + 15F894B928F90A2C003B9519 /* NSObject+BKAssociatedObjects.h in Headers */, + 15F894CA28F90A2C003B9519 /* NSMutableIndexSet+BlocksKit.h in Headers */, + 15F894A628F90A2C003B9519 /* NSURLConnection+BlocksKit.h in Headers */, + 15F894AB28F90A2C003B9519 /* A2BlockInvocation.h in Headers */, + 15F894B828F90A2C003B9519 /* BKMacros.h in Headers */, + 15F894B428F90A2C003B9519 /* NSArray+BlocksKit.h in Headers */, + 15F894E928F90A2C003B9519 /* UIBarButtonItem+BlocksKit.h in Headers */, + 15F894CE28F90A2C003B9519 /* NSSet+BlocksKit.h in Headers */, + 15F894D628F90A2C003B9519 /* UIActionSheet+BlocksKit.h in Headers */, + 15F894E128F90A2C003B9519 /* UITextView+BlocksKit.h in Headers */, + 15F894C028F90A2C003B9519 /* NSMutableDictionary+BlocksKit.h in Headers */, + 15F894CD28F90A2C003B9519 /* NSInvocation+BlocksKit.h in Headers */, + 15F894A828F90A2C003B9519 /* NSCache+BlocksKit.h in Headers */, + 15F894C328F90A2C003B9519 /* NSDictionary+BlocksKit.h in Headers */, + 15F894E628F90A2C003B9519 /* UIView+BlocksKit.h in Headers */, + 15F894D128F90A2C003B9519 /* NSMutableOrderedSet+BlocksKit.h in Headers */, + 15F894B728F90A2C003B9519 /* NSMapTable+BlocksKit.h in Headers */, + 15F894AE28F90A2C003B9519 /* NSObject+A2BlockDelegate.h in Headers */, + 15F8940F28F90527003B9519 /* YDBlockKit.h in Headers */, + 15F894D728F90A2C003B9519 /* UIPopoverController+BlocksKit.h in Headers */, + 15F894D928F90A2C003B9519 /* UIControl+BlocksKit.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8941728F90541003B9519 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F8942128F90542003B9519 /* YDAuthorizationUtil.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8942928F90551003B9519 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F894F628F90BBA003B9519 /* YDJPushConfigProtocol.h in Headers */, + 15F894F228F90BBA003B9519 /* YDJPushManager.h in Headers */, + 15F8943328F90552003B9519 /* YDJPush.h in Headers */, + 15F894F528F90BBA003B9519 /* YDJPushConfig.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8943B28F90574003B9519 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F8950228F90E84003B9519 /* YDBatchCommand.h in Headers */, + 15F8944528F90575003B9519 /* YDNetwrokManager.h in Headers */, + 15F8950728F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.h in Headers */, + 15F8950A28F90E84003B9519 /* YDCommandConfig.h in Headers */, + 15F8950428F90E84003B9519 /* YDCommand.h in Headers */, + 15F8950528F90E84003B9519 /* YDCommand+YDError.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -1752,6 +2412,12 @@ 15F8936728F81164003B9519 /* PBXTargetDependency */, 15F8938628F81272003B9519 /* PBXTargetDependency */, 15F893AB28F81F47003B9519 /* PBXTargetDependency */, + 15F893CF28F900AE003B9519 /* PBXTargetDependency */, + 15F893FF28F90511003B9519 /* PBXTargetDependency */, + 15F8941128F90527003B9519 /* PBXTargetDependency */, + 15F8942328F90542003B9519 /* PBXTargetDependency */, + 15F8943528F90552003B9519 /* PBXTargetDependency */, + 15F8944728F90575003B9519 /* PBXTargetDependency */, ); name = YDFoundationDemo; productName = YDFoundation; @@ -1946,6 +2612,120 @@ productReference = 15F893A428F81F46003B9519 /* YDImageService.framework */; productType = "com.apple.product-type.framework"; }; + 15F893C728F900AC003B9519 /* YDSafeThread */ = { + isa = PBXNativeTarget; + buildConfigurationList = 15F893D228F900AE003B9519 /* Build configuration list for PBXNativeTarget "YDSafeThread" */; + buildPhases = ( + 38E4F73FDEBF773068D8230F /* [CP] Check Pods Manifest.lock */, + 15F893C328F900AC003B9519 /* Headers */, + 15F893C428F900AC003B9519 /* Sources */, + 15F893C528F900AC003B9519 /* Frameworks */, + 15F893C628F900AC003B9519 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YDSafeThread; + productName = YDSafeThread; + productReference = 15F893C828F900AD003B9519 /* YDSafeThread.framework */; + productType = "com.apple.product-type.framework"; + }; + 15F893F728F90510003B9519 /* YDEmptyView */ = { + isa = PBXNativeTarget; + buildConfigurationList = 15F8940428F90511003B9519 /* Build configuration list for PBXNativeTarget "YDEmptyView" */; + buildPhases = ( + FDEFC84370466A892BFABA5E /* [CP] Check Pods Manifest.lock */, + 15F893F328F90510003B9519 /* Headers */, + 15F893F428F90510003B9519 /* Sources */, + 15F893F528F90510003B9519 /* Frameworks */, + 15F893F628F90510003B9519 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YDEmptyView; + productName = YDEmptyView; + productReference = 15F893F828F90510003B9519 /* YDEmptyView.framework */; + productType = "com.apple.product-type.framework"; + }; + 15F8940928F90526003B9519 /* YDBlockKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 15F8941428F90528003B9519 /* Build configuration list for PBXNativeTarget "YDBlockKit" */; + buildPhases = ( + D9D4607DC8C1A878F74E3625 /* [CP] Check Pods Manifest.lock */, + 15F8940528F90526003B9519 /* Headers */, + 15F8940628F90526003B9519 /* Sources */, + 15F8940728F90526003B9519 /* Frameworks */, + 15F8940828F90526003B9519 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YDBlockKit; + productName = YDBlockKit; + productReference = 15F8940A28F90526003B9519 /* YDBlockKit.framework */; + productType = "com.apple.product-type.framework"; + }; + 15F8941B28F90541003B9519 /* YDAuthorizationUtil */ = { + isa = PBXNativeTarget; + buildConfigurationList = 15F8942628F90543003B9519 /* Build configuration list for PBXNativeTarget "YDAuthorizationUtil" */; + buildPhases = ( + 3D43EB89DFE8B8529F68D1D0 /* [CP] Check Pods Manifest.lock */, + 15F8941728F90541003B9519 /* Headers */, + 15F8941828F90541003B9519 /* Sources */, + 15F8941928F90541003B9519 /* Frameworks */, + 15F8941A28F90541003B9519 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YDAuthorizationUtil; + productName = YDAuthorizationUtil; + productReference = 15F8941C28F90541003B9519 /* YDAuthorizationUtil.framework */; + productType = "com.apple.product-type.framework"; + }; + 15F8942D28F90551003B9519 /* YDJPush */ = { + isa = PBXNativeTarget; + buildConfigurationList = 15F8943828F90553003B9519 /* Build configuration list for PBXNativeTarget "YDJPush" */; + buildPhases = ( + 617EF16DA46C95BFCAF7D8F2 /* [CP] Check Pods Manifest.lock */, + 15F8942928F90551003B9519 /* Headers */, + 15F8942A28F90551003B9519 /* Sources */, + 15F8942B28F90551003B9519 /* Frameworks */, + 15F8942C28F90551003B9519 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YDJPush; + productName = YDJPush; + productReference = 15F8942E28F90551003B9519 /* YDJPush.framework */; + productType = "com.apple.product-type.framework"; + }; + 15F8943F28F90574003B9519 /* YDNetwrokManager */ = { + isa = PBXNativeTarget; + buildConfigurationList = 15F8944A28F90576003B9519 /* Build configuration list for PBXNativeTarget "YDNetwrokManager" */; + buildPhases = ( + 3BB52F35F970165BF8CEC14E /* [CP] Check Pods Manifest.lock */, + 15F8943B28F90574003B9519 /* Headers */, + 15F8943C28F90574003B9519 /* Sources */, + 15F8943D28F90574003B9519 /* Frameworks */, + 15F8943E28F90574003B9519 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YDNetwrokManager; + productName = YDNetwrokManager; + productReference = 15F8944028F90574003B9519 /* YDNetwrokManager.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -2005,6 +2785,24 @@ 15F893A328F81F46003B9519 = { CreatedOnToolsVersion = 14.0.1; }; + 15F893C728F900AC003B9519 = { + CreatedOnToolsVersion = 14.0.1; + }; + 15F893F728F90510003B9519 = { + CreatedOnToolsVersion = 14.0.1; + }; + 15F8940928F90526003B9519 = { + CreatedOnToolsVersion = 14.0.1; + }; + 15F8941B28F90541003B9519 = { + CreatedOnToolsVersion = 14.0.1; + }; + 15F8942D28F90551003B9519 = { + CreatedOnToolsVersion = 14.0.1; + }; + 15F8943F28F90574003B9519 = { + CreatedOnToolsVersion = 14.0.1; + }; }; }; buildConfigurationList = 155FF8B728F6636B00057B87 /* Build configuration list for PBXProject "YDFoundation" */; @@ -2036,6 +2834,12 @@ 15F8935F28F81163003B9519 /* YDMediator */, 15F8937E28F81272003B9519 /* YDClearCacheService */, 15F893A328F81F46003B9519 /* YDImageService */, + 15F893C728F900AC003B9519 /* YDSafeThread */, + 15F893F728F90510003B9519 /* YDEmptyView */, + 15F8940928F90526003B9519 /* YDBlockKit */, + 15F8941B28F90541003B9519 /* YDAuthorizationUtil */, + 15F8942D28F90551003B9519 /* YDJPush */, + 15F8943F28F90574003B9519 /* YDNetwrokManager */, ); }; /* End PBXProject section */ @@ -2156,37 +2960,79 @@ ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 0A486CCCA692C763FA9F65DF /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; + 15F893C628F900AC003B9519 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + runOnlyForDeploymentPostprocessing = 0; + }; + 15F893F628F90510003B9519 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Demo-YDMediator-checkManifestLockResult.txt", + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8940828F90526003B9519 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; - 1CBC90CDC98E53F9488B5CAC /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; + 15F8941A28F90541003B9519 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8942C28F90551003B9519 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8943E28F90574003B9519 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0A486CCCA692C763FA9F65DF /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Demo-YDMediator-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 1CBC90CDC98E53F9488B5CAC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", @@ -2269,6 +3115,50 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 38E4F73FDEBF773068D8230F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Demo-YDSafeThread-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3BB52F35F970165BF8CEC14E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Demo-YDNetwrokManager-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3CF4D2623D1DC0B30DD15057 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2286,6 +3176,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Demo-YDFoundationDemo/Pods-Demo-YDFoundationDemo-resources.sh\"\n"; showEnvVarsInLog = 0; }; + 3D43EB89DFE8B8529F68D1D0 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Demo-YDAuthorizationUtil-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 46EC8A11CD534B0915B1A371 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2352,6 +3264,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 617EF16DA46C95BFCAF7D8F2 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Demo-YDJPush-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 8BB9E339713E77658956D3F7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2396,6 +3330,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + D9D4607DC8C1A878F74E3625 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Demo-YDBlockKit-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; EBA7EFCBB59B96703F199705 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2501,6 +3457,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + FDEFC84370466A892BFABA5E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Demo-YDEmptyView-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -2710,6 +3688,106 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 15F893C428F900AC003B9519 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F893F228F90237003B9519 /* YDSafeThreadPool.m in Sources */, + 15F893EB28F90237003B9519 /* YDSafeThread.docc in Sources */, + 15F893E628F90237003B9519 /* YDThreadSafeMutableArray.m in Sources */, + 15F893EC28F90237003B9519 /* YDLoopThread.m in Sources */, + 15F893E528F90237003B9519 /* YDThreadSafeMutableSet.m in Sources */, + 15F893E828F90237003B9519 /* YDThreadSafeMutableDictionary.m in Sources */, + 15F893EF28F90237003B9519 /* YDMainThread.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F893F428F90510003B9519 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F893FC28F90511003B9519 /* YDEmptyView.docc in Sources */, + 15F8945628F90962003B9519 /* UIViewController+YDMistakesShow.m in Sources */, + 15F8945228F90962003B9519 /* YDEmptyView.m in Sources */, + 15F8945328F90962003B9519 /* YDEmptyViewConfig.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8940628F90526003B9519 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F894DF28F90A2C003B9519 /* UIGestureRecognizer+BlocksKit.m in Sources */, + 15F894B528F90A2C003B9519 /* NSObject+BKBlockObservation.m in Sources */, + 15F894B628F90A2C003B9519 /* NSTimer+BlocksKit.m in Sources */, + 15F894D828F90A2C003B9519 /* UITextView+BlocksKit.m in Sources */, + 15F894BC28F90A2C003B9519 /* NSInvocation+BlocksKit.m in Sources */, + 15F894AA28F90A2C003B9519 /* NSObject+A2BlockDelegate.m in Sources */, + 15F894D228F90A2C003B9519 /* NSMutableDictionary+BlocksKit.m in Sources */, + 15F894E528F90A2C003B9519 /* UIControl+BlocksKit.m in Sources */, + 15F894C428F90A2C003B9519 /* NSObject+BKAssociatedObjects.m in Sources */, + 15F894B128F90A2C003B9519 /* NSMutableArray+BlocksKit.m in Sources */, + 15F894CC28F90A2C003B9519 /* NSIndexSet+BlocksKit.m in Sources */, + 15F894C828F90A2C003B9519 /* NSArray+BlocksKit.m in Sources */, + 15F894D328F90A2C003B9519 /* NSMutableSet+BlocksKit.m in Sources */, + 15F894DB28F90A2C003B9519 /* UIBarButtonItem+BlocksKit.m in Sources */, + 15F894B228F90A2C003B9519 /* NSMutableIndexSet+BlocksKit.m in Sources */, + 15F894DE28F90A2C003B9519 /* UIView+BlocksKit.m in Sources */, + 15F894C228F90A2C003B9519 /* NSOrderedSet+BlocksKit.m in Sources */, + 15F894E328F90A2C003B9519 /* UIImagePickerController+BlocksKit.m in Sources */, + 15F894C928F90A2C003B9519 /* NSObject+BKBlockExecution.m in Sources */, + 15F894AC28F90A2C003B9519 /* A2DynamicDelegate.m in Sources */, + 15F894A728F90A2C003B9519 /* NSCache+BlocksKit.m in Sources */, + 15F894E428F90A2C003B9519 /* UITextField+BlocksKit.m in Sources */, + 15F894BD28F90A2C003B9519 /* NSSet+BlocksKit.m in Sources */, + 15F894CF28F90A2C003B9519 /* NSNumber+BlocksKit.m in Sources */, + 15F894E828F90A2C003B9519 /* UIImage+BlocksKit.m in Sources */, + 15F894AF28F90A2C003B9519 /* NSDictionary+BlocksKit.m in Sources */, + 15F894A328F90A2C003B9519 /* A2BlockInvocation.m in Sources */, + 15F894D028F90A2C003B9519 /* NSObject+YDAsyncBlock.m in Sources */, + 15F894AD28F90A2C003B9519 /* NSObject+A2DynamicDelegate.m in Sources */, + 15F8940E28F90527003B9519 /* YDBlockKit.docc in Sources */, + 15F894E228F90A2C003B9519 /* UIActionSheet+BlocksKit.m in Sources */, + 15F894DD28F90A2C003B9519 /* UIAlertView+BlocksKit.m in Sources */, + 15F894C528F90A2C003B9519 /* NSMapTable+BlocksKit.m in Sources */, + 15F894E028F90A2C003B9519 /* UIPopoverController+BlocksKit.m in Sources */, + 15F894A928F90A2C003B9519 /* NSURLConnection+BlocksKit.m in Sources */, + 15F894C128F90A2C003B9519 /* NSMutableOrderedSet+BlocksKit.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8941828F90541003B9519 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F894EC28F90AC2003B9519 /* YDAuthorizationUtil.m in Sources */, + 15F8942028F90542003B9519 /* YDAuthorizationUtil.docc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8942A28F90551003B9519 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F894F428F90BBA003B9519 /* YDJPushManager.m in Sources */, + 15F8943228F90552003B9519 /* YDJPush.docc in Sources */, + 15F894F328F90BBA003B9519 /* YDJPushConfig.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 15F8943C28F90574003B9519 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 15F8950B28F90E84003B9519 /* YDCommandConfig.m in Sources */, + 15F8950828F90E84003B9519 /* YDCommand+YDError.m in Sources */, + 15F8950628F90E84003B9519 /* YDBatchCommand.m in Sources */, + 15F8950328F90E84003B9519 /* AFJSONResponseSerializer+YDAgent.m in Sources */, + 15F8944428F90575003B9519 /* YDNetwrokManager.docc in Sources */, + 15F8950928F90E84003B9519 /* YDCommand.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -2788,6 +3866,36 @@ target = 15F893A328F81F46003B9519 /* YDImageService */; targetProxy = 15F893AA28F81F47003B9519 /* PBXContainerItemProxy */; }; + 15F893CF28F900AE003B9519 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 15F893C728F900AC003B9519 /* YDSafeThread */; + targetProxy = 15F893CE28F900AE003B9519 /* PBXContainerItemProxy */; + }; + 15F893FF28F90511003B9519 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 15F893F728F90510003B9519 /* YDEmptyView */; + targetProxy = 15F893FE28F90511003B9519 /* PBXContainerItemProxy */; + }; + 15F8941128F90527003B9519 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 15F8940928F90526003B9519 /* YDBlockKit */; + targetProxy = 15F8941028F90527003B9519 /* PBXContainerItemProxy */; + }; + 15F8942328F90542003B9519 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 15F8941B28F90541003B9519 /* YDAuthorizationUtil */; + targetProxy = 15F8942228F90542003B9519 /* PBXContainerItemProxy */; + }; + 15F8943528F90552003B9519 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 15F8942D28F90551003B9519 /* YDJPush */; + targetProxy = 15F8943428F90552003B9519 /* PBXContainerItemProxy */; + }; + 15F8944728F90575003B9519 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 15F8943F28F90574003B9519 /* YDNetwrokManager */; + targetProxy = 15F8944628F90575003B9519 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -3246,7 +4354,7 @@ INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3275,7 +4383,7 @@ INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3850,21 +4958,393 @@ }; name = Release; }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 150204CC28F7B21000025BA4 /* Build configuration list for PBXNativeTarget "YDAvoidCrashKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 150204CD28F7B21000025BA4 /* Debug */, - 150204CE28F7B21000025BA4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + 15F893D328F900AE003B9519 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EB531028C1ED0AD4B3B72BD2 /* Pods-Demo-YDSafeThread.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDSafeThread; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; }; - 1502055D28F7BF7B00025BA4 /* Build configuration list for PBXNativeTarget "YDTimer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( + 15F893D428F900AE003B9519 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 44C8EB858C8DD40502E6DDEF /* Pods-Demo-YDSafeThread.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDSafeThread; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 15F8940228F90511003B9519 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C05B3C218AFA4B96B3DB8308 /* Pods-Demo-YDEmptyView.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDEmptyView; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 15F8940328F90511003B9519 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 763FF49E40ABB2F7D7FB84ED /* Pods-Demo-YDEmptyView.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDEmptyView; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 15F8941528F90528003B9519 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6304683E8D2BCA770F960101 /* Pods-Demo-YDBlockKit.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDBlockKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 15F8941628F90528003B9519 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F7E9A61095E8C747A896F27 /* Pods-Demo-YDBlockKit.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDBlockKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 15F8942728F90543003B9519 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8855BF7F12FFD2C8A8499E80 /* Pods-Demo-YDAuthorizationUtil.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDAuthorizationUtil; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 15F8942828F90543003B9519 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7409A637F6BE705FCAFD0909 /* Pods-Demo-YDAuthorizationUtil.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDAuthorizationUtil; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 15F8943928F90553003B9519 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D6A1A33CBE5A4512CA515E43 /* Pods-Demo-YDJPush.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDJPush; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 15F8943A28F90553003B9519 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DD75EF7AAE74C527603EE635 /* Pods-Demo-YDJPush.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDJPush; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 15F8944B28F90576003B9519 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4A1891D1BC49B726E8532901 /* Pods-Demo-YDNetwrokManager.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDNetwrokManager; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 15F8944C28F90576003B9519 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DCCC1DCB022DF68DA763F14B /* Pods-Demo-YDNetwrokManager.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = C3V5PUXTCL; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.wyd.YDNetwrokManager; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 150204CC28F7B21000025BA4 /* Build configuration list for PBXNativeTarget "YDAvoidCrashKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 150204CD28F7B21000025BA4 /* Debug */, + 150204CE28F7B21000025BA4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1502055D28F7BF7B00025BA4 /* Build configuration list for PBXNativeTarget "YDTimer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( 1502055E28F7BF7B00025BA4 /* Debug */, 1502055F28F7BF7B00025BA4 /* Release */, ); @@ -4006,6 +5486,60 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 15F893D228F900AE003B9519 /* Build configuration list for PBXNativeTarget "YDSafeThread" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15F893D328F900AE003B9519 /* Debug */, + 15F893D428F900AE003B9519 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 15F8940428F90511003B9519 /* Build configuration list for PBXNativeTarget "YDEmptyView" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15F8940228F90511003B9519 /* Debug */, + 15F8940328F90511003B9519 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 15F8941428F90528003B9519 /* Build configuration list for PBXNativeTarget "YDBlockKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15F8941528F90528003B9519 /* Debug */, + 15F8941628F90528003B9519 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 15F8942628F90543003B9519 /* Build configuration list for PBXNativeTarget "YDAuthorizationUtil" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15F8942728F90543003B9519 /* Debug */, + 15F8942828F90543003B9519 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 15F8943828F90553003B9519 /* Build configuration list for PBXNativeTarget "YDJPush" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15F8943928F90553003B9519 /* Debug */, + 15F8943A28F90553003B9519 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 15F8944A28F90576003B9519 /* Build configuration list for PBXNativeTarget "YDNetwrokManager" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15F8944B28F90576003B9519 /* Debug */, + 15F8944C28F90576003B9519 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 155FF8B428F6636B00057B87 /* Project object */; diff --git a/YDFoundationDemo/AppDelegate.m b/YDFoundationDemo/AppDelegate.m index 32ac8ba..7f974f5 100644 --- a/YDFoundationDemo/AppDelegate.m +++ b/YDFoundationDemo/AppDelegate.m @@ -24,7 +24,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( #pragma mark - UISceneSession lifecycle -- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { +- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)){ // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; diff --git a/YDJPush/YDJPush.docc/YDJPush.md b/YDJPush/YDJPush.docc/YDJPush.md new file mode 100755 index 0000000..93e421d --- /dev/null +++ b/YDJPush/YDJPush.docc/YDJPush.md @@ -0,0 +1,13 @@ +# ``YDJPush`` + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` \ No newline at end of file diff --git a/YDJPush/YDJPush.h b/YDJPush/YDJPush.h new file mode 100644 index 0000000..b8e68b4 --- /dev/null +++ b/YDJPush/YDJPush.h @@ -0,0 +1,18 @@ +// +// YDJPush.h +// YDJPush +// +// Created by 王远东 on 2022/10/14. +// + +#import + +//! Project version number for YDJPush. +FOUNDATION_EXPORT double YDJPushVersionNumber; + +//! Project version string for YDJPush. +FOUNDATION_EXPORT const unsigned char YDJPushVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/YDJPush/YDJPushConfig.h b/YDJPush/YDJPushConfig.h new file mode 100644 index 0000000..755f4e6 --- /dev/null +++ b/YDJPush/YDJPushConfig.h @@ -0,0 +1,18 @@ +// +// YDJPushConfig.h +// YDJPush +// +// Created by 王远东 on 2022/9/1. +// Copyright © 2022 wangyuandong. All rights reserved. +// + +#import +#import "YDJPushConfigProtocol.h" +NS_ASSUME_NONNULL_BEGIN + +@interface YDJPushConfig : NSObject + +- (void)configParams; +@end + +NS_ASSUME_NONNULL_END diff --git a/YDJPush/YDJPushConfig.m b/YDJPush/YDJPushConfig.m new file mode 100644 index 0000000..cba538a --- /dev/null +++ b/YDJPush/YDJPushConfig.m @@ -0,0 +1,30 @@ +// +// YDJPushConfig.m +// YDJPush +// +// Created by 王远东 on 2022/9/1. +// Copyright © 2022 wangyuandong. All rights reserved. +// + +#import "YDJPushConfig.h" + +@implementation YDJPushConfig +@synthesize appKey = _appKey; +@synthesize isProduction = _isProduction; +@synthesize uid = _uid; +@synthesize tags = _tags; +@synthesize loginNotificationName = _loginNotificationName; +@synthesize logoutNotificationName = _logoutNotificationName; + +- (instancetype)init { + if (self = [super init]){ + [self configParams]; + } + return self; +} + +- (void)configParams { + +} + +@end diff --git a/YDJPush/YDJPushConfigProtocol.h b/YDJPush/YDJPushConfigProtocol.h new file mode 100644 index 0000000..8747cda --- /dev/null +++ b/YDJPush/YDJPushConfigProtocol.h @@ -0,0 +1,23 @@ +// +// Created by 王远东 on 2022/9/1. +// + +#import + +@protocol YDJPushConfigProtocol +/** 极光注册的appkey 需要外部区分scheme */ +@property (nonatomic, copy)NSString *appKey; +/** 是否生产环境. 如果为开发状态,设置为 NO; 如果为生产状态,应改为 YES */ +@property (nonatomic, assign)BOOL isProduction; + +/** 添加的tags 例如:tags = [NSSet setWithObjects:@"VERSION1_2_0",@"VERSION1_4_0", nil]; */ +@property (nonatomic, copy)NSSet *tags; +/** 当前登录状态下的uid */ +@property (nonatomic, copy)NSString *uid; + +/** 登录通知 */ +@property (nonatomic, copy)NSString *loginNotificationName; +/** 登出通知 */ +@property (nonatomic, copy)NSString *logoutNotificationName; + +@end \ No newline at end of file diff --git a/YDJPush/YDJPushManager.h b/YDJPush/YDJPushManager.h new file mode 100644 index 0000000..d764e12 --- /dev/null +++ b/YDJPush/YDJPushManager.h @@ -0,0 +1,60 @@ +// +// Created by 王远东 on 2022/9/1. +// + +#import +#import +#import "YDJPushConfigProtocol.h" + +typedef void (^AuthorizationStatusDenied)(void); +typedef void (^YDMessageInfo)(NSDictionary* userInfo); + +@interface YDJPushManager : NSObject +@property (nonatomic, copy, readonly)NSString *registrationID; + +/** + 单例初始化(推荐) + + @return YDJPushManager + */ ++ (instancetype)shared; + +/** + 单例初始化 + + @param config 配置项 + @return JPushManager + */ ++ (instancetype)shareWithConfig:(id)config; + + +/** + 启动SDK + + @param launchOptions 启动参数 + @param notiBlock apns消息回调 + @param msgBlock 极光消息回调 + */ +- (void)configJPush:(NSDictionary *)launchOptions didReceiveNotification:(YDMessageInfo)notiBlock didReceiveMessage:(YDMessageInfo)msgBlock; + + +/// 启动SDK +/// @param launchOptions 启动参数 +/// @param registSuccessBlock 极光注册成功回调register id +/// @param notiBlock apns消息回调 +/// @param msgBlock 极光消息回调 +- (void)configJPush:(NSDictionary *)launchOptions didRegistSuccess:(void(^)(NSString *registerID))registSuccessBlock didReceiveNotification:(YDMessageInfo)notiBlock didReceiveMessage:(YDMessageInfo)msgBlock; + ++ (void)registerForRemoteNotificationTypes; ++ (void)registerDeviceToken:(NSData *)deviceToken; ++ (void)setUserAlias:(NSString *)alias; ++ (void)handleRemoteNotification:(NSDictionary *)userInfo; ++ (void)resetBadge; + +/** + 通知权限判断 + + @param deniedBlock 无权限回调 + */ ++ (void)checkNotiDeniedStatus:(AuthorizationStatusDenied)deniedBlock; +@end diff --git a/YDJPush/YDJPushManager.m b/YDJPush/YDJPushManager.m new file mode 100644 index 0000000..8e18c21 --- /dev/null +++ b/YDJPush/YDJPushManager.m @@ -0,0 +1,258 @@ +// +// Created by 王远东 on 2022/9/1. +// + +#import "YDJPushManager.h" +#import "JPUSHService.h" +#import "YDJPushConfig.h" +// iOS10注册APNs所需头文件 +#ifdef NSFoundationVersionNumber_iOS_9_x_Max +#import +#endif + +@interface YDJPushManager() + +@property (nonatomic, copy)YDMessageInfo messageBlock; +@property (nonatomic, copy)YDMessageInfo notiBlock; +@property (nonatomic, strong)id config; + +@end + +@implementation YDJPushManager +static YDJPushManager *_instance; + ++ (instancetype)shared{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _instance = [[self alloc] initWithConfig:[YDJPushConfig new]]; + }); + return _instance; +} + ++ (instancetype)shareWithConfig:(id)config +{ + [YDJPushManager shared].config = config; + return [YDJPushManager shared]; +} + +- (instancetype)initWithConfig:(id)config{ + if (self = [super init]) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkDidReceiveMessage:) name:kJPFNetworkDidReceiveMessageNotification object:nil]; + self.config = config; + } + return self; +} + + ++ (NSInteger)getJPushSeq { + static NSInteger seq = 0; + return seq ++; +} + ++ (void)resetBadge { + [JPUSHService resetBadge]; + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; +} + +//收到消息(非APNS) +- (void)networkDidReceiveMessage:(NSNotification *)notification { + NSDictionary *userInfo = [notification userInfo]; + NSLog(@"message : %@",userInfo); + if (userInfo[@"extras"]) { + if (self.messageBlock) { + self.messageBlock(userInfo); + } + } +} + +- (void)configJPush:(NSDictionary *)launchOptions didReceiveNotification:(YDMessageInfo)notiBlock didReceiveMessage:(YDMessageInfo)msgBlock{ + + self.notiBlock = notiBlock; + self.messageBlock = msgBlock; + + [JPUSHService setupWithOption:launchOptions appKey:self.config.appKey channel:nil apsForProduction:self.config.isProduction]; + + [YDJPushManager registerForRemoteNotificationTypes]; + + [self registrationJPushID:nil]; +} + +- (void)configJPush:(NSDictionary *)launchOptions didRegistSuccess:(void(^)(NSString *registerID))registSuccessBlock didReceiveNotification:(YDMessageInfo)notiBlock didReceiveMessage:(YDMessageInfo)msgBlock { + self.notiBlock = notiBlock; + self.messageBlock = msgBlock; + + [JPUSHService setupWithOption:launchOptions appKey:self.config.appKey channel:nil apsForProduction:self.config.isProduction]; + + [YDJPushManager registerForRemoteNotificationTypes]; + + [self registrationJPushID:registSuccessBlock]; +} + +- (void)registrationJPushID:(void(^)(NSString * registerID))registSuccessBlock { + //2.1.9版本新增获取registration id block接口。 + [JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) { + if(resCode == 0){ + [self configJPushAlias:self.config.uid]; + NSLog(@"registrationID获取成功:%@",registrationID); + if (registSuccessBlock) { + registSuccessBlock(registrationID); + } + } + else{ + if (resCode == 1011) { + NSLog(@"模拟器 registrationID获取失败,code:%d",resCode); + return; + } + NSLog(@"registrationID获取失败,code:%d",resCode); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self registrationJPushID:registSuccessBlock]; + }); + } + }]; +} + +//使用uid +- (void)configJPushAlias:(NSString *)uid{ + + NSString *alias = uid.length > 0 ? uid : self.registrationID; + + [[self class] setUserAlias:alias]; + [self setJPUSHUserTag]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loginIn:) name:self.config.loginNotificationName object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logout:) name:self.config.logoutNotificationName object:nil]; +} + +//登录 +- (void)loginIn:(NSNotification *)noti{ + [[self class] setUserAlias:self.config.uid]; + [self setJPUSHUserTag]; +} + +//登出 +- (void)logout:(NSNotification *)noti{ + [[self class] setUserAlias:self.registrationID]; + [self setJPUSHUserTag]; +} + +- (void)setJPUSHUserTag { + if (self.config.tags.count) { + //存在自定义tag + [JPUSHService setTags:self.config.tags completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) { + if (iResCode != 0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self setJPUSHUserTag]; + }); + } + } seq:[[self class] getJPushSeq]]; + } +} + +- (NSString *)registrationID +{ + return [JPUSHService registrationID]; +} + ++ (void)registerDeviceToken:(NSData *)deviceToken +{ + [JPUSHService registerDeviceToken:deviceToken]; +} + ++ (void)registerForRemoteNotificationTypes +{ + //Required + if (@available(iOS 10.0, *)) { + JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init]; + entity.types = + (UNAuthorizationOptionAlert| + UNAuthorizationOptionBadge| + UNAuthorizationOptionSound); + [JPUSHService registerForRemoteNotificationConfig:entity delegate:_instance]; + } else { + //可以添加自定义categories + [JPUSHService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge| UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil]; + } +} + ++ (void)setUserAlias:(NSString *)alias{ + [JPUSHService setAlias:alias completion:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) { + NSLog(@"rescode: %tu, \niAlias: %@, \nseq: %zd\n", iResCode, iAlias , seq); + if (iResCode == 0) { + //同步极光别名注册信息 + }else { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [[self class] setUserAlias:alias]; + }); + } + } seq:[self getJPushSeq]]; +} + ++ (void)handleRemoteNotification: (NSDictionary *) userInfo +{ + [JPUSHService handleRemoteNotification:userInfo]; +} + ++ (void)tagsAliasCallback:(int)iResCode tags:(NSSet*)tags alias:(NSString*)alias +{ + NSLog(@"rescode: %d, \ntags: %@, \nalias: %@\n", iResCode, tags , alias); +} + + +#pragma mark- JPUSHRegisterDelegate +// iOS 10 Support 在ios10之前如果当前应用在前台 不会弹系统通知 在ios10 以后实现该方法会在应用在前台的时候弹系统通知 +- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler API_AVAILABLE(ios(10.0)){ + // Required + NSDictionary *userInfo = notification.request.content.userInfo; + if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { + [JPUSHService handleRemoteNotification:userInfo]; + } + NSLog(@"userInfo : %@",userInfo); + if (self.messageBlock) { + self.messageBlock(userInfo); + } + +// NSInteger pushtype = [[userInfo objectForKey:@"pushtype"] integerValue]; + + completionHandler(UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以选择设置 + + +} + +// iOS 10 Support 点通知中心的消息进入应用时 会调用该方法 +- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){ + // Required + NSDictionary *userInfo = response.notification.request.content.userInfo; + NSLog(@"userInfo : %@",userInfo); + + if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { + [JPUSHService handleRemoteNotification:userInfo]; + } + if (self.messageBlock) { + self.messageBlock(userInfo); + } + if(self.notiBlock) { + self.notiBlock(userInfo); + } + completionHandler(); //系统要求执行这个方法 +} + ++ (void)checkNotiDeniedStatus:(AuthorizationStatusDenied)deniedBlock{ + if (@available(iOS 10,*)) { + [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { + if (settings.authorizationStatus == UNAuthorizationStatusDenied) { + //没权限 + if (deniedBlock) deniedBlock(); + } + }]; + } + else { + UIUserNotificationSettings * setting = [[UIApplication sharedApplication] currentUserNotificationSettings]; + if (setting.types == UIUserNotificationTypeNone) { + //没权限 + if (deniedBlock) deniedBlock(); + } + } +} + +@end \ No newline at end of file diff --git a/YDNetwrokManager/LICENSE b/YDNetwrokManager/LICENSE new file mode 100644 index 0000000..76a0539 --- /dev/null +++ b/YDNetwrokManager/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 wangyuandong + +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/YDNetwrokManager/YDCommand/AFJSONResponseSerializer+YDAgent.h b/YDNetwrokManager/YDCommand/AFJSONResponseSerializer+YDAgent.h new file mode 100644 index 0000000..454b351 --- /dev/null +++ b/YDNetwrokManager/YDCommand/AFJSONResponseSerializer+YDAgent.h @@ -0,0 +1,16 @@ +// +// AFJSONResponseSerializer+YDAgent.h +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface AFJSONResponseSerializer (YDAgent) + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDNetwrokManager/YDCommand/AFJSONResponseSerializer+YDAgent.m b/YDNetwrokManager/YDCommand/AFJSONResponseSerializer+YDAgent.m new file mode 100644 index 0000000..3a2f5d3 --- /dev/null +++ b/YDNetwrokManager/YDCommand/AFJSONResponseSerializer+YDAgent.m @@ -0,0 +1,42 @@ +// +// AFJSONResponseSerializer+YDAgent.m +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import "AFJSONResponseSerializer+YDAgent.h" +#import + +@implementation AFJSONResponseSerializer (YDAgent) + ++ (void)load{ + // 实现init 与 dt_init方法的交换 + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + SEL org_Selector = @selector(init); + SEL dt_Selector = @selector(swizzling_init); + + Method org_method = class_getInstanceMethod([self class], org_Selector); + Method dt_method = class_getInstanceMethod([self class], dt_Selector); + + BOOL isAdd = class_addMethod(self, org_Selector, method_getImplementation(dt_method), method_getTypeEncoding(dt_method)); + if (isAdd) { + class_replaceMethod(self, dt_Selector, method_getImplementation(org_method), method_getTypeEncoding(org_method)); + }else{ + method_exchangeImplementations(org_method, dt_method); + } + + }); +} + +- (instancetype)swizzling_init { + + AFJSONResponseSerializer * serializer = [self swizzling_init]; + serializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"text/json",@"application/json",nil]; +#pragma mark - 每次清理下缓存保证不会因AFN缓存出现致命问题 + [[NSURLCache sharedURLCache] removeAllCachedResponses]; + return serializer; +} + +@end diff --git a/YDNetwrokManager/YDCommand/YDBatchCommand.h b/YDNetwrokManager/YDCommand/YDBatchCommand.h new file mode 100644 index 0000000..df6e18e --- /dev/null +++ b/YDNetwrokManager/YDCommand/YDBatchCommand.h @@ -0,0 +1,55 @@ +// +// YDBatchCommand.h +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import +#import "YDCommand.h" +#import + +@class YDBatchCommand; +typedef void(^YDBatchRequestCompletionBlock)(__kindof YDBatchCommand *request); + +@interface YDBatchCommand : YTKBatchRequest + +@property (nonatomic, strong, readonly) NSError *lastError; + +#pragma mark 普通调用方法 +/** + * 提供类方法直接调用 + * @param aCommands 设置需要的网络请求数组 + * @param aCompletion 完成后都会走该block,以属性 NSError *error 来区分成功失败 + */ ++ (instancetype)ydBatchRequestArray:(NSArray*)aCommands completion:(YDBatchRequestCompletionBlock)aCompletion; +/** + * 提供类方法直接调用 + * @param aCommands 设置需要的网络请求数组 + * @param aSuccess 成功回调 + * @param aFailure 失败回调 + */ ++ (instancetype)ydBatchRequestArray:(NSArray*)aCommands success:(YDBatchRequestCompletionBlock)aSuccess failure:(YDBatchRequestCompletionBlock)aFailure; + +/** + * 提供类方法直接调用 + * @param aCommands 设置需要的网络请求数组 + * @param aNext 成功失败都会走 如收回下拉刷新 + * @param aSuccess 成功回调 + * @param aFailure 失败回调 + */ ++ (instancetype)ydBatchRequestArray:(NSArray*)aCommands withNext:(YDBatchRequestCompletionBlock)aNext success:(YDBatchRequestCompletionBlock)aSuccess failure:(YDBatchRequestCompletionBlock)aFailure; + +/** + * 提供类方法直接调用 + * @param aCommands 设置需要的网络请求数组 + * @param aEnable YES 使用先缓存后网络功能 NO则直接走网络 主要是基本只有第一次才需要使用YES + * @param aCache YES 走网络网络时缓存数据, NO不缓存数据, 主要用于下拉刷新纪录缓存,加载更多不需要 + * @param aRefresh 调用显示刷新动画 + * @param aCompletion 完成后都会走该block,以属性 NSError *error 来区分成功失败 isDataFromCache 来区分是否从缓存走的 + */ ++ (instancetype)ydBatchLocalRequestArray:(NSArray*)aCommands enable:(BOOL)aEnable saveCache:(BOOL)aCache refresh:(void (^)(void))aRefresh completion:(YDBatchRequestCompletionBlock)aCompletion; + +@end + + diff --git a/YDNetwrokManager/YDCommand/YDBatchCommand.m b/YDNetwrokManager/YDCommand/YDBatchCommand.m new file mode 100644 index 0000000..08aefc3 --- /dev/null +++ b/YDNetwrokManager/YDCommand/YDBatchCommand.m @@ -0,0 +1,171 @@ +// +// YDBatchCommand.m +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import "YDBatchCommand.h" +#import "YDCommand.h" + +@implementation YDBatchCommand + +#pragma mark - 简便调用方法提供 +- (NSError *)lastError { + __block NSError *error = nil; + [self.requestArray enumerateObjectsUsingBlock:^(YTKRequest * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if (obj.error) { + error = obj.error; + } + }]; + return error; +} + +#pragma mark 普通调用方法 +/** + * 提供类方法直接调用 + * @param aCommands 设置需要的网络请求数组 + * @param aCompletion 完成后都会走该block,以属性 NSError *error 来区分成功失败 + */ ++ (instancetype)ydBatchRequestArray:(NSArray*)aCommands completion:(YDBatchRequestCompletionBlock)aCompletion { + return [self ydBatchRequestArray:aCommands success:aCompletion failure:aCompletion]; +} + +/** + * 提供类方法直接调用 + * @param aCommands 设置需要的网络请求数组 + * @param aSuccess 成功回调 + * @param aFailure 失败回调 + */ ++ (instancetype)ydBatchRequestArray:(NSArray*)aCommands success:(YDBatchRequestCompletionBlock)aSuccess failure:(YDBatchRequestCompletionBlock)aFailure { + return [self ydBatchRequestArray:aCommands withNext:nil success:aSuccess failure:aFailure]; +} + + +/** + * 提供类方法直接调用 + * @param aCommands 设置需要的网络请求数组 + * @param aNext 成功失败都会走 如收回下拉刷新 + * @param aSuccess 成功回调 + * @param aFailure 失败回调 + */ ++ (instancetype)ydBatchRequestArray:(NSArray*)aCommands withNext:(YDBatchRequestCompletionBlock)aNext success:(YDBatchRequestCompletionBlock)aSuccess failure:(YDBatchRequestCompletionBlock)aFailure { + + YDBatchCommand *batchRequest = [[YDBatchCommand alloc] initWithRequestArray:aCommands]; + [batchRequest traverseRequest:^(YDCommand *aCmd) { + [aCmd defaultParame]; + }]; + + void (^success)(YTKBatchRequest *batchRequest) = ^(YTKBatchRequest *request){ + + [request.requestArray enumerateObjectsUsingBlock:^(YTKRequest * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + YDCommand *aCmd = (YDCommand *)obj; + if (aCmd.localCachefailureTime <= 2 && aCmd.isDataFromCache) { + [aCmd setValue:[NSNumber numberWithBool:NO] forKey:@"dataFromCache"]; + *stop = YES; + } + }]; + + if (aNext) { + aNext(batchRequest); + } + if (aSuccess) { + aSuccess(batchRequest); + } + }; + + void (^failure)(YTKBatchRequest *batchRequest) = ^(YTKBatchRequest *request){ + if (aNext) { + aNext(batchRequest); + } + if (aSuccess) { + aSuccess(batchRequest); + } + }; + + [batchRequest startWithCompletionBlockWithSuccess:success failure:failure]; + return batchRequest; +} + +/** + * 提供类方法直接调用 + * @param aCommands 设置需要的网络请求数组 + * @param aEnable YES 使用先缓存后网络功能 NO则直接走网络 主要是基本只有第一次才需要使用YES + * @param aCache YES 走网络网络时缓存数据, NO不缓存数据, 主要用于下拉刷新纪录缓存,加载更多不需要 + * @param aRefresh 调用显示刷新动画 + * @param aCompletion 完成后都会走该block,以属性 NSError *error 来区分成功失败 isDataFromCache 来区分是否从缓存走的 + */ ++ (instancetype)ydBatchLocalRequestArray:(NSArray*)aCommands enable:(BOOL)aEnable saveCache:(BOOL)aCache refresh:(void (^)(void))aRefresh completion:(YDBatchRequestCompletionBlock)aCompletion { + + if (!aEnable) { + [aCommands enumerateObjectsUsingBlock:^(YDCommand * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + obj.localCachefailureTime = aCache ? 1 : -1; + }]; + return [self ydBatchRequestArray:aCommands completion:aCompletion]; + } + + YDBatchCommand *batchRequest = [[YDBatchCommand alloc] initWithRequestArray:aCommands]; + __block BOOL isOK = YES; + [batchRequest traverseRequest:^(YDCommand *aCmd) { + [aCmd defaultParame]; + aCmd.ignoreCache = NO; + //取缓存要无限大 + aCmd.localCachefailureTime = 60 * 60 * 24 * 365; + NSError *error = nil; + if ([aCmd loadCacheWithError:&error]) { + if (error == nil) { + [aCmd setValue:[NSNumber numberWithBool:YES] forKey:@"dataFromCache"]; + [aCmd requestCompleteFilter]; + + }else { + isOK = NO; + } + }else { + isOK = NO; + } + }]; + if (isOK && aCompletion) { + aCompletion(batchRequest); + } + + [batchRequest traverseRequest:^(YDCommand *aCmd) { + aCmd.ignoreCache = YES; + aCmd.localCachefailureTime = 1; + }]; + + [UIView setAnimationsEnabled:NO]; + if (aRefresh) { + aRefresh(); + } + [UIView setAnimationsEnabled:YES]; + NSDate *startDate = [NSDate date]; + + void (^block)(YTKBatchRequest *batchRequest) = ^(YTKBatchRequest *request){ + + [batchRequest traverseRequest:^(YDCommand *aCmd) { + [aCmd setValue:[NSNumber numberWithBool:NO] forKey:@"dataFromCache"]; + }]; + // 强制认为不是从缓存走的 + NSTimeInterval time = - [startDate timeIntervalSinceNow]; + // 时间判断是为了更好的UI体验 + if (time < 0.2) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + aCompletion(batchRequest); + }); + }else { + aCompletion(batchRequest); + } + }; + [batchRequest startWithCompletionBlockWithSuccess:block failure:block]; + return batchRequest; +} + +- (void)traverseRequest:(void (^)(YDCommand *aCmd))aParame { + [self.requestArray enumerateObjectsUsingBlock:^(YTKRequest * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSAssert([obj isKindOfClass:[YDCommand class]], @"必须是 YDCommand"); + if (aParame) { + aParame((YDCommand *)obj); + } + }]; +} +@end diff --git a/YDNetwrokManager/YDCommand/YDCommand+YDError.h b/YDNetwrokManager/YDCommand/YDCommand+YDError.h new file mode 100644 index 0000000..3041f4c --- /dev/null +++ b/YDNetwrokManager/YDCommand/YDCommand+YDError.h @@ -0,0 +1,18 @@ +// +// YDCommand+YDError.h +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import "YDCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface YDCommand (YDError) + +- (void)dealWithServiceErrorStatus:(NSInteger)aStatus userInfo:(NSDictionary *)aDic; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YDNetwrokManager/YDCommand/YDCommand+YDError.m b/YDNetwrokManager/YDCommand/YDCommand+YDError.m new file mode 100644 index 0000000..ea93fff --- /dev/null +++ b/YDNetwrokManager/YDCommand/YDCommand+YDError.m @@ -0,0 +1,37 @@ +// +// YDCommand+YDError.m +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import "YDCommand+YDError.h" + +@implementation YDCommand (YDError) + +- (void)dealWithServiceErrorStatus:(NSInteger)aStatus userInfo:(NSDictionary *)aDic { + +// switch (aStatus) { +// case -1: // 授权被停用 要重新授权 +// { +// NSMutableDictionary *dicM = [[NSMutableDictionary alloc] init]; +// if (aDic[@"msg"]) { +// dicM[kAuthorizationError] = aDic[@"msg"]; +// } +// if (aDic[@"device"]) { +// dicM[kAuthorizationDevice] = aDic[@"device"]; +// } +// if (aDic[@"phone"]) { +// dicM[kAuthorizationPhone] = aDic[@"phone"]; +// } +// [ArtAppDelegate reauthorizationBy:[dicM copy]]; +// +// } +// break; +// +// default: +// break; +// } +} + +@end diff --git a/YDNetwrokManager/YDCommand/YDCommand.h b/YDNetwrokManager/YDCommand/YDCommand.h new file mode 100644 index 0000000..9bda807 --- /dev/null +++ b/YDNetwrokManager/YDCommand/YDCommand.h @@ -0,0 +1,110 @@ +// +// YDCommand.h +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import + +/* 1.书写网络请求参数属性以 input_ 开头内部会自行拼接requestArgument 无需自己拼写参数 + * 2.返回200 OK 但带有服务端错误的处理 失败成功请覆盖 - (void)requestSuccess:(NSDictionary *)aDic; - (void)requestFailureEnd; 均在主线程处理。不要使用delegate方法 暂未处理 + * 3.YTK 的缓存处理不适用于部分应用场景,ignoreCache是一级判断 再通过 - (NSInteger)cacheTimeInSeconds方法来判断从网络走还是缓存走,无法实现先走缓存再走网络的实现。加入localCachefailureTime属性通过动态改变 localCachefailureTime 来实现,而cacheTimeInSeconds方法返回该值。拿缓存时设置无限大,走网络则看情况。 + */ +@interface YDCommand : YTKRequest + +// 本地缓存失效时间 为负数 无效 如果本地时间 - 缓存时间 超过 失效时间 也走网络 +@property (nonatomic, assign) NSInteger localCachefailureTime; + +//获取本次请求的所有参数 +@property (nonatomic, strong) id allParmas; + +//获取本次请求的header头 +@property (nonatomic, strong) NSDictionary *allHeader; + +// 错误是服务端返回错误 +@property (nonatomic, assign) BOOL isServiceError; + +@property (nonatomic, strong, readonly) NSDictionary *requestParamsDic; + + +#pragma mark - 需重写的方法 +// 配置默认参数,仅对以下提供的方法有用 +- (void)defaultParame; + +// 成功失败调用 请勿使用YTK的有额外处理 +- (void)requestSuccess:(NSDictionary *)aDic; +- (void)requestFailureEnd; + +// 缓存时 忽略参数 以及 添加额外参数 +- (NSArray *)ignoreCacheParmasKeys; +- (NSDictionary *)additionalCacheParmas; + ++ (void)doTest; + +#pragma mark - 提供简单的调用方法 + +#pragma mark - 为网络请求添加自己的缓存处理 为后面缓存的相关操作提供便利,代码中无需一级级向下传递,也可动态修改缓存数据 ++ (instancetype)ydSaveDic:(NSDictionary *)dic toCommandSetParame:(YTKRequestCompletionBlock)aParame; + +#pragma mark 普通调用方法 +/** + * 提供类方法直接调用 + * @param aParame 该block用于参数设置 + * @param aNext 成功失败都会走 如收回下拉刷新 + * @param aSuccess 成功回调 + * @param aFailure 失败回调 + */ ++ (instancetype)ydCommandSetParame:(YTKRequestCompletionBlock)aParame completionWithNext:(YTKRequestCompletionBlock)aNext Success:(YTKRequestCompletionBlock)aSuccess failure:(YTKRequestCompletionBlock)aFailure; + +/** + * 提供类方法直接调用 + * @param aParame 该block用于参数设置 + * @param aSuccess 成功回调 + * @param aFailure 失败回调 + */ ++ (instancetype)ydCommandSetParame:(YTKRequestCompletionBlock)aParame completionWithSuccess:(YTKRequestCompletionBlock)aSuccess failure:(YTKRequestCompletionBlock)aFailure; + +/** + * 提供类方法直接调用 + * @param aParame 该block用于参数设置 + * @param aCompletion 完成后都会走该block,以属性 NSError *error 来区分成功失败 + */ ++ (instancetype)ydCommandSetParame:(YTKRequestCompletionBlock)aParame completion:(YTKRequestCompletionBlock)aCompletion; + +#pragma mark 先拿缓存再走数据调用方法 待添加准备用于先展示数据 然后下拉刷新使用 +/** + * 提供类方法直接调用 + * @param aParame 该block用于参数设置 该方法中设置 localCachefailureTime 无效 aEnable为YES时请求localCachefailureTime=1 + * @param aEnable YES 使用先缓存后网络功能 NO则直接走网络 主要是基本只有第一次才需要使用YES + * @param aCache YES 走网络网络时缓存数据, NO不缓存数据, 主要用于下拉刷新纪录缓存,加载更多不需要 + * @param aRefresh 调用显示刷新动画 + * @param aCompletion 完成后都会走该block,以属性 NSError *error 来区分成功失败 isDataFromCache 来区分是否从缓存走的 + */ ++ (instancetype)ydCommandlocalSetParame:(YTKRequestCompletionBlock)aParame enable:(BOOL)aEnable saveCache:(BOOL)aCache refresh:(void (^)(void))aRefresh completion:(YTKRequestCompletionBlock)aCompletion; + +#pragma mark 先展示缓存,保存网络数据 可设置 localCachefailureTime 来减缓请求的频率 +/** + * 提供类方法直接调用 + * @param aParame 该block用于参数设置 可设置 localCachefailureTime 来减缓请求的频率 + * @param ashowLoading 无缓存数据开始加载网络数据时调用 + * @param aDismiss 无缓存数据结束加载网络数据时调用 + * @param aCompletion 完成后都会走该block,以属性 NSError *error 来区分成功失败 isDataFromCache 来区分是否从缓存走的 + */ ++ (instancetype)ydCommandlocalSetParame:(YTKRequestCompletionBlock)aParame showLoading:(void (^)(void))ashowLoading dismiss:(void (^)(void))aDismiss completion:(YTKRequestCompletionBlock)aCompletion; + ++ (instancetype)ydCommandlocalSetParame:(YTKRequestCompletionBlock)aParame completion:(YTKRequestCompletionBlock)aCompletion; + +/** + * 提供类方法直接调用 + * @param aParame 该block用于参数设置 可设置 localCachefailureTime 来减缓请求的频率 + * @param ashowLoading 无缓存数据开始加载网络数据时调用 + * @param aDismiss 无缓存数据结束加载网络数据时调用 + * @param aCompletion 完成后只走一次 以属性 NSError *error 来区分成功失败 isDataFromCache 来区分是否从缓存走的 + */ ++ (instancetype)ydCommandlocalCompletionSetParame:(YTKRequestCompletionBlock)aParame showLoading:(void (^)(void))ashowLoading dismiss:(void (^)(void))aDismiss completion:(YTKRequestCompletionBlock)aCompletion; + ++ (instancetype)ydCommandlocalCompletionSetParame:(YTKRequestCompletionBlock)aParame completion:(YTKRequestCompletionBlock)aCompletion; + +@end + diff --git a/YDNetwrokManager/YDCommand/YDCommand.m b/YDNetwrokManager/YDCommand/YDCommand.m new file mode 100644 index 0000000..e9aa0fa --- /dev/null +++ b/YDNetwrokManager/YDCommand/YDCommand.m @@ -0,0 +1,501 @@ +// +// YDCommand.m +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import "YDCommand.h" +#import "YDCommandConfig.h" +#import "YDCommand+YDError.h" +#import +#import + +@interface YDCommand () + +// 单写多读 +@property (atomic, assign) BOOL isSaveCache; +@property (nonatomic, strong, readwrite) NSDictionary *requestParamsDic; + +@end + +@implementation YDCommand + +- (instancetype)init { + if (self = [super init]) { + [YDCommandConfig shared]; //保证被配置 + } + return self; +} + +- (NSTimeInterval)requestTimeoutInterval { + return 15.f; +} + +// 我们的请求基本都是post +- (YTKRequestMethod)requestMethod { + return YTKRequestMethodPOST; +} + +// 参数绑定 +- (id)requestArgument { + return [self requestParams]; +} + +//获取请求的所有参数信息 +- (id)allParmas +{ + return [self requestArgument]; +} + +- (id)cacheFileNameFilterForRequestArgument:(id)argument { + NSAssert([argument isKindOfClass:[NSDictionary class]], @"绑定参数是字典"); + if ([self ignoreCacheParmasKeys] == nil && [self additionalCacheParmas] == nil) { + return [super cacheFileNameFilterForRequestArgument:argument]; + } + NSMutableDictionary *dicM = [argument mutableCopy]; + [[self ignoreCacheParmasKeys] enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [dicM removeObjectForKey:obj]; + }]; + + NSDictionary *additional = [self additionalCacheParmas]; + if (additional.count > 0) { + [dicM addEntriesFromDictionary:additional]; + } + + return [dicM copy]; +} + +// header头添加 +- (NSDictionary *)requestHeaderFieldValueDictionary { + if (_allHeader == nil) { + _allHeader = [[YDCommandConfig shared] getCommandHeader]; + } + return _allHeader; +} + +- (YTKResponseSerializerType)responseSerializerType { + return YTKResponseSerializerTypeJSON; +} + +// 缓存时间问题 +- (NSInteger)cacheTimeInSeconds { + return self.localCachefailureTime; +} + +///// 做一些额外的解析处理 +- (void)requestCompleteFilter { + // super 实际什么都没干 + [super requestCompleteFilter]; + NSError *error = nil; + id object = self.responseJSONObject; + if (object && ![object isKindOfClass:[NSDictionary class]]) { + error = [NSError errorWithDomain:@"服务端返回的数据怎么不是字典" code:1 userInfo:@{NSLocalizedDescriptionKey : @"服务端返回的数据怎么不是字典"}]; + }else { + NSDictionary *dic = (NSDictionary *)object; + NSInteger status = [dic[@"status"] integerValue]; + + NSNumber *curtime = dic[@"curtime"]; + if (curtime == nil) { + NSHTTPURLResponse *re = (NSHTTPURLResponse *)self.requestTask.response; + if ([re isKindOfClass:[NSHTTPURLResponse class]]) { + NSDictionary *header = [re allHeaderFields]; + curtime = header[@"curtime"]; + if (!curtime) { + static NSDateFormatter *formatter = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + formatter = [[NSDateFormatter alloc] init]; + formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + formatter.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'"; + formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + formatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; + }); + NSString *dateString = header[@"Date"]; + if (dateString) { + NSDate *date = [formatter dateFromString:dateString]; + if (date) { + curtime = @(date.timeIntervalSince1970); +#ifdef DEBUG + NSTimeInterval interval = curtime.doubleValue - NSDate.date.timeIntervalSince1970; + if (fabs(interval) >= 5) { + NSMutableString *log = @"\n❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌".mutableCopy; + [log appendString:@"\n❌服务器时间和本地时间差异过大"]; + [log appendFormat:@"\n❌服务器时间:%@",dateString]; + [log appendFormat:@"\n❌转换后时间:%@",date]; + [log appendFormat:@"\n❌时间戳:%@",curtime]; + [log appendFormat:@"\n❌时间差:%lf",interval]; + [log appendString:@"\n❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌"]; + NSLog(@"%@",log); + } +#endif + } + } + } + } + } + + if (!self.isDataFromCache) { + if (curtime) { + [[YDCommandConfig shared] updateTimeDifferenceByServiceTime:[curtime doubleValue]]; + } + } + + + if ( status != 0) { + error = [NSError errorWithDomain:dic[@"msg"] code:[dic[@"status"] integerValue] userInfo:@{NSLocalizedDescriptionKey : dic[@"msg"]}]; + // 处理服务端数据错误 + if (status < 0 && !self.isDataFromCache) { // status < 0 并且数据不是从缓存拿的 + [self dealWithServiceErrorStatus:status userInfo:dic]; + } + self.isServiceError = YES; + } + } + + if (error == nil) { + // 日志 + [YDCommandConfig configRequestCompleteFilter:self]; + [self requestSuccess:object]; + return; + } + + // 设置错误 取消成功的block回调 调用失败的block + [self setValue:error forKey:@"error"]; + NSLog(@"%@",error); + [self requestFailedFilter]; + + // YTK成功自己会掉不用多调用一次 +// if (self.failureCompletionBlock) { +// self.failureCompletionBlock(self); +// } +// YTK 内部有处理,这里提前nil 可能会导致异步缓存出错 +// self.failureCompletionBlock = nil; +// self.successCompletionBlock = nil; +} + + +/// Called on the main thread when request failed. +- (void)requestFailedFilter { + // 日志 + [YDCommandConfig configRequestFailedFilter:self]; + NSLog(@"%@_%@",self.requestUrl,self.requestArgument); + NSLog(@"%@",self.error); + if (self.error.code == 3840) { + if ([self.responseData isKindOfClass:[NSData class]]) { +// NSString *jsonstr = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; +// [[YDLogCollectService shared] logCollectDescribe:jsonstr]; + NSString *errorstr = [NSString stringWithFormat:@"Request %@ failed, status code = %ld, error = %@", + self.requestUrl, (long)self.responseStatusCode, self.error.localizedDescription]; +// [[YDLogCollectService shared] logCollectDescribe:errorstr]; + if (errorstr.length > 80) { + errorstr = [errorstr substringToIndex:78]; + } + NSError *error = [NSError errorWithDomain:@"解析错误" code:3840 userInfo:@{NSLocalizedDescriptionKey:errorstr}]; + [self setValue:error forKey:@"error"]; + } + } + [self requestFailureEnd]; +} + +//- (void)requestCompletePreprocessor { +// [super requestCompletePreprocessor]; +//} + +////这里暂不考虑JSONModel解析错误的问题 +//- (void)requestFailedPreprocessor +//{ +// //note:子类如需继承,必须必须调用 [super requestFailedPreprocessor]; +// [super requestFailedPreprocessor]; +// +// NSError * error = self.error; +// +// if ([error.domain isEqualToString:AFURLResponseSerializationErrorDomain]) +// { +// //AFNetworking处理过的错误 +// +// }else if ([error.domain isEqualToString:YTKRequestValidationErrorDomain]) +// { +// //猿题库处理过的错误 +// +// }else{ +// //系统级别的domain错误,无网络等[NSURLErrorDomain] +// //根据error的code去定义显示的信息,保证显示的内容可以便捷的控制 +// } +//} + +// 异步缓存 requestParams 使用了运行时可能导致 释放问题以此处理 +- (void)saveResponseDataToCacheFile:(NSData *)data { + self.isSaveCache = YES; + [super saveResponseDataToCacheFile:data]; + self.isSaveCache = NO; +} + +#pragma mark - 需重写的方法 +// 配置默认参数,仅对以下提供的方法有用 +- (void)defaultParame { + +} + +- (void)requestSuccess:(NSDictionary *)aDic { + +} + +- (void)requestFailureEnd { + +} + ++ (void)doTest +{ + +} + +// 缓存时 忽略参数 以及 添加额外参数 +- (NSArray *)ignoreCacheParmasKeys { + return nil; +} + +- (NSDictionary *)additionalCacheParmas { + return nil; +} + +#pragma mark - 提供简单的调用方法 +#pragma mark - 为网络请求添加自己的缓存处理 为后面缓存的相关操作提供便利,代码中无需一级级向下传递,也可动态修改缓存数据 ++ (instancetype)ydSaveDic:(NSDictionary *)dic toCommandSetParame:(YTKRequestCompletionBlock)aParame { + if (dic == nil) { + return nil; + } + YDCommand *aCmd = [[self alloc] init]; + [aCmd defaultParame]; + if (aParame) { + aParame(aCmd); + } + NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:0 error:nil]; + [aCmd saveResponseDataToCacheFile:data]; + return aCmd; +} + ++ (instancetype)ydCommandSetParame:(YTKRequestCompletionBlock)aParame completion:(YTKRequestCompletionBlock)aCompletion { + + return [self ydCommandSetParame:aParame completionWithSuccess:aCompletion failure:aCompletion]; +} + ++ (instancetype)ydCommandSetParame:(YTKRequestCompletionBlock)aParame completionWithSuccess:(YTKRequestCompletionBlock)aSuccess failure:(YTKRequestCompletionBlock)aFailure { + + return [self ydCommandSetParame:aParame completionWithNext:nil Success:aSuccess failure:aFailure]; +} + ++ (instancetype)ydCommandSetParame:(YTKRequestCompletionBlock)aParame completionWithNext:(YTKRequestCompletionBlock)aNext Success:(YTKRequestCompletionBlock)aSuccess failure:(YTKRequestCompletionBlock)aFailure { + + YDCommand *aCmd = [[self alloc] init]; + [aCmd defaultParame]; + if (aParame) { + aParame(aCmd); + } + + YTKRequestCompletionBlock success = ^(YTKBaseRequest *request) { + + if (aCmd.localCachefailureTime <= 2 && aCmd.isDataFromCache) { + [aCmd setValue:[NSNumber numberWithBool:NO] forKey:@"dataFromCache"]; + } + if (aNext) { + aNext(aCmd); + } + + if (request.error) { + + if (aFailure) { + aFailure(aCmd); + } + + }else { + + if (aSuccess) { + aSuccess(aCmd); + } + + } + + }; + + YTKRequestCompletionBlock failure = aNext == nil ? aFailure : ^(YTKBaseRequest *request) { + aNext(aCmd); + if (aFailure) { + aFailure(aCmd); + } + }; + + [aCmd startWithCompletionBlockWithSuccess:success failure:failure]; + return aCmd; +} + +#pragma mark 先拿缓存再走数据调用方法 待添加准备用于先展示数据 然后下拉刷新使用待后面考虑如何处理 ++ (instancetype)ydCommandlocalSetParame:(YTKRequestCompletionBlock)aParame enable:(BOOL)aEnable saveCache:(BOOL)aCache refresh:(void (^)(void))aRefresh completion:(YTKRequestCompletionBlock)aCompletion { + + if (!aEnable) { + return [self ydCommandSetParame:^(__kindof YDCommand * _Nonnull request) { + if (aParame) { + aParame(request); + } + request.localCachefailureTime = aCache ? 1 : -1; + }completion:aCompletion]; + } + + YDCommand *aCmd = [[self alloc] init]; + aCmd.ignoreCache = NO; + [aCmd defaultParame]; + if (aParame) { + aParame(aCmd); + } + //记录原来值 因为取缓存要无限大 + aCmd.localCachefailureTime = 60 * 60 * 24 * 365; + + NSError *error = nil; + if ([aCmd loadCacheWithError:&error]) { + if (error == nil) { + [aCmd setValue:[NSNumber numberWithBool:YES] forKey:@"dataFromCache"]; + [aCmd requestCompleteFilter]; + if (aCompletion) { + aCompletion(aCmd); + } + } + } + + // 要使用缓存 必须大于0 + aCmd.localCachefailureTime = 1; + [UIView setAnimationsEnabled:NO]; + if (aRefresh) { + aRefresh(); + } + [UIView setAnimationsEnabled:YES]; + NSDate *startDate = [NSDate date]; + YTKRequestCompletionBlock block = aCompletion == nil ? aCompletion : ^(YDCommand *request) { + // 强制认为不是从缓存走的 + [aCmd setValue:[NSNumber numberWithBool:NO] forKey:@"dataFromCache"]; + NSTimeInterval time = - [startDate timeIntervalSinceNow]; + // 时间判断是为了更好的UI体验 + if (time < 0.2) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + aCompletion(aCmd); + }); + }else { + aCompletion(aCmd); + } + }; + [aCmd startWithCompletionBlockWithSuccess:block failure:block]; + return aCmd; +} + +#pragma mark 先展示缓存,保存网络数据 可设置 localCachefailureTime 来减缓请求的频率 ++ (instancetype)ydCommandlocalSetParame:(YTKRequestCompletionBlock)aParame completion:(YTKRequestCompletionBlock)aCompletion { + return [self ydCommandlocalSetParame:aParame showLoading:nil dismiss:nil completion:aCompletion]; +} + ++ (instancetype)ydCommandlocalSetParame:(YTKRequestCompletionBlock)aParame showLoading:(void (^)(void))ashowLoading dismiss:(void (^)(void))aDismiss completion:(YTKRequestCompletionBlock)aCompletion { + return [self ydCommandlocalSetParame:aParame completionFirst:NO showLoading:ashowLoading dismiss:aDismiss completion:aCompletion]; +} + ++ (instancetype)ydCommandlocalCompletionSetParame:(YTKRequestCompletionBlock)aParame completion:(YTKRequestCompletionBlock)aCompletion { + return [self ydCommandlocalCompletionSetParame:aParame showLoading:nil dismiss:nil completion:aCompletion]; +} + ++ (instancetype)ydCommandlocalCompletionSetParame:(YTKRequestCompletionBlock)aParame showLoading:(void (^)(void))ashowLoading dismiss:(void (^)(void))aDismiss completion:(YTKRequestCompletionBlock)aCompletion { + return [self ydCommandlocalSetParame:aParame completionFirst:YES showLoading:ashowLoading dismiss:aDismiss completion:aCompletion]; +} + ++ (instancetype)ydCommandlocalSetParame:(YTKRequestCompletionBlock)aParame completionFirst:(BOOL)aFirst showLoading:(void (^)(void))ashowLoading dismiss:(void (^)(void))aDismiss completion:(YTKRequestCompletionBlock)aCompletion { + + YDCommand *aCmd = [[self alloc] init]; + aCmd.ignoreCache = NO; + [aCmd defaultParame]; + if (aParame) { + aParame(aCmd); + } + //记录原来值 因为取缓存要无限大 + NSInteger localCachefailureTime = aCmd.localCachefailureTime; + aCmd.localCachefailureTime = 60 * 60 * 24 * 365; + + BOOL needShow = YES; + NSError *error = nil; + if ([aCmd loadCacheWithError:&error]) { + if (error == nil) { + [aCmd setValue:[NSNumber numberWithBool:YES] forKey:@"dataFromCache"]; + [aCmd requestCompleteFilter]; + if (aCompletion) { + aCompletion(aCmd); + } + needShow = NO; + } + } + + // 要使用缓存 必须大于0 + aCmd.localCachefailureTime = localCachefailureTime <= 0 ? 1 : localCachefailureTime; + + if (needShow && ashowLoading) { + ashowLoading(); + } + + YTKRequestCompletionBlock block = aCompletion == nil ? aCompletion : ^(YTKBaseRequest *request) { + if (aDismiss) { + aDismiss(); + } + // 走缓存就不再回调 + if (aCmd.isDataFromCache) { + return; + } + // aFirst 只走一次回调 + if (aFirst && !needShow) { + return; + } + + // 有缓存 请求错误则不予处理 + if (!needShow && request.error) { + return; + } + + aCompletion(aCmd); + }; + [aCmd startWithCompletionBlockWithSuccess:block failure:block]; + return aCmd; +} + +#pragma mark - 获取类属性,用于参数自动拼接 +- (NSDictionary *)requestParams { + + if (self.isSaveCache && self.requestParamsDic) { + return self.requestParamsDic; + } + NSMutableDictionary *dicM = [[NSMutableDictionary alloc] init]; + unsigned int outCount; + objc_property_t *properties = class_copyPropertyList([self class], &outCount); + for (NSInteger index = 0; index < outCount; index++) { + NSString *tmpName = [NSString stringWithFormat:@"%s",property_getName(properties[index])]; + NSObject *tmpValue = [self valueForKey:tmpName]; + + NSString* prefix = @"input_"; + if (tmpValue && [tmpName hasPrefix:prefix]) { + NSString* key = [tmpName substringFromIndex:prefix.length]; +// if ([tmpValue isKindOfClass:[NSNumber class]]) { +// if ([((NSNumber *)tmpValue) integerValue] <= 0.001) { +// continue; +// } +// } + [dicM setObject:tmpValue forKey:key]; + } + } + + if (properties) { + free(properties); + } + NSDictionary *dic = [dicM copy]; + self.requestParamsDic = dic; + return dic; +} + +- (void)dealloc { +#ifdef DEBUG +// NSLog(@"%s",__func__); +#endif +} + + +@end diff --git a/YDNetwrokManager/YDCommandConfig.h b/YDNetwrokManager/YDCommandConfig.h new file mode 100644 index 0000000..edabed1 --- /dev/null +++ b/YDNetwrokManager/YDCommandConfig.h @@ -0,0 +1,80 @@ +// +// YDCommandConfig.h +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import +#import +#import "YDCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface YDCommandConfig : NSObject + +// 服务器主地址 +@property (nonatomic, copy, readonly) NSString *apiBaseURLString; +// 数据统计服务器地址 +@property (nonatomic, copy, readonly) NSString *dataAcquisitionURLString; +// 与服务器的协议版本号 +@property (nonatomic, copy) NSString *protocolVersion; + +// 分页数 +@property (nonatomic, assign) NSInteger pageSize; +@property (nonatomic, copy) NSString *pageSizeString; + +// 超时时间 +@property (nonatomic, assign)CGFloat timeoutInterval; + +// 本地时间与 服务器时间差 +@property (nonatomic, assign, readonly)NSTimeInterval timeDifference; + +@property (nonatomic, strong, readonly) NSString* documentDirectory; +@property (nonatomic, strong, readonly) NSString* cacheDirectory; +@property (nonatomic, strong, readonly) NSString* fileCacheDirectory; +@property (nonatomic, strong, readonly) NSString* tmpDirectory; + + ++ (instancetype)shared; + + +/** + 配置根路径 + + @param url 根路径 + */ ++ (NSString *)configBaseUrl; + +/** + 配置java版的统计地址 + + @param url 地址 + */ ++ (NSString *)configDataAcquisitionURL; + ++ (NSDictionary *)configHeader; + +/** + 配置userAgent + + @param info userAgent + */ ++ (NSDictionary *)configUserAgent; + ++ (NSString *)configFilterCacheDirPath; + ++ (void)configRequestCompleteFilter:(YDCommand *)command; ++ (void)configRequestFailedFilter:(YDCommand *)command; + +- (void)updateTimeDifferenceByServiceTime:(NSTimeInterval)aServiceTime; + + + +- (NSDictionary *)getCommandHeader; + +- (NSString *)getUserAgent; +- (NSDictionary *)getUserAgentDic; +@end + +NS_ASSUME_NONNULL_END diff --git a/YDNetwrokManager/YDCommandConfig.m b/YDNetwrokManager/YDCommandConfig.m new file mode 100644 index 0000000..5d8da49 --- /dev/null +++ b/YDNetwrokManager/YDCommandConfig.m @@ -0,0 +1,232 @@ +// +// YDCommandConfig.m +// YDNetworkManager +// +// Created by wangyuandong on 2021/9/30. +// + +#import "YDCommandConfig.h" +#import +#import + +#define kPageSize 20 +#define kProtocolVersion @"1.0" + +@interface YDCommandConfig () + +@property (nonatomic, strong) NSString* tmpDirectory; +// 文件存储目录 +@property (nonatomic, strong) NSString* fileCacheDirectory; + +@property (nonatomic, copy) NSString *apiBaseURLString; +@property (nonatomic, copy) NSString *dataAcquisitionURLString; + +@property(nonatomic, copy) NSDictionary * header; +@property(nonatomic, copy) NSDictionary * userAgent; + +@property (nonatomic, copy) NSString *cachePath; // 网络缓存地址 +@property (nonatomic, assign) NSUInteger maxCacheSize; // 最大缓存大小 + +// 本地时间与 服务器时间差 +@property (nonatomic, assign)NSTimeInterval timeDifference; + +@end + +@implementation YDCommandConfig + ++ (instancetype)shared +{ + static dispatch_once_t onceToken; + static YDCommandConfig* shared = nil; + dispatch_once(&onceToken, ^{ + shared = [[self alloc] init]; + [shared construct]; + + }); + + return shared; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.apiBaseURLString = [YDCommandConfig configBaseUrl]; + self.dataAcquisitionURLString = [YDCommandConfig configDataAcquisitionURL]; + YTKNetworkConfig *config = [YTKNetworkConfig sharedConfig]; + config.baseUrl = self.apiBaseURLString; + [config addCacheDirPathFilter:self]; +#ifdef DEBUG + config.debugLogEnabled = YES; +#endif + } + return self; +} + +- (void)construct { + self.protocolVersion = kProtocolVersion; + //self.cachePath = [self.documentDirectory stringByAppendingPathComponent:@"StudioCommand"]; + self.maxCacheSize = 1 * 1024 * 1024; +} + +- (void)updateTimeDifferenceByServiceTime:(NSTimeInterval)aServiceTime { +// timeDifference + if (aServiceTime <= 1487940000) { + return; + } + NSTimeInterval localTime = [NSDate new].timeIntervalSince1970; + self.timeDifference = aServiceTime - localTime; +} + +#pragma mark - YTKCacheDirPathFilterProtocol +/* [config addCacheDirPathFilter:self]; + * 遵守该协议将缓存地址放到 documentDirectory下 + */ +- (NSString *)filterCacheDirPath:(NSString *)originPath withRequest:(YTKBaseRequest *)request { + return [YDCommandConfig configFilterCacheDirPath]; +} + +- (CGFloat)timeoutInterval { + if (_timeoutInterval < 1.f) { + _timeoutInterval = 5.f; + } + + return _timeoutInterval; +} + + + +- (NSDictionary *)getCommandHeader { + NSDictionary * dic = [YDCommandConfig configHeader]; + + NSMutableDictionary *requestHeaders = [[NSMutableDictionary alloc] init];; + if (dic) [requestHeaders setDictionary:dic]; + + [requestHeaders setObject:[[UIDevice currentDevice] systemVersion] forKey:@"opertaion"]; + + //设备名 + [requestHeaders setObject: [UIDevice currentDevice].name forKey:@"device"]; + [requestHeaders setObject: @"ios-skzs-mobile" forKey:@"devicetype"]; + + NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970]; + + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) + { + [requestHeaders setObject:[NSString stringWithFormat:@"h%tu", (long)timestamp] forKey:@"imei"]; + [requestHeaders setObject:@"2" forKey:@"platform"]; + } + else + { + [requestHeaders setObject:[NSString stringWithFormat:@"p%tu", (long)timestamp] forKey:@"imei"]; + [requestHeaders setObject:@"3" forKey:@"platform"]; + } + + + [requestHeaders setObject:[NSString stringWithFormat:@"%tu", (long)(timestamp*1000.)] forKey:@"timestamp"]; + + if ([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN) + { + [requestHeaders setObject:@"2" forKey:@"network"]; + } + else if ([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi) + { + [requestHeaders setObject:@"1" forKey:@"network"]; + } + else + { + [requestHeaders setObject:@"1" forKey:@"network"]; + } + + NSString *ua = [self getUserAgent]; + if (ua.length > 0) { + [requestHeaders setObject:ua forKey:@"useragent"]; + } + return requestHeaders; +} + +- (NSString *)getUserAgent +{ + NSDictionary * blockDic = [YDCommandConfig configUserAgent]; + NSMutableDictionary *dic = [[NSMutableDictionary alloc] init]; + if (blockDic) [dic setDictionary:blockDic]; + + NSMutableArray *arr = [NSMutableArray array]; + [dic enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) { + NSString *str = [NSString stringWithFormat:@"%@=%@",key,obj]; + [arr addObject:str]; + }]; + NSString *queryStr = [arr componentsJoinedByString:@"&"]; + return queryStr; +} + +- (NSDictionary *)getUserAgentDic +{ + NSDictionary * blockDic = [YDCommandConfig configUserAgent]; + NSMutableDictionary *dic = [[NSMutableDictionary alloc] init]; + if (blockDic) [dic setDictionary:blockDic]; + + + return dic; +} + +#pragma mark - ReadOnly property getter +- (NSString *)documentDirectory { + return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; +} + +- (NSString *)cacheDirectory { + return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; +} + +- (NSString *)tmpDirectory { + return NSTemporaryDirectory(); +} + +- (NSString *)fileCacheDirectory { + NSFileManager * fm = [NSFileManager defaultManager]; + NSString * fileCachePath = [self.cacheDirectory stringByAppendingPathComponent:@"filecache"]; + if (![fm fileExistsAtPath:fileCachePath]) { + [fm createDirectoryAtPath:fileCachePath withIntermediateDirectories:YES attributes:nil error:nil]; + } + return fileCachePath; +} + + + +- (NSInteger)pageSize { + if (!_pageSize) { + return kPageSize; + } + return _pageSize; +} + +- (NSString *)pageSizeString { + if (!_pageSizeString || _pageSizeString.length == 0) { + return [NSString stringWithFormat:@"%d", kPageSize]; + } + return _pageSizeString; +} + + + +#pragma mark - 此处为外部配置方法, 这里只是做占位, 具体内容需要外部分类中重写 ++ (NSString *)configBaseUrl { + return @""; +} ++ (NSString *)configDataAcquisitionURL { + return @""; +} ++ (NSDictionary *)configHeader { + return nil; +} ++ (NSDictionary *)configUserAgent { + return nil; +} ++ (NSString *)configFilterCacheDirPath { + return nil; +} + ++ (void)configRequestCompleteFilter:(YDCommand *)command {} ++ (void)configRequestFailedFilter:(YDCommand *)command {} + +@end diff --git a/YDNetwrokManager/YDNetwrokManager.docc/YDNetwrokManager.md b/YDNetwrokManager/YDNetwrokManager.docc/YDNetwrokManager.md new file mode 100755 index 0000000..5cca262 --- /dev/null +++ b/YDNetwrokManager/YDNetwrokManager.docc/YDNetwrokManager.md @@ -0,0 +1,13 @@ +# ``YDNetwrokManager`` + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` \ No newline at end of file diff --git a/YDNetwrokManager/YDNetwrokManager.h b/YDNetwrokManager/YDNetwrokManager.h new file mode 100644 index 0000000..f689e18 --- /dev/null +++ b/YDNetwrokManager/YDNetwrokManager.h @@ -0,0 +1,18 @@ +// +// YDNetwrokManager.h +// YDNetwrokManager +// +// Created by 王远东 on 2022/10/14. +// + +#import + +//! Project version number for YDNetwrokManager. +FOUNDATION_EXPORT double YDNetwrokManagerVersionNumber; + +//! Project version string for YDNetwrokManager. +FOUNDATION_EXPORT const unsigned char YDNetwrokManagerVersionString[]; + +#import "YDCommandConfig.h" +#import "YDCommand.h" +#import "YDBatchCommand.h" diff --git a/YDPreLoader/YDPreLoaderManager.m b/YDPreLoader/YDPreLoaderManager.m index 85f1191..6349fd7 100644 --- a/YDPreLoader/YDPreLoaderManager.m +++ b/YDPreLoader/YDPreLoaderManager.m @@ -166,9 +166,10 @@ - (YDPreLoaderModel *)getPreloadModel: (NSString *)urlStr NSURL *proxyUrl = [KTVHTTPCache proxyURLWithOriginalURL: [NSURL URLWithString:urlStr]]; KTVHCDataCacheItem *item = [KTVHTTPCache cacheCacheItemWithURL:proxyUrl]; double cachePrecent = 1.0 * item.cacheLength / item.totalLength; + // 判断缓存已经超过10%了 -// if (cachePrecent >= self.preloadPrecent) -// return nil; + if (cachePrecent >= self.preloadPrecent) + return nil; // NSDictionary * headers = KTVHCRangeFillToRequestHeaders(KTVHCMakeRange(0, 1024 * 1024 * 0.1), @{}); diff --git a/YDSafeThread/YDSafeThread.docc/YDSafeThread.md b/YDSafeThread/YDSafeThread.docc/YDSafeThread.md new file mode 100755 index 0000000..374c799 --- /dev/null +++ b/YDSafeThread/YDSafeThread.docc/YDSafeThread.md @@ -0,0 +1,13 @@ +# ``YDSafeThread`` + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` \ No newline at end of file