├── CoreDataExample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── bryan.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── bryan.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── CoreDataExample.xcscheme │ └── xcschememanagement.plist ├── CoreDataExample.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── CoreDataExample.xccheckout └── xcuserdata │ └── bryan.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ ├── WorkspaceSettings.xcsettings │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── CoreDataExample ├── CoreDataExample-Info.plist ├── CoreDataExample-Prefix.pch ├── CoreDataExample.xcdatamodeld │ ├── .xccurrentversion │ └── CoreDataExample.xcdatamodel │ │ └── contents ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── TMAppDelegate.h ├── TMAppDelegate.m ├── TMCoreDataController.h ├── TMCoreDataController.m ├── TMDashboardViewController.h ├── TMDashboardViewController.m ├── TMFetchedResultsControllerDelegate.h ├── TMFetchedResultsControllerDelegate.m ├── TMPost.h ├── TMPost.m ├── en.lproj │ └── InfoPlist.strings └── main.m ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── BuildHeaders │ ├── JXHTTP │ │ ├── JXHTTP.h │ │ ├── JXHTTPDataBody.h │ │ ├── JXHTTPFileBody.h │ │ ├── JXHTTPFormEncodedBody.h │ │ ├── JXHTTPJSONBody.h │ │ ├── JXHTTPMultipartBody.h │ │ ├── JXHTTPOperation+Convenience.h │ │ ├── JXHTTPOperation.h │ │ ├── JXHTTPOperationDelegate.h │ │ ├── JXHTTPOperationQueue.h │ │ ├── JXHTTPOperationQueueDelegate.h │ │ ├── JXHTTPRequestBody.h │ │ ├── JXOperation.h │ │ ├── JXURLConnectionOperation.h │ │ └── JXURLEncoding.h │ └── TMTumblrSDK │ │ ├── TMAPIClient.h │ │ ├── TMOAuth.h │ │ ├── TMSDKFunctions.h │ │ ├── TMTumblrActivity.h │ │ ├── TMTumblrAppClient.h │ │ └── TMTumblrAuthenticator.h ├── Headers │ ├── JXHTTP │ │ ├── JXHTTP.h │ │ ├── JXHTTPDataBody.h │ │ ├── JXHTTPFileBody.h │ │ ├── JXHTTPFormEncodedBody.h │ │ ├── JXHTTPJSONBody.h │ │ ├── JXHTTPMultipartBody.h │ │ ├── JXHTTPOperation+Convenience.h │ │ ├── JXHTTPOperation.h │ │ ├── JXHTTPOperationDelegate.h │ │ ├── JXHTTPOperationQueue.h │ │ ├── JXHTTPOperationQueueDelegate.h │ │ ├── JXHTTPRequestBody.h │ │ ├── JXOperation.h │ │ ├── JXURLConnectionOperation.h │ │ └── JXURLEncoding.h │ └── TMTumblrSDK │ │ ├── TMAPIClient.h │ │ ├── TMOAuth.h │ │ ├── TMSDKFunctions.h │ │ ├── TMTumblrActivity.h │ │ ├── TMTumblrAppClient.h │ │ └── TMTumblrAuthenticator.h ├── JXHTTP │ ├── JXHTTP │ │ ├── JXHTTP.h │ │ ├── JXHTTPDataBody.h │ │ ├── JXHTTPDataBody.m │ │ ├── JXHTTPFileBody.h │ │ ├── JXHTTPFileBody.m │ │ ├── JXHTTPFormEncodedBody.h │ │ ├── JXHTTPFormEncodedBody.m │ │ ├── JXHTTPJSONBody.h │ │ ├── JXHTTPJSONBody.m │ │ ├── JXHTTPMultipartBody.h │ │ ├── JXHTTPMultipartBody.m │ │ ├── JXHTTPOperation+Convenience.h │ │ ├── JXHTTPOperation+Convenience.m │ │ ├── JXHTTPOperation.h │ │ ├── JXHTTPOperation.m │ │ ├── JXHTTPOperationDelegate.h │ │ ├── JXHTTPOperationQueue.h │ │ ├── JXHTTPOperationQueue.m │ │ ├── JXHTTPOperationQueueDelegate.h │ │ ├── JXHTTPRequestBody.h │ │ ├── JXOperation.h │ │ ├── JXOperation.m │ │ ├── JXURLConnectionOperation.h │ │ ├── JXURLConnectionOperation.m │ │ ├── JXURLEncoding.h │ │ └── JXURLEncoding.m │ ├── LICENSE.txt │ └── README.md ├── Manifest.lock ├── Pods-JXHTTP-Private.xcconfig ├── Pods-JXHTTP-dummy.m ├── Pods-JXHTTP-prefix.pch ├── Pods-JXHTTP.xcconfig ├── Pods-TMTumblrSDK-Private.xcconfig ├── Pods-TMTumblrSDK-dummy.m ├── Pods-TMTumblrSDK-prefix.pch ├── Pods-TMTumblrSDK.xcconfig ├── Pods-acknowledgements.markdown ├── Pods-acknowledgements.plist ├── Pods-dummy.m ├── Pods-environment.h ├── Pods-resources.sh ├── Pods.xcconfig ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── bryan.xcuserdatad │ │ └── xcschemes │ │ ├── Pods-JXHTTP.xcscheme │ │ ├── Pods-TMTumblrSDK.xcscheme │ │ ├── Pods.xcscheme │ │ └── xcschememanagement.plist └── TMTumblrSDK │ ├── LICENSE │ ├── README.md │ └── TMTumblrSDK │ ├── APIClient │ ├── TMAPIClient.h │ └── TMAPIClient.m │ ├── Activity │ ├── TMTumblrActivity.h │ ├── TMTumblrActivity.m │ ├── UIActivityTumblr.png │ ├── UIActivityTumblr@2x.png │ ├── UIActivityTumblr@2x~ipad.png │ ├── UIActivityTumblr~ipad.png │ └── UIActivityTumblr~ipad@2x.png │ ├── AppClient │ ├── TMTumblrAppClient.h │ └── TMTumblrAppClient.m │ ├── Authentication │ ├── TMOAuth.h │ ├── TMOAuth.m │ ├── TMTumblrAuthenticator.h │ └── TMTumblrAuthenticator.m │ └── Core │ ├── TMSDKFunctions.h │ └── TMSDKFunctions.m └── README.md /CoreDataExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/project.xcworkspace/xcuserdata/bryan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TumblrArchive/CoreDataExample/fdcba11b8581ccafeceaa7423967f332c985ffb5/CoreDataExample.xcodeproj/project.xcworkspace/xcuserdata/bryan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/xcuserdata/bryan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/xcuserdata/bryan.xcuserdatad/xcschemes/CoreDataExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/xcuserdata/bryan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CoreDataExample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 939BCF57193CBB9B00B84FB1 16 | 17 | primary 18 | 19 | 20 | 939BCF77193CBB9B00B84FB1 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /CoreDataExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CoreDataExample.xcworkspace/xcshareddata/CoreDataExample.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 6785B030-705A-4105-A855-195C308374DE 9 | IDESourceControlProjectName 10 | CoreDataExample 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | FB730834-BBA2-4C16-B6E0-7021D7A17E58 14 | ssh://github.ewr01.tumblr.net/bryan/CoreDataExample.git 15 | 16 | IDESourceControlProjectPath 17 | CoreDataExample.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | FB730834-BBA2-4C16-B6E0-7021D7A17E58 21 | .. 22 | 23 | IDESourceControlProjectURL 24 | ssh://github.ewr01.tumblr.net/bryan/CoreDataExample.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | FB730834-BBA2-4C16-B6E0-7021D7A17E58 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | FB730834-BBA2-4C16-B6E0-7021D7A17E58 36 | IDESourceControlWCCName 37 | CoreDataExample 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /CoreDataExample.xcworkspace/xcuserdata/bryan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TumblrArchive/CoreDataExample/fdcba11b8581ccafeceaa7423967f332c985ffb5/CoreDataExample.xcworkspace/xcuserdata/bryan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CoreDataExample.xcworkspace/xcuserdata/bryan.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CoreDataExample.xcworkspace/xcuserdata/bryan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /CoreDataExample/CoreDataExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | me.irace.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CoreDataExample/CoreDataExample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #import 17 | #endif 18 | -------------------------------------------------------------------------------- /CoreDataExample/CoreDataExample.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | CoreDataExample.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /CoreDataExample/CoreDataExample.xcdatamodeld/CoreDataExample.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /CoreDataExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /CoreDataExample/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /CoreDataExample/TMAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMAppDelegate.h 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TMAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /CoreDataExample/TMAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMAppDelegate.m 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMAppDelegate.h" 10 | #import "TMDashboardViewController.h" 11 | #import "TMAPIClient.h" 12 | #import "TMCoreDataController.h" 13 | 14 | @implementation TMAppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 17 | [[TMCoreDataController sharedInstance] setUp]; 18 | 19 | #warning Needs keys/secrets 20 | /* 21 | In order for this to work, you need to create a Tumblr API application, which can be done here: https://www.tumblr.com/oauth/apps. 22 | You can then use the API console to get a token and token secret: https://api.tumblr.com/console 23 | */ 24 | [TMAPIClient sharedInstance].OAuthConsumerKey = @""; 25 | [TMAPIClient sharedInstance].OAuthConsumerSecret = @""; 26 | [TMAPIClient sharedInstance].OAuthToken = @""; 27 | [TMAPIClient sharedInstance].OAuthTokenSecret = @""; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 30 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[TMDashboardViewController alloc] init]]; 31 | self.window.backgroundColor = [UIColor whiteColor]; 32 | [self.window makeKeyAndVisible]; 33 | 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /CoreDataExample/TMCoreDataController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMCoreDataController.h 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | typedef void (^TMCoreDataControllerBlock)(NSManagedObjectContext *context); 10 | 11 | /** 12 | * Encapsulates an entire [Core Data stack](http://floriankugler.com/blog/2013/4/2/the-concurrent-core-data-stack) 13 | * and exposes methods necessary to perform operations on managed object contexts associated with both the main queue 14 | * and a private queue. 15 | */ 16 | @interface TMCoreDataController : NSObject 17 | 18 | /** 19 | * Context associated with the main queue. Can be passed directly to read-only methods that require a context but don't 20 | * require the context to be saved afterwards (in which case `performMainContextBlock:` would be a better option). 21 | */ 22 | @property (nonatomic, strong, readonly) NSManagedObjectContext *mainContext; 23 | 24 | + (instancetype)sharedInstance; 25 | 26 | - (void)setUp; 27 | 28 | /** 29 | * Provides a block with a private queue context and performs the block on the aforementioned queue, synchronously. 30 | * Saves the context (and any ancestor contexts, recursively) afterwards. 31 | * 32 | * @param block Block provided with a private queue context and performed on the aforementioned queue. 33 | */ 34 | - (void)performBackgroundBlockAndWait:(TMCoreDataControllerBlock)block; 35 | 36 | /** 37 | * Provides a block with the main queue context and performs the block on the main queue, synchronously. 38 | * Saves the context (and any ancestor contexts, recursively) afterwards. 39 | * 40 | * @param block Block provided with the main queue context and performed on the main queue. 41 | */ 42 | - (void)performMainContextBlock:(TMCoreDataControllerBlock)block; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /CoreDataExample/TMCoreDataController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMCoreDataController.m 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMCoreDataController.h" 10 | 11 | static NSString * const ManagedObjectModelResourceName = @"CoreDataExample"; 12 | static NSString * const ManagedObjectModelExtension = @"momd"; 13 | static NSString * const PersistentStorePath = @"Tumblr.sqlite"; 14 | 15 | @interface TMCoreDataController() 16 | 17 | @property (nonatomic, strong) NSManagedObjectContext *masterContext; 18 | @property (nonatomic, strong) NSManagedObjectContext *mainContext; 19 | 20 | @end 21 | 22 | @implementation TMCoreDataController 23 | 24 | #pragma mark - Initialization 25 | 26 | + (instancetype)sharedInstance { 27 | static TMCoreDataController *instance; 28 | 29 | static dispatch_once_t onceToken; 30 | dispatch_once(&onceToken, ^{ 31 | instance = [[self alloc] init]; 32 | }); 33 | 34 | return instance; 35 | } 36 | 37 | - (void)setUp { 38 | NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL: 39 | [[NSBundle mainBundle] URLForResource:ManagedObjectModelResourceName 40 | withExtension:ManagedObjectModelExtension]]; 41 | 42 | NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel]; 43 | 44 | _masterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 45 | _masterContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; 46 | _masterContext.persistentStoreCoordinator = persistentStoreCoordinator; 47 | 48 | _mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 49 | _mainContext.parentContext = _masterContext; 50 | 51 | NSURL *persistentStoreURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] 52 | firstObject] 53 | URLByAppendingPathComponent:PersistentStorePath]; 54 | 55 | [self addPersistentStoreAtURL:persistentStoreURL toCoordinator:persistentStoreCoordinator requiringCompatabilityWithModel:managedObjectModel]; 56 | 57 | void (^registerToSaveMainContextWhenObservingNotificationWithName)(NSString *) = ^(NSString *notificationName) { 58 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveMainContext) name:notificationName object:nil]; 59 | }; 60 | registerToSaveMainContextWhenObservingNotificationWithName(UIApplicationDidEnterBackgroundNotification); 61 | registerToSaveMainContextWhenObservingNotificationWithName(UIApplicationWillTerminateNotification); 62 | } 63 | 64 | #pragma mark - Private 65 | 66 | /** 67 | * Add a persistent store to a coordinator. If a store already exists on disk, reuse it iff it is compatable with the 68 | * provided managed object model. Otherwise, delete the store on disk and create a new one. 69 | */ 70 | - (NSPersistentStore *)addPersistentStoreAtURL:(NSURL *)persistentStoreURL 71 | toCoordinator:(NSPersistentStoreCoordinator *)coordinator 72 | requiringCompatabilityWithModel:(NSManagedObjectModel *)model { 73 | BOOL storeWasRecreated = NO; 74 | 75 | if ([[NSFileManager defaultManager] fileExistsAtPath:[persistentStoreURL path]]) { 76 | NSError *storeMetadataError = nil; 77 | NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType 78 | URL:persistentStoreURL 79 | error:&storeMetadataError]; 80 | 81 | // If store is incompatible with the managed object model, remove the store file 82 | if (storeMetadataError || ![model isConfiguration:nil compatibleWithStoreMetadata:storeMetadata]) { 83 | storeWasRecreated = YES; 84 | 85 | NSError *removeStoreError = nil; 86 | 87 | if (![[NSFileManager defaultManager] removeItemAtURL:persistentStoreURL error:&removeStoreError]) { 88 | NSLog(@"Error removing store file at URL '%@': %@, %@", persistentStoreURL, removeStoreError, [removeStoreError userInfo]); 89 | } 90 | } 91 | } 92 | 93 | NSError *addStoreError = nil; 94 | NSPersistentStore *store = [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:persistentStoreURL 95 | options:nil error:&addStoreError]; 96 | 97 | if (!store) { 98 | NSLog(@"Unable to add store: %@, %@", addStoreError, [addStoreError userInfo]); 99 | } 100 | 101 | return store; 102 | } 103 | 104 | /** 105 | * Save the provided managed object context as well as its parent context(s) (recursively) 106 | */ 107 | - (void)saveContext:(NSManagedObjectContext *)context { 108 | if ([context hasChanges]) { 109 | NSError *error; 110 | 111 | if (![context save:&error]) { 112 | NSLog(@"Error saving context: %@ %@ %@", self, error, [error userInfo]); 113 | } 114 | 115 | [self saveContext:context.parentContext]; 116 | } 117 | } 118 | 119 | /** 120 | * Save the main queue's context as well as its parent context(s) (recursively) 121 | */ 122 | - (void)saveMainContext { 123 | [self saveContext:self.mainContext]; 124 | } 125 | 126 | #pragma mark - Block operations 127 | 128 | /** 129 | * Perform a block on a new context (with private queue concurrency type), and then save the context as well as its 130 | * parent context(s) (recursively). 131 | */ 132 | - (void)performBackgroundBlockAndWait:(TMCoreDataControllerBlock)block { 133 | NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 134 | backgroundContext.parentContext = self.mainContext; 135 | 136 | if (block) { 137 | [backgroundContext performBlockAndWait:^{ 138 | block(backgroundContext); 139 | 140 | [self saveContext:backgroundContext]; 141 | }]; 142 | } 143 | } 144 | 145 | /** 146 | * Perform a block on the main queue's context, and then save the context as well as its parent context(s) (recursively). 147 | */ 148 | - (void)performMainContextBlock:(TMCoreDataControllerBlock)block { 149 | if (block) { 150 | block(self.mainContext); 151 | 152 | [self saveMainContext]; 153 | } 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /CoreDataExample/TMDashboardViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMDashboardViewController.h 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | @interface TMDashboardViewController : UITableViewController 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /CoreDataExample/TMDashboardViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMDashboardViewController.m 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMDashboardViewController.h" 10 | #import "TMFetchedResultsControllerDelegate.h" 11 | #import "TMPost.h" 12 | #import "TMAPIClient.h" 13 | #import "TMCoreDataController.h" 14 | 15 | @interface TMDashboardViewController() 16 | 17 | @property (nonatomic) NSFetchedResultsController *fetchedResultsController; 18 | @property (nonatomic) TMFetchedResultsControllerDelegate *fetchedResultsControllerDelegate; 19 | 20 | @end 21 | 22 | @implementation TMDashboardViewController 23 | 24 | - (id)initWithStyle:(UITableViewStyle)style { 25 | if (self = [super initWithStyle:style]) { 26 | self.title = @"Dashboard"; 27 | } 28 | 29 | return self; 30 | } 31 | 32 | #pragma mark - UIViewController 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | 37 | self.refreshControl = [[UIRefreshControl alloc] init]; 38 | [self.refreshControl addTarget:self action:@selector(refresh) forControlEvents:UIControlEventValueChanged]; 39 | 40 | self.fetchedResultsControllerDelegate = [[TMFetchedResultsControllerDelegate alloc] initWithTableView:self.tableView]; 41 | 42 | self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:[TMPost allPostsFetchRequest] 43 | managedObjectContext:[TMCoreDataController sharedInstance].mainContext 44 | sectionNameKeyPath:nil 45 | cacheName:nil]; 46 | self.fetchedResultsController.delegate = self.fetchedResultsControllerDelegate; 47 | 48 | [self.fetchedResultsController performFetch:nil]; 49 | 50 | [self refresh]; 51 | } 52 | 53 | #pragma mark - Actions 54 | 55 | - (void)refresh { 56 | [[TMAPIClient sharedInstance] dashboard:nil callback:^(NSDictionary *response, NSError *error) { 57 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 58 | [[TMCoreDataController sharedInstance] performBackgroundBlockAndWait:^(NSManagedObjectContext *context) { 59 | // Delete all existing posts 60 | 61 | NSArray *cachedPosts = [context executeFetchRequest:[TMPost allPostsFetchRequest] error:nil]; 62 | 63 | for (TMPost *cachedPost in [cachedPosts reverseObjectEnumerator]) { 64 | [context deleteObject:cachedPost]; 65 | } 66 | 67 | // Create new posts out of API response 68 | 69 | NSArray *responsePostDictionaries = response[@"posts"]; 70 | 71 | for (NSDictionary *responsePostDictionary in responsePostDictionaries) { 72 | [context insertObject:[TMPost postFromDictionary:responsePostDictionary inContext:context]]; 73 | } 74 | }]; 75 | 76 | dispatch_async(dispatch_get_main_queue(), ^{ 77 | [self.refreshControl endRefreshing]; 78 | }); 79 | }); 80 | }]; 81 | } 82 | 83 | #pragma mark - UITableViewDataSource 84 | 85 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 86 | return [[self.fetchedResultsController sections] count]; 87 | } 88 | 89 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 90 | return [self.fetchedResultsController.sections[section] numberOfObjects]; 91 | } 92 | 93 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 94 | static NSString *CellIdentifier = @"CellIdentifier"; 95 | 96 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 97 | 98 | if (!cell) { 99 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier]; 100 | } 101 | 102 | TMPost *post = [self.fetchedResultsController objectAtIndexPath:indexPath]; 103 | 104 | cell.textLabel.text = post.blogName; 105 | cell.detailTextLabel.text = post.postID; 106 | 107 | return cell; 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /CoreDataExample/TMFetchedResultsControllerDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMFetchedResultsControllerDelegate.h 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | @interface TMFetchedResultsControllerDelegate : NSObject 10 | 11 | - (instancetype)initWithTableView:(UITableView *)tableView; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CoreDataExample/TMFetchedResultsControllerDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMFetchedResultsControllerDelegate.m 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMFetchedResultsControllerDelegate.h" 10 | 11 | @interface TMFetchedResultsControllerDelegate() 12 | 13 | @property (nonatomic, weak) UITableView *tableView; 14 | 15 | @end 16 | 17 | @implementation TMFetchedResultsControllerDelegate 18 | 19 | - (instancetype)initWithTableView:(UITableView *)tableView { 20 | if (self = [super init]) { 21 | _tableView = tableView; 22 | } 23 | 24 | return self; 25 | } 26 | 27 | #pragma mark - NSFetchedResultsControllerDelegate 28 | 29 | - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { 30 | [self.tableView beginUpdates]; 31 | } 32 | 33 | - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { 34 | [self.tableView endUpdates]; 35 | } 36 | 37 | - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo 38 | atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { 39 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:sectionIndex]; 40 | 41 | switch (type) { 42 | case NSFetchedResultsChangeInsert: { 43 | [self.tableView insertSections:indexSet withRowAnimation:UITableViewRowAnimationFade]; 44 | 45 | break; 46 | } 47 | case NSFetchedResultsChangeDelete:{ 48 | [self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationFade]; 49 | 50 | break; 51 | } 52 | } 53 | } 54 | 55 | - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)object atIndexPath:(NSIndexPath *)indexPath 56 | forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { 57 | 58 | void (^deleteIndexPathRows)(void) = ^{ 59 | [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 60 | }; 61 | 62 | void (^insertNewIndexPathRows)(void) = ^{ 63 | [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 64 | }; 65 | 66 | switch (type) { 67 | case NSFetchedResultsChangeInsert: { 68 | insertNewIndexPathRows(); 69 | 70 | break; 71 | } 72 | case NSFetchedResultsChangeDelete: { 73 | deleteIndexPathRows(); 74 | 75 | break; 76 | } 77 | case NSFetchedResultsChangeMove: { 78 | deleteIndexPathRows(); 79 | insertNewIndexPathRows(); 80 | 81 | break; 82 | } 83 | case NSFetchedResultsChangeUpdate: { 84 | [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 85 | 86 | break; 87 | } 88 | } 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /CoreDataExample/TMPost.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMPost.h 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TMPost : NSManagedObject 12 | 13 | @property (nonatomic, copy) NSString *postID; 14 | @property (nonatomic, copy) NSString *blogName; 15 | 16 | + (instancetype)postFromDictionary:(NSDictionary *)dictionary inContext:(NSManagedObjectContext *)context; 17 | 18 | + (NSFetchRequest *)allPostsFetchRequest; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /CoreDataExample/TMPost.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMPost.m 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMPost.h" 10 | 11 | @implementation TMPost 12 | 13 | @dynamic postID; 14 | @dynamic blogName; 15 | 16 | + (instancetype)postFromDictionary:(NSDictionary *)dictionary inContext:(NSManagedObjectContext *)context { 17 | TMPost *post = [[[self class] alloc] initWithEntity:[NSEntityDescription entityForName:@"Post" inManagedObjectContext:context] 18 | insertIntoManagedObjectContext:context]; 19 | post.postID = [dictionary[@"id"] stringValue]; 20 | post.blogName = dictionary[@"blog_name"]; 21 | 22 | return post; 23 | } 24 | 25 | + (NSFetchRequest *)allPostsFetchRequest { 26 | NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Post"]; 27 | fetchRequest.predicate = [NSPredicate predicateWithFormat:@"postID != nil"]; 28 | fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"postID" ascending:NO]]; 29 | 30 | return fetchRequest; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /CoreDataExample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CoreDataExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CoreDataExample 4 | // 5 | // Created by Bryan Irace on 6/2/14. 6 | // Copyright (c) 2014 Bryan Irace. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "TMAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([TMAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '7.0' 2 | 3 | pod 'TMTumblrSDK' 4 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - JXHTTP (1.0.0) 3 | - TMTumblrSDK (1.0.8): 4 | - TMTumblrSDK/Activity 5 | - TMTumblrSDK/APIClient 6 | - TMTumblrSDK/AppClient 7 | - TMTumblrSDK/Core 8 | - TMTumblrSDK/Activity (1.0.8) 9 | - TMTumblrSDK/APIClient (1.0.8): 10 | - JXHTTP (= 1.0.0) 11 | - TMTumblrSDK/APIClient/Authentication 12 | - TMTumblrSDK/APIClient/Authentication (1.0.8): 13 | - JXHTTP (= 1.0.0) 14 | - TMTumblrSDK/Core 15 | - TMTumblrSDK/AppClient (1.0.8): 16 | - TMTumblrSDK/Core 17 | - TMTumblrSDK/Core (1.0.8) 18 | 19 | DEPENDENCIES: 20 | - TMTumblrSDK 21 | 22 | SPEC CHECKSUMS: 23 | JXHTTP: dd1fe9c6fa21e7b2be9724ef2be16c66aafc44b4 24 | TMTumblrSDK: b9fada23e88b42ade2b492a6db95bfb1dff6304b 25 | 26 | COCOAPODS: 0.33.1 27 | -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTP.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTP.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPDataBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPDataBody.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPFileBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPFileBody.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPFormEncodedBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPFormEncodedBody.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPJSONBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPJSONBody.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPMultipartBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPMultipartBody.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPOperation+Convenience.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperation+Convenience.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPOperation.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperation.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPOperationDelegate.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperationDelegate.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPOperationQueue.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperationQueue.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPOperationQueueDelegate.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperationQueueDelegate.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXHTTPRequestBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPRequestBody.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXOperation.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXOperation.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXURLConnectionOperation.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXURLConnectionOperation.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/JXHTTP/JXURLEncoding.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXURLEncoding.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/TMTumblrSDK/TMAPIClient.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/APIClient/TMAPIClient.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/TMTumblrSDK/TMOAuth.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/Authentication/TMOAuth.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/TMTumblrSDK/TMSDKFunctions.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/Core/TMSDKFunctions.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/TMTumblrSDK/TMTumblrActivity.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/Activity/TMTumblrActivity.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/TMTumblrSDK/TMTumblrAppClient.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/AppClient/TMTumblrAppClient.h -------------------------------------------------------------------------------- /Pods/BuildHeaders/TMTumblrSDK/TMTumblrAuthenticator.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/Authentication/TMTumblrAuthenticator.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTP.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTP.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPDataBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPDataBody.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPFileBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPFileBody.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPFormEncodedBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPFormEncodedBody.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPJSONBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPJSONBody.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPMultipartBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPMultipartBody.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPOperation+Convenience.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperation+Convenience.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPOperation.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperation.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPOperationDelegate.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperationDelegate.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPOperationQueue.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperationQueue.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPOperationQueueDelegate.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPOperationQueueDelegate.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXHTTPRequestBody.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXHTTPRequestBody.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXOperation.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXOperation.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXURLConnectionOperation.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXURLConnectionOperation.h -------------------------------------------------------------------------------- /Pods/Headers/JXHTTP/JXURLEncoding.h: -------------------------------------------------------------------------------- 1 | ../../JXHTTP/JXHTTP/JXURLEncoding.h -------------------------------------------------------------------------------- /Pods/Headers/TMTumblrSDK/TMAPIClient.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/APIClient/TMAPIClient.h -------------------------------------------------------------------------------- /Pods/Headers/TMTumblrSDK/TMOAuth.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/Authentication/TMOAuth.h -------------------------------------------------------------------------------- /Pods/Headers/TMTumblrSDK/TMSDKFunctions.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/Core/TMSDKFunctions.h -------------------------------------------------------------------------------- /Pods/Headers/TMTumblrSDK/TMTumblrActivity.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/Activity/TMTumblrActivity.h -------------------------------------------------------------------------------- /Pods/Headers/TMTumblrSDK/TMTumblrAppClient.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/AppClient/TMTumblrAppClient.h -------------------------------------------------------------------------------- /Pods/Headers/TMTumblrSDK/TMTumblrAuthenticator.h: -------------------------------------------------------------------------------- 1 | ../../TMTumblrSDK/TMTumblrSDK/Authentication/TMTumblrAuthenticator.h -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTP.h: -------------------------------------------------------------------------------- 1 | #pragma HC SVNT DRACONES 2 | 3 | // Core 4 | #import "JXOperation.h" 5 | #import "JXURLConnectionOperation.h" 6 | #import "JXHTTPOperation.h" 7 | #import "JXHTTPOperationQueue.h" 8 | 9 | // Protocol 10 | #import "JXHTTPRequestBody.h" 11 | #import "JXHTTPOperationDelegate.h" 12 | #import "JXHTTPOperationQueueDelegate.h" 13 | 14 | // Convenience 15 | #import "JXURLEncoding.h" 16 | #import "JXHTTPOperation+Convenience.h" 17 | 18 | // Request Body 19 | #import "JXHTTPDataBody.h" 20 | #import "JXHTTPFileBody.h" 21 | #import "JXHTTPFormEncodedBody.h" 22 | #import "JXHTTPJSONBody.h" 23 | #import "JXHTTPMultipartBody.h" 24 | 25 | // Error Logging 26 | #define JXError(error) if (error) { \ 27 | NSLog(@"%@ (%d) ERROR: %@", \ 28 | [[NSString stringWithUTF8String:__FILE__] lastPathComponent], \ 29 | __LINE__, [error localizedDescription]); } 30 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPDataBody.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPDataBody` conforms to and provides functionality for streaming 3 | request bodies directly from memory, with any content type. 4 | */ 5 | 6 | #import "JXHTTPRequestBody.h" 7 | 8 | @interface JXHTTPDataBody : NSObject 9 | 10 | /** 11 | The data to upload. 12 | */ 13 | @property (strong, nonatomic) NSData *data; 14 | 15 | /** 16 | The MIME content type of the data to upload. 17 | */ 18 | @property (copy, nonatomic) NSString *httpContentType; 19 | 20 | /** 21 | Creates a new `JXHTTPDataBody` with the given data. 22 | 23 | @param data The data to upload. 24 | @returns A request body. 25 | */ 26 | + (instancetype)withData:(NSData *)data; 27 | 28 | /** 29 | Creates a new `JXHTTPDataBody` with the given data and MIME content type. 30 | 31 | @param data The data to upload. 32 | @param contentType The MIME content type of the data to upload. 33 | @returns A request body. 34 | */ 35 | + (instancetype)withData:(NSData *)data contentType:(NSString *)contentType; 36 | 37 | /** 38 | Creates a new `JXHTTPDataBody` with the given data and MIME content type. 39 | 40 | @param data The data to upload. 41 | @param contentType The MIME content type of the data to upload. 42 | @returns A request body. 43 | */ 44 | - (instancetype)initWithData:(NSData *)data contentType:(NSString *)contentType; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPDataBody.m: -------------------------------------------------------------------------------- 1 | #import "JXHTTPDataBody.h" 2 | 3 | @implementation JXHTTPDataBody 4 | 5 | #pragma mark - Initialization 6 | 7 | - (instancetype)initWithData:(NSData *)data contentType:(NSString *)contentType 8 | { 9 | if (self = [super init]) { 10 | self.data = data; 11 | self.httpContentType = contentType; 12 | } 13 | return self; 14 | } 15 | 16 | + (instancetype)withData:(NSData *)data contentType:(NSString *)contentType 17 | { 18 | return [[self alloc] initWithData:data contentType:contentType]; 19 | } 20 | 21 | + (instancetype)withData:(NSData *)data 22 | { 23 | return [self withData:data contentType:nil]; 24 | } 25 | 26 | #pragma mark - 27 | 28 | - (NSInputStream *)httpInputStream 29 | { 30 | return [[NSInputStream alloc] initWithData:self.data]; 31 | } 32 | 33 | - (long long)httpContentLength 34 | { 35 | return [self.data length]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPFileBody.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPFileBody` conforms to and provides functionality for streaming 3 | request bodies directly from disk, with any content type. 4 | */ 5 | 6 | #import "JXHTTPRequestBody.h" 7 | 8 | @interface JXHTTPFileBody : NSObject 9 | 10 | /** 11 | The path of the file to upload. 12 | */ 13 | @property (copy, nonatomic) NSString *filePath; 14 | 15 | /** 16 | The MIME content type of the file to upload. 17 | */ 18 | @property (copy, nonatomic) NSString *httpContentType; 19 | 20 | /** 21 | Creates a new `JXHTTPFileBody` with the given local file path. 22 | 23 | @param filePath The local path of the file. 24 | @returns A request body. 25 | */ 26 | + (instancetype)withFilePath:(NSString *)filePath; 27 | 28 | /** 29 | Creates a new `JXHTTPFileBody` with the given local file path. 30 | 31 | @param filePath The local path of the file. 32 | @param contentType The MIME content type of the file. 33 | @returns A request body. 34 | */ 35 | + (instancetype)withFilePath:(NSString *)filePath contentType:(NSString *)contentType; 36 | 37 | /** 38 | Creates a new `JXHTTPFileBody` with the given local file path. 39 | 40 | @param filePath The local path of the file. 41 | @param contentType The MIME content type of the file. 42 | @returns A request body. 43 | */ 44 | - (instancetype)initWithFilePath:(NSString *)filePath contentType:(NSString *)contentType; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPFileBody.m: -------------------------------------------------------------------------------- 1 | #import "JXHTTPFileBody.h" 2 | #import "JXHTTP.h" 3 | 4 | @implementation JXHTTPFileBody 5 | 6 | #pragma mark - Initialization 7 | 8 | - (instancetype)initWithFilePath:(NSString *)filePath contentType:(NSString *)contentType 9 | { 10 | if (self = [super init]) { 11 | self.filePath = filePath; 12 | self.httpContentType = contentType; 13 | } 14 | return self; 15 | } 16 | 17 | + (instancetype)withFilePath:(NSString *)filePath contentType:(NSString *)contentType 18 | { 19 | return [[self alloc] initWithFilePath:filePath contentType:contentType]; 20 | } 21 | 22 | + (instancetype)withFilePath:(NSString *)filePath 23 | { 24 | return [self withFilePath:filePath contentType:nil]; 25 | } 26 | 27 | #pragma mark - 28 | 29 | - (NSInputStream *)httpInputStream 30 | { 31 | return [[NSInputStream alloc] initWithFileAtPath:self.filePath]; 32 | } 33 | 34 | - (long long)httpContentLength 35 | { 36 | if (![self.filePath length]) 37 | return NSURLResponseUnknownLength; 38 | 39 | NSError *error = nil; 40 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.filePath error:&error]; 41 | JXError(error); 42 | 43 | NSNumber *fileSize = [attributes objectForKey:NSFileSize]; 44 | if (fileSize) 45 | return [fileSize longLongValue]; 46 | 47 | return NSURLResponseUnknownLength; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPFormEncodedBody.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPFormEncodedBody` conforms to and provides functionality for 3 | form-encoded request bodies (i.e., the type usually sent by browsers.) 4 | 5 | Reports its content type as `application/x-www-form-urlencoded; charset=utf-8`. 6 | */ 7 | 8 | #import "JXHTTPRequestBody.h" 9 | 10 | @interface JXHTTPFormEncodedBody : NSObject 11 | 12 | /** 13 | A dictionary that will be form-encoded upon transmission. 14 | */ 15 | @property (strong, readonly, nonatomic) NSMutableDictionary *dictionary; 16 | 17 | /** 18 | Creates a new `JXHTTPFormEncodedBody`. 19 | 20 | @see 21 | 22 | @param dictionary A dictionary that will be form-encoded upon transmission. 23 | @returns A request body. 24 | */ 25 | + (instancetype)withDictionary:(NSDictionary *)dictionary; 26 | 27 | /** 28 | Creates a new `JXHTTPFormEncodedBody`. 29 | 30 | @see 31 | 32 | @param dictionary A dictionary that will be form-encoded upon transmission. 33 | @returns A request body. 34 | */ 35 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPFormEncodedBody.m: -------------------------------------------------------------------------------- 1 | #import "JXHTTPFormEncodedBody.h" 2 | #import "JXURLEncoding.h" 3 | 4 | @interface JXHTTPFormEncodedBody () 5 | @property (strong, nonatomic) NSMutableDictionary *dictionary; 6 | @end 7 | 8 | @implementation JXHTTPFormEncodedBody 9 | 10 | #pragma mark - Initialization 11 | 12 | - (instancetype)init 13 | { 14 | if (self = [super init]) { 15 | self.dictionary = [[NSMutableDictionary alloc] init]; 16 | } 17 | return self; 18 | } 19 | 20 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 21 | { 22 | if (self = [self init]) { 23 | self.dictionary = [[NSMutableDictionary alloc] initWithDictionary:dictionary]; 24 | } 25 | return self; 26 | } 27 | 28 | + (instancetype)withDictionary:(NSDictionary *)dictionary 29 | { 30 | return [[self alloc] initWithDictionary:dictionary]; 31 | } 32 | 33 | #pragma mark - Private Methods 34 | 35 | - (NSData *)requestData 36 | { 37 | return [[JXURLEncoding formEncodedDictionary:self.dictionary] dataUsingEncoding:NSUTF8StringEncoding]; 38 | } 39 | 40 | #pragma mark - 41 | 42 | - (NSInputStream *)httpInputStream 43 | { 44 | return [[NSInputStream alloc] initWithData:[self requestData]]; 45 | } 46 | 47 | - (NSString *)httpContentType 48 | { 49 | return @"application/x-www-form-urlencoded; charset=utf-8"; 50 | } 51 | 52 | - (long long)httpContentLength 53 | { 54 | return [[self requestData] length]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPJSONBody.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPJSONBody` conforms to and provides functionality for JSON 3 | POST request bodies. 4 | 5 | Reports its content type as `application/json; charset=utf-8`. 6 | */ 7 | 8 | #import "JXHTTPRequestBody.h" 9 | 10 | @interface JXHTTPJSONBody : NSObject 11 | 12 | /** 13 | Creates a new `JXHTTPJSONBody`. 14 | 15 | @param data The UTF-8 data of a previously serialized JSON string. 16 | @returns A JSON request body. 17 | */ 18 | + (instancetype)withData:(NSData *)data; 19 | 20 | /** 21 | Creates a new `JXHTTPJSONBody`. 22 | 23 | @param string A previously serialized JSON string. 24 | @returns A JSON request body. 25 | */ 26 | + (instancetype)withString:(NSString *)string; 27 | 28 | /** 29 | Creates a new `JXHTTPJSONBody`. 30 | 31 | @param dictionaryOrArray A dictionary or array, which will be serialized into a JSON string. 32 | @returns A JSON request body. 33 | */ 34 | + (instancetype)withJSONObject:(id)dictionaryOrArray; 35 | 36 | /** 37 | Creates a new `JXHTTPJSONBody`. 38 | 39 | @param data The UTF-8 data of a previously serialized JSON string. 40 | @returns A JSON request body. 41 | */ 42 | - (instancetype)initWithData:(NSData *)data; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPJSONBody.m: -------------------------------------------------------------------------------- 1 | #import "JXHTTPJSONBody.h" 2 | #import "JXHTTP.h" 3 | 4 | @interface JXHTTPJSONBody () 5 | @property (strong, nonatomic) NSData *requestData; 6 | @end 7 | 8 | @implementation JXHTTPJSONBody 9 | 10 | #pragma mark - Initialization 11 | 12 | - (instancetype)initWithData:(NSData *)data 13 | { 14 | if (self = [super init]) { 15 | self.requestData = data; 16 | } 17 | return self; 18 | } 19 | 20 | + (instancetype)withData:(NSData *)data 21 | { 22 | return [[self alloc] initWithData:data]; 23 | } 24 | 25 | + (instancetype)withString:(NSString *)string 26 | { 27 | return [self withData:[string dataUsingEncoding:NSUTF8StringEncoding]]; 28 | } 29 | 30 | + (instancetype)withJSONObject:(id)dictionaryOrArray 31 | { 32 | NSError *error = nil; 33 | NSData *data = [NSJSONSerialization dataWithJSONObject:dictionaryOrArray options:0 error:&error]; 34 | JXError(error); 35 | 36 | return [self withData:data]; 37 | } 38 | 39 | #pragma mark - 40 | 41 | - (NSInputStream *)httpInputStream 42 | { 43 | return [[NSInputStream alloc] initWithData:self.requestData]; 44 | } 45 | 46 | - (NSString *)httpContentType 47 | { 48 | return @"application/json; charset=utf-8"; 49 | } 50 | 51 | - (long long)httpContentLength 52 | { 53 | return [self.requestData length]; 54 | } 55 | 56 | #pragma mark - 57 | 58 | - (void)httpOperationDidFinishLoading:(JXHTTPOperation *)operation 59 | { 60 | self.requestData = nil; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPMultipartBody.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPMultipartBody` conforms to and provides functionality for 3 | mulitpart MIME request bodies (with potentially multiple content types). 4 | 5 | File parts are streamed from disk using an `NSInputStream` and therefore memory 6 | efficient even in the case of very large files. 7 | */ 8 | 9 | #import "JXHTTPRequestBody.h" 10 | 11 | @interface JXHTTPMultipartBody : NSObject 12 | 13 | /** 14 | The stream buffer size for the file input stream. In most cases you shouldn't need to 15 | change this, the 64K default is intended to match the output stream of `NSURLConnection`. 16 | */ 17 | @property (assign, nonatomic) NSUInteger streamBufferLength; 18 | 19 | /** 20 | Creates a new `JXHTTPMultipartBody`. 21 | 22 | @param stringParameters A dictionary of keys and values that will be added as a text part. 23 | @returns A multipart request body. 24 | */ 25 | + (instancetype)withDictionary:(NSDictionary *)stringParameters; 26 | 27 | /** 28 | Creates a new `JXHTTPMultipartBody`. 29 | 30 | @param stringParameters A dictionary of keys and values that will be added as a `text/plain` part. 31 | @returns A multipart request body. 32 | */ 33 | - (instancetype)initWithDictionary:(NSDictionary *)stringParameters; 34 | 35 | /** 36 | Adds a new `text/plain` part with the specified key. 37 | 38 | @param string The value. 39 | @param key The key. 40 | */ 41 | - (void)addString:(NSString *)string forKey:(NSString *)key; 42 | 43 | /** 44 | Adds a new `text/plain` part with the specified key and overwrites any existing value. 45 | 46 | @param string The value. 47 | @param key The key. 48 | */ 49 | - (void)setString:(NSString *)string forKey:(NSString *)key; 50 | 51 | /** 52 | Adds a new data part with the specified content type. 53 | 54 | @param data The data. 55 | @param key The key. 56 | @param contentTypeOrNil The content type, or `nil` to default to `application/octet-stream`. 57 | @param fileNameOrNil A suggested file name for the recipient, ignored by most servers (safe to pass nil). 58 | */ 59 | - (void)addData:(NSData *)data forKey:(NSString *)key contentType:(NSString *)contentTypeOrNil fileName:(NSString *)fileNameOrNil; 60 | 61 | /** 62 | Adds a new data part with the specified content type and overwrites any existing value. 63 | 64 | @param data The data. 65 | @param key The key. 66 | @param contentTypeOrNil The content type, or `nil` to default to `application/octet-stream`. 67 | @param fileNameOrNil A suggested file name for the recipient, ignored by most servers (safe to pass nil). 68 | */ 69 | - (void)setData:(NSData *)data forKey:(NSString *)key contentType:(NSString *)contentTypeOrNil fileName:(NSString *)fileNameOrNil; 70 | 71 | /** 72 | Adds a new file part with the specified content type. The file will not be accessed until 73 | the connection begins but it must exist at the time it's added. 74 | 75 | @param filePath The local path of the file. 76 | @param key The key. 77 | @param contentTypeOrNil The content type, or `nil` to default to `application/octet-stream`. 78 | @param fileNameOrNil A suggested file name for the recipient, ignored by most servers (safe to pass nil). 79 | */ 80 | - (void)addFile:(NSString *)filePath forKey:(NSString *)key contentType:(NSString *)contentTypeOrNil fileName:(NSString *)fileNameOrNil; 81 | 82 | /** 83 | Adds a new file part with the specified content type. The file will not be accessed until 84 | the connection begins but it must exist at the time it's added. Overwrites any existing value. 85 | 86 | @param filePath The local path of the file. 87 | @param key The key. 88 | @param contentTypeOrNil The content type, or `nil` to default to `application/octet-stream`. 89 | @param fileNameOrNil A suggested file name for the recipient, ignored by most servers (safe to pass nil). 90 | */ 91 | - (void)setFile:(NSString *)filePath forKey:(NSString *)key contentType:(NSString *)contentTypeOrNil fileName:(NSString *)fileNameOrNil; 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPOperation+Convenience.h: -------------------------------------------------------------------------------- 1 | /** 2 | These methods and properties provide convenient access to the request and response objects of 3 | the underlying `NSURLConnection`. In most cases they simply pass through the property of the 4 | same name. 5 | */ 6 | 7 | #import "JXHTTPOperation.h" 8 | 9 | @interface JXHTTPOperation (JXHTTPOperationConvenience) 10 | 11 | @property (assign, nonatomic) NSURLCacheStoragePolicy requestCachePolicy; 12 | @property (assign, nonatomic) BOOL requestShouldUsePipelining; 13 | @property (strong, nonatomic) NSURL *requestMainDocumentURL; 14 | @property (assign, nonatomic) NSTimeInterval requestTimeoutInterval; 15 | @property (assign, nonatomic) NSURLRequestNetworkServiceType requestNetworkServiceType; 16 | @property (strong, nonatomic) NSURL *requestURL; 17 | @property (strong, nonatomic) NSDictionary *requestHeaders; 18 | @property (strong, nonatomic) NSString *requestMethod; 19 | @property (assign, nonatomic) BOOL requestShouldHandleCookies; 20 | 21 | - (void)addValue:(NSString *)valueString forRequestHeader:(NSString *)headerFieldString; 22 | - (void)setValue:(NSString *)valueString forRequestHeader:(NSString *)headerFieldString; 23 | 24 | - (NSData *)responseData; 25 | - (NSString *)responseString; 26 | - (id)responseJSON; 27 | - (NSDictionary *)responseHeaders; 28 | - (NSInteger)responseStatusCode; 29 | - (NSString *)responseStatusString; 30 | - (long long)responseExpectedContentLength; 31 | - (NSString *)responseExpectedFileName; 32 | - (NSString *)responseMIMEType; 33 | - (NSString *)responseTextEncodingName; 34 | - (NSURL *)responseURL; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPOperation+Convenience.m: -------------------------------------------------------------------------------- 1 | #import "JXHTTPOperation+Convenience.h" 2 | #import "JXHTTP.h" 3 | 4 | @implementation JXHTTPOperation (JXHTTPOperationConvenience) 5 | 6 | #pragma mark - Request 7 | 8 | - (NSURLCacheStoragePolicy)requestCachePolicy 9 | { 10 | return self.request.cachePolicy; 11 | } 12 | 13 | - (void)setRequestCachePolicy:(NSURLCacheStoragePolicy)requestCachePolicy 14 | { 15 | self.request.cachePolicy = requestCachePolicy; 16 | } 17 | 18 | - (BOOL)requestShouldUsePipelining 19 | { 20 | return self.request.HTTPShouldUsePipelining; 21 | } 22 | 23 | - (void)setRequestShouldUsePipelining:(BOOL)requestShouldUsePipelining 24 | { 25 | self.request.HTTPShouldUsePipelining = requestShouldUsePipelining; 26 | } 27 | 28 | - (NSURL *)requestMainDocumentURL 29 | { 30 | return self.request.mainDocumentURL; 31 | } 32 | 33 | - (void)setRequestMainDocumentURL:(NSURL *)requestMainDocumentURL 34 | { 35 | self.request.mainDocumentURL = requestMainDocumentURL; 36 | } 37 | 38 | - (NSTimeInterval)requestTimeoutInterval 39 | { 40 | return self.request.timeoutInterval; 41 | } 42 | 43 | - (void)setRequestTimeoutInterval:(NSTimeInterval)requestTimeoutInterval 44 | { 45 | self.request.timeoutInterval = requestTimeoutInterval; 46 | } 47 | 48 | - (NSURLRequestNetworkServiceType)requestNetworkServiceType 49 | { 50 | return self.request.networkServiceType; 51 | } 52 | 53 | - (void)setRequestNetworkServiceType:(NSURLRequestNetworkServiceType)requestNetworkServiceType 54 | { 55 | self.request.networkServiceType = requestNetworkServiceType; 56 | } 57 | 58 | - (NSURL *)requestURL 59 | { 60 | return self.request.URL; 61 | } 62 | 63 | - (void)setRequestURL:(NSURL *)requestURL 64 | { 65 | self.request.URL = requestURL; 66 | } 67 | 68 | - (NSDictionary *)requestHeaders 69 | { 70 | return [self.request allHTTPHeaderFields]; 71 | } 72 | 73 | - (void)setRequestHeaders:(NSDictionary *)requestHeaders 74 | { 75 | [self.request setAllHTTPHeaderFields:requestHeaders]; 76 | } 77 | 78 | - (NSString *)requestMethod 79 | { 80 | return self.request.HTTPMethod; 81 | } 82 | 83 | - (void)setRequestMethod:(NSString *)requestMethod 84 | { 85 | self.request.HTTPMethod = requestMethod; 86 | } 87 | 88 | - (BOOL)requestShouldHandleCookies 89 | { 90 | return self.request.HTTPShouldHandleCookies; 91 | } 92 | 93 | - (void)setRequestShouldHandleCookies:(BOOL)requestShouldHandleCookies 94 | { 95 | self.request.HTTPShouldHandleCookies = requestShouldHandleCookies; 96 | } 97 | 98 | - (void)addValue:(NSString *)valueString forRequestHeader:(NSString *)headerFieldString 99 | { 100 | [self.request addValue:valueString forHTTPHeaderField:headerFieldString]; 101 | } 102 | 103 | - (void)setValue:(NSString *)valueString forRequestHeader:(NSString *)headerFieldString 104 | { 105 | [self.request setValue:valueString forHTTPHeaderField:headerFieldString]; 106 | } 107 | 108 | #pragma mark - Response 109 | 110 | - (NSData *)responseData 111 | { 112 | NSData *data = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; 113 | if (data) 114 | return data; 115 | 116 | return [NSData dataWithContentsOfMappedFile:self.responseDataFilePath]; 117 | } 118 | 119 | - (NSString *)responseString 120 | { 121 | return [[NSString alloc] initWithData:[self responseData] encoding:NSUTF8StringEncoding]; 122 | } 123 | 124 | - (id)responseJSON 125 | { 126 | NSData *data = [self responseData]; 127 | if (!data) 128 | return nil; 129 | 130 | NSError *error = nil; 131 | id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 132 | JXError(error); 133 | 134 | return json; 135 | } 136 | 137 | - (NSDictionary *)responseHeaders 138 | { 139 | return [(NSHTTPURLResponse *)self.response allHeaderFields]; 140 | } 141 | 142 | - (NSInteger)responseStatusCode 143 | { 144 | return [(NSHTTPURLResponse *)self.response statusCode]; 145 | } 146 | 147 | - (NSString *)responseStatusString 148 | { 149 | return [NSHTTPURLResponse localizedStringForStatusCode:[self responseStatusCode]]; 150 | } 151 | 152 | - (long long)responseExpectedContentLength 153 | { 154 | return self.response.expectedContentLength; 155 | } 156 | 157 | - (NSString *)responseExpectedFileName 158 | { 159 | return self.response.suggestedFilename; 160 | } 161 | 162 | - (NSString *)responseMIMEType 163 | { 164 | return self.response.MIMEType; 165 | } 166 | 167 | - (NSString *)responseTextEncodingName 168 | { 169 | return self.response.textEncodingName; 170 | } 171 | 172 | - (NSURL *)responseURL 173 | { 174 | return self.response.URL; 175 | } 176 | 177 | @end 178 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPOperation.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPOperation` adds many features to , including blocks 3 | and delegate methods via the protocol. Blocks are 4 | performed serially on a private background queue unless 5 | is set to `YES`. 6 | 7 | can be used to group multiple operations for progress tracking. 8 | 9 | Request data (i.e., POST bodies) can be supplied with an `NSInputStream` via the 10 | property, which takes any object conforming to the 11 | protocol. The most common are premade: 12 | 13 | - (browser-style form encoding) 14 | - (multipart streaming from disk or memory) 15 | - (single part streaming from disk) 16 | - (JSON from collection objects or string) 17 | - (raw request data) 18 | 19 | In addition, this family of classes makes use of to escape strings 20 | and to provide easier access to the underlying 21 | properties of request and response objects. 22 | 23 | On iOS, the system network activity indicator is updated by default. It waits on a 24 | 0.25 second timer from the end of the last operation to prevent flickering. 25 | 26 | ## Examples ## 27 | 28 | ### Basic Operation ### 29 | 30 | JXHTTPOperation *op = [JXHTTPOperation withURLString:@"http://jxhttp.com/"]; 31 | op.didFinishLoadingBlock = ^(JXHTTPOperation *op) { 32 | NSLog(@"some html: %@", op.responseString); 33 | }; 34 | 35 | [[JXHTTPOperationQueue sharedQueue] addOperation:op]; 36 | 37 | */ 38 | 39 | #import "JXURLConnectionOperation.h" 40 | #import "JXHTTPOperationDelegate.h" 41 | #import "JXHTTPRequestBody.h" 42 | 43 | typedef void (^JXHTTPBlock)(JXHTTPOperation *operation); 44 | typedef NSCachedURLResponse * (^JXHTTPCacheBlock)(JXHTTPOperation *operation, NSCachedURLResponse *response); 45 | typedef NSURLRequest * (^JXHTTPRedirectBlock)(JXHTTPOperation *operation, NSURLRequest *request, NSURLResponse *response); 46 | 47 | @interface JXHTTPOperation : JXURLConnectionOperation 48 | 49 | /// @name Core 50 | 51 | /** 52 | An optional, non-retained object conforming to the protocol. 53 | 54 | Safe to access from any thread at any time. 55 | 56 | @warning Delegate methods will be called on a background thread. No other 57 | guarantees are made about thread continuity. 58 | */ 59 | @property (weak) NSObject *delegate; 60 | 61 | /** 62 | An optional request body object. The protocol extends , 63 | and has an opportunity to respond to the same messages immediately after the . 64 | 65 | Safe to access from any thread at any time, should only be changed before operation start. 66 | */ 67 | @property (strong) NSObject *requestBody; 68 | 69 | /** 70 | A string guaranteed to be unique for the lifetime of the application process, useful 71 | for keying operations stored in a collection. 72 | 73 | Safe to access from any thread at any time. 74 | */ 75 | @property (strong, readonly) NSString *uniqueString; 76 | 77 | /** 78 | A convenience property for creating an `NSOutputStream` that streams response data to disk. 79 | If this property is nil when the operation starts the `outputStream` property is used instead, 80 | which defaults to in-memory storage. 81 | 82 | @warning Unlike most properties of `JXHTTPOperation`, `responseDataFilePath` should not be accessed 83 | from mulitiple threads simultaneously and should only be changed before operation start. 84 | */ 85 | @property (copy, nonatomic) NSString *responseDataFilePath; 86 | 87 | /** 88 | A user-supplied object retained for the lifetime of the operation. 89 | 90 | Safe to access and change from any thread at any time. 91 | */ 92 | @property (strong) id userObject; 93 | 94 | /// @name Security 95 | 96 | @property (strong, readonly) NSURLAuthenticationChallenge *authenticationChallenge; 97 | @property (strong) NSURLCredential *credential; 98 | @property (assign) BOOL useCredentialStorage; 99 | @property (assign) BOOL trustAllHosts; 100 | @property (copy) NSArray *trustedHosts; 101 | @property (copy) NSString *username; 102 | @property (copy) NSString *password; 103 | 104 | /// @name Progress 105 | 106 | /** 107 | A number between 0.0 and 1.0 indicating the download progress of the response, 108 | or `NSURLResponseUnknownLength` if the expected content length is unknown. 109 | 110 | Safe to access from any thread at any time. 111 | */ 112 | @property (strong, readonly) NSNumber *downloadProgress; 113 | 114 | /** 115 | A number between 0.0 and 1.0 indicating the upload progress of the response, 116 | or `NSURLResponseUnknownLength` if the expected content length is unknown. 117 | 118 | Safe to access from any thread at any time. 119 | */ 120 | @property (strong, readonly) NSNumber *uploadProgress; 121 | 122 | /** 123 | If `YES`, the system network activity indicator is updated (iOS only). Defaults to `YES`. 124 | A timer is used to prevent flickering. 125 | 126 | Safe to access and change from any thread at any time. 127 | */ 128 | @property (assign) BOOL updatesNetworkActivityIndicator; 129 | 130 | /// @name Timing 131 | 132 | /** 133 | The start date of the operation or `nil` if the operation has not started. 134 | 135 | Safe to access from any thread at any time. 136 | */ 137 | @property (strong, readonly) NSDate *startDate; 138 | 139 | /** 140 | The finish date of the operation or `nil` if the operation has not finished. 141 | 142 | Safe to access from any thread at any time. 143 | */ 144 | @property (strong, readonly) NSDate *finishDate; 145 | 146 | /** 147 | The number of seconds elapsed since the operation started, 0.0 if it has not. 148 | 149 | Safe to access from any thread at any time. 150 | */ 151 | @property (readonly) NSTimeInterval elapsedSeconds; 152 | 153 | /// @name Blocks 154 | 155 | /** 156 | If `YES`, blocks are dispatched to the main queue and run on the main thread. 157 | 158 | Defaults to `NO`, blocks are run a private serial dispatch queue. 159 | 160 | Safe to access and change from any thread at any time. 161 | */ 162 | @property (assign) BOOL performsBlocksOnMainQueue; 163 | 164 | /** 165 | Performed at the very start of the operation, before the connection object is created. 166 | 167 | Safe to access and change from any thread at any time. 168 | 169 | @see 170 | */ 171 | @property (copy) JXHTTPBlock willStartBlock; 172 | 173 | /** 174 | Performed when the underlying `NSURLConnection` requires a new, unopened stream 175 | for the when a retransmission is necessary. 176 | 177 | Safe to access and change from any thread at any time. 178 | 179 | @warning For notification purposes only, do not use this block to supply the stream. 180 | 181 | @see 182 | */ 183 | @property (copy) JXHTTPBlock willNeedNewBodyStreamBlock; 184 | 185 | /** 186 | Performed when the underlying `NSURLConnection` receives a request for authentication. 187 | 188 | Safe to access and change from any thread at any time. 189 | 190 | @warning For notification purposes only, do not use this block to supply the credential. 191 | @see 192 | */ 193 | @property (copy) JXHTTPBlock willSendRequestForAuthenticationChallengeBlock; 194 | 195 | /** 196 | Performed immediately after the underlying `NSURLConnection` begins. 197 | 198 | Safe to access and change from any thread at any time. 199 | 200 | @see 201 | */ 202 | @property (copy) JXHTTPBlock didStartBlock; 203 | 204 | /** 205 | Performed immediately after the underlying `NSURLConnection` receives a response. 206 | 207 | Safe to access and change from any thread at any time. 208 | 209 | @see 210 | */ 211 | @property (copy) JXHTTPBlock didReceiveResponseBlock; 212 | 213 | /** 214 | Performed every time the underlying `NSURLConnection` receives response data. 215 | 216 | Safe to access and change from any thread at any time. 217 | 218 | @see 219 | */ 220 | @property (copy) JXHTTPBlock didReceiveDataBlock; 221 | 222 | /** 223 | Performed every time the underlying `NSURLConnection` sends request data. 224 | 225 | Safe to access and change from any thread at any time. 226 | 227 | @see 228 | */ 229 | @property (copy) JXHTTPBlock didSendDataBlock; 230 | 231 | /** 232 | Performed when the underlying `NSURLConnection` finishes loading successfully. 233 | 234 | Safe to access and change from any thread at any time. 235 | 236 | @see 237 | */ 238 | @property (copy) JXHTTPBlock didFinishLoadingBlock; 239 | 240 | /** 241 | Performed when the underlying `NSURLConnection` fails to load. The `error` property 242 | is available for inspection. 243 | 244 | Safe to access and change from any thread at any time. 245 | 246 | @see 247 | */ 248 | @property (copy) JXHTTPBlock didFailBlock; 249 | 250 | /** 251 | TKTK 252 | 253 | Safe to access and change from any thread at any time. 254 | 255 | @see 256 | */ 257 | @property (copy) JXHTTPCacheBlock willCacheResponseBlock; 258 | 259 | /** 260 | TKTK 261 | 262 | Safe to access and change from any thread at any time. 263 | 264 | @see 265 | */ 266 | @property (copy) JXHTTPRedirectBlock willSendRequestRedirectBlock; 267 | 268 | /// @name Initialization 269 | 270 | /** 271 | Creates a new `JXHTTPOperation` with the specified URL. 272 | 273 | @param urlString The URL to request. 274 | @returns An operation. 275 | */ 276 | + (instancetype)withURLString:(NSString *)urlString; 277 | 278 | /** 279 | Creates a new `JXHTTPOperation` with the specified URL and query parameters, 280 | escaped via . 281 | 282 | @param urlString The URL to request. 283 | @param parameters A dictionary of keys and values to form the query string. 284 | @returns An operation. 285 | */ 286 | + (instancetype)withURLString:(NSString *)urlString queryParameters:(NSDictionary *)parameters; 287 | 288 | @end 289 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPOperationDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPOperationDelegate` is a protocol that allows any object to receive synchronous callbacks about 3 | the progress of a `JXHTTPOperation` and optionally influence its execution. 4 | 5 | These methods may be called from different threads. 6 | */ 7 | @class JXHTTPOperation; 8 | 9 | @protocol JXHTTPOperationDelegate 10 | 11 | @optional 12 | 13 | /** 14 | Called at the very beginning of the operation, before the connection starts and before the 15 | request body is setup. 16 | 17 | @param operation The operation. 18 | */ 19 | - (void)httpOperationWillStart:(JXHTTPOperation *)operation; 20 | 21 | /** 22 | Called when the connection needs a new, unopened body stream. 23 | 24 | @param operation The operation. 25 | */ 26 | - (void)httpOperationWillNeedNewBodyStream:(JXHTTPOperation *)operation; 27 | 28 | /** 29 | Called when the underlying `NSURLConnection` receives `willSendRequestForAuthenticationChallenge:`, 30 | see the `NSURLConnectionDelegate` docs for more information. 31 | 32 | The `NSURLAuthenticationChallenge` is available for inspection via the `authenticateChallenge` 33 | property of the operation. To supply your own `NSURLCredential` in response to the challenge, set 34 | the `credential` property before this method exits. 35 | 36 | In most cases, it's easier to respond to HTTP basic auth challenges via the `username` and `password` 37 | properties, which can be set before the operation begins instead of using this callback. 38 | 39 | Similarly, SSL challenges can be met with the `trustedHosts` or `trustAllHosts` properties. 40 | 41 | If this method is implemented and no `credential` is supplied, the operation attempts to proceed 42 | by calling `continueWithoutCredentialForAuthenticationChallenge:` on the sender. 43 | 44 | @param operation The operation. 45 | */ 46 | - (void)httpOperationWillSendRequestForAuthenticationChallenge:(JXHTTPOperation *)operation; 47 | 48 | /** 49 | Called just after the connection starts. At this point all streams will be open. 50 | 51 | @param operation The operation. 52 | */ 53 | - (void)httpOperationDidStart:(JXHTTPOperation *)operation; 54 | 55 | /** 56 | Called when the connection receives a response (zero or once per operation). 57 | 58 | @param operation The operation. 59 | */ 60 | - (void)httpOperationDidReceiveResponse:(JXHTTPOperation *)operation; 61 | 62 | /** 63 | Called periodically as the connection receives data. 64 | 65 | @param operation The operation. 66 | */ 67 | - (void)httpOperationDidReceiveData:(JXHTTPOperation *)operation; 68 | 69 | /** 70 | Called periodically as the connection sends data. 71 | 72 | @param operation The operation. 73 | */ 74 | - (void)httpOperationDidSendData:(JXHTTPOperation *)operation; 75 | 76 | /** 77 | Called when the connection finishes loading data, but before the operation has completed. 78 | 79 | @param operation The operation. 80 | */ 81 | - (void)httpOperationDidFinishLoading:(JXHTTPOperation *)operation; 82 | 83 | /** 84 | Called when the operation fails (zero or once per operation). 85 | 86 | @param operation The operation. 87 | */ 88 | - (void)httpOperationDidFail:(JXHTTPOperation *)operation; 89 | 90 | /** 91 | Called before the operation caches a response. The response can be modified before storage. 92 | 93 | Returning nil will prevent the request from being cached. See the `NSURLConnectionDataDelegate` 94 | docs for more information. 95 | 96 | @param operation The operation. 97 | @param cachedResponse The response to be cached. 98 | @returns A modified cache response, the original cache response, or nil if no caching should occur. 99 | */ 100 | - (NSCachedURLResponse *)httpOperation:(JXHTTPOperation *)operation willCacheResponse:(NSCachedURLResponse *)cachedResponse; 101 | 102 | /** 103 | Called before the underlying `NSURLConnection` changes URLs in response to a redirection. 104 | May be called multiple times. 105 | 106 | See the `NSURLConnectionDataDelegate` docs for more information. 107 | 108 | @param operation The operation. 109 | @param request The proposed redirect request. 110 | @param redirectResponse The response that caused the direct (may be nil). 111 | @returns A new request for redirection, the original request to continue redirection,or nil to prevent redirection. 112 | */ 113 | - (NSURLRequest *)httpOperation:(JXHTTPOperation *)operation willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse; 114 | @end 115 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPOperationQueue.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPOperationQueue` is an `NSOperationQueue` subclass and can be used to group 3 | multiple instances of for progress tracking, timing, or cancellation. 4 | It provides blocks and delegate methods via the protocol. 5 | 6 | Any `NSOperation` subclass can be safely added to this queue; classes other than 7 | receive no special treatment. 8 | 9 | Progress and byte count properties are reset every time a new operation is added when 10 | the queue is empty. 11 | 12 | ## Example ## 13 | 14 | JXHTTPOperationQueue *queue = [[JXHTTPOperationQueue alloc] init]; 15 | 16 | queue.didFinishBlock = ^(JXHTTPOperationQueue *queue) { 17 | NSLog(@"queue finished at %@", queue.finishDate); 18 | NSLog(@"total bytes downloaded: %@", queue.bytesDownloaded); 19 | }; 20 | 21 | for (NSUInteger i = 0; i < 10; i++) { 22 | JXHTTPOperation *op = [JXHTTPOperation withURLString:@"http://jxhttp.com/"]; 23 | 24 | op.didFinishLoadingBlock = ^(JXHTTPOperation *op) { 25 | NSLog(@"operation %d finished loading at %@", i, op.finishDate); 26 | }; 27 | 28 | [queue addOperation:op]; 29 | } 30 | */ 31 | 32 | #import "JXHTTPOperationQueueDelegate.h" 33 | 34 | typedef void (^JXHTTPQueueBlock)(JXHTTPOperationQueue *queue); 35 | 36 | @interface JXHTTPOperationQueue : NSOperationQueue 37 | 38 | /// @name Core 39 | 40 | /** 41 | The operation's delegate, conforming to . 42 | 43 | Safe to access from any thread at any time. 44 | */ 45 | @property (weak) NSObject *delegate; 46 | 47 | /** 48 | A string guaranteed to be unique for the lifetime of the application process. 49 | 50 | Safe to access from any thread at any time. 51 | */ 52 | @property (strong, readonly) NSString *uniqueString; 53 | 54 | /// @name Progress 55 | 56 | /** 57 | A number between 0 and 1 representing the percentage of download progress. 58 | 59 | Safe to access from any thread at any time. 60 | */ 61 | @property (strong, readonly) NSNumber *downloadProgress; 62 | 63 | /** 64 | A number between 0 and 1 representing the percentage of upload progress. 65 | 66 | Safe to access from any thread at any time. 67 | */ 68 | @property (strong, readonly) NSNumber *uploadProgress; 69 | 70 | /** 71 | The total number of bytes downloaded. 72 | 73 | Safe to access from any thread at any time. 74 | */ 75 | @property (strong, readonly) NSNumber *bytesDownloaded; 76 | 77 | /** 78 | The total number of bytes uploaded. 79 | 80 | Safe to access from any thread at any time. 81 | */ 82 | @property (strong, readonly) NSNumber *bytesUploaded; 83 | 84 | /** 85 | The total number of bytes expected to be downloaded. 86 | 87 | Safe to access from any thread at any time. 88 | */ 89 | @property (strong, readonly) NSNumber *expectedDownloadBytes; 90 | 91 | /** 92 | The total number of bytes expected to be uploaded. 93 | 94 | Safe to access from any thread at any time. 95 | */ 96 | @property (strong, readonly) NSNumber *expectedUploadBytes; 97 | 98 | /// @name Timing 99 | 100 | /** 101 | The date the request started. 102 | 103 | Safe to access from any thread at any time. 104 | */ 105 | @property (strong, readonly) NSDate *startDate; 106 | 107 | /** 108 | The date the request finished (or failed). 109 | 110 | Safe to access from any thread at any time. 111 | */ 112 | @property (strong, readonly) NSDate *finishDate; 113 | 114 | /** 115 | The number of seconds elapsed between the and the (or the 116 | current date if the request is still running). `0.0` if the request has not started. 117 | 118 | Safe to access from any thread at any time. 119 | */ 120 | @property (readonly) NSTimeInterval elapsedSeconds; 121 | 122 | /// @name Blocks 123 | 124 | @property (assign) BOOL performsBlocksOnMainQueue; 125 | @property (copy) JXHTTPQueueBlock willStartBlock; 126 | @property (copy) JXHTTPQueueBlock willFinishBlock; 127 | @property (copy) JXHTTPQueueBlock didStartBlock; 128 | @property (copy) JXHTTPQueueBlock didUploadBlock; 129 | @property (copy) JXHTTPQueueBlock didDownloadBlock; 130 | @property (copy) JXHTTPQueueBlock didMakeProgressBlock; 131 | @property (copy) JXHTTPQueueBlock didFinishBlock; 132 | 133 | /** 134 | A singleton queue with a default `maxConcurrentOperationCount` of `4`. 135 | 136 | @returns The shared HTTP operation queue. 137 | */ 138 | + (instancetype)sharedQueue; 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPOperationQueueDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPOperationQueueDelegate` is a protocol that allows any object to receive synchronous 3 | callbacks about the progress of a `JXHTTPOperationQueue`. 4 | 5 | These methods may be called from different threads. 6 | */ 7 | 8 | @class JXHTTPOperationQueue; 9 | 10 | @protocol JXHTTPOperationQueueDelegate 11 | @optional 12 | 13 | /** 14 | Called when the queue adds an operation from an empty state. 15 | 16 | @param queue The queue. 17 | */ 18 | - (void)httpOperationQueueWillStart:(JXHTTPOperationQueue *)queue; 19 | 20 | /** 21 | Called when the queue has completed all operations. 22 | 23 | @param queue The queue. 24 | */ 25 | - (void)httpOperationQueueWillFinish:(JXHTTPOperationQueue *)queue; 26 | 27 | /** 28 | Called just after the first operation is added to the queue. 29 | 30 | @param queue The queue. 31 | */ 32 | - (void)httpOperationQueueDidStart:(JXHTTPOperationQueue *)queue; 33 | 34 | /** 35 | Called periodically as operations in the queue upload data. 36 | 37 | @param queue The queue. 38 | */ 39 | - (void)httpOperationQueueDidUpload:(JXHTTPOperationQueue *)queue; 40 | 41 | /** 42 | Called periodically as operations in the queue download data. 43 | 44 | @param queue The queue. 45 | */ 46 | - (void)httpOperationQueueDidDownload:(JXHTTPOperationQueue *)queue; 47 | 48 | /** 49 | Called periodically as operations in the queue download or upload data. 50 | 51 | @param queue The queue. 52 | */ 53 | - (void)httpOperationQueueDidMakeProgress:(JXHTTPOperationQueue *)queue; 54 | 55 | /** 56 | Called when the last operation in the queue finishes. 57 | 58 | @param queue The queue. 59 | */ 60 | - (void)httpOperationQueueDidFinish:(JXHTTPOperationQueue *)queue; 61 | @end 62 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXHTTPRequestBody.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXHTTPRequestBody` is a protocol that allows any object to provide request body data for a 3 | `JXHTTPOperation`. Conforming objects also receive the standard 4 | calls (if implemented). This allows, for example, last minute creation of an input stream 5 | when the operation starts. 6 | 7 | All methods are guaranteed to be called serially from the same thread. 8 | */ 9 | 10 | #import "JXHTTPOperationDelegate.h" 11 | 12 | @protocol JXHTTPRequestBody 13 | @required 14 | 15 | /** 16 | A stream that will supply the data for the request body section of an HTTP request. 17 | 18 | @warning This method may be called multiple times and must return a new, unopened stream. 19 | 20 | @returns An input stream. 21 | */ 22 | - (NSInputStream *)httpInputStream; 23 | 24 | /** 25 | A string that represents the MIME content type of the request data. If nil, defaults to 26 | `application/octet-stream`. 27 | 28 | @returns A string. 29 | */ 30 | - (NSString *)httpContentType; 31 | 32 | /** 33 | The expected length of the request data, used to calculate upload progress. If unknown 34 | this method is expected to return `NSURLResponseUnknownLength`. 35 | 36 | @returns An integer. 37 | */ 38 | - (long long)httpContentLength; 39 | @end 40 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXOperation.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXOperation` is an abstract `NSOperation` subclass that implements all the 3 | methods necessary for what Apple calls "concurrent" operations. See the sections 4 | titled _Subclassing Notes_ and _Multicore Considerations_ in the `NSOperation` 5 | class reference, this class does all of it for you. 6 | 7 | The main advantage of concurrent operations is that they allow for the use of 8 | asynchronous APIs. Normally, when the `main` method of an `NSOperation` exits, 9 | the operation marks itself as finished and deallocation is imminent. In a 10 | concurrent operation, nothing happens when `main` exits and it's up to you to 11 | manually report that the operation has finished (via KVO). In the meantime you 12 | can wait for delegate callbacks or any other asynchronous event. 13 | 14 | Unfortunately, `NSOperation` has some quirks and ensuring thread safety as 15 | the operation changes state can be tricky. `JXOperation` makes it easier by 16 | providing a simple method that can be called at any time from any 17 | thread, without worrying about the operation's current state. 18 | 19 | Heavily inspired by Dave Dribin, and building on his work detailed here: 20 | 21 | 22 | 23 | 24 | */ 25 | 26 | @interface JXOperation : NSOperation 27 | 28 | /// @name Operation State 29 | 30 | /** 31 | `YES` while the operation is executing, otherwise `NO`. 32 | 33 | Safe to access from any thread at any time. 34 | */ 35 | @property (assign, readonly) BOOL isExecuting; 36 | 37 | /** 38 | `YES` if the operation has finished, otherwise `NO`. 39 | 40 | Safe to access from any thread at any time. 41 | */ 42 | @property (assign, readonly) BOOL isFinished; 43 | 44 | /** 45 | Upon being set to `YES`, retrieves a `UIBackgroundTaskIdentifier` to allow the 46 | operation to continue running when the application enters the background. 47 | 48 | Safe to access from any thread at any time. 49 | 50 | @warning Changing this property to `YES` after the operation has started will 51 | have no effect. Changing it to `NO` will discontinue background execution. 52 | */ 53 | @property (assign) BOOL continuesInAppBackground; 54 | 55 | /// @name Initialization 56 | 57 | /** 58 | Creates a new operation. 59 | 60 | @returns An operation. 61 | */ 62 | + (instancetype)operation; 63 | 64 | /// @name Starting & Finishing 65 | 66 | /** 67 | Starts the operation and blocks the calling thread until it has finished. 68 | */ 69 | - (void)startAndWaitUntilFinished; 70 | 71 | /** 72 | Called just before the operation finishes on the same thread (including as a result of 73 | being cancelled). Guaranteed to be called only once. Subclasses should override this 74 | method (and call `super`) instead of overriding . 75 | 76 | @warning Do not call this method yourself. 77 | */ 78 | - (void)willFinish; 79 | 80 | /** 81 | Ends the operation and marks it for removal from its queue (if applicable). 82 | Subclasses must eventually call this method to cause the operation to finish, 83 | typically after the work performed in `main` is complete. 84 | 85 | This method is safe to call multiple times from any thread at any time, including 86 | before the operation has started. 87 | 88 | @warning To ensure thread saftey, do not override this method (use 89 | instead). After the operation has finished do not attempt to access any of its 90 | properties as it may have been released by a queue or other retaining object from a 91 | different thread. 92 | */ 93 | - (void)finish; 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXOperation.m: -------------------------------------------------------------------------------- 1 | #import "JXOperation.h" 2 | 3 | @interface JXOperation () 4 | 5 | @property (assign) BOOL isExecuting; 6 | @property (assign) BOOL isFinished; 7 | 8 | #if OS_OBJECT_USE_OBJC 9 | @property (strong) dispatch_queue_t stateQueue; 10 | #else 11 | @property (assign) dispatch_queue_t stateQueue; 12 | #endif 13 | 14 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 15 | @property (assign) UIBackgroundTaskIdentifier backgroundTaskID; 16 | #endif 17 | 18 | @end 19 | 20 | @implementation JXOperation 21 | 22 | #pragma mark - Initialization 23 | 24 | - (void)dealloc 25 | { 26 | [self endAppBackgroundTask]; 27 | 28 | #if !OS_OBJECT_USE_OBJC 29 | dispatch_release(_stateQueue); 30 | _stateQueue = NULL; 31 | #endif 32 | } 33 | 34 | - (instancetype)init 35 | { 36 | if (self = [super init]) { 37 | NSString *queueName = [[NSString alloc] initWithFormat:@"%@.%p.state", NSStringFromClass([self class]), self]; 38 | self.stateQueue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL); 39 | 40 | self.isExecuting = NO; 41 | self.isFinished = NO; 42 | self.continuesInAppBackground = NO; 43 | 44 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 45 | self.backgroundTaskID = UIBackgroundTaskInvalid; 46 | #endif 47 | } 48 | return self; 49 | } 50 | 51 | + (instancetype)operation 52 | { 53 | return [[self alloc] init]; 54 | } 55 | 56 | #pragma mark - NSOperation 57 | 58 | - (void)start 59 | { 60 | __block BOOL shouldStart = YES; 61 | 62 | dispatch_sync(self.stateQueue, ^{ 63 | if (![self isReady] || [self isCancelled] || self.isExecuting || self.isFinished) { 64 | shouldStart = NO; 65 | } else { 66 | [self willChangeValueForKey:@"isExecuting"]; 67 | self.isExecuting = YES; 68 | [self didChangeValueForKey:@"isExecuting"]; 69 | 70 | if (self.continuesInAppBackground) 71 | [self startAppBackgroundTask]; 72 | } 73 | }); 74 | 75 | if (!shouldStart || [self isCancelled]) 76 | return; 77 | 78 | @autoreleasepool { 79 | [self main]; 80 | } 81 | } 82 | 83 | - (void)main 84 | { 85 | NSAssert(NO, @"subclasses must implement and eventually call finish", nil); 86 | } 87 | 88 | #pragma mark - Public Methods 89 | 90 | - (BOOL)isConcurrent 91 | { 92 | return YES; 93 | } 94 | 95 | - (void)cancel 96 | { 97 | [super cancel]; 98 | 99 | [self finish]; 100 | } 101 | 102 | - (void)willFinish 103 | { 104 | [self endAppBackgroundTask]; 105 | } 106 | 107 | - (void)finish 108 | { 109 | dispatch_sync(self.stateQueue, ^{ 110 | if (self.isFinished) 111 | return; 112 | 113 | [self willFinish]; 114 | 115 | if (self.isExecuting) { 116 | [self willChangeValueForKey:@"isExecuting"]; 117 | [self willChangeValueForKey:@"isFinished"]; 118 | self.isExecuting = NO; 119 | self.isFinished = YES; 120 | [self didChangeValueForKey:@"isExecuting"]; 121 | [self didChangeValueForKey:@"isFinished"]; 122 | } else if (!self.isFinished) { 123 | self.isExecuting = NO; 124 | self.isFinished = YES; 125 | } 126 | }); 127 | } 128 | 129 | - (void)startAndWaitUntilFinished 130 | { 131 | NSOperationQueue *tempQueue = [[NSOperationQueue alloc] init]; 132 | [tempQueue addOperation:self]; 133 | [tempQueue waitUntilAllOperationsAreFinished]; 134 | } 135 | 136 | #pragma mark - Private Methods 137 | 138 | - (void)startAppBackgroundTask 139 | { 140 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 141 | 142 | if (self.backgroundTaskID != UIBackgroundTaskInvalid || [self isCancelled]) 143 | return; 144 | 145 | __weak __typeof(self) weakSelf = self; 146 | 147 | dispatch_async(dispatch_get_main_queue(), ^{ 148 | __typeof(weakSelf) strongSelf = weakSelf; 149 | 150 | if (!strongSelf || [strongSelf isCancelled] || strongSelf.isFinished) 151 | return; 152 | 153 | UIBackgroundTaskIdentifier taskID = UIBackgroundTaskInvalid; 154 | taskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 155 | [[UIApplication sharedApplication] endBackgroundTask:taskID]; 156 | }]; 157 | 158 | strongSelf.backgroundTaskID = taskID; 159 | }); 160 | 161 | #endif 162 | } 163 | 164 | - (void)endAppBackgroundTask 165 | { 166 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 167 | 168 | UIBackgroundTaskIdentifier taskID = self.backgroundTaskID; 169 | if (taskID == UIBackgroundTaskInvalid) 170 | return; 171 | 172 | self.backgroundTaskID = UIBackgroundTaskInvalid; 173 | 174 | dispatch_async(dispatch_get_main_queue(), ^{ 175 | [[UIApplication sharedApplication] endBackgroundTask:taskID]; 176 | }); 177 | 178 | #endif 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXURLConnectionOperation.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXURLConnectionOperation` is a subclass that encapsulates an 3 | `NSURLConnection`. It implements a basic set of delegate methods for writing 4 | response data to an `NSOutputStream` and counting the bytes transferred. 5 | 6 | All connections and streams are scheduled on the runloop of a 7 | that never exits. This is preferable to using the runloop of a thread provided 8 | by a queue because GCD manages its threads dynamically and there's no guarantee 9 | about the lifetime of a GCD thread. There is also evidence that manipulating 10 | the runloop of a GCD thread confuses the GCD scheduler. For more discussion: 11 | 12 | 13 | 14 | From the _Concurrency Programming Guide_: "Although you can obtain information 15 | about the underlying thread running a task, it is better to avoid doing so." 16 | And so we provide our own thread. 17 | 18 | ## Example ## 19 | 20 | Although `JXURLConnectionOperation` is essentially a building block towards 21 | it can be used effectively (if tediously) on its own: 22 | 23 | NSURL *url = [[NSURL alloc] initWithString:@"http://jxhttp.com/"]; 24 | JXURLConnectionOperation *op = [[JXURLConnectionOperation alloc] initWithURL:url]; 25 | [op startAndWaitUntilFinished]; 26 | 27 | NSData *responseData = [op.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; 28 | NSString *someHTML = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; 29 | NSLog(@"%@", someHTML); 30 | */ 31 | 32 | #import "JXOperation.h" 33 | 34 | @interface JXURLConnectionOperation : JXOperation 35 | 36 | /// @name Connection 37 | 38 | /** 39 | The request used to create the connection when the operation starts. 40 | 41 | Safe to access from any thread at any time. 42 | */ 43 | @property (strong, readonly) NSMutableURLRequest *request; 44 | 45 | /** 46 | The connection response if one has been received, otherwise `nil`. 47 | 48 | Safe to access from any thread at any time. 49 | */ 50 | @property (strong, readonly) NSURLResponse *response; 51 | 52 | /** 53 | The connection error if one has been received, otherwise `nil`. 54 | 55 | Safe to access from any thread at any time. 56 | */ 57 | @property (strong, readonly) NSError *error; 58 | 59 | /** 60 | The stream to which downloaded bytes will be written. 61 | 62 | This can be set to any `NSOutputStream` or subclass thereof, providing that 63 | it is new and unopened. If this property is `nil` when the operation starts 64 | an `outputStreamToMemory` is created by default. 65 | 66 | Safe to access from any thread at any time. 67 | 68 | @warning Do not change this property after the operation has started. 69 | */ 70 | @property (strong) NSOutputStream *outputStream; 71 | 72 | /// @name Progress 73 | 74 | /** 75 | The number of bytes downloaded. 76 | 77 | Safe to access from any thread at any time. 78 | */ 79 | @property (assign, readonly) long long bytesDownloaded; 80 | 81 | /** 82 | The number of bytes uploaded. 83 | 84 | Safe to access from any thread at any time. 85 | */ 86 | @property (assign, readonly) long long bytesUploaded; 87 | 88 | /// @name Initialization 89 | 90 | /** 91 | A shared thread that never exits. All connections and streams are scheduled 92 | on this thread's runloop. 93 | 94 | Safe to access from any thread at any time. 95 | 96 | @returns The shared thread used by all connections and streams. 97 | */ 98 | + (NSThread *)sharedThread; 99 | 100 | /** 101 | Creates a new operation with a specified URL. 102 | 103 | @param url The URL to request. 104 | @returns An operation. 105 | */ 106 | - (instancetype)initWithURL:(NSURL *)url; 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXURLConnectionOperation.m: -------------------------------------------------------------------------------- 1 | #import "JXURLConnectionOperation.h" 2 | 3 | @interface JXURLConnectionOperation () 4 | @property (strong) NSURLConnection *connection; 5 | @property (strong) NSMutableURLRequest *request; 6 | @property (strong) NSURLResponse *response; 7 | @property (strong) NSError *error; 8 | @property (assign) long long bytesDownloaded; 9 | @property (assign) long long bytesUploaded; 10 | @end 11 | 12 | @implementation JXURLConnectionOperation 13 | 14 | #pragma mark - Initialization 15 | 16 | - (void)dealloc 17 | { 18 | [self stopConnection]; 19 | } 20 | 21 | - (instancetype)init 22 | { 23 | if (self = [super init]) { 24 | self.connection = nil; 25 | self.request = nil; 26 | self.response = nil; 27 | self.error = nil; 28 | self.outputStream = nil; 29 | 30 | self.bytesDownloaded = 0LL; 31 | self.bytesUploaded = 0LL; 32 | } 33 | return self; 34 | } 35 | 36 | - (instancetype)initWithURL:(NSURL *)url 37 | { 38 | if (self = [self init]) { 39 | self.request = [[NSMutableURLRequest alloc] initWithURL:url]; 40 | } 41 | return self; 42 | } 43 | 44 | #pragma mark - NSOperation 45 | 46 | - (void)main 47 | { 48 | if ([self isCancelled]) 49 | return; 50 | 51 | [self startConnection]; 52 | } 53 | 54 | - (void)willFinish 55 | { 56 | [super willFinish]; 57 | 58 | [self stopConnection]; 59 | } 60 | 61 | #pragma mark - Scheduling 62 | 63 | - (void)startConnection 64 | { 65 | if ([NSThread currentThread] != [[self class] sharedThread]) { 66 | [self performSelector:@selector(startConnection) onThread:[[self class] sharedThread] withObject:nil waitUntilDone:YES]; 67 | return; 68 | } 69 | 70 | if ([self isCancelled]) 71 | return; 72 | 73 | if (!self.outputStream) 74 | self.outputStream = [[NSOutputStream alloc] initToMemory]; 75 | 76 | [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 77 | 78 | self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; 79 | [self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 80 | [self.connection start]; 81 | } 82 | 83 | - (void)stopConnection 84 | { 85 | if ([NSThread currentThread] != [[self class] sharedThread]) { 86 | [self performSelector:@selector(stopConnection) onThread:[[self class] sharedThread] withObject:nil waitUntilDone:YES]; 87 | return; 88 | } 89 | 90 | [self.connection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 91 | [self.connection cancel]; 92 | 93 | [self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 94 | [self.outputStream close]; 95 | } 96 | 97 | + (NSThread *)sharedThread 98 | { 99 | static NSThread *thread = nil; 100 | static dispatch_once_t predicate; 101 | 102 | dispatch_once(&predicate, ^{ 103 | thread = [[NSThread alloc] initWithTarget:self selector:@selector(runLoopForever) object:nil]; 104 | [thread start]; 105 | }); 106 | 107 | return thread; 108 | } 109 | 110 | + (void)runLoopForever 111 | { 112 | [[NSThread currentThread] setName:@"JXHTTP"]; 113 | 114 | while (YES) { 115 | @autoreleasepool { 116 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 117 | } 118 | } 119 | } 120 | 121 | #pragma mark - 122 | 123 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 124 | { 125 | if ([self isCancelled]) 126 | return; 127 | 128 | self.error = error; 129 | 130 | [self finish]; 131 | } 132 | 133 | #pragma mark - 134 | 135 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)urlResponse 136 | { 137 | if ([self isCancelled]) 138 | return; 139 | 140 | self.response = urlResponse; 141 | 142 | [self.outputStream open]; 143 | } 144 | 145 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 146 | { 147 | if ([self isCancelled]) 148 | return; 149 | 150 | if ([self.outputStream hasSpaceAvailable]) { 151 | NSInteger bytesWritten = [self.outputStream write:[data bytes] maxLength:[data length]]; 152 | 153 | if (bytesWritten != -1) 154 | self.bytesDownloaded += bytesWritten; 155 | } 156 | } 157 | 158 | - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytes totalBytesWritten:(NSInteger)total totalBytesExpectedToWrite:(NSInteger)expected 159 | { 160 | if ([self isCancelled]) 161 | return; 162 | 163 | self.bytesUploaded += bytes; 164 | } 165 | 166 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection 167 | { 168 | if ([self isCancelled]) 169 | return; 170 | 171 | [self finish]; 172 | } 173 | 174 | @end 175 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXURLEncoding.h: -------------------------------------------------------------------------------- 1 | /** 2 | `JXURLEncoding` is an abstract class providing methods for encoding strings according 3 | to [RFC 3986]( http://www.ietf.org/rfc/rfc3986.txt ) (aka "percent escaping"). 4 | */ 5 | 6 | @interface JXURLEncoding : NSObject 7 | 8 | /** 9 | Encodes a string according to RFC 3986. 10 | 11 | @param string A string to encode. 12 | @returns An encoded string. 13 | */ 14 | + (NSString *)encodedString:(NSString *)string; 15 | 16 | /** 17 | Encodes a string according to RFC 3986, but with `+` replacing spaces instead of `%20`. 18 | Commonly used by web browsers to submit forms. When in doubt, use . 19 | 20 | @param string A string to encode. 21 | @returns An encoded string. 22 | */ 23 | + (NSString *)formEncodedString:(NSString *)string; 24 | 25 | /** 26 | Encodes a dictionary according to RFC 3986, with keys sorted alphabetically and flattened 27 | into a query string. Dictionary values must be one of `NSString`, `NSArray`, or `NSDictionary`. 28 | 29 | ### Example ### 30 | 31 | NSDictionary *params = @{ 32 | @"make": @"Ferrari", 33 | @"model": @"458 Italia", 34 | @"options": @[ @"heated seats", @"cup holders" ] 35 | }; 36 | 37 | NSString *escaped = [JXURLEncoding encodedDictionary:params]; 38 | // make=Ferrari&model=458%20Italia&options[0]=heated%20seats&options[1]=cup&20holders 39 | 40 | @param dictionary A dictionary to encode. 41 | @returns An encoded string. 42 | */ 43 | + (NSString *)encodedDictionary:(NSDictionary *)dictionary; 44 | 45 | /** 46 | Encodes a dictionary according to RFC 3986, with keys sorted alphabetically and flattened 47 | into a query string. Dictionary values must be one of `NSString`, `NSArray`, or `NSDictionary`. 48 | 49 | Identical to but with `+` replacing spaces instead of `%20`. 50 | 51 | ### Example ### 52 | 53 | NSDictionary *params = @{ 54 | @"make": @"Ferrari", 55 | @"model": @"458 Italia", 56 | @"options": @[ @"heated seats", @"cup holders" ] 57 | }; 58 | 59 | NSString *escaped = [JXURLEncoding encodedDictionary:params]; 60 | // make=Ferrari&model=458+Italia&options[0]=heated+seats&options[1]=cup+holders 61 | 62 | @param dictionary A dictionary to encode. 63 | @returns An encoded string. 64 | */ 65 | + (NSString *)formEncodedDictionary:(NSDictionary *)dictionary; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Pods/JXHTTP/JXHTTP/JXURLEncoding.m: -------------------------------------------------------------------------------- 1 | #import "JXURLEncoding.h" 2 | 3 | @implementation JXURLEncoding 4 | 5 | #pragma mark - NSString Encoding 6 | 7 | + (NSString *)encodedString:(NSString *)string 8 | { 9 | if (![string length]) 10 | return @""; 11 | 12 | CFStringRef static const charsToLeave = CFSTR("-._~"); // RFC 3986 unreserved 13 | CFStringRef static const charsToEscape = CFSTR(":/?#[]@!$&'()*+,;="); // RFC 3986 reserved 14 | CFStringRef escapedString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, 15 | (__bridge CFStringRef)string, 16 | charsToLeave, 17 | charsToEscape, 18 | kCFStringEncodingUTF8); 19 | return (__bridge_transfer NSString *)escapedString; 20 | } 21 | 22 | + (NSString *)formEncodedString:(NSString *)string 23 | { 24 | if (![string length]) 25 | return @""; 26 | 27 | return [[self encodedString:string] stringByReplacingOccurrencesOfString:@"%20" withString:@"+"]; 28 | } 29 | 30 | #pragma mark - NSDictionary Encoding 31 | 32 | + (NSString *)encodedDictionary:(NSDictionary *)dictionary 33 | { 34 | if (![dictionary count]) 35 | return @""; 36 | 37 | NSMutableArray *arguments = [[NSMutableArray alloc] initWithCapacity:[dictionary count]]; 38 | NSArray *sortedKeys = [[dictionary allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; 39 | 40 | for (NSString *key in sortedKeys) { 41 | [self encodeObject:[dictionary objectForKey:key] withKey:key andSubKey:nil intoArray:arguments]; 42 | } 43 | 44 | return [arguments componentsJoinedByString:@"&"]; 45 | } 46 | 47 | + (NSString *)formEncodedDictionary:(NSDictionary *)dictionary 48 | { 49 | if (![dictionary count]) 50 | return @""; 51 | 52 | return [[self encodedDictionary:dictionary] stringByReplacingOccurrencesOfString:@"%20" withString:@"+"]; 53 | } 54 | 55 | #pragma mark - Private Methods 56 | 57 | + (void)encodeObject:(id)object withKey:(NSString *)key andSubKey:(NSString *)subKey intoArray:(NSMutableArray *)array 58 | { 59 | if (!object || ![key length]) 60 | return; 61 | 62 | NSString *objectKey = nil; 63 | 64 | if (subKey) { 65 | objectKey = [[NSString alloc] initWithFormat:@"%@[%@]", [self encodedString:key], [self encodedString:subKey]]; 66 | } else { 67 | objectKey = [self encodedString:key]; 68 | } 69 | 70 | if ([object isKindOfClass:[NSDictionary class]]) { 71 | NSArray *sortedKeys = [[object allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; 72 | for (NSString *insideKey in sortedKeys) { 73 | [self encodeObject:[object objectForKey:insideKey] withKey:objectKey andSubKey:insideKey intoArray:array]; 74 | } 75 | } else if ([object isKindOfClass:[NSArray class]]) { 76 | for (NSString *arrayObject in (NSArray *)object) { 77 | NSString *arrayKey = [[NSString alloc] initWithFormat:@"%@[]", objectKey]; 78 | [self encodeObject:arrayObject withKey:arrayKey andSubKey:nil intoArray:array]; 79 | } 80 | } else if ([object isKindOfClass:[NSNumber class]]) { 81 | [array addObject:[[NSString alloc] initWithFormat:@"%@=%@", objectKey, [object stringValue]]]; 82 | } else { 83 | NSString *encodedString = [self encodedString:object]; 84 | [array addObject:[[NSString alloc] initWithFormat:@"%@=%@", objectKey, encodedString]]; 85 | } 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Pods/JXHTTP/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Justin Ouellette 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /Pods/JXHTTP/README.md: -------------------------------------------------------------------------------- 1 | # JXHTTP # 2 | 3 | JXHTTP is a networking library for iOS and OS X. It leverages operation queues and GCD to provide a powerful wrapper for Cocoa's built-in `NSURLConnection` object, adding many useful features like block response objects and progress tracking across multiple requests. It strives to be as lightweight and readable as possible, making it easy to use or customize for advanced behavior. 4 | 5 | To get started, simply [download the latest tag](https://github.com/jstn/JXHTTP/tags) and drop the `JXHTTP` folder into your Xcode project. There are zero external dependencies or special compiler flags, just `#import "JXHTTP.h"` somewhere convenient. A complete docset is included for use in Xcode or [Dash](http://kapeli.com/dash/), and available online at [jxhttp.com](http://jxhttp.com/docs/html/). JXHTTP is also available as a [CocoaPod](http://cocoapods.org/?q=name%3AJXHTTP). 6 | 7 | JXHTTP requires iOS 5.0 or OS X 10.7 or newer. 8 | 9 | ## Advantages ## 10 | 11 | [JXHTTPOperation](JXHTTP/JXHTTPOperation.h) offers a number of advantages over using vanilla `NSURLConnection` without an operation wrapper: 12 | 13 | - requests can be easily grouped, prioritized, cancelled, and executed concurrently 14 | - data can be streamed to and from disk for excellent memory efficiency 15 | - requests can optionally continue executing when the app is sent to the background 16 | - progress is easily tracked via delegate methods, response blocks, KVO, or all three 17 | - requests run entirely on background threads, away from the main thread and UI 18 | - particular attention has been paid to thread safety and is well-documented throughout 19 | 20 | JXHTTP is production-ready and currently powers the [Tumblr iOS SDK](http://tumblr.com/mobile); thousands of successful requests were performed while you read this paragraph! 21 | 22 | ## Examples ## 23 | 24 | See the included [example project](example/) for a real-world use case in iOS. 25 | 26 | ### Asynchronous ### 27 | 28 | ```objective-c 29 | JXHTTPOperation *op = [JXHTTPOperation withURLString:@"https://encrypted.google.com/"]; 30 | op.didFinishLoadingBlock = ^(JXHTTPOperation *op) { 31 | NSLog(@"%@", op.responseString); 32 | }; 33 | 34 | [[JXHTTPOperationQueue sharedQueue] addOperation:op]; 35 | ``` 36 | 37 | ### Synchronous ### 38 | 39 | ```objective-c 40 | JXHTTPOperation *op = [JXHTTPOperation withURLString:@"https://encrypted.google.com/"]; 41 | [op startAndWaitUntilFinished]; 42 | 43 | NSLog(@"%@", op.responseString); 44 | ``` 45 | 46 | ### Complex ### 47 | 48 | ```objective-c 49 | NSURL *postURL = [NSURL URLWithString:@"https://web.site/api/POST"]; 50 | NSDictionary *postParams = @{ @"make": @"Ferrari", @"model": @"458 Italia" }; 51 | 52 | JXHTTPOperation *op = [[JXHTTPOperation alloc] initWithURL:postURL]; 53 | op.requestBody = [[JXHTTPFormEncodedBody alloc] initWithDictionary:postParams]; 54 | op.requestCachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData; 55 | op.responseDataFilePath = @"/tmp/downloaded_data"; 56 | op.trustedHosts = @[ postURL.host ]; 57 | op.performsBlocksOnMainQueue = YES; 58 | 59 | op.didSendDataBlock = ^(JXHTTPOperation *op) { 60 | NSLog(@"%lld bytes uploaded so far", op.bytesUploaded); 61 | }; 62 | 63 | [[JXHTTPOperationQueue sharedQueue] addOperation:op]; 64 | ``` 65 | 66 | ## Contact ## 67 | 68 | JXHTTP was created by [Justin Ouellette](http://justinouellette.com/). 69 | 70 | Email [jstn@jxhttp.com](mailto:jstn@jxhttp.com) with questions. -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - JXHTTP (1.0.0) 3 | - TMTumblrSDK (1.0.8): 4 | - TMTumblrSDK/Activity 5 | - TMTumblrSDK/APIClient 6 | - TMTumblrSDK/AppClient 7 | - TMTumblrSDK/Core 8 | - TMTumblrSDK/Activity (1.0.8) 9 | - TMTumblrSDK/APIClient (1.0.8): 10 | - JXHTTP (= 1.0.0) 11 | - TMTumblrSDK/APIClient/Authentication 12 | - TMTumblrSDK/APIClient/Authentication (1.0.8): 13 | - JXHTTP (= 1.0.0) 14 | - TMTumblrSDK/Core 15 | - TMTumblrSDK/AppClient (1.0.8): 16 | - TMTumblrSDK/Core 17 | - TMTumblrSDK/Core (1.0.8) 18 | 19 | DEPENDENCIES: 20 | - TMTumblrSDK 21 | 22 | SPEC CHECKSUMS: 23 | JXHTTP: dd1fe9c6fa21e7b2be9724ef2be16c66aafc44b4 24 | TMTumblrSDK: b9fada23e88b42ade2b492a6db95bfb1dff6304b 25 | 26 | COCOAPODS: 0.33.1 27 | -------------------------------------------------------------------------------- /Pods/Pods-JXHTTP-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-JXHTTP.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders" "${PODS_ROOT}/BuildHeaders/JXHTTP" "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/JXHTTP" "${PODS_ROOT}/Headers/TMTumblrSDK" 4 | OTHER_LDFLAGS = -ObjC ${PODS_JXHTTP_OTHER_LDFLAGS} 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /Pods/Pods-JXHTTP-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_JXHTTP : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_JXHTTP 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Pods-JXHTTP-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /Pods/Pods-JXHTTP.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_JXHTTP_OTHER_LDFLAGS = -framework Foundation -weak_framework UIKit -------------------------------------------------------------------------------- /Pods/Pods-TMTumblrSDK-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-TMTumblrSDK.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders" "${PODS_ROOT}/BuildHeaders/TMTumblrSDK" "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/JXHTTP" "${PODS_ROOT}/Headers/TMTumblrSDK" 4 | OTHER_LDFLAGS = -ObjC ${PODS_TMTUMBLRSDK_OTHER_LDFLAGS} 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /Pods/Pods-TMTumblrSDK-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_TMTumblrSDK : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_TMTumblrSDK 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Pods-TMTumblrSDK-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /Pods/Pods-TMTumblrSDK.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_TMTUMBLRSDK_OTHER_LDFLAGS = -framework Foundation -weak_framework UIKit -------------------------------------------------------------------------------- /Pods/Pods-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods : NSObject 3 | @end 4 | @implementation PodsDummy_Pods 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Pods-environment.h: -------------------------------------------------------------------------------- 1 | 2 | // To check if a library is compiled with CocoaPods you 3 | // can use the `COCOAPODS` macro definition which is 4 | // defined in the xcconfigs so it is available in 5 | // headers also when they are imported in the client 6 | // project. 7 | 8 | 9 | // JXHTTP 10 | #define COCOAPODS_POD_AVAILABLE_JXHTTP 11 | #define COCOAPODS_VERSION_MAJOR_JXHTTP 1 12 | #define COCOAPODS_VERSION_MINOR_JXHTTP 0 13 | #define COCOAPODS_VERSION_PATCH_JXHTTP 0 14 | 15 | // TMTumblrSDK 16 | #define COCOAPODS_POD_AVAILABLE_TMTumblrSDK 17 | #define COCOAPODS_VERSION_MAJOR_TMTumblrSDK 1 18 | #define COCOAPODS_VERSION_MINOR_TMTumblrSDK 0 19 | #define COCOAPODS_VERSION_PATCH_TMTumblrSDK 8 20 | 21 | // TMTumblrSDK/APIClient 22 | #define COCOAPODS_POD_AVAILABLE_TMTumblrSDK_APIClient 23 | #define COCOAPODS_VERSION_MAJOR_TMTumblrSDK_APIClient 1 24 | #define COCOAPODS_VERSION_MINOR_TMTumblrSDK_APIClient 0 25 | #define COCOAPODS_VERSION_PATCH_TMTumblrSDK_APIClient 8 26 | 27 | // TMTumblrSDK/APIClient/Authentication 28 | #define COCOAPODS_POD_AVAILABLE_TMTumblrSDK_APIClient_Authentication 29 | #define COCOAPODS_VERSION_MAJOR_TMTumblrSDK_APIClient_Authentication 1 30 | #define COCOAPODS_VERSION_MINOR_TMTumblrSDK_APIClient_Authentication 0 31 | #define COCOAPODS_VERSION_PATCH_TMTumblrSDK_APIClient_Authentication 8 32 | 33 | // TMTumblrSDK/Activity 34 | #define COCOAPODS_POD_AVAILABLE_TMTumblrSDK_Activity 35 | #define COCOAPODS_VERSION_MAJOR_TMTumblrSDK_Activity 1 36 | #define COCOAPODS_VERSION_MINOR_TMTumblrSDK_Activity 0 37 | #define COCOAPODS_VERSION_PATCH_TMTumblrSDK_Activity 8 38 | 39 | // TMTumblrSDK/AppClient 40 | #define COCOAPODS_POD_AVAILABLE_TMTumblrSDK_AppClient 41 | #define COCOAPODS_VERSION_MAJOR_TMTumblrSDK_AppClient 1 42 | #define COCOAPODS_VERSION_MINOR_TMTumblrSDK_AppClient 0 43 | #define COCOAPODS_VERSION_PATCH_TMTumblrSDK_AppClient 8 44 | 45 | // TMTumblrSDK/Core 46 | #define COCOAPODS_POD_AVAILABLE_TMTumblrSDK_Core 47 | #define COCOAPODS_VERSION_MAJOR_TMTumblrSDK_Core 1 48 | #define COCOAPODS_VERSION_MINOR_TMTumblrSDK_Core 0 49 | #define COCOAPODS_VERSION_PATCH_TMTumblrSDK_Core 8 50 | 51 | -------------------------------------------------------------------------------- /Pods/Pods-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 5 | > "$RESOURCES_TO_COPY" 6 | 7 | install_resource() 8 | { 9 | case $1 in 10 | *.storyboard) 11 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 12 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 13 | ;; 14 | *.xib) 15 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 16 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 17 | ;; 18 | *.framework) 19 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 21 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 22 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 23 | ;; 24 | *.xcdatamodel) 25 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 26 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 27 | ;; 28 | *.xcdatamodeld) 29 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 30 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 31 | ;; 32 | *.xcassets) 33 | ;; 34 | /*) 35 | echo "$1" 36 | echo "$1" >> "$RESOURCES_TO_COPY" 37 | ;; 38 | *) 39 | echo "${PODS_ROOT}/$1" 40 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 41 | ;; 42 | esac 43 | } 44 | install_resource "TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr.png" 45 | install_resource "TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr@2x.png" 46 | install_resource "TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr@2x~ipad.png" 47 | install_resource "TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr~ipad.png" 48 | install_resource "TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr~ipad@2x.png" 49 | 50 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 51 | if [[ "${ACTION}" == "install" ]]; then 52 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 53 | fi 54 | rm -f "$RESOURCES_TO_COPY" 55 | 56 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ `xcrun --find actool` ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ] 57 | then 58 | case "${TARGETED_DEVICE_FAMILY}" in 59 | 1,2) 60 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 61 | ;; 62 | 1) 63 | TARGET_DEVICE_ARGS="--target-device iphone" 64 | ;; 65 | 2) 66 | TARGET_DEVICE_ARGS="--target-device ipad" 67 | ;; 68 | *) 69 | TARGET_DEVICE_ARGS="--target-device mac" 70 | ;; 71 | esac 72 | find "${PWD}" -name "*.xcassets" -print0 | xargs -0 actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 73 | fi 74 | -------------------------------------------------------------------------------- /Pods/Pods.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/JXHTTP" "${PODS_ROOT}/Headers/TMTumblrSDK" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers" -isystem "${PODS_ROOT}/Headers/JXHTTP" -isystem "${PODS_ROOT}/Headers/TMTumblrSDK" 4 | OTHER_LDFLAGS = -ObjC -framework Foundation -weak_framework UIKit 5 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/bryan.xcuserdatad/xcschemes/Pods-JXHTTP.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/bryan.xcuserdatad/xcschemes/Pods-TMTumblrSDK.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/bryan.xcuserdatad/xcschemes/Pods.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/bryan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-JXHTTP.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-TMTumblrSDK.xcscheme 13 | 14 | isShown 15 | 16 | 17 | Pods.xcscheme 18 | 19 | isShown 20 | 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | 575669C318D1479286D118E3 26 | 27 | primary 28 | 29 | 30 | 59E5373C93D1430C9C69ECCE 31 | 32 | primary 33 | 34 | 35 | E8946277823C474AB54B797E 36 | 37 | primary 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/README.md: -------------------------------------------------------------------------------- 1 | # Tumblr SDK for iOS 2 | 3 | An unopinionated and flexible library for easily integrating Tumblr data into 4 | your iOS or OS X application. The library uses ARC and requires at least iOS 5 or 5 | OS X 10.7. 6 | 7 | ``` objectivec 8 | [[TMAPIClient sharedInstance] blogInfo:@"bryan" success:^ (id result, NSError *error) { 9 | if (error) { 10 | NSLog(@"Bummer, dude: %@", error); 11 | return; 12 | } 13 | 14 | NSLog(@"Blog description: %@", result[@"description"]); 15 | }]; 16 | ``` 17 | 18 | If you have any feature requests, please let us know by creating an issue or 19 | submitting a pull request. Please use the Tumblr API [responsibly](http://www.tumblr.com/docs/en/api_agreement). 20 | 21 | [![Build Status](https://travis-ci.org/tumblr/TMTumblrSDK.png)](https://travis-ci.org/tumblr/TMTumblrSDK) 22 | 23 | ## Table of Contents 24 | 25 | * [Getting started](#getting-started) 26 | * [CocoaPods](#cocoapods) 27 | * [Documentation](#documentation) 28 | * [Authentication](#authentication) 29 | * [OAuth](#oauth) 30 | * [xAuth](#xauth) 31 | * [API client](#api-client) 32 | * [Inter-app communication](#inter-app-communication) 33 | * [App client](#app-client) 34 | * [URL schemes](#url-schemes) 35 | * [UIDocumentInteractionController](#uidocumentinteractioncontroller) 36 | * [UIActivityViewController](#uiactivityviewcontroller) 37 | * [Example](#example) 38 | * [Contact](#contact) 39 | * [License](#license) 40 | 41 | ## Getting started 42 | 43 | ### CocoaPods 44 | 45 | [CocoaPods](http://cocoapods.org) is the recommended way to add the Tumblr 46 | SDK to your project. *Using CocoaPods means you don't need to worry about 47 | cloning or adding this repository as a git submodule.* CocoaPods is a package 48 | manager like `gem` (Ruby) and `npm` (Node.js), but for Objective-C projects. 49 | 50 | Module authors create "pods" which are versioned and stored in a central 51 | repository. App developers create "podfiles" to specify their apps' 52 | dependencies and use the CocoaPods command line tool to: 53 | 54 | * Fetch the dependencies specified in their podfile 55 | * Recursively fetch all subdependencies 56 | * Create an Xcode workspace that includes the pods, links any necessary libraries, 57 | configures header search paths, enables ARC where appropriate, and more 58 | 59 | If you're new to CocoaPods, the website contains lots of helpful [documentation](http://docs.cocoapods.org). 60 | 61 | To install the Tumblr SDK you can simply create a 62 | [podfile](https://github.com/CocoaPods/CocoaPods/wiki/A-Podfile) 63 | in your application's root directory that looks as follows: 64 | 65 | ``` ruby 66 | platform :ios, '5.0' 67 | 68 | pod 'TMTumblrSDK' 69 | ``` 70 | 71 | After running `pod install`, you'll have an Xcode workspace that includes not 72 | only your application but also the Tumblr SDK and its dependencies. That's really 73 | all there is to it. 74 | 75 | You will get the latest version of the SDK by referring to it simply by name 76 | (`TMTumblrSDK`). [This guide](http://docs.cocoapods.org/guides/dependency_versioning.html) 77 | explains how explicit dependency versions can instead be specified. 78 | 79 | This SDK is really comprised of numerous "sub-pods." If you'd rather not import 80 | everything, feel free to mix and match as you see fit: 81 | 82 | * `TMTumblrSDK/APIClient` 83 | * `TMTumblrSDK/APIClient/Authentication` 84 | * `TMTumblrSDK/AppClient` 85 | * `TMTumblrSDK/Activity` 86 | 87 | 88 | Each component is described in more detail throughout this README. 89 | 90 | ### Documentation 91 | 92 | Appledoc for the SDK can be found [here](http://cocoadocs.org/docsets/TMTumblrSDK). 93 | If you install the Tumblr SDK using CocoaPods, the docset is automatically added 94 | to Xcode for you. 95 | 96 | ## Authentication 97 | 98 | Import `TMAPIClient.h`. Configure the `[TMAPIClient sharedInstance]` singleton 99 | with your app’s Tumblr consumer key and secret: 100 | 101 | ``` objectivec 102 | [TMAPIClient sharedInstance].OAuthConsumerKey = @"ADISJdadsoj2dj38dj29dj38jd9238jdk92djasdjASDaoijsd"; 103 | [TMAPIClient sharedInstance].OAuthConsumerSecret = @"MGI39kdasdoka3240989ASFjoiajsfomdasd39129ASDAPDOJa"; 104 | ``` 105 | 106 | If you don't already have a consumer key/secret you can 107 | register [here](http://www.tumblr.com/oauth/apps). 108 | 109 | The authentication methods detailed below will provide the API client with a token and token secret. The 110 | SDK does *not* currently persist these values; you are responsible for storing them and setting them on 111 | the API client on subsequent app launches, before making any API requests. This may change in a future 112 | release. 113 | 114 | ### OAuth 115 | In your app’s `Info.plist`, specify a custom URL scheme that the browser can 116 | use to return to your application once the user has permitted or denied 117 | access to Tumblr: 118 | 119 | ``` xml 120 | CFBundleURLTypes 121 | 122 | 123 | CFBundleURLSchemes 124 | 125 | myapp 126 | 127 | 128 | 129 | ``` 130 | 131 | In your app delegate, allow the `TMAPIClient` singleton to handle incoming URL 132 | requests. On iOS this looks like: 133 | 134 | ``` objectivec 135 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication 136 | annotation:(id)annotation { 137 | return [[TMAPIClient sharedInstance] handleOpenURL:url]; 138 | } 139 | ``` 140 | 141 | And on OS X: 142 | 143 | ``` objectivec 144 | - (void)applicationWillFinishLaunching:(NSNotification *)notification { 145 | NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager]; 146 | [appleEventManager setEventHandler:self andSelector:@selector(handleURLEvent:withReplyEvent:) 147 | forEventClass:kInternetEventClass andEventID:kAEGetURL]; 148 | } 149 | 150 | - (void)handleURLEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent { 151 | NSString *calledURL = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; 152 | 153 | [[TMAPIClient sharedInstance] handleOpenURL:[NSURL URLWithString:calledURL]]; 154 | } 155 | ``` 156 | 157 | Initiate the three-legged OAuth flow, by specifying the URL scheme that your 158 | app will respond to: 159 | 160 | ``` objectivec 161 | [[TMAPIClient sharedInstance] authenticate:@"myapp" callback:^(NSError *error) { 162 | // You are now authenticated (if !error) 163 | }]; 164 | ``` 165 | 166 | ### xAuth 167 | 168 | Please note that xAuth access 169 | [must be specifically requested](http://www.tumblr.com/oauth/apps) 170 | for your application. 171 | 172 | Use the `TMAPIClient` singleton to retrieve an OAuth token and secret given a 173 | user’s email address and password: 174 | 175 | ``` objectivec 176 | [[TMAPIClient sharedInstance] xAuth:@"foo@foo.bar" password:@"12345" callback:^(NSError *error) { 177 | // You are now authenticated (if !error) 178 | }]; 179 | ``` 180 | 181 | If you're only interested in authentication, the 182 | `TMTumblrSDK/APIClient/Authentication` sub-pod can be installed by itself. 183 | 184 | ## API client 185 | 186 | Please view the [API documentation](http://www.tumblr.com/docs/en/api/v2) for 187 | full usage instructions. 188 | 189 | There are two ways of retrieving data from the API: 190 | 191 | ``` objectivec 192 | // `void` methods for immediate requests, preferable when the caller does not need a reference to an actual request object: 193 | 194 | [[TMAPIClient sharedInstance] userInfo:^(id result, NSError *error) { 195 | if (!error) 196 | NSLog(@"Got some user info"); 197 | }]; 198 | 199 | // Methods that return configured, signed `JXHTTPOperation` instances and require the client to explicitly send the request separately. 200 | 201 | JXHTTPOperation *likesRequest = [[TMAPIClient sharedInstance] likesRequest:@"bryan" parameters:nil]; 202 | ``` 203 | 204 | The API client is built on top of the 205 | [JXHTTP](https://github.com/jstn/JXHTTP) networking library. If you're only 206 | interested in the API client, the `TMTumblrSDK/APIClient` sub-pod can be 207 | installed by itself. 208 | 209 | ## Inter-app communication 210 | 211 | ### App client 212 | 213 | The `TMTumblrAppClient` class provides a simple interface for interacting with 214 | [Tumblr for iOS](https://itunes.apple.com/us/app/tumblr/id305343404?mt=8) if the 215 | user has it installed. Only a few basic endpoints are supported for now but more 216 | will be added in the near future: 217 | 218 | ``` objectivec 219 | if (![TMTumblrAppClient isTumblrInstalled]) { 220 | [TMTumblrAppClient viewInAppStore]; 221 | } 222 | 223 | [TMTumblrAppClient viewDashboard]; 224 | 225 | [TMTumblrAppClient viewTag:@"gif"]; 226 | 227 | [TMTumblrAppClient viewBlog:@"bryan"]; 228 | 229 | [TMTumblrAppClient viewPost:@"43724939726" blogName:@"bryan"]; 230 | ``` 231 | 232 | If you're only interested in the app client, 233 | the `TMTumblrSDK/AppClient` sub-pod can be installed by itself. 234 | 235 | #### URL schemes 236 | 237 | Tumblr for iOS exposes actions using the [x-callback-url](http://x-callback-url.com/) 238 | specification. The `TMTumblrAppClient` class merely provides a convenient 239 | interface on top of the following URLs: 240 | 241 | ``` 242 | tumblr://x-callback-url/dashboard 243 | tumblr://x-callback-url/tag?tag=gif 244 | tumblr://x-callback-url/blog?blogName=bryan 245 | tumblr://x-callback-url/blog?blogName=bryan&postID=43724939726 246 | 247 | // The post URLs below also support `x-success` and `x-cancel` callback parameters 248 | 249 | tumblr://x-callback-url/text?title=Title&body=Body&tags=gif&tags=lol 250 | tumblr://x-callback-url/quote?quote=Quote&source=Source 251 | tumblr://x-callback-url/link?title=Bryan&url=bryan.io&description=Website 252 | tumblr://x-callback-url/chat?title=Title&body=Body&tags=gif&tags=lol 253 | ``` 254 | 255 | If you don't want to use this SDK and would rather hit these URLs directly, please go 256 | right ahead. 257 | 258 | ### UIDocumentInteractionController 259 | 260 | Photos and videos can be passed to Tumblr for iOS using Apple's 261 | standard [UIDocumentInteractionController](http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIDocumentInteractionController_class/Reference/Reference.html). 262 | 263 | To include a caption, set the `annotation` property on the document 264 | interaction controller to an `NSDictionary` containing a `TumblrCaption` 265 | key, mapped to your caption (an `NSString`). To include tags, add a 266 | `TumblrTags` key to the dictionary, mapped an an `NSArray` of `NSStrings`. 267 | 268 | If you want *only* the Tumblr app to show up in a document interaction controller, 269 | you can specify the file extension `tumblrphoto` and custom UTI `com.tumblr.photo`. 270 | 271 | ### UIActivityViewController 272 | 273 | The SDK includes a [UIActivity subclass](https://github.com/tumblr/TMTumblrSDK/blob/master/TMTumblrSDK/Activity/TMTumblrActivity.h) 274 | for including Tumblr in a standard `UIActivityViewController`. It currently 275 | provides only the activity icon and title, but you can hook it up however you 276 | see fit and we may provide a more integrated solution in the future. 277 | 278 | If you're only interested in this UIActivity subclass, 279 | the `TMTumblrSDK/Activity` sub-pod can be installed by itself. 280 | 281 | ### Example 282 | 283 | The repository includes a [sample application](https://github.com/tumblr/TMTumblrSDK/tree/master/Examples/AppClientExample) 284 | which shows all of the inter-app hooks in action. 285 | 286 | ![Screenshot of Tumblr activity icon](https://raw.github.com/tumblr/TMTumblrSDK/master/Examples/AppClientExample/screenshot.png) 287 | 288 | ## Dependencies 289 | 290 | * [JXHTTP](https://github.com/jstn/JXHTTP) 291 | 292 | ## Contact 293 | 294 | * [Bryan Irace](bryan@tumblr.com) 295 | * [Tumblr API discussion group](https://groups.google.com/group/tumblr-api/) 296 | 297 | ## License 298 | 299 | Copyright 2012 Tumblr, Inc. 300 | 301 | Licensed under the Apache License, Version 2.0 (the “License”); you may not use 302 | this file except in compliance with the License. You may obtain a copy of the 303 | License at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0). 304 | 305 | > Unless required by applicable law or agreed to in writing, software 306 | > distributed under the License is distributed on an “AS IS” BASIS, WITHOUT 307 | > WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 308 | > License for the specific language governing permissions and limitations under 309 | > the License. 310 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Activity/TMTumblrActivity.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMTumblrActivity.h 3 | // TumblrAppClient 4 | // 5 | // Created by Bryan Irace on 3/19/13. 6 | // Copyright (c) 2013 Tumblr. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | `TMTumblrActivity` provides the bare minimum `UIActivity` subclass necessary to start sharing to Tumblr: just the name 13 | and icon. 14 | 15 | For now this is essentially a blank canvas to be used in conjunction with either `TMAPIClient` or `TMTumblrAppClient`. 16 | This should be subclassed in accordance with the `UIActivity` documentation to actually perform an action based on 17 | provided activity items. In the future we may provide a more integrated solution. 18 | */ 19 | @interface TMTumblrActivity : UIActivity 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Activity/TMTumblrActivity.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMTumblrActivity.m 3 | // TumblrAppClient 4 | // 5 | // Created by Bryan Irace on 3/19/13. 6 | // Copyright (c) 2013 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMTumblrActivity.h" 10 | 11 | @implementation TMTumblrActivity 12 | 13 | - (BOOL)canPerformWithActivityItems:(NSArray *)activityItems { 14 | return YES; 15 | } 16 | 17 | - (NSString *)activityType { 18 | return NSStringFromClass([self class]); 19 | } 20 | 21 | - (NSString *)activityTitle { 22 | return @"Tumblr"; 23 | } 24 | 25 | - (UIImage *)activityImage { 26 | return [UIImage imageNamed:@"UIActivityTumblr"]; 27 | } 28 | 29 | #ifdef __IPHONE_7_0 30 | + (UIActivityCategory)activityCategory { 31 | return UIActivityCategoryShare; 32 | } 33 | #endif 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TumblrArchive/CoreDataExample/fdcba11b8581ccafeceaa7423967f332c985ffb5/Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr.png -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TumblrArchive/CoreDataExample/fdcba11b8581ccafeceaa7423967f332c985ffb5/Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr@2x.png -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TumblrArchive/CoreDataExample/fdcba11b8581ccafeceaa7423967f332c985ffb5/Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr@2x~ipad.png -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TumblrArchive/CoreDataExample/fdcba11b8581ccafeceaa7423967f332c985ffb5/Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr~ipad.png -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr~ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TumblrArchive/CoreDataExample/fdcba11b8581ccafeceaa7423967f332c985ffb5/Pods/TMTumblrSDK/TMTumblrSDK/Activity/UIActivityTumblr~ipad@2x.png -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/AppClient/TMTumblrAppClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMTumblrAppClient.h 3 | // TumblrAppClient 4 | // 5 | // Created by Bryan Irace on 11/24/12. 6 | // Copyright (c) 2012 Tumblr. All rights reserved. 7 | // 8 | 9 | /** 10 | Convenience wrapper around the URLs that [Tumblr for iOS](https://itunes.apple.com/us/app/tumblr/id305343404) knows how 11 | to open. 12 | */ 13 | @interface TMTumblrAppClient : NSObject 14 | 15 | /// Check if the Tumblr app is installed 16 | + (BOOL)isTumblrInstalled; 17 | 18 | /// Open Tumblr for iOS in the App Store 19 | + (void)viewInAppStore; 20 | 21 | /// View the authenticated user's dashboard 22 | + (void)viewDashboard; 23 | 24 | /// View a tag 25 | + (void)viewTag:(NSString *)tag; 26 | 27 | /// View a blog 28 | + (void)viewBlog:(NSString *)blogName; 29 | 30 | /// View a blog's post 31 | + (void)viewPost:(NSString *)postID blogName:(NSString *)blogName; 32 | 33 | /// Create a text post with a title, body, and tags 34 | + (void)createTextPost:(NSString *)title body:(NSString *)body tags:(NSArray *)tags; 35 | 36 | /// Create a text post with a title, body, tags, and success/cancel URLs 37 | + (void)createTextPost:(NSString *)title body:(NSString *)body tags:(NSArray *)tags success:(NSURL *)successURL 38 | cancel:(NSURL *)cancelURL; 39 | 40 | /// Create a quote post with a quote, source, and tags 41 | + (void)createQuotePost:(NSString *)quote source:(NSString *)source tags:(NSArray *)tags; 42 | 43 | /// Create a quote post with a quote, source, tags, and success/cancel URLs 44 | + (void)createQuotePost:(NSString *)quote source:(NSString *)source tags:(NSArray *)tags success:(NSURL *)successURL 45 | cancel:(NSURL *)cancelURL; 46 | 47 | /// Create a link post with a title, URL, description, and tags 48 | + (void)createLinkPost:(NSString *)title URLString:(NSString *)URLString description:(NSString *)description 49 | tags:(NSArray *)tags; 50 | 51 | /// Create a link post with a title, URL, description, tags, and success/cancel URLs 52 | + (void)createLinkPost:(NSString *)title URLString:(NSString *)URLString description:(NSString *)description 53 | tags:(NSArray *)tags success:(NSURL *)successURL cancel:(NSURL *)cancelURL; 54 | 55 | /// Create a chat post with a title, body, and tags 56 | + (void)createChatPost:(NSString *)title body:(NSString *)body tags:(NSArray *)tags; 57 | 58 | /// Create a chat post with a title, body, tags, and success/cancel URLs 59 | + (void)createChatPost:(NSString *)title body:(NSString *)body tags:(NSArray *)tags success:(NSURL *)successURL 60 | cancel:(NSURL *)cancelURL; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/AppClient/TMTumblrAppClient.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMTumblrAppClient.m 3 | // TumblrAppClient 4 | // 5 | // Created by Bryan Irace on 11/24/12. 6 | // Copyright (c) 2012 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMTumblrAppClient.h" 10 | 11 | #import 12 | #import "TMSDKFunctions.h" 13 | 14 | @implementation TMTumblrAppClient 15 | 16 | + (BOOL)isTumblrInstalled { 17 | return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tumblr://"]]; 18 | } 19 | 20 | + (void)viewInAppStore { 21 | [[UIApplication sharedApplication] openURL: 22 | [NSURL URLWithString:@"itms-apps://itunes.apple.com/us/app/tumblr/id305343404"]]; 23 | } 24 | 25 | + (void)viewDashboard { 26 | [self performAction:@"dashboard" parameters:nil]; 27 | } 28 | 29 | + (void)viewTag:(NSString *)tag { 30 | if (tag) { 31 | [self performAction:@"tag" parameters:@{ @"tag" : tag }]; 32 | } 33 | } 34 | 35 | + (void)viewBlog:(NSString *)blogName { 36 | if (blogName) { 37 | [self performAction:@"blog" parameters:@{ @"blogName" : blogName }]; 38 | } 39 | } 40 | 41 | + (void)viewPost:(NSString *)postID blogName:(NSString *)blogName { 42 | if (blogName) { 43 | NSMutableDictionary *params = [[NSMutableDictionary alloc] initWithDictionary:@{ @"blogName" : blogName }]; 44 | [params setValue:postID forKey:@"postID"]; 45 | 46 | [self performAction:@"blog" parameters:params]; 47 | } 48 | } 49 | 50 | + (void)createTextPost:(NSString *)title body:(NSString *)body tags:(NSArray *)tags { 51 | [self createTextPost:title body:body tags:tags success:nil cancel:nil]; 52 | } 53 | 54 | + (void)createTextPost:(NSString *)title body:(NSString *)body tags:(NSArray *)tags success:(NSURL *)successURL 55 | cancel:(NSURL *)cancelURL { 56 | NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; 57 | [params setValue:title forKey:@"title"]; 58 | [params setValue:body forKey:@"body"]; 59 | [params setValue:tags forKey:@"tags"]; 60 | 61 | [self performAction:@"text" parameters:params success:successURL cancel:cancelURL]; 62 | } 63 | 64 | + (void)createQuotePost:(NSString *)quote source:(NSString *)source tags:(NSArray *)tags { 65 | [self createQuotePost:quote source:source tags:tags success:nil cancel:nil]; 66 | } 67 | 68 | + (void)createQuotePost:(NSString *)quote source:(NSString *)source tags:(NSArray *)tags success:(NSURL *)successURL 69 | cancel:(NSURL *)cancelURL { 70 | NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; 71 | [params setValue:quote forKey:@"quote"]; 72 | [params setValue:source forKey:@"source"]; 73 | [params setValue:tags forKey:@"tags"]; 74 | 75 | [self performAction:@"quote" parameters:params success:successURL cancel:cancelURL]; 76 | } 77 | 78 | + (void)createLinkPost:(NSString *)title URLString:(NSString *)URLString description:(NSString *)description 79 | tags:(NSArray *)tags { 80 | [self createLinkPost:title URLString:URLString description:description tags:tags success:nil cancel:nil]; 81 | } 82 | 83 | + (void)createLinkPost:(NSString *)title URLString:(NSString *)URLString description:(NSString *)description 84 | tags:(NSArray *)tags success:(NSURL *)successURL cancel:(NSURL *)cancelURL { 85 | NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; 86 | [params setValue:title forKey:@"title"]; 87 | [params setValue:URLString forKey:@"url"]; 88 | [params setValue:description forKey:@"description"]; 89 | [params setValue:tags forKey:@"tags"]; 90 | 91 | [self performAction:@"link" parameters:params success:successURL cancel:cancelURL]; 92 | } 93 | 94 | + (void)createChatPost:(NSString *)title body:(NSString *)body tags:(NSArray *)tags { 95 | [self createChatPost:title body:body tags:tags success:nil cancel:nil]; 96 | } 97 | 98 | + (void)createChatPost:(NSString *)title body:(NSString *)body tags:(NSArray *)tags success:(NSURL *)successURL 99 | cancel:(NSURL *)cancelURL { 100 | NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; 101 | [params setValue:title forKey:@"title"]; 102 | [params setValue:body forKey:@"body"]; 103 | [params setValue:tags forKey:@"tags"]; 104 | 105 | [self performAction:@"chat" parameters:params success:successURL cancel:cancelURL]; 106 | } 107 | 108 | #pragma mark - Private 109 | 110 | + (void)performAction:(NSString *)action parameters:(NSDictionary *)parameters { 111 | [self performAction:action parameters:parameters success:nil cancel:nil]; 112 | } 113 | 114 | + (void)performAction:(NSString *)action parameters:(NSDictionary *)parameters success:(NSURL *)successURL cancel:(NSURL *)cancelURL { 115 | if ([self isTumblrInstalled]) { 116 | NSMutableDictionary *mutableParameters = [[NSMutableDictionary alloc] initWithDictionary:parameters]; 117 | [mutableParameters setValue:@"TMTumblrSDK" forKey:@"referrer"]; 118 | [mutableParameters setValue:[successURL absoluteString] forKey:@"x-success"]; 119 | [mutableParameters setValue:[cancelURL absoluteString] forKey:@"x-cancel"]; 120 | 121 | NSString *URLString = [NSString stringWithFormat:@"tumblr://x-callback-url/%@?%@", action, 122 | TMDictionaryToQueryString(mutableParameters)]; 123 | 124 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:URLString]]; 125 | } 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Authentication/TMOAuth.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMOAuth.h 3 | // TumblrAuthentication 4 | // 5 | // Created by Bryan Irace on 11/19/12. 6 | // Copyright (c) 2012 Tumblr. All rights reserved. 7 | // 8 | 9 | @interface TMOAuth : NSObject 10 | 11 | /// Base string used to generate the OAuth signature 12 | @property (nonatomic, strong, readonly) NSString *baseString; 13 | 14 | /// Authentication header value 15 | @property (nonatomic, strong, readonly) NSString *headerString; 16 | 17 | /** 18 | Build an authentication header for a Tumblr API request. 19 | 20 | @param URL API request URL 21 | @param method HTTP method (GET or POST) 22 | @param postParameters POST body parameters 23 | @param nonce Unique request identifier 24 | @param consumerKey OAuth consumer key 25 | @param consumerSecret OAuth consumer secret 26 | @param token OAuth user token 27 | @param tokenSecret OAuth user secret 28 | */ 29 | - (id)initWithURL:(NSURL *)URL method:(NSString *)method postParameters:(NSDictionary *)postParameters 30 | nonce:(NSString *)nonce consumerKey:(NSString *)consumerKey consumerSecret:(NSString *)consumerSecret 31 | token:(NSString *)token tokenSecret:(NSString *)tokenSecret; 32 | 33 | /// Convenience method for generating an OAuth header string 34 | + (NSString *)headerForURL:(NSURL *)URL method:(NSString *)method postParameters:(NSDictionary *)postParameters 35 | nonce:(NSString *)nonce consumerKey:(NSString *)consumerKey consumerSecret:(NSString *)consumerSecret 36 | token:(NSString *)token tokenSecret:(NSString *)tokenSecret; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Authentication/TMOAuth.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMOAuth.m 3 | // TumblrAuthentication 4 | // 5 | // Created by Bryan Irace on 11/19/12. 6 | // Copyright (c) 2012 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMOAuth.h" 10 | 11 | #import 12 | #import 13 | #import 14 | #import "TMSDKFunctions.h" 15 | 16 | #ifndef __IPHONE_7_0 17 | @interface NSData (NSDeprecated) 18 | // This method was retroactively made public as of iOS 7. 19 | // https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/Classes/NSData_Class/DeprecationAppendix/AppendixADeprecatedAPI.html#//apple_ref/doc/uid/20000172-SW39 20 | - (NSString *)base64Encoding; 21 | @end 22 | #endif 23 | 24 | @interface TMOAuth() 25 | 26 | NSString *generateBaseString(NSString *baseURL, NSString *method, NSDictionary *headers, NSDictionary *queryParameters, 27 | NSDictionary *postParameters); 28 | 29 | NSString *sign(NSString *baseString, NSString *consumerSecret, NSString *tokenSecret); 30 | 31 | NSString *UNIXTimestamp(NSDate *date); 32 | 33 | NSData *HMACSHA1(NSString *dataString, NSString *keyString); 34 | 35 | @end 36 | 37 | 38 | @implementation TMOAuth 39 | 40 | + (NSString *)headerForURL:(NSURL *)URL method:(NSString *)method postParameters:(NSDictionary *)postParameters 41 | nonce:(NSString *)nonce consumerKey:(NSString *)consumerKey consumerSecret:(NSString *)consumerSecret 42 | token:(NSString *)token tokenSecret:(NSString *)tokenSecret { 43 | TMOAuth *auth = [[TMOAuth alloc] initWithURL:URL method:method postParameters:postParameters nonce:nonce 44 | consumerKey:consumerKey consumerSecret:consumerSecret token:token tokenSecret:tokenSecret]; 45 | return auth.headerString; 46 | } 47 | 48 | - (id)initWithURL:(NSURL *)URL method:(NSString *)method postParameters:(NSDictionary *)postParameters 49 | nonce:(NSString *)nonce consumerKey:(NSString *)consumerKey consumerSecret:(NSString *)consumerSecret 50 | token:(NSString *)token tokenSecret:(NSString *)tokenSecret { 51 | if (self = [super init]) { 52 | NSMutableDictionary *headerParameters = [[NSMutableDictionary alloc] initWithDictionary:@{ 53 | @"oauth_timestamp" : UNIXTimestamp([NSDate date]), 54 | @"oauth_nonce" : nonce, 55 | @"oauth_version" : @"1.0", 56 | @"oauth_signature_method" : @"HMAC-SHA1", 57 | @"oauth_consumer_key" : consumerKey, 58 | }]; 59 | 60 | if (token && token.length > 0) 61 | headerParameters[@"oauth_token"] = token; 62 | 63 | NSDictionary *queryParameters = TMQueryStringToDictionary(URL.query); 64 | 65 | NSString *baseURLString = [[URL absoluteString] componentsSeparatedByString:@"?"][0]; 66 | 67 | NSString *baseString = generateBaseString(baseURLString, method, headerParameters, queryParameters, postParameters); 68 | 69 | _baseString = baseString; 70 | 71 | headerParameters[@"oauth_signature"] = sign(baseString, consumerSecret, tokenSecret); 72 | 73 | NSMutableArray *components = [NSMutableArray array]; 74 | 75 | for (NSString *key in headerParameters) 76 | [components addObject:[NSString stringWithFormat:@"%@=\"%@\"", key, TMURLEncode(headerParameters[key])]]; 77 | 78 | _headerString = [NSString stringWithFormat:@"OAuth %@", [components componentsJoinedByString:@","]]; 79 | } 80 | 81 | return self; 82 | } 83 | 84 | #pragma mark - Private 85 | 86 | NSString *generateBaseString(NSString *baseURL, NSString *method, NSDictionary *headers, NSDictionary *queryParameters, 87 | NSDictionary *postParameters) { 88 | NSMutableDictionary *signatureParameters = [NSMutableDictionary dictionaryWithDictionary:headers]; 89 | [signatureParameters addEntriesFromDictionary:queryParameters]; 90 | [signatureParameters addEntriesFromDictionary:postParameters]; 91 | 92 | NSString *parameterString = TMDictionaryToQueryString(signatureParameters); 93 | 94 | return [NSString stringWithFormat:@"%@&%@&%@", method, TMURLEncode(baseURL), TMURLEncode(parameterString)]; 95 | } 96 | 97 | NSString *sign(NSString *baseString, NSString *consumerSecret, NSString *tokenSecret) { 98 | NSString *keyString = [NSString stringWithFormat:@"%@&%@", consumerSecret, tokenSecret ? tokenSecret : @""]; 99 | 100 | NSData *hashedData = HMACSHA1(baseString, keyString); 101 | NSString *base64EncodedString = nil; 102 | 103 | if ([hashedData respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { 104 | base64EncodedString = [hashedData base64EncodedStringWithOptions:0]; 105 | } else { 106 | base64EncodedString = [hashedData base64Encoding]; 107 | } 108 | 109 | return base64EncodedString; 110 | } 111 | 112 | NSString *UNIXTimestamp(NSDate *date) { 113 | return [NSString stringWithFormat:@"%f", round([date timeIntervalSince1970])]; 114 | } 115 | 116 | NSData *HMACSHA1(NSString *dataString, NSString *keyString) { 117 | NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding]; 118 | NSData *key = [keyString dataUsingEncoding:NSUTF8StringEncoding]; 119 | 120 | void *buffer = malloc(CC_SHA1_DIGEST_LENGTH); 121 | CCHmac(kCCHmacAlgSHA1, [key bytes], [key length], [data bytes], [data length], buffer); 122 | 123 | return [NSData dataWithBytesNoCopy:buffer length:CC_SHA1_DIGEST_LENGTH freeWhenDone:YES]; 124 | } 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Authentication/TMTumblrAuthenticator.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMTumblrAuthenticator.h 3 | // TumblrAuthentication 4 | // 5 | // Created by Bryan Irace on 11/19/12. 6 | // Copyright (c) 2012 Tumblr. All rights reserved. 7 | // 8 | 9 | typedef void (^TMAuthenticationCallback)(NSString *, NSString *, NSError *); 10 | 11 | /** 12 | Provides three-legged OAuth and xAuth implementations for authenticating with the Tumblr API. 13 | */ 14 | @interface TMTumblrAuthenticator : NSObject 15 | 16 | /// OAuth consumer key. Must be set prior to authenticating or making any API requests. 17 | @property (nonatomic, copy) NSString *OAuthConsumerKey; 18 | 19 | /// OAuth consumer key. Must be set prior to authenticating or making any API requests. 20 | @property (nonatomic, copy) NSString *OAuthConsumerSecret; 21 | 22 | + (TMTumblrAuthenticator *)sharedInstance; 23 | 24 | /** 25 | Authenticate via three-legged OAuth. 26 | 27 | Your `TMTumblrAuthenticator` instance's `handleOpenURL:` method must also be called from your `UIApplicationDelegate`'s 28 | `application:openURL:sourceApplication:annotation:` method in order to receive the tokens. 29 | 30 | @param URLScheme a URL scheme that your application can handle requests to. 31 | */ 32 | - (void)authenticate:(NSString *)URLScheme callback:(TMAuthenticationCallback)callback; 33 | 34 | /** 35 | Authenticate via three-legged OAuth. This should be called from your `UIApplicationDelegate`'s 36 | `application:openURL:sourceApplication:annotation:` method in order to receive the tokens. 37 | 38 | This method is the last part of the authentication flow started by calling `authenticate:callback:` 39 | */ 40 | - (BOOL)handleOpenURL:(NSURL *)url; 41 | 42 | /** 43 | Authenticate via xAuth. 44 | 45 | Please note that xAuth access [must be specifically requested](http://www.tumblr.com/oauth/apps) for your application. 46 | */ 47 | - (void)xAuth:(NSString *)emailAddress password:(NSString *)password callback:(TMAuthenticationCallback)callback; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Authentication/TMTumblrAuthenticator.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMTumblrAuthenticator.m 3 | // TumblrAuthentication 4 | // 5 | // Created by Bryan Irace on 11/19/12. 6 | // Copyright (c) 2012 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMTumblrAuthenticator.h" 10 | 11 | #import "TMOAuth.h" 12 | #import "TMSDKFunctions.h" 13 | 14 | #if __IPHONE_OS_VERSION_MIN_REQUIRED 15 | #import 16 | #else 17 | #import 18 | #endif 19 | 20 | typedef void (^NSURLConnectionCompletionHandler)(NSURLResponse *, NSData *, NSError *); 21 | 22 | @interface TMTumblrAuthenticator() 23 | 24 | @property (nonatomic, copy) TMAuthenticationCallback threeLeggedOAuthCallback; 25 | @property (nonatomic, copy) NSString *threeLeggedOAuthTokenSecret; 26 | 27 | NSMutableURLRequest *mutableRequestWithURLString(NSString *URLString); 28 | 29 | NSError *errorWithStatusCode(NSInteger statusCode); 30 | 31 | NSDictionary *formEncodedDataToDictionary(NSData *data); 32 | 33 | @end 34 | 35 | @implementation TMTumblrAuthenticator 36 | 37 | + (id)sharedInstance { 38 | static TMTumblrAuthenticator *instance; 39 | static dispatch_once_t predicate; 40 | dispatch_once(&predicate, ^{ instance = [[TMTumblrAuthenticator alloc] init]; }); 41 | return instance; 42 | } 43 | 44 | - (void)authenticate:(NSString *)URLScheme callback:(TMAuthenticationCallback)callback { 45 | // Clear token secret in case authentication was previously started but not finished 46 | self.threeLeggedOAuthTokenSecret = nil; 47 | 48 | NSString *tokenRequestURLString = [NSString stringWithFormat:@"http://www.tumblr.com/oauth/request_token?oauth_callback=%@", 49 | TMURLEncode([NSString stringWithFormat:@"%@://tumblr-authorize", URLScheme])]; 50 | 51 | NSMutableURLRequest *request = mutableRequestWithURLString(tokenRequestURLString); 52 | [[self class] signRequest:request withParameters:nil consumerKey:self.OAuthConsumerKey 53 | consumerSecret:self.OAuthConsumerSecret token:nil tokenSecret:nil]; 54 | 55 | NSURLConnectionCompletionHandler handler = ^(NSURLResponse *response, NSData *data, NSError *error) { 56 | if (error) { 57 | if (callback) { 58 | callback(nil, nil, error); 59 | } 60 | 61 | return; 62 | } 63 | 64 | NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; 65 | 66 | if (statusCode == 200) { 67 | self.threeLeggedOAuthCallback = callback; 68 | 69 | NSDictionary *responseParameters = formEncodedDataToDictionary(data); 70 | self.threeLeggedOAuthTokenSecret = responseParameters[@"oauth_token_secret"]; 71 | 72 | NSURL *authURL = [NSURL URLWithString: 73 | [NSString stringWithFormat:@"https://www.tumblr.com/oauth/authorize?oauth_token=%@", 74 | responseParameters[@"oauth_token"]]]; 75 | 76 | #if __IPHONE_OS_VERSION_MIN_REQUIRED 77 | [[UIApplication sharedApplication] openURL:authURL]; 78 | #else 79 | [[NSWorkspace sharedWorkspace] openURL:authURL]; 80 | #endif 81 | 82 | } else { 83 | if (callback) { 84 | callback(nil, nil, errorWithStatusCode(statusCode)); 85 | } 86 | } 87 | }; 88 | 89 | [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:handler]; 90 | } 91 | 92 | - (BOOL)handleOpenURL:(NSURL *)url { 93 | if (![url.host isEqualToString:@"tumblr-authorize"]) { 94 | return NO; 95 | } 96 | 97 | void(^clearState)() = ^ { 98 | self.threeLeggedOAuthTokenSecret = nil; 99 | self.threeLeggedOAuthCallback = nil; 100 | }; 101 | 102 | NSDictionary *URLParameters = TMQueryStringToDictionary(url.query); 103 | 104 | if ([[URLParameters allKeys] count] == 0) { 105 | if (self.threeLeggedOAuthCallback) { 106 | self.threeLeggedOAuthCallback(nil, nil, [NSError errorWithDomain:@"Permission denied by user" code:0 userInfo:nil]); 107 | } 108 | 109 | clearState(); 110 | 111 | return NO; 112 | } 113 | 114 | NSString *OAuthToken = URLParameters[@"oauth_token"]; 115 | 116 | NSDictionary *requestParameters = @{ @"oauth_verifier" : URLParameters[@"oauth_verifier"] }; 117 | 118 | NSMutableURLRequest *request = mutableRequestWithURLString(@"https://www.tumblr.com/oauth/access_token"); 119 | request.HTTPMethod = @"POST"; 120 | request.HTTPBody = [TMDictionaryToQueryString(requestParameters) dataUsingEncoding:NSUTF8StringEncoding]; 121 | 122 | [[self class] signRequest:request withParameters:requestParameters consumerKey:self.OAuthConsumerKey 123 | consumerSecret:self.OAuthConsumerSecret token:OAuthToken tokenSecret:self.threeLeggedOAuthTokenSecret]; 124 | 125 | NSURLConnectionCompletionHandler handler = ^(NSURLResponse *response, NSData *data, NSError *error) { 126 | if (error) { 127 | if (self.threeLeggedOAuthCallback) { 128 | self.threeLeggedOAuthCallback(nil, nil, error); 129 | } 130 | } else { 131 | NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; 132 | 133 | if (self.threeLeggedOAuthCallback) { 134 | if (statusCode == 200) { 135 | NSDictionary *responseParameters = formEncodedDataToDictionary(data); 136 | 137 | self.threeLeggedOAuthCallback(responseParameters[@"oauth_token"], responseParameters[@"oauth_token_secret"], nil); 138 | 139 | } else { 140 | self.threeLeggedOAuthCallback(nil, nil, errorWithStatusCode(statusCode)); 141 | } 142 | } 143 | } 144 | 145 | clearState(); 146 | }; 147 | 148 | [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:handler]; 149 | 150 | return YES; 151 | } 152 | 153 | - (void)xAuth:(NSString *)emailAddress password:(NSString *)password callback:(TMAuthenticationCallback)callback { 154 | NSDictionary *requestParameters = @{ 155 | @"x_auth_username" : emailAddress, 156 | @"x_auth_password" : password, 157 | @"x_auth_mode" : @"client_auth", 158 | @"api_key" : self.OAuthConsumerKey 159 | }; 160 | 161 | NSMutableURLRequest *request = mutableRequestWithURLString(@"https://www.tumblr.com/oauth/access_token"); 162 | request.HTTPMethod = @"POST"; 163 | request.HTTPBody = [TMDictionaryToQueryString(requestParameters) dataUsingEncoding:NSUTF8StringEncoding]; 164 | 165 | [[self class] signRequest:request withParameters:requestParameters consumerKey:self.OAuthConsumerKey 166 | consumerSecret:self.OAuthConsumerSecret token:nil tokenSecret:nil]; 167 | 168 | NSURLConnectionCompletionHandler handler = ^(NSURLResponse *response, NSData *data, NSError *error) { 169 | if (error) { 170 | if (callback) { 171 | callback(nil, nil, error); 172 | } 173 | 174 | return; 175 | } 176 | 177 | NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; 178 | 179 | if (statusCode == 200) { 180 | NSDictionary *responseParameters = formEncodedDataToDictionary(data); 181 | 182 | if (callback) { 183 | callback(responseParameters[@"oauth_token"], responseParameters[@"oauth_token_secret"], nil); 184 | } 185 | 186 | } else { 187 | if (callback) { 188 | callback(nil, nil, errorWithStatusCode(statusCode)); 189 | } 190 | } 191 | }; 192 | 193 | [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:handler]; 194 | } 195 | 196 | #pragma mark - NSObject 197 | 198 | 199 | #pragma mark - Helpers 200 | 201 | + (void)signRequest:(NSMutableURLRequest *)request 202 | withParameters:(NSDictionary *)parameters 203 | consumerKey:(NSString *)consumerKey 204 | consumerSecret:(NSString *)consumerSecret 205 | token:(NSString *)OAuthToken 206 | tokenSecret:(NSString *)OAuthTokenSecret { 207 | [request setValue:@"TMTumblrSDK" forHTTPHeaderField:@"User-Agent"]; 208 | 209 | [request setValue:[TMOAuth headerForURL:request.URL 210 | method:request.HTTPMethod 211 | postParameters:parameters 212 | nonce:[[NSProcessInfo processInfo] globallyUniqueString] 213 | consumerKey:consumerKey 214 | consumerSecret:consumerSecret 215 | token:OAuthToken 216 | tokenSecret:OAuthTokenSecret] forHTTPHeaderField:@"Authorization"]; 217 | } 218 | 219 | NSMutableURLRequest *mutableRequestWithURLString(NSString *URLString) { 220 | return [NSMutableURLRequest requestWithURL:[NSURL URLWithString:URLString]]; 221 | } 222 | 223 | NSError *errorWithStatusCode(NSInteger statusCode) { 224 | return [NSError errorWithDomain:@"Authentication request failed" code:statusCode userInfo:nil]; 225 | } 226 | 227 | NSDictionary *formEncodedDataToDictionary(NSData *data) { 228 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 229 | NSDictionary *dictionary = TMQueryStringToDictionary(string); 230 | 231 | return dictionary; 232 | } 233 | 234 | @end 235 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Core/TMSDKFunctions.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMSDKFunctions.h 3 | // TMTumblrSDK 4 | // 5 | // Created by Bryan Irace on 3/24/13. 6 | // Copyright (c) 2013 Tumblr. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TMSDKFunctions : NSObject 12 | 13 | NSString *TMURLDecode(NSString *string); 14 | 15 | NSString *TMURLEncode(NSString *string); 16 | 17 | NSDictionary *TMQueryStringToDictionary(NSString *query); 18 | 19 | NSString *TMDictionaryToQueryString(NSDictionary *dictionary); 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Pods/TMTumblrSDK/TMTumblrSDK/Core/TMSDKFunctions.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMSDKFunctions.m 3 | // TMTumblrSDK 4 | // 5 | // Created by Bryan Irace on 3/24/13. 6 | // Copyright (c) 2013 Tumblr. All rights reserved. 7 | // 8 | 9 | #import "TMSDKFunctions.h" 10 | 11 | @implementation TMSDKFunctions 12 | 13 | NSString *TMURLDecode(NSString *string) { 14 | return (NSString *)CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapes(NULL, (CFStringRef)string, 15 | CFSTR(""))); 16 | } 17 | 18 | NSString *TMURLEncode(id value) { 19 | NSString *string; 20 | 21 | if ([value isKindOfClass:[NSString class]]) 22 | string = (NSString *)value; 23 | else 24 | string = [value stringValue]; 25 | 26 | return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)string, NULL, 27 | CFSTR("!*'();:@&=+$,/?%#[]%"), kCFStringEncodingUTF8)); 28 | } 29 | 30 | NSDictionary *TMQueryStringToDictionary(NSString *query) { 31 | NSMutableDictionary *mutableParameterDictionary = [[NSMutableDictionary alloc] init]; 32 | 33 | NSArray *parameters = [query componentsSeparatedByString:@"&"]; 34 | 35 | for (NSString *parameter in parameters) { 36 | NSArray *keyValuePair = [parameter componentsSeparatedByString:@"="]; 37 | 38 | if (keyValuePair.count == 2) { 39 | NSString *key = TMURLDecode(keyValuePair[0]); 40 | NSString *value = TMURLDecode(keyValuePair[1]); 41 | 42 | id existingValueForKey = mutableParameterDictionary[key]; 43 | 44 | if (existingValueForKey) { 45 | if ([existingValueForKey isKindOfClass:[NSMutableArray class]]) 46 | [(NSMutableArray *)existingValueForKey addObject:value]; 47 | else 48 | [mutableParameterDictionary setObject:[NSMutableArray arrayWithObjects:existingValueForKey, value, nil] 49 | forKey:key]; 50 | } else 51 | [mutableParameterDictionary setObject:value forKey:key]; 52 | } 53 | } 54 | 55 | return [NSDictionary dictionaryWithDictionary:mutableParameterDictionary]; 56 | } 57 | 58 | NSString *TMDictionaryToQueryString(NSDictionary *dictionary) { 59 | NSMutableArray *parameters = [NSMutableArray array]; 60 | 61 | void (^addParameter)(NSString *key, NSString *value) = ^(NSString *key, NSString *value) { 62 | [parameters addObject:[NSString stringWithFormat:@"%@=%@", TMURLEncode(key), TMURLEncode(value)]]; 63 | }; 64 | 65 | for (NSString *key in [[dictionary allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) { 66 | id value = dictionary[key]; 67 | 68 | if ([value isKindOfClass:[NSArray class]]) { 69 | for (NSString *arrayValue in (NSArray *)value) 70 | addParameter(key, arrayValue); 71 | } else 72 | addParameter(key, value); 73 | } 74 | 75 | return [parameters componentsJoinedByString:@"&"]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Core Data Example 2 | 3 | This is a sample project whose purpose is to gather feedback on the best way to use Core Data in a multithreaded environment. Here's a [blog post](http://bryan.io/post/89082476661/core-data-sample-project) explaining the motivation: 4 | 5 | > We’ve been using Core Data for persistence in [Tumblr for iOS](https://itunes.apple.com/us/app/tumblr/id305343404) for years now, but are always interested in re-evaluating our approach to make sure that we’re leveraging the SDK as effectively as possible. 6 | 7 | The focal point is the [TMCoreDataController](https://github.com/tumblr/CoreDataExample/blob/master/CoreDataExample/TMCoreDataController.h) class, which exposes a block-based API for updating managed object contexts both from the main queue as well as in the background. 8 | 9 | The application simply fetches, persists, and displays a little bit of data from the Tumblr API, in order to provide a real-world example of how `TMCoreDataController` is expected to be used. 10 | 11 | If you have any feedback, please create [issues](https://github.ewr01.tumblr.net/bryan/CoreDataExample/issues) or [pull requests](https://github.ewr01.tumblr.net/bryan/CoreDataExample/pulls). Thanks for taking a look! 12 | 13 | ## Approach 14 | 15 | `TMCoreDataController` is implemented using three-levels of parent/child managed object contexts (based off of the suggestions made in [this blog post](http://floriankugler.com/blog/2013/4/2/the-concurrent-core-data-stack)): 16 | 17 | * **Master** context (private queue concurrency type) – long lived 18 | * **Main** context (main queue concurrency type) – long lived 19 | * **Background** contexts (private queue concurrency type) – a new one created each time 20 | 21 | Write operations should only be done through `TMCoreDataController`'s `performBackgroundBlockAndWait:` and `performMainContextBlock:` methods, both of which save the context being written to as well as any parent contexts (recursively) immediately after. 22 | 23 | ### Thought process 24 | 25 | * Disk writes are slow, so we want them to happen off of the main thread. Hence writing to disk occurs in the background when the master context is saved. 26 | * Main context is written to and read from on the main queue via interaction with user interface. 27 | * Background updates (e.g. updating our Core Data database off the back of network requests) occur on a background context. A new background context is created each time to make sure they are up to date. 28 | * Background context is a child of the main context such that the UI is updated after our background changes are saved. 29 | * When a managed object context is saved, its parent context(s) are also saved, recursively, so ensure that all of our changes are written to disk. 30 | 31 | In this particular example, the “heavy lifting” of updating our database with new posts is done in `TMDashboardViewController`'s `refresh` method. The code in this particular project is pretty simple and contrived, but assume that a lot more work would be done here in a real production app. It is expected that user interaction may result in updates to the main queue's context while this background block is being executed. 32 | 33 | ## Running CoreDataExample 34 | 35 | To run the sample application, you need to add OAuth keys, tokens, and secrets to [TMAppDelegate](https://github.com/tumblr/CoreDataExample/blob/master/CoreDataExample/TMAppDelegate.m#L24). If you don't already have one, you can create a Tumblr API application [here](https://www.tumblr.com/oauth/apps) and generate a token and secret using our [API console](https://api.tumblr.com/console). 36 | 37 | ## Questions 38 | 39 | What problems are there with this approach? How could this be improved? 40 | 41 | Specifically: 42 | 43 | * Is the recursive managed object context save the best way to ensure that all of our data is kept in sync? 44 | 45 | * I'm worried that this architecture makes us susceptible to deadlocks, as outlined in [this article](http://wbyoung.tumblr.com/post/27851725562/core-data-growing-pains): 46 | 47 | > With NSFetchedResultsController and nested contexts, it’s pretty easy to do. Using the same UIManagedDocument setup described above, executing fetch requests in the private queue context while using NSFetchedResultsController with the main queue context will likely deadlock. If you start both at about the same time it happens with almost 100% consistency. NSFetchedResultsController is probably acquiring a lock that it shouldn’t be. This has been reported as fixed for an upcoming release of iOS. 48 | 49 | Unfortunately I've seen a couple of crashes which may corroborate the above, such as: 50 | 51 | Incident Identifier: 5168BD71-BFD2-4DF6-84C4-240364B888DD 52 | CrashReporter Key: 289E38A8-3E8A-41CB-877F-1FEEF852AF30 53 | Hardware Model: iPhone6,1 54 | Version: 251 55 | Code Type: ARM-64 56 | Parent Process: launchd [1] 57 | 58 | Date/Time: 2014-05-13T01:23:26Z 59 | OS Version: iPhone OS 7.1.1 (11D201) 60 | Report Version: 104 61 | 62 | Exception Type: SIGSEGV 63 | Exception Codes: SEGV_ACCERR at 0xbf02beb8 64 | Crashed Thread: 0 65 | 66 | Application Specific Information: 67 | objc_msgSend() selector name: isFault 68 | 69 | Thread 0 Crashed: 70 | 0 libobjc.A.dylib 0x00000001978fc1d0 objc_msgSend + 16 71 | 1 CoreData 0x000000018b1782a8 __92-[NSManagedObjectContext(_NestedContextSupport) newValuesForObjectWithID:withContext:error:]_block_invoke + 276 72 | 2 libdispatch.dylib 0x0000000197ec3fd4 _dispatch_client_callout + 12 73 | 3 libdispatch.dylib 0x0000000197ec9c84 _dispatch_barrier_sync_f_invoke + 44 74 | 4 CoreData 0x000000018b16ccec _perform + 120 75 | 5 CoreData 0x000000018b178150 -[NSManagedObjectContext(_NestedContextSupport) newValuesForObjectWithID:withContext:error:] + 124 76 | 6 CoreData 0x000000018b0e8b00 _PFFaultHandlerLookupRow + 356 77 | 7 CoreData 0x000000018b0e8604 _PF_FulfillDeferredFault + 252 78 | 8 CoreData 0x000000018b0e8458 _sharedIMPL_pvfk_core + 60 79 | 9 ExampleApplication 0x00000001000887cc 0x100054000 + 214988 80 | 10 ExampleApplication 0x00000001000625c0 0x100054000 + 58816 81 | 11 libdispatch.dylib 0x0000000197ec4014 _dispatch_call_block_and_release + 20 82 | 12 libdispatch.dylib 0x0000000197ec3fd4 _dispatch_client_callout + 12 83 | 13 libdispatch.dylib 0x0000000197ec9ea8 _dispatch_after_timer_callback + 76 84 | 14 libdispatch.dylib 0x0000000197ec3fd4 _dispatch_client_callout + 12 85 | 15 libdispatch.dylib 0x0000000197ec5b90 _dispatch_source_invoke + 496 86 | 16 libdispatch.dylib 0x0000000197ec7180 _dispatch_main_queue_callback_4CF + 240 87 | 17 CoreFoundation 0x000000018b3a2c2c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8 88 | 18 CoreFoundation 0x000000018b3a0f6c __CFRunLoopRun + 1448 89 | 19 CoreFoundation 0x000000018b2e1c20 CFRunLoopRunSpecific + 448 90 | 20 GraphicsServices 0x0000000190fc9c0c GSEventRunModal + 164 91 | 21 UIKit 0x000000018e412fdc UIApplicationMain + 1152 92 | 22 ExampleApplication 0x000000010005df3c 0x100054000 + 40764 93 | 23 libdyld.dylib 0x0000000197edfaa0 start + 0 94 | 95 | How can this be mitigated, other than just optimizing all of our fetches and saves to prevent the persistent store coordinator from being locked for longer than it needs to be? 96 | 97 | ### Marcus Zarra's suggestions for how to prevent deadlocks (thanks for your feedback, Marcus!) 98 | 99 | * [“Long fetch, long save, long activity to the PSC locks it. Simple rule, don't lock the PSC for a long time.”](https://twitter.com/mzarra/status/466302788863938560) 100 | * [“Smaller saves during the import, more intelligent fetching for your update/insert question. Those are the normal first pass items.”](https://twitter.com/mzarra/status/466304487859048448) 101 | * [“Doing a count instead of a fetch, fetching ids only, predicate performance, batch size, things like that can make a huge difference.”](https://twitter.com/mzarra/status/466316613227409408) 102 | 103 | Marcus provides a *ton* of free Core Data help on Stack Overflow as well as on Twitter, you should support him by checking out his [Core Data book](http://pragprog.com/book/mzcd2/core-data). 104 | 105 | ## Thank you! 106 | 107 | Bryan Irace ([Email](mailto:bryan@tumblr.com), [Twitter](http://twitter.com/irace)) 108 | 109 | --------------------------------------------------------------------------------