samhuri.net-ios/Pods/PromiseKit/objc/UIViewController+PromiseKit.m

199 lines
7.8 KiB
Objective-C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#import <AssetsLibrary/AssetsLibrary.h>
#import <objc/message.h>
#import <objc/runtime.h>
#import "PromiseKit/Promise.h"
#import <UIKit/UIStoryboardSegue.h>
#import <UIKit/UINavigationController.h>
#import <UIKit/UIImagePickerController.h>
#import "UIViewController+PromiseKit.h"
static const char *kSegueFulfiller = "kSegueFulfiller";
static const char *kSegueRejecter = "kSegueRejecter";
@interface PMKMFDelegater : NSObject
@end
@interface PMKUIImagePickerControllerDelegate : NSObject <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@end
@implementation UIViewController (PromiseKit)
- (PMKPromise *)promiseViewController:(UIViewController *)vc animated:(BOOL)animated completion:(void(^)(void))block
{
[self presentViewController:vc animated:animated completion:block];
if ([vc isKindOfClass:NSClassFromString(@"MFMailComposeViewController")]) {
PMKMFDelegater *delegater = [PMKMFDelegater new];
PMKRetain(delegater);
SEL selector = NSSelectorFromString(@"setMailComposeDelegate:");
IMP imp = [vc methodForSelector:selector];
void (*func)(id, SEL, id) = (void *)imp;
func(vc, selector, delegater);
}
else if ([vc isKindOfClass:NSClassFromString(@"MFMessageComposeViewController")]) {
PMKMFDelegater *delegater = [PMKMFDelegater new];
PMKRetain(delegater);
SEL selector = NSSelectorFromString(@"setMessageComposeDelegate:");
IMP imp = [vc methodForSelector:selector];
void (*func)(id, SEL, id) = (void *)imp;
func(vc, selector, delegater);
}
else if ([vc isKindOfClass:NSClassFromString(@"UIImagePickerController")]) {
PMKUIImagePickerControllerDelegate *delegator = [PMKUIImagePickerControllerDelegate new];
PMKRetain(delegator);
[(UIImagePickerController *)vc setDelegate:delegator];
}
else if ([vc isKindOfClass:NSClassFromString(@"SLComposeViewController")]) {
return [PMKPromise new:^(PMKPromiseFulfiller fulfiller, PMKPromiseRejecter rejecter) {
id block = ^(int result){
fulfiller(@(result));
[self dismissViewControllerAnimated:animated completion:nil];
};
[vc setValue:block forKey:@"completionHandler"];
}];
}
else if ([vc isKindOfClass:[UINavigationController class]])
vc = [(id)vc viewControllers].firstObject;
if (!vc) {
id err = [NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"Cannot promise a `nil` viewcontroller"}];
return [PMKPromise promiseWithValue:err];
}
return [PMKPromise new:^(id fulfiller, id rejecter){
objc_setAssociatedObject(vc, @selector(fulfill:), fulfiller, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(vc, @selector(reject:), rejecter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}].finally(^{
[self dismissViewControllerAnimated:animated completion:nil];
});
}
static void classOverridingSelector(const char* newClassPrefix, id target, SEL originalSelector, SEL overrideSelector) {
Class klass = [target class];
NSString *className = NSStringFromClass(klass);
if (strncmp(newClassPrefix, [className UTF8String], strlen(newClassPrefix)) != 0) {
NSString* subclassName = [NSString stringWithFormat:@"%s%@", newClassPrefix, className];
Class subclass = NSClassFromString(subclassName);
if (subclass == nil) {
subclass = objc_allocateClassPair(klass, [subclassName UTF8String], 0);
if (subclass != nil) {
objc_registerClassPair(subclass);
Method swizzledMethod = class_getInstanceMethod(klass, overrideSelector);
BOOL methodAdded = class_addMethod(subclass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (!methodAdded)
@throw @"Swizzle failure.";
}
}
if (subclass != nil) {
object_setClass(target, subclass);
}
}
}
#define _PMKS(x) #x
#define PMKPrefix _PMKS(PromiseKitUIKit_)
#define PMKPrefixify(x) PromiseKitUIKit_##x
- (PMKPromise *)promiseSegueWithIdentifier:(NSString *)identifier sender:(id) sender {
return [PMKPromise new:^(id fulfiller, id rejecter){
classOverridingSelector(PMKPrefix, self, @selector(prepareForSegue:sender:), @selector(PMKPrefixify(prepareForSegue:sender:)));
objc_setAssociatedObject(self, kSegueFulfiller, fulfiller, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, kSegueRejecter, rejecter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self performSegueWithIdentifier:identifier sender:sender];
}].finally(^{
[self dismissViewControllerAnimated:YES completion:nil];
});
}
- (void)PMKPrefixify(prepareForSegue):(UIStoryboardSegue *)segue sender:(id)sender {
id fulfiller = objc_getAssociatedObject(segue.sourceViewController, kSegueFulfiller);
id rejecter = objc_getAssociatedObject(segue.sourceViewController, kSegueRejecter);
objc_setAssociatedObject(self, kSegueFulfiller, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, kSegueRejecter, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(segue.destinationViewController, @selector(fulfill:), fulfiller, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(segue.destinationViewController, @selector(reject:), rejecter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Class zuper = class_getSuperclass([self class]);
SEL prepareForSegueSelector = @selector(prepareForSegue:sender:);
if ([zuper instancesRespondToSelector:prepareForSegueSelector]) {
struct objc_super objcSuper;
objcSuper.receiver = self;
objcSuper.super_class = zuper;
((void(*)(id, SEL, id, id))objc_msgSendSuper)((__bridge id)(&objcSuper), prepareForSegueSelector, segue, sender);
}
}
- (void)fulfill:(id)result {
void (^fulfiller)(id) = objc_getAssociatedObject(self, _cmd);
fulfiller(result);
}
- (void)reject:(NSError *)error {
void (^rejecter)(id) = objc_getAssociatedObject(self, _cmd);
rejecter(error);
}
@end
@implementation PMKMFDelegater
- (void)mailComposeController:(id)controller didFinishWithResult:(int)result error:(NSError *)error {
if (error)
[controller reject:error];
else
[controller fulfill:@(result)];
PMKRelease(self);
}
- (void)messageComposeViewController:(id)controller didFinishWithResult:(int)result {
if (result == 2)
[controller reject:[NSError errorWithDomain:PMKErrorDomain code:PMKOperationFailed userInfo:@{NSLocalizedDescriptionKey: @"The users attempt to save or send the message was unsuccessful."}]];
else
[controller fulfill:@(result)];
PMKRelease(self);
}
@end
@implementation PMKUIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
id img = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage];
id url = info[UIImagePickerControllerReferenceURL];
[[ALAssetsLibrary new] assetForURL:url resultBlock:^(ALAsset *asset) {
NSUInteger const N = (NSUInteger)asset.defaultRepresentation.size;
uint8_t *bytes = malloc(N);
[asset.defaultRepresentation getBytes:bytes fromOffset:0 length:N error:nil];
id data = [NSData dataWithBytes:bytes length:N];
free(bytes);
[picker fulfill:PMKManifold(img, data, info)];
PMKRelease(self);
}
failureBlock:^(NSError *error){
[picker reject:error];
PMKRelease(self);
}];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker fulfill:nil];
PMKRelease(self);
}
@end