From 0e152451376079305fe66d4c53c46a4918366fe2 Mon Sep 17 00:00:00 2001 From: Nicolas Seriot Date: Mon, 18 Nov 2013 23:30:33 +0100 Subject: [PATCH] added uploadProgressBlock to keep track of media upload progres, renamed existring progressBlock into downloadProgressBlock, added st_ prefix to some categories --- STTwitter/STHTTPRequest+STTwitter.h | 3 +- STTwitter/STHTTPRequest+STTwitter.m | 25 +- STTwitter/STTwitterAPI.h | 100 ++--- STTwitter/STTwitterAPI.m | 392 +++++++++--------- STTwitter/STTwitterAppOnly.m | 53 ++- STTwitter/STTwitterOAuth.h | 12 +- STTwitter/STTwitterOAuth.m | 102 +++-- STTwitter/STTwitterOS.m | 8 +- STTwitter/STTwitterProtocol.h | 3 +- .../UserInterfaceState.xcuserstate | Bin 26117 -> 26098 bytes .../UserInterfaceState.xcuserstate | Bin 51838 -> 55225 bytes .../UserInterfaceState.xcuserstate | Bin 52875 -> 54026 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 62 +++ demo_osx/STTwitterDemoOSX/STClientVC.m | 37 +- demo_osx/STTwitterDemoOSX/STConsoleVC.m | 116 +++--- 15 files changed, 521 insertions(+), 392 deletions(-) diff --git a/STTwitter/STHTTPRequest+STTwitter.h b/STTwitter/STHTTPRequest+STTwitter.h index 4c49843..40988c9 100644 --- a/STTwitter/STHTTPRequest+STTwitter.h +++ b/STTwitter/STHTTPRequest+STTwitter.h @@ -11,7 +11,8 @@ @interface STHTTPRequest (STTwitter) + (STHTTPRequest *)twitterRequestWithURLString:(NSString *)urlString - stTwitterProgressBlock:(void(^)(id json))progressBlock + stTwitterUploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock + stTwitterDownloadProgressBlock:(void(^)(id json))downloadProgressBlock stTwitterSuccessBlock:(void(^)(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json))successBlock stTwitterErrorBlock:(void(^)(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock; diff --git a/STTwitter/STHTTPRequest+STTwitter.m b/STTwitter/STHTTPRequest+STTwitter.m index 0756a1d..5580520 100644 --- a/STTwitter/STHTTPRequest+STTwitter.m +++ b/STTwitter/STHTTPRequest+STTwitter.m @@ -21,13 +21,13 @@ + (NSError *)errorFromResponseData:(NSData *)responseData { NSError *jsonError = nil; NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&jsonError]; - + NSString *message = nil; NSInteger code = 0; if([json isKindOfClass:[NSDictionary class]]) { // assume {"errors":[{"message":"Bad Authentication data","code":215}]} - + id errors = [json valueForKey:@"errors"]; if([errors isKindOfClass:[NSArray class]] && [(NSArray *)errors count] > 0) { NSDictionary *errorDictionary = [errors lastObject]; @@ -40,7 +40,7 @@ + (NSError *)errorFromResponseData:(NSData *)responseData { message = errors; } } - + if(message) { NSError *error = [NSError errorWithDomain:NSStringFromClass([self class]) code:code userInfo:@{NSLocalizedDescriptionKey : message}]; return error; @@ -50,10 +50,11 @@ + (NSError *)errorFromResponseData:(NSData *)responseData { } + (STHTTPRequest *)twitterRequestWithURLString:(NSString *)urlString - stTwitterProgressBlock:(void(^)(id json))progressBlock + stTwitterUploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock + stTwitterDownloadProgressBlock:(void(^)(id json))downloadProgressBlock stTwitterSuccessBlock:(void(^)(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json))successBlock stTwitterErrorBlock:(void(^)(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { - + __block STHTTPRequest *r = [self requestWithURLString:urlString]; __weak STHTTPRequest *wr = r; @@ -61,15 +62,17 @@ + (STHTTPRequest *)twitterRequestWithURLString:(NSString *)urlString r.timeoutSeconds = 0; + r.uploadProgressBlock = uploadProgressBlock; + r.downloadProgressBlock = ^(NSData *data, NSInteger totalBytesReceived, NSInteger totalBytesExpectedToReceive) { - if(progressBlock == nil) return; + if(downloadProgressBlock == nil) return; NSError *jsonError = nil; id json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError]; if(json) { - progressBlock(json); + downloadProgressBlock(json); return; } @@ -87,10 +90,10 @@ + (STHTTPRequest *)twitterRequestWithURLString:(NSString *)urlString NSError *jsonError = nil; id json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError]; if(json == nil) { -// errorBlock(wr.responseHeaders, jsonError); + // errorBlock(wr.responseHeaders, jsonError); return; // not enough information to say it's an error } - progressBlock(json); + downloadProgressBlock(json); } }; @@ -132,8 +135,8 @@ + (STHTTPRequest *)twitterRequestWithURLString:(NSString *)urlString STLog(@"-- body: %@", wr.responseString); -// BOOL isCancellationError = [[error domain] isEqualToString:@"STHTTPRequest"] && ([error code] == kSTHTTPRequestCancellationError); -// if(isCancellationError) return; + // BOOL isCancellationError = [[error domain] isEqualToString:@"STHTTPRequest"] && ([error code] == kSTHTTPRequestCancellationError); + // if(isCancellationError) return; errorBlock(wr.requestHeaders, wr.responseHeaders, error); }; diff --git a/STTwitter/STTwitterAPI.h b/STTwitter/STTwitterAPI.h index adb2f41..79a97f9 100644 --- a/STTwitter/STTwitterAPI.h +++ b/STTwitter/STTwitterAPI.h @@ -115,26 +115,28 @@ NS_ENUM(NSUInteger, STTwitterAPIErrorCode) { #pragma mark Generic methods to GET and POST - (id)fetchResource:(NSString *)resource - HTTPMethod:(NSString *)HTTPMethod - baseURLString:(NSString *)baseURLString - parameters:(NSDictionary *)params - progressBlock:(void (^)(id request, id response))progressBlock // TODO: handle progressBlock? - successBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response))successBlock - errorBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock; + HTTPMethod:(NSString *)HTTPMethod + baseURLString:(NSString *)baseURLString + parameters:(NSDictionary *)params +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void (^)(id request, id response))downloadProgressBlock + successBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response))successBlock + errorBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock; - (id)getResource:(NSString *)resource - baseURLString:(NSString *)baseURLString - parameters:(NSDictionary *)parameters - progressBlock:(void(^)(id json))progressBlock - successBlock:(void(^)(NSDictionary *rateLimits, id json))successBlock - errorBlock:(void(^)(NSError *error))errorBlock; + baseURLString:(NSString *)baseURLString + parameters:(NSDictionary *)parameters +downloadProgressBlock:(void(^)(id json))progredownloadProgressBlockssBlock + successBlock:(void(^)(NSDictionary *rateLimits, id json))successBlock + errorBlock:(void(^)(NSError *error))errorBlock; - (id)postResource:(NSString *)resource - baseURLString:(NSString *)baseURLString - parameters:(NSDictionary *)parameters - progressBlock:(void(^)(id json))progressBlock - successBlock:(void(^)(NSDictionary *rateLimits, id response))successBlock - errorBlock:(void(^)(NSError *error))errorBlock; + baseURLString:(NSString *)baseURLString + parameters:(NSDictionary *)parameters +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id json))downloadProgressBlock + successBlock:(void(^)(NSDictionary *rateLimits, id response))successBlock + errorBlock:(void(^)(NSError *error))errorBlock; #pragma mark Timelines @@ -372,6 +374,7 @@ NS_ENUM(NSUInteger, STTwitterAPIErrorCode) { longitude:(NSString *)longitude placeID:(NSString *)placeID displayCoordinates:(NSNumber *)displayCoordinates + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock successBlock:(void(^)(NSDictionary *status))successBlock errorBlock:(void(^)(NSError *error))errorBlock; @@ -382,6 +385,7 @@ NS_ENUM(NSUInteger, STTwitterAPIErrorCode) { placeID:(NSString *)placeID latitude:(NSString *)latitude longitude:(NSString *)longitude + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock successBlock:(void(^)(NSDictionary *status))successBlock errorBlock:(void(^)(NSError *error))errorBlock; @@ -455,18 +459,18 @@ NS_ENUM(NSUInteger, STTwitterAPIErrorCode) { */ - (id)postStatusesFilterUserIDs:(NSArray *)userIDs - keywordsToTrack:(NSArray *)keywordsToTrack - locationBoundingBoxes:(NSArray *)locationBoundingBoxes - delimited:(NSNumber *)delimited - stallWarnings:(NSNumber *)stallWarnings - progressBlock:(void(^)(id response))progressBlock - stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock - errorBlock:(void(^)(NSError *error))errorBlock; + keywordsToTrack:(NSArray *)keywordsToTrack + locationBoundingBoxes:(NSArray *)locationBoundingBoxes + delimited:(NSNumber *)delimited + stallWarnings:(NSNumber *)stallWarnings + progressBlock:(void(^)(id response))progressBlock + stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock + errorBlock:(void(^)(NSError *error))errorBlock; // convenience - (id)postStatusesFilterKeyword:(NSString *)keyword - progressBlock:(void(^)(id response))progressBlock - errorBlock:(void(^)(NSError *error))errorBlock; + progressBlock:(void(^)(id response))progressBlock + errorBlock:(void(^)(NSError *error))errorBlock; /* GET statuses/sample @@ -475,10 +479,10 @@ NS_ENUM(NSUInteger, STTwitterAPIErrorCode) { */ - (id)getStatusesSampleDelimited:(NSNumber *)delimited - stallWarnings:(NSNumber *)stallWarnings - progressBlock:(void(^)(id response))progressBlock - stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock - errorBlock:(void(^)(NSError *error))errorBlock; + stallWarnings:(NSNumber *)stallWarnings + progressBlock:(void(^)(id response))progressBlock + stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock + errorBlock:(void(^)(NSError *error))errorBlock; /* GET statuses/firehose @@ -489,11 +493,11 @@ NS_ENUM(NSUInteger, STTwitterAPIErrorCode) { */ - (id)getStatusesFirehoseWithCount:(NSString *)count - delimited:(NSNumber *)delimited - stallWarnings:(NSNumber *)stallWarnings - progressBlock:(void(^)(id response))progressBlock - stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock - errorBlock:(void(^)(NSError *error))errorBlock; + delimited:(NSNumber *)delimited + stallWarnings:(NSNumber *)stallWarnings + progressBlock:(void(^)(id response))progressBlock + stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock + errorBlock:(void(^)(NSError *error))errorBlock; /* GET user @@ -502,14 +506,14 @@ NS_ENUM(NSUInteger, STTwitterAPIErrorCode) { */ - (id)getUserStreamDelimited:(NSNumber *)delimited - stallWarnings:(NSNumber *)stallWarnings + stallWarnings:(NSNumber *)stallWarnings includeMessagesFromFollowedAccounts:(NSNumber *)includeMessagesFromFollowedAccounts - includeReplies:(NSNumber *)includeReplies - keywordsToTrack:(NSArray *)keywordsToTrack - locationBoundingBoxes:(NSArray *)locationBoundingBoxes - progressBlock:(void(^)(id response))progressBlock - stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock - errorBlock:(void(^)(NSError *error))errorBlock; + includeReplies:(NSNumber *)includeReplies + keywordsToTrack:(NSArray *)keywordsToTrack + locationBoundingBoxes:(NSArray *)locationBoundingBoxes + progressBlock:(void(^)(id response))progressBlock + stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock + errorBlock:(void(^)(NSError *error))errorBlock; /* GET site @@ -518,13 +522,13 @@ includeMessagesFromFollowedAccounts:(NSNumber *)includeMessagesFromFollowedAccou */ - (id)getSiteStreamForUserIDs:(NSArray *)userIDs - delimited:(NSNumber *)delimited - stallWarnings:(NSNumber *)stallWarnings - restrictToUserMessages:(NSNumber *)restrictToUserMessages - includeReplies:(NSNumber *)includeReplies - progressBlock:(void(^)(id response))progressBlock - stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock - errorBlock:(void(^)(NSError *error))errorBlock; + delimited:(NSNumber *)delimited + stallWarnings:(NSNumber *)stallWarnings + restrictToUserMessages:(NSNumber *)restrictToUserMessages + includeReplies:(NSNumber *)includeReplies + progressBlock:(void(^)(id response))progressBlock + stallWarningBlock:(void(^)(NSString *code, NSString *message, NSUInteger percentFull))stallWarningBlock + errorBlock:(void(^)(NSError *error))errorBlock; #pragma mark Direct Messages diff --git a/STTwitter/STTwitterAPI.m b/STTwitter/STTwitterAPI.m index 856fe16..1976e33 100644 --- a/STTwitter/STTwitterAPI.m +++ b/STTwitter/STTwitterAPI.m @@ -247,15 +247,17 @@ - (id)fetchResource:(NSString *)resource HTTPMethod:(NSString *)HTTPMethod baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)params - progressBlock:(void (^)(id request, id response))progressBlock - successBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response))successBlock - errorBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id request, id response))downloadProgressBlock + successBlock:(void(^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response))successBlock + errorBlock:(void(^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { return [_oauth fetchResource:resource HTTPMethod:HTTPMethod baseURLString:baseURLString parameters:params - progressBlock:progressBlock + uploadProgressBlock:uploadProgressBlock + downloadProgressBlock:downloadProgressBlock successBlock:successBlock errorBlock:errorBlock]; } @@ -263,7 +265,8 @@ - (id)fetchResource:(NSString *)resource - (id)getResource:(NSString *)resource baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)parameters - progressBlock:(void(^)(id json))progressBlock +//uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id json))downloadProgressBlock successBlock:(void(^)(NSDictionary *rateLimits, id json))successBlock errorBlock:(void(^)(NSError *error))errorBlock { @@ -271,19 +274,21 @@ - (id)getResource:(NSString *)resource HTTPMethod:@"GET" baseURLString:baseURLString parameters:parameters - progressBlock:^(id request, id response) { - if(progressBlock) progressBlock(response); - } successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response) { - if(successBlock) successBlock(responseHeaders, response); - } errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { - if(errorBlock) errorBlock(error); - }]; + uploadProgressBlock:nil + downloadProgressBlock:^(id request, id response) { + if(downloadProgressBlock) downloadProgressBlock(response); + } successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response) { + if(successBlock) successBlock(responseHeaders, response); + } errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { + if(errorBlock) errorBlock(error); + }]; } - (id)postResource:(NSString *)resource baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)parameters - progressBlock:(void(^)(id json))progressBlock +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id json))downloadProgressBlock successBlock:(void(^)(NSDictionary *rateLimits, id response))successBlock errorBlock:(void(^)(NSError *error))errorBlock { @@ -291,28 +296,31 @@ - (id)postResource:(NSString *)resource HTTPMethod:@"POST" baseURLString:baseURLString parameters:parameters - progressBlock:^(id request, id response) { - if(progressBlock) progressBlock(response); - } successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response) { - if(successBlock) successBlock(responseHeaders, response); - } errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { - if(errorBlock) errorBlock(error); - }]; + uploadProgressBlock:uploadProgressBlock + downloadProgressBlock:^(id request, id response) { + if(downloadProgressBlock) downloadProgressBlock(response); + } successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response) { + if(successBlock) successBlock(responseHeaders, response); + } errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { + if(errorBlock) errorBlock(error); + }]; } - (void)postResource:(NSString *)resource baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)parameters - progressBlock:(void(^)(id json))progressBlock + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id json))downloadProgressBlock errorBlock:(void(^)(NSError *error))errorBlock { [_oauth fetchResource:resource HTTPMethod:@"POST" baseURLString:baseURLString parameters:parameters - progressBlock:^(id request, id response) { - if(progressBlock) progressBlock(response); - } successBlock:nil + uploadProgressBlock:uploadProgressBlock + downloadProgressBlock:^(id request, id response) { + if(downloadProgressBlock) downloadProgressBlock(response); + } successBlock:nil errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { errorBlock(error); }]; @@ -321,16 +329,17 @@ - (void)postResource:(NSString *)resource - (void)getResource:(NSString *)resource baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)parameters - progressBlock:(void(^)(id json))progressBlock +downloadProgressBlock:(void(^)(id json))downloadProgressBlock errorBlock:(void(^)(NSError *error))errorBlock { [_oauth fetchResource:resource HTTPMethod:@"GET" baseURLString:baseURLString parameters:parameters - progressBlock:^(id request, id response) { - if(progressBlock) progressBlock(response); - } successBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:^(id request, id response) { + if(downloadProgressBlock) downloadProgressBlock(response); + } successBlock:nil errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { errorBlock(error); }]; @@ -345,11 +354,12 @@ - (void)getAPIResource:(NSString *)resource [self getResource:resource baseURLString:kBaseURLStringAPI parameters:parameters - progressBlock:progressBlock +downloadProgressBlock:progressBlock successBlock:successBlock errorBlock:errorBlock]; } +// convenience - (void)getAPIResource:(NSString *)resource parameters:(NSDictionary *)parameters successBlock:(void(^)(NSDictionary *rateLimits, id json))successBlock @@ -358,13 +368,14 @@ - (void)getAPIResource:(NSString *)resource [self getResource:resource baseURLString:kBaseURLStringAPI parameters:parameters - progressBlock:nil +downloadProgressBlock:nil successBlock:successBlock errorBlock:errorBlock]; } - (void)postAPIResource:(NSString *)resource parameters:(NSDictionary *)parameters + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock progressBlock:(void(^)(id json))progressBlock successBlock:(void(^)(NSDictionary *rateLimits, id json))successBlock errorBlock:(void(^)(NSError *error))errorBlock { @@ -372,11 +383,13 @@ - (void)postAPIResource:(NSString *)resource [self postResource:resource baseURLString:kBaseURLStringAPI parameters:parameters - progressBlock:progressBlock + uploadProgressBlock:uploadProgressBlock + downloadProgressBlock:progressBlock successBlock:successBlock errorBlock:errorBlock]; } +// convenience - (void)postAPIResource:(NSString *)resource parameters:(NSDictionary *)parameters successBlock:(void(^)(NSDictionary *rateLimits, id json))successBlock @@ -385,7 +398,8 @@ - (void)postAPIResource:(NSString *)resource [self postResource:resource baseURLString:kBaseURLStringAPI parameters:parameters - progressBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:nil successBlock:successBlock errorBlock:errorBlock]; } @@ -425,9 +439,9 @@ - (void)profileImageFor:(NSString *)screenName errorBlock:(void(^)(NSError *error))errorBlock { - [self getUserInformationFor:screenName - successBlock:^(NSDictionary *response) { - NSString *imageURLString = [response objectForKey:@"profile_image_url"]; + [self getUserInformationFor:screenName + successBlock:^(NSDictionary *response) { + NSString *imageURLString = [response objectForKey:@"profile_image_url"]; __block STHTTPRequest *r = [STHTTPRequest requestWithURLString:imageURLString]; __weak STHTTPRequest *wr = r; @@ -447,9 +461,9 @@ - (void)profileImageFor:(NSString *)screenName r.errorBlock = ^(NSError *error) { errorBlock(error); }; - } errorBlock:^(NSError *error) { - errorBlock(error); - }]; + } errorBlock:^(NSError *error) { + errorBlock(error); + }]; } #pragma mark Timelines @@ -480,9 +494,9 @@ - (void)getStatusesMentionTimelineWithCount:(NSString *)count } - (void)getMentionsTimelineSinceID:(NSString *)sinceID - count:(NSUInteger)count - successBlock:(void(^)(NSArray *statuses))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { + count:(NSUInteger)count + successBlock:(void(^)(NSArray *statuses))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { [self getStatusesMentionTimelineWithCount:[@(count) description] sinceID:nil @@ -619,7 +633,7 @@ - (void)getTimeline:(NSString *)timeline - (void)getUserTimelineWithScreenName:(NSString *)screenName sinceID:(NSString *)sinceID maxID:(NSString *)maxID - count:(NSUInteger)count + count:(NSUInteger)count successBlock:(void(^)(NSArray *statuses))successBlock errorBlock:(void(^)(NSError *error))errorBlock { @@ -640,11 +654,11 @@ - (void)getUserTimelineWithScreenName:(NSString *)screenName } - (void)getUserTimelineWithScreenName:(NSString *)screenName - count:(NSUInteger)count + count:(NSUInteger)count successBlock:(void(^)(NSArray *statuses))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getUserTimelineWithScreenName:screenName + [self getUserTimelineWithScreenName:screenName sinceID:nil maxID:nil count:count @@ -835,6 +849,7 @@ - (void)postStatusUpdate:(NSString *)status longitude:(NSString *)longitude placeID:(NSString *)placeID displayCoordinates:(NSNumber *)displayCoordinates + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock successBlock:(void(^)(NSDictionary *status))successBlock errorBlock:(void(^)(NSError *error))errorBlock { @@ -852,11 +867,14 @@ - (void)postStatusUpdate:(NSString *)status md[@"media[]"] = [mediaDataArray objectAtIndex:0]; md[kSTPOSTDataKey] = @"media[]"; - [self postAPIResource:@"statuses/update_with_media.json" parameters:md successBlock:^(NSDictionary *rateLimits, id response) { - successBlock(response); - } errorBlock:^(NSError *error) { - errorBlock(error); - }]; + [self postResource:@"statuses/update_with_media.json" + baseURLString:kBaseURLStringAPI + parameters:md + uploadProgressBlock:uploadProgressBlock + downloadProgressBlock:nil + successBlock:^(NSDictionary *rateLimits, id response) { + successBlock(response); + } errorBlock:errorBlock]; } - (void)postStatusUpdate:(NSString *)status @@ -865,6 +883,7 @@ - (void)postStatusUpdate:(NSString *)status placeID:(NSString *)placeID latitude:(NSString *)latitude longitude:(NSString *)longitude + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock successBlock:(void(^)(NSDictionary *status))successBlock errorBlock:(void(^)(NSError *error))errorBlock { @@ -884,6 +903,7 @@ - (void)postStatusUpdate:(NSString *)status longitude:longitude placeID:placeID displayCoordinates:@(YES) + uploadProgressBlock:uploadProgressBlock successBlock:^(NSDictionary *status) { successBlock(status); } errorBlock:^(NSError *error) { @@ -1076,8 +1096,8 @@ - (void)getSearchTweetsWithQuery:(NSString *)q maxID:(NSString *)maxID // eg. "54321" includeEntities:(NSNumber *)includeEntities callback:(NSString *)callback // eg. "processTweets" - successBlock:(void(^)(NSDictionary *searchMetadata, NSArray *statuses))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { + successBlock:(void(^)(NSDictionary *searchMetadata, NSArray *statuses))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { NSMutableDictionary *md = [NSMutableDictionary dictionary]; @@ -1108,8 +1128,8 @@ - (void)getSearchTweetsWithQuery:(NSString *)q } - (void)getSearchTweetsWithQuery:(NSString *)q - successBlock:(void(^)(NSDictionary *searchMetadata, NSArray *statuses))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { + successBlock:(void(^)(NSDictionary *searchMetadata, NSArray *statuses))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { [self getSearchTweetsWithQuery:q geocode:nil @@ -1164,23 +1184,24 @@ - (id)postStatusesFilterUserIDs:(NSArray *)userIDs return [self postResource:@"statuses/filter.json" baseURLString:kBaseURLStringStream parameters:md - progressBlock:^(id json) { - - NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; - if(stallWarning && stallWarningBlock) { - stallWarningBlock([stallWarning valueForKey:@"code"], - [stallWarning valueForKey:@"message"], - [[stallWarning valueForKey:@"percent_full"] integerValue]); - } else { - progressBlock(json); - } - - } successBlock:^(NSDictionary *rateLimits, id response) { - // reaching successBlock for a stream request is an error - errorBlock(response); - } errorBlock:^(NSError *error) { - errorBlock(error); - }]; + uploadProgressBlock:nil + downloadProgressBlock:^(id json) { + + NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; + if(stallWarning && stallWarningBlock) { + stallWarningBlock([stallWarning valueForKey:@"code"], + [stallWarning valueForKey:@"message"], + [[stallWarning valueForKey:@"percent_full"] integerValue]); + } else { + progressBlock(json); + } + + } successBlock:^(NSDictionary *rateLimits, id response) { + // reaching successBlock for a stream request is an error + errorBlock(response); + } errorBlock:^(NSError *error) { + errorBlock(error); + }]; } // convenience @@ -1214,23 +1235,23 @@ - (id)getStatusesSampleDelimited:(NSNumber *)delimited return [self getResource:@"statuses/sample.json" baseURLString:kBaseURLStringStream parameters:md - progressBlock:^(id json) { - - NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; - if(stallWarning && stallWarningBlock) { - stallWarningBlock([stallWarning valueForKey:@"code"], - [stallWarning valueForKey:@"message"], - [[stallWarning valueForKey:@"percent_full"] integerValue]); - } else { - progressBlock(json); - } - - } successBlock:^(NSDictionary *rateLimits, id json) { - // reaching successBlock for a stream request is an error - errorBlock(json); - } errorBlock:^(NSError *error) { - errorBlock(error); - }]; + downloadProgressBlock:^(id json) { + + NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; + if(stallWarning && stallWarningBlock) { + stallWarningBlock([stallWarning valueForKey:@"code"], + [stallWarning valueForKey:@"message"], + [[stallWarning valueForKey:@"percent_full"] integerValue]); + } else { + progressBlock(json); + } + + } successBlock:^(NSDictionary *rateLimits, id json) { + // reaching successBlock for a stream request is an error + errorBlock(json); + } errorBlock:^(NSError *error) { + errorBlock(error); + }]; } // GET statuses/firehose @@ -1249,23 +1270,23 @@ - (id)getStatusesFirehoseWithCount:(NSString *)count return [self getResource:@"statuses/firehose.json" baseURLString:kBaseURLStringStream parameters:md - progressBlock:^(id json) { - - NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; - if(stallWarning && stallWarningBlock) { - stallWarningBlock([stallWarning valueForKey:@"code"], - [stallWarning valueForKey:@"message"], - [[stallWarning valueForKey:@"percent_full"] integerValue]); - } else { - progressBlock(json); - } - - } successBlock:^(NSDictionary *rateLimits, id json) { - // reaching successBlock for a stream request is an error - errorBlock(json); - } errorBlock:^(NSError *error) { - errorBlock(error); - }]; + downloadProgressBlock:^(id json) { + + NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; + if(stallWarning && stallWarningBlock) { + stallWarningBlock([stallWarning valueForKey:@"code"], + [stallWarning valueForKey:@"message"], + [[stallWarning valueForKey:@"percent_full"] integerValue]); + } else { + progressBlock(json); + } + + } successBlock:^(NSDictionary *rateLimits, id json) { + // reaching successBlock for a stream request is an error + errorBlock(json); + } errorBlock:^(NSError *error) { + errorBlock(error); + }]; } // GET user @@ -1295,23 +1316,23 @@ - (id)getUserStreamDelimited:(NSNumber *)delimited return [self getResource:@"user.json" baseURLString:kBaseURLStringUserStream parameters:md - progressBlock:^(id json) { - - NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; - if(stallWarning && stallWarningBlock) { - stallWarningBlock([stallWarning valueForKey:@"code"], - [stallWarning valueForKey:@"message"], - [[stallWarning valueForKey:@"percent_full"] integerValue]); - } else { - progressBlock(json); - } - - } successBlock:^(NSDictionary *rateLimits, id json) { - // reaching successBlock for a stream request is an error - errorBlock(json); - } errorBlock:^(NSError *error) { - errorBlock(error); - }]; + downloadProgressBlock:^(id json) { + + NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; + if(stallWarning && stallWarningBlock) { + stallWarningBlock([stallWarning valueForKey:@"code"], + [stallWarning valueForKey:@"message"], + [[stallWarning valueForKey:@"percent_full"] integerValue]); + } else { + progressBlock(json); + } + + } successBlock:^(NSDictionary *rateLimits, id json) { + // reaching successBlock for a stream request is an error + errorBlock(json); + } errorBlock:^(NSError *error) { + errorBlock(error); + }]; } // GET site @@ -1337,23 +1358,23 @@ - (id)getSiteStreamForUserIDs:(NSArray *)userIDs return [self getResource:@"site.json" baseURLString:kBaseURLStringSiteStream parameters:md - progressBlock:^(id json) { - - NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; - if(stallWarning && stallWarningBlock) { - stallWarningBlock([stallWarning valueForKey:@"code"], - [stallWarning valueForKey:@"message"], - [[stallWarning valueForKey:@"percent_full"] integerValue]); - } else { - progressBlock(json); - } - - } successBlock:^(NSDictionary *rateLimits, id json) { - // reaching successBlock for a stream request is an error - errorBlock(json); - } errorBlock:^(NSError *error) { - errorBlock(error); - }]; + downloadProgressBlock:^(id json) { + + NSDictionary *stallWarning = [[self class] stallWarningDictionaryFromJSON:json]; + if(stallWarning && stallWarningBlock) { + stallWarningBlock([stallWarning valueForKey:@"code"], + [stallWarning valueForKey:@"message"], + [[stallWarning valueForKey:@"percent_full"] integerValue]); + } else { + progressBlock(json); + } + + } successBlock:^(NSDictionary *rateLimits, id json) { + // reaching successBlock for a stream request is an error + errorBlock(json); + } errorBlock:^(NSError *error) { + errorBlock(error); + }]; } #pragma mark Direct Messages @@ -1382,9 +1403,9 @@ - (void)getDirectMessagesSinceID:(NSString *)sinceID // convenience - (void)getDirectMessagesSinceID:(NSString *)sinceID - count:(NSUInteger)count - successBlock:(void(^)(NSArray *messages))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { + count:(NSUInteger)count + successBlock:(void(^)(NSArray *messages))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { NSString *countString = count > 0 ? [@(count) description] : nil; @@ -1438,10 +1459,10 @@ - (void)getDirectMessagesShowWithID:(NSString *)messageID - (void)postDestroyDirectMessageWithID:(NSString *)messageID includeEntities:(NSNumber *)includeEntities - successBlock:(void(^)(NSDictionary *message))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { + successBlock:(void(^)(NSDictionary *message))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { - NSMutableDictionary *md = [NSMutableDictionary dictionary]; + NSMutableDictionary *md = [NSMutableDictionary dictionary]; md[@"id"] = messageID; if(includeEntities) md[@"include_entities"] = [includeEntities boolValue] ? @"1" : @"0"; @@ -1456,7 +1477,7 @@ - (void)postDirectMessage:(NSString *)status to:(NSString *)screenName successBlock:(void(^)(NSDictionary *message))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - NSMutableDictionary *md = [NSMutableDictionary dictionaryWithObject:status forKey:@"text"]; + NSMutableDictionary *md = [NSMutableDictionary dictionaryWithObject:status forKey:@"text"]; [md setObject:screenName forKey:@"screen_name"]; [self postAPIResource:@"direct_messages/new.json" parameters:md successBlock:^(NSDictionary *rateLimits, id response) { @@ -1516,7 +1537,7 @@ - (void)getFriendsIDsForUserID:(NSString *)userID } - (void)getFriendsIDsForScreenName:(NSString *)screenName - successBlock:(void(^)(NSArray *friends))successBlock + successBlock:(void(^)(NSArray *friends))successBlock errorBlock:(void(^)(NSError *error))errorBlock { [self getFriendsIDsForUserID:nil @@ -1564,7 +1585,7 @@ - (void)getFollowersIDsForUserID:(NSString *)userID } - (void)getFollowersIDsForScreenName:(NSString *)screenName - successBlock:(void(^)(NSArray *followers))successBlock + successBlock:(void(^)(NSArray *followers))successBlock errorBlock:(void(^)(NSError *error))errorBlock { [self getFollowersIDsForUserID:nil @@ -1666,8 +1687,8 @@ - (void)postFriendshipsCreateForScreenName:(NSString *)screenName } - (void)postFollow:(NSString *)screenName - successBlock:(void(^)(NSDictionary *user))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { + successBlock:(void(^)(NSDictionary *user))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { [self postFriendshipsCreateForScreenName:screenName orUserID:nil successBlock:^(NSDictionary *user) { successBlock(user); @@ -1695,9 +1716,9 @@ - (void)postFriendshipsDestroyScreenName:(NSString *)screenName } - (void)postUnfollow:(NSString *)screenName - successBlock:(void(^)(NSDictionary *user))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { - + successBlock:(void(^)(NSDictionary *user))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { + [self postFriendshipsDestroyScreenName:screenName orUserID:nil successBlock:^(NSDictionary *user) { successBlock(user); } errorBlock:^(NSError *error) { @@ -1816,7 +1837,7 @@ - (void)getFriendsListForUserID:(NSString *)userID } - (void)getFriendsForScreenName:(NSString *)screenName - successBlock:(void(^)(NSArray *friends))successBlock + successBlock:(void(^)(NSArray *friends))successBlock errorBlock:(void(^)(NSError *error))errorBlock { [self getFriendsListForUserID:nil @@ -1867,10 +1888,10 @@ - (void)getFollowersListForUserID:(NSString *)userID // convenience - (void)getFollowersForScreenName:(NSString *)screenName - successBlock:(void(^)(NSArray *followers))successBlock + successBlock:(void(^)(NSArray *followers))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getFollowersListForUserID:nil + [self getFollowersListForUserID:nil orScreenName:screenName cursor:nil skipStatus:nil @@ -1993,7 +2014,7 @@ - (void)postAccountUpdateProfileWithName:(NSString *)name - (void)postUpdateProfile:(NSDictionary *)profileData successBlock:(void(^)(NSDictionary *myInfo))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self postAPIResource:@"account/update_profile.json" parameters:profileData successBlock:^(NSDictionary *rateLimits, id response) { + [self postAPIResource:@"account/update_profile.json" parameters:profileData successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -2222,8 +2243,8 @@ - (void)getUsersShowForUserID:(NSString *)userID } - (void)getUserInformationFor:(NSString *)screenName - successBlock:(void(^)(NSDictionary *user))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { + successBlock:(void(^)(NSDictionary *user))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { [self getUsersShowForUserID:nil orScreenName:screenName includeEntities:nil successBlock:^(NSDictionary *user) { successBlock(user); @@ -3740,7 +3761,7 @@ - (void)postUsersReportSpamForScreenName:(NSString *)screenName // GET help/configuration - (void)getHelpConfigurationWithSuccessBlock:(void(^)(NSDictionary *currentConfiguration))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"help/configuration.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"help/configuration.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3750,7 +3771,7 @@ - (void)getHelpConfigurationWithSuccessBlock:(void(^)(NSDictionary *currentConfi // GET help/languages - (void)getHelpLanguagesWithSuccessBlock:(void (^)(NSArray *languages))successBlock errorBlock:(void (^)(NSError *))errorBlock { - [self getAPIResource:@"help/languages.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"help/languages.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3760,7 +3781,7 @@ - (void)getHelpLanguagesWithSuccessBlock:(void (^)(NSArray *languages))successBl // GET help/privacy - (void)getHelpPrivacyWithSuccessBlock:(void(^)(NSString *tos))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"help/privacy.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"help/privacy.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { successBlock([response valueForKey:@"privacy"]); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3770,7 +3791,7 @@ - (void)getHelpPrivacyWithSuccessBlock:(void(^)(NSString *tos))successBlock // GET help/tos - (void)getHelpTermsOfServiceWithSuccessBlock:(void(^)(NSString *tos))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"help/tos.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"help/tos.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { successBlock([response valueForKey:@"tos"]); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3779,12 +3800,12 @@ - (void)getHelpTermsOfServiceWithSuccessBlock:(void(^)(NSString *tos))successBlo // GET application/rate_limit_status - (void)getRateLimitsForResources:(NSArray *)resources // eg. statuses,friends,trends,help - successBlock:(void(^)(NSDictionary *rateLimits))successBlock - errorBlock:(void(^)(NSError *error))errorBlock { - NSDictionary *d = nil; - if (resources) + successBlock:(void(^)(NSDictionary *rateLimits))successBlock + errorBlock:(void(^)(NSError *error))errorBlock { + NSDictionary *d = nil; + if (resources) d = @{ @"resources" : [resources componentsJoinedByString:@","] }; - [self getAPIResource:@"application/rate_limit_status.json" parameters:d successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"application/rate_limit_status.json" parameters:d successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3816,7 +3837,7 @@ - (void)_getActivityAboutMeSinceID:(NSString *)sinceID if(modelVersion) md[@"model_version"] = [modelVersion boolValue] ? @"true" : @"false"; if(sendErrorCodes) md[@"send_error_codes"] = [sendErrorCodes boolValue] ? @"1" : @"0"; - [self getAPIResource:@"activity/about_me.json" parameters:md successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"activity/about_me.json" parameters:md successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3846,7 +3867,7 @@ - (void)_getActivityByFriendsSinceID:(NSString *)sinceID if(latestResults) md[@"latest_results"] = [latestResults boolValue] ? @"true" : @"false"; if(sendErrorCodes) md[@"send_error_codes"] = [sendErrorCodes boolValue] ? @"1" : @"0"; - [self getAPIResource:@"activity/by_friends.json" parameters:md successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"activity/by_friends.json" parameters:md successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3860,7 +3881,7 @@ - (void)_getStatusesActivitySummaryForStatusID:(NSString *)statusID NSString *resource = [NSString stringWithFormat:@"statuses/%@/activity/summary.json", statusID]; - [self getAPIResource:resource parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:resource parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { NSArray *favoriters = [response valueForKey:@"favoriters"]; NSArray *repliers = [response valueForKey:@"repliers"]; @@ -3884,7 +3905,7 @@ - (void)_getConversationShowForStatusID:(NSString *)statusID NSDictionary *d = @{@"id":statusID}; - [self getAPIResource:@"conversation/show.json" parameters:d successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"conversation/show.json" parameters:d successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3895,7 +3916,7 @@ - (void)_getConversationShowForStatusID:(NSString *)statusID - (void)_getDiscoverHighlightWithSuccessBlock:(void(^)(NSDictionary *metadata, NSArray *modules))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"discover/highlight.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"discover/highlight.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { NSDictionary *metadata = [response valueForKey:@"metadata"]; NSArray *modules = [response valueForKey:@"modules"]; @@ -3910,7 +3931,7 @@ - (void)_getDiscoverHighlightWithSuccessBlock:(void(^)(NSDictionary *metadata, N - (void)_getDiscoverUniversalWithSuccessBlock:(void(^)(NSDictionary *metadata, NSArray *modules))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"discover/universal.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"discover/universal.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { NSDictionary *metadata = [response valueForKey:@"metadata"]; NSArray *modules = [response valueForKey:@"modules"]; @@ -3925,7 +3946,7 @@ - (void)_getDiscoverUniversalWithSuccessBlock:(void(^)(NSDictionary *metadata, N - (void)_getMediaTimelineWithSuccessBlock:(void(^)(NSArray *statuses))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"statuses/media_timeline.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"statuses/media_timeline.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { @@ -3937,7 +3958,7 @@ - (void)_getMediaTimelineWithSuccessBlock:(void(^)(NSArray *statuses))successBlo - (void)_getUsersRecommendationsWithSuccessBlock:(void(^)(NSArray *recommendations))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"users/recommendations.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"users/recommendations.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3948,7 +3969,7 @@ - (void)_getUsersRecommendationsWithSuccessBlock:(void(^)(NSArray *recommendatio - (void)_getTimelineHomeWithSuccessBlock:(void(^)(id response))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"timeline/home.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"timeline/home.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3969,7 +3990,7 @@ - (void)_getStatusesMentionsTimelineWithCount:(NSString *)count if(includeEntities) md[@"include_entities"] = [includeEntities boolValue] ? @"true" : @"false"; if(includeMyRetweet) md[@"include_my_retweet"] = [includeMyRetweet boolValue] ? @"true" : @"false"; - [self getAPIResource:@"statuses/mentions_timeline.json" parameters:md successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"statuses/mentions_timeline.json" parameters:md successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -3980,7 +4001,7 @@ - (void)_getStatusesMentionsTimelineWithCount:(NSString *)count - (void)_getTrendsAvailableWithSuccessBlock:(void(^)(NSArray *places))successBlock errorBlock:(void(^)(NSError *error))errorBlock { - [self getAPIResource:@"trends/available.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { + [self getAPIResource:@"trends/available.json" parameters:nil successBlock:^(NSDictionary *rateLimits, id response) { successBlock(response); } errorBlock:^(NSError *error) { errorBlock(error); @@ -4037,13 +4058,14 @@ - (void)_postAccountGenerateWithADC:(NSString *)adc [self postResource:@"account/generate.json" baseURLString:@"https://api.twitter.com/1" parameters:md - progressBlock:^(id json) { - // - } successBlock:^(NSDictionary *rateLimits, id response) { - successBlock(response); - } errorBlock:^(NSError *error) { - errorBlock(error); - }]; + uploadProgressBlock:nil + downloadProgressBlock:^(id json) { + // + } successBlock:^(NSDictionary *rateLimits, id response) { + successBlock(response); + } errorBlock:^(NSError *error) { + errorBlock(error); + }]; } @end diff --git a/STTwitter/STTwitterAppOnly.m b/STTwitter/STTwitterAppOnly.m index ade9299..a08af74 100644 --- a/STTwitter/STTwitterAppOnly.m +++ b/STTwitter/STTwitterAppOnly.m @@ -60,7 +60,8 @@ - (void)invalidateBearerTokenWithSuccessBlock:(void(^)())successBlock baseURLString:@"https://api.twitter.com" parameters:@{ @"access_token" : _bearerToken } useBasicAuth:YES - progressBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:nil successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { if([json isKindOfClass:[NSDictionary class]] == NO) { @@ -115,7 +116,8 @@ - (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBl baseURLString:@"https://api.twitter.com" parameters:@{ @"grant_type" : @"client_credentials" } useBasicAuth:YES - progressBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:nil successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { NSString *tokenType = [json valueForKey:@"token_type"]; @@ -168,13 +170,14 @@ - (STHTTPRequest *)getResource:(NSString *)resource // NSString *requestID = [[NSUUID UUID] UUIDString]; __block STHTTPRequest *r = [STHTTPRequest twitterRequestWithURLString:urlString - stTwitterProgressBlock:^(id json) { - if(progressBlock) progressBlock(r, json); - } stTwitterSuccessBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { - successBlock(r, requestHeaders, responseHeaders, json); - } stTwitterErrorBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { - errorBlock(r, requestHeaders, responseHeaders, error); - }]; + stTwitterUploadProgressBlock:nil + stTwitterDownloadProgressBlock:^(id json) { + if(progressBlock) progressBlock(r, json); + } stTwitterSuccessBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { + successBlock(r, requestHeaders, responseHeaders, json); + } stTwitterErrorBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { + errorBlock(r, requestHeaders, responseHeaders, error); + }]; if(_bearerToken) { [r setHeaderWithName:@"Authorization" value:[NSString stringWithFormat:@"Bearer %@", _bearerToken]]; } @@ -188,7 +191,8 @@ - (id)fetchResource:(NSString *)resource HTTPMethod:(NSString *)HTTPMethod baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)params - progressBlock:(void(^)(id r, id json))progressBlock +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id r, id json))downloadProgressBlock successBlock:(void(^)(id r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json))successBlock errorBlock:(void(^)(id r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { @@ -201,7 +205,7 @@ - (id)fetchResource:(NSString *)resource return [self getResource:resource baseURLString:baseURLString parameters:params - progressBlock:progressBlock + progressBlock:downloadProgressBlock successBlock:successBlock errorBlock:errorBlock]; @@ -210,7 +214,8 @@ - (id)fetchResource:(NSString *)resource return [self postResource:resource baseURLString:baseURLString parameters:params - progressBlock:progressBlock + uploadProgressBlock:uploadProgressBlock + downloadProgressBlock:downloadProgressBlock successBlock:successBlock errorBlock:errorBlock]; @@ -224,20 +229,22 @@ - (id)postResource:(NSString *)resource baseURLString:(NSString *)baseURLString // no trailing slash parameters:(NSDictionary *)params useBasicAuth:(BOOL)useBasicAuth - progressBlock:(void(^)(id request, id json))progressBlock +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id request, id json))downloadProgressBlock successBlock:(void(^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json))successBlock errorBlock:(void(^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, resource]; __block STHTTPRequest *r = [STHTTPRequest twitterRequestWithURLString:urlString - stTwitterProgressBlock:^(id json) { - if(progressBlock) progressBlock(r, json); - } stTwitterSuccessBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { - successBlock(r, requestHeaders, responseHeaders, json); - } stTwitterErrorBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { - errorBlock(r, requestHeaders, responseHeaders, error); - }]; + stTwitterUploadProgressBlock:uploadProgressBlock + stTwitterDownloadProgressBlock:^(id json) { + if(downloadProgressBlock) downloadProgressBlock(r, json); + } stTwitterSuccessBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { + successBlock(r, requestHeaders, responseHeaders, json); + } stTwitterErrorBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { + errorBlock(r, requestHeaders, responseHeaders, error); + }]; r.POSTDictionary = params; @@ -264,7 +271,8 @@ - (id)postResource:(NSString *)resource - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)params - progressBlock:(void(^)(id r, id json))progressBlock + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock + downloadProgressBlock:(void(^)(id r, id json))downloadProgressBlock successBlock:(void(^)(id r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json))successBlock errorBlock:(void(^)(id r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { @@ -272,7 +280,8 @@ - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:baseURLString parameters:params useBasicAuth:NO - progressBlock:progressBlock + uploadProgressBlock:uploadProgressBlock + downloadProgressBlock:downloadProgressBlock successBlock:successBlock errorBlock:errorBlock]; } diff --git a/STTwitter/STTwitterOAuth.h b/STTwitter/STTwitterOAuth.h index 52e7931..4f24659 100644 --- a/STTwitter/STTwitterOAuth.h +++ b/STTwitter/STTwitterOAuth.h @@ -68,13 +68,13 @@ NS_ENUM(NSUInteger, STTwitterOAuthErrorCode) { @end @interface NSString (STTwitterOAuth) -+ (NSString *)random32Characters; -- (NSString *)signHmacSHA1WithKey:(NSString *)key; -- (NSDictionary *)parametersDictionary; -- (NSString *)urlEncodedString; ++ (NSString *)st_random32Characters; +- (NSString *)st_signHmacSHA1WithKey:(NSString *)key; +- (NSDictionary *)st_parametersDictionary; +- (NSString *)st_urlEncodedString; @end @interface NSURL (STTwitterOAuth) -- (NSString *)normalizedForOauthSignatureString; -- (NSArray *)rawGetParametersDictionaries; +- (NSString *)st_normalizedForOauthSignatureString; +- (NSArray *)st_rawGetParametersDictionaries; @end diff --git a/STTwitter/STTwitterOAuth.m b/STTwitter/STTwitterOAuth.m index aad5cdd..4e5ef1c 100644 --- a/STTwitter/STTwitterOAuth.m +++ b/STTwitter/STTwitterOAuth.m @@ -97,8 +97,8 @@ + (NSArray *)encodedParametersDictionaries:(NSArray *)parameters { NSString *key = [[d allKeys] lastObject]; NSString *value = [[d allValues] lastObject]; - NSString *encodedKey = [key urlEncodedString]; - NSString *encodedValue = [value urlEncodedString]; + NSString *encodedKey = [key st_urlEncodedString]; + NSString *encodedValue = [value st_urlEncodedString]; [encodedParameters addObject:@{encodedKey : encodedValue}]; } @@ -159,7 +159,7 @@ - (NSString *)loginTypeDescription { - (NSString *)oauthNonce { if(_testOauthNonce) return _testOauthNonce; - return [NSString random32Characters]; + return [NSString st_random32Characters]; } + (NSString *)signatureBaseStringWithHTTPMethod:(NSString *)httpMethod url:(NSURL *)url allParametersUnsorted:(NSArray *)parameters { @@ -186,8 +186,8 @@ + (NSString *)signatureBaseStringWithHTTPMethod:(NSString *)httpMethod url:(NSUR NSString *signatureBaseString = [NSString stringWithFormat:@"%@&%@&%@", [httpMethod uppercaseString], - [[url normalizedForOauthSignatureString] urlEncodedString], - [encodedParametersString urlEncodedString]]; + [[url st_normalizedForOauthSignatureString] st_urlEncodedString], + [encodedParametersString st_urlEncodedString]]; return signatureBaseString; } @@ -204,8 +204,8 @@ + (NSString *)oauthSignatureWithHTTPMethod:(NSString *)httpMethod url:(NSURL *)u Note that there are some flows, such as when obtaining a request token, where the token secret is not yet known. In this case, the signing key should consist of the percent encoded consumer secret followed by an ampersand character '&'. */ - NSString *encodedConsumerSecret = [consumerSecret urlEncodedString]; - NSString *encodedTokenSecret = [tokenSecret urlEncodedString]; + NSString *encodedConsumerSecret = [consumerSecret st_urlEncodedString]; + NSString *encodedTokenSecret = [tokenSecret st_urlEncodedString]; NSString *signingKey = [NSString stringWithFormat:@"%@&", encodedConsumerSecret]; @@ -213,7 +213,7 @@ + (NSString *)oauthSignatureWithHTTPMethod:(NSString *)httpMethod url:(NSURL *)u signingKey = [signingKey stringByAppendingString:encodedTokenSecret]; } - NSString *oauthSignature = [signatureBaseString signHmacSHA1WithKey:signingKey]; + NSString *oauthSignature = [signatureBaseString st_signHmacSHA1WithKey:signingKey]; return oauthSignature; } @@ -261,10 +261,11 @@ - (void)postTokenRequest:(void(^)(NSURL *url, NSString *oauthToken))successBlock baseURLString:@"https://api.twitter.com" parameters:@{} oauthCallback:theOAuthCallback - progressBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:nil successBlock:^(STHTTPRequest *r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id body) { - NSDictionary *d = [body parametersDictionary]; + NSDictionary *d = [body st_parametersDictionary]; NSString *s = [NSString stringWithFormat:@"https://api.twitter.com/oauth/authorize?%@", body]; @@ -286,7 +287,8 @@ - (void)postReverseOAuthTokenRequest:(void(^)(NSString *authenticationHeader))su baseURLString:@"https://api.twitter.com" parameters:@{@"x_auth_mode" : @"reverse_auth"} oauthCallback:nil - progressBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:nil successBlock:^(STHTTPRequest *r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id body) { successBlock(body); @@ -309,7 +311,7 @@ - (void)postXAuthAccessTokenRequestWithUsername:(NSString *)username baseURLString:@"https://api.twitter.com" parameters:d successBlock:^(STHTTPRequest *request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSString *body) { - NSDictionary *dict = [body parametersDictionary]; + NSDictionary *dict = [body st_parametersDictionary]; // https://api.twitter.com/oauth/authorize?oauth_token=OAUTH_TOKEN&oauth_token_secret=OAUTH_TOKEN_SECRET&user_id=USER_ID&screen_name=SCREEN_NAME @@ -339,7 +341,7 @@ - (void)postAccessTokenRequestWithPIN:(NSString *)pin baseURLString:@"https://api.twitter.com" parameters:d successBlock:^(STHTTPRequest *request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSString *body) { - NSDictionary *dict = [body parametersDictionary]; + NSDictionary *dict = [body st_parametersDictionary]; // https://api.twitter.com/oauth/authorize?oauth_token=OAUTH_TOKEN&oauth_token_secret=OAUTH_TOKEN_SECRET&user_id=USER_ID&screen_name=SCREEN_NAME @@ -385,7 +387,7 @@ - (void)signRequest:(STHTTPRequest *)r isMediaUpload:(BOOL)isMediaUpload oauthCa // "In the HTTP request the parameters are URL encoded, but you should collect the raw values." // https://dev.twitter.com/docs/auth/creating-signature - NSMutableArray *oauthAndPOSTandGETParameters = [[r.url rawGetParametersDictionaries] mutableCopy]; + NSMutableArray *oauthAndPOSTandGETParameters = [[r.url st_rawGetParametersDictionaries] mutableCopy]; [oauthAndPOSTandGETParameters addObjectsFromArray:oauthAndPOSTParameters]; NSString *signature = [[self class] oauthSignatureWithHTTPMethod:httpMethod @@ -412,7 +414,7 @@ - (void)signRequest:(STHTTPRequest *)r { - (STHTTPRequest *)getResource:(NSString *)resource baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)params - progressBlock:(void (^)(STHTTPRequest *r, id json))progressBlock + downloadProgressBlock:(void (^)(STHTTPRequest *r, id json))downloadProgressBlock successBlock:(void (^)(STHTTPRequest *r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json))successBlock errorBlock:(void (^)(STHTTPRequest *r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { @@ -434,13 +436,14 @@ - (STHTTPRequest *)getResource:(NSString *)resource // __block NSString *requestID = [[NSUUID UUID] UUIDString]; __block STHTTPRequest *r = [STHTTPRequest twitterRequestWithURLString:urlString - stTwitterProgressBlock:^(id json) { - if(progressBlock) progressBlock(r, json); - } stTwitterSuccessBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { - successBlock(r, requestHeaders, responseHeaders, json); - } stTwitterErrorBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { - errorBlock(r, requestHeaders, responseHeaders, error); - }]; + stTwitterUploadProgressBlock:nil + stTwitterDownloadProgressBlock:^(id json) { + if(downloadProgressBlock) downloadProgressBlock(r, json); + } stTwitterSuccessBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { + successBlock(r, requestHeaders, responseHeaders, json); + } stTwitterErrorBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { + errorBlock(r, requestHeaders, responseHeaders, error); + }]; [self signRequest:r]; @@ -453,7 +456,8 @@ - (id)fetchResource:(NSString *)resource HTTPMethod:(NSString *)HTTPMethod baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)params - progressBlock:(void(^)(id r, id json))progressBlock +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id r, id json))downloadProgressBlock successBlock:(void(^)(id r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json))successBlock errorBlock:(void(^)(id r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { @@ -466,7 +470,7 @@ - (id)fetchResource:(NSString *)resource return [self getResource:resource baseURLString:baseURLString parameters:params - progressBlock:progressBlock + downloadProgressBlock:downloadProgressBlock successBlock:successBlock errorBlock:errorBlock]; @@ -475,7 +479,9 @@ - (id)fetchResource:(NSString *)resource return [self postResource:resource baseURLString:baseURLString parameters:params - progressBlock:progressBlock + oauthCallback:nil + uploadProgressBlock:uploadProgressBlock + downloadProgressBlock:downloadProgressBlock successBlock:successBlock errorBlock:errorBlock]; @@ -489,20 +495,22 @@ - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:(NSString *)baseURLString // no trailing slash parameters:(NSDictionary *)params oauthCallback:(NSString *)oauthCallback - progressBlock:(void(^)(STHTTPRequest *r, id json))progressBlock + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock + downloadProgressBlock:(void(^)(STHTTPRequest *r, id json))downloadProgressBlock successBlock:(void(^)(STHTTPRequest *r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response))successBlock errorBlock:(void(^)(STHTTPRequest *r, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, resource]; __block STHTTPRequest *r = [STHTTPRequest twitterRequestWithURLString:urlString - stTwitterProgressBlock:^(id json) { - if(progressBlock) progressBlock(r, json); - } stTwitterSuccessBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { - successBlock(r, requestHeaders, responseHeaders, json); - } stTwitterErrorBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { - errorBlock(r, requestHeaders, responseHeaders, error); - }]; + stTwitterUploadProgressBlock:uploadProgressBlock + stTwitterDownloadProgressBlock:^(id json) { + if(downloadProgressBlock) downloadProgressBlock(r, json); + } stTwitterSuccessBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, id json) { + successBlock(r, requestHeaders, responseHeaders, json); + } stTwitterErrorBlock:^(NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { + errorBlock(r, requestHeaders, responseHeaders, error); + }]; r.POSTDictionary = params; @@ -530,6 +538,7 @@ - (STHTTPRequest *)postResource:(NSString *)resource return r; } +// convenience - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:(NSString *)baseURLString // no trailing slash parameters:(NSDictionary *)params @@ -541,11 +550,13 @@ - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:baseURLString parameters:params oauthCallback:nil - progressBlock:progressBlock + uploadProgressBlock:nil + downloadProgressBlock:progressBlock successBlock:successBlock errorBlock:errorBlock]; } +// convenience - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:(NSString *)baseURLString // no trailing slash parameters:(NSDictionary *)params @@ -557,11 +568,13 @@ - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:baseURLString parameters:params oauthCallback:oauthCallback - progressBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:nil successBlock:successBlock errorBlock:errorBlock]; } +// convenience - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:(NSString *)baseURLString // no trailing slash parameters:(NSDictionary *)params @@ -572,7 +585,8 @@ - (STHTTPRequest *)postResource:(NSString *)resource baseURLString:baseURLString parameters:params oauthCallback:nil - progressBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:nil successBlock:successBlock errorBlock:errorBlock]; } @@ -581,7 +595,7 @@ - (STHTTPRequest *)postResource:(NSString *)resource @implementation NSURL (STTwitterOAuth) -- (NSArray *)rawGetParametersDictionaries { +- (NSArray *)st_rawGetParametersDictionaries { NSString *q = [self query]; @@ -600,7 +614,7 @@ - (NSArray *)rawGetParametersDictionaries { return ma; } -- (NSString *)normalizedForOauthSignatureString { +- (NSString *)st_normalizedForOauthSignatureString { return [NSString stringWithFormat:@"%@://%@%@", [self scheme], [self host], [self path]]; } @@ -608,22 +622,22 @@ - (NSString *)normalizedForOauthSignatureString { @implementation NSString (STTwitterOAuth) -+ (NSString *)randomString { ++ (NSString *)st_randomString { CFUUIDRef cfuuid = CFUUIDCreate (kCFAllocatorDefault); NSString *uuid = (__bridge_transfer NSString *)(CFUUIDCreateString (kCFAllocatorDefault, cfuuid)); CFRelease (cfuuid); return uuid; } -+ (NSString *)random32Characters { - NSString *randomString = [self randomString]; ++ (NSString *)st_random32Characters { + NSString *randomString = [self st_randomString]; NSAssert([randomString length] >= 32, @""); return [randomString substringToIndex:32]; } -- (NSString *)signHmacSHA1WithKey:(NSString *)key { +- (NSString *)st_signHmacSHA1WithKey:(NSString *)key { unsigned char buf[CC_SHA1_DIGEST_LENGTH]; CCHmac(kCCHmacAlgSHA1, [key UTF8String], [key length], [self UTF8String], [self length], buf); @@ -631,7 +645,7 @@ - (NSString *)signHmacSHA1WithKey:(NSString *)key { return [data base64Encoding]; } -- (NSDictionary *)parametersDictionary { +- (NSDictionary *)st_parametersDictionary { NSArray *parameters = [self componentsSeparatedByString:@"&"]; @@ -649,7 +663,7 @@ - (NSDictionary *)parametersDictionary { return md; } -- (NSString *)urlEncodedString { +- (NSString *)st_urlEncodedString { // https://dev.twitter.com/docs/auth/percent-encoding-parameters // http://tools.ietf.org/html/rfc3986#section-2.1 diff --git a/STTwitter/STTwitterOS.m b/STTwitter/STTwitterOS.m index efbdd2c..6e7ae89 100644 --- a/STTwitter/STTwitterOS.m +++ b/STTwitter/STTwitterOS.m @@ -152,6 +152,7 @@ - (id)fetchAPIResource:(NSString *)resource baseURLString:(NSString *)baseURLString httpMethod:(NSInteger)httpMethod parameters:(NSDictionary *)params + uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock // ignored completionBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response))completionBlock errorBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { @@ -275,7 +276,8 @@ - (id)fetchResource:(NSString *)resource HTTPMethod:(NSString *)HTTPMethod baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)params - progressBlock:(void (^)(id request, id response))progressBlock // TODO: handle progressBlock? +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock // ignored +downloadProgressBlock:(void (^)(id request, id response))progressBlock // FIXME: how to handle progressBlock? successBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response))successBlock errorBlock:(void (^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock { @@ -299,6 +301,7 @@ - (id)fetchResource:(NSString *)resource baseURLString:baseURLStringWithTrailingSlash httpMethod:slRequestMethod parameters:d + uploadProgressBlock:uploadProgressBlock completionBlock:successBlock errorBlock:errorBlock]; } @@ -368,7 +371,8 @@ - (void)postReverseAuthAccessTokenWithAuthenticationHeader:(NSString *)authentic HTTPMethod:@"POST" baseURLString:@"https://api.twitter.com" parameters:d - progressBlock:nil + uploadProgressBlock:nil + downloadProgressBlock:nil successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response) { NSDictionary *d = [[self class] parametersDictionaryFromAmpersandSeparatedParameterString:response]; diff --git a/STTwitter/STTwitterProtocol.h b/STTwitter/STTwitterProtocol.h index 7c0a389..3e1ae25 100644 --- a/STTwitter/STTwitterProtocol.h +++ b/STTwitter/STTwitterProtocol.h @@ -20,7 +20,8 @@ HTTPMethod:(NSString *)HTTPMethod baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)params - progressBlock:(void(^)(id request, id response))progressBlock +uploadProgressBlock:(void(^)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))uploadProgressBlock +downloadProgressBlock:(void(^)(id request, id response))progressBlock successBlock:(void(^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response))successBlock errorBlock:(void(^)(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error))errorBlock; diff --git a/demo_cli/clitter.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstate b/demo_cli/clitter.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstate index d1f8f4c42fd8350b99d5e970ed3f44178e0b2c7a..c6ff866b019e26585c14801d37c00a521243733f 100644 GIT binary patch delta 9899 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 delta 10078 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 diff --git a/demo_ios/STTwitterDemoIOS.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstate b/demo_ios/STTwitterDemoIOS.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstate index 6b02b145d38c11b4b9f7172be131abb80ee704ce..700b1d2f0b4c991a07efd49b9e06a25d6f3b44c0 100644 GIT binary patch delta 17829 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 delta 23287 zcmb4qcUTnH_cuE;yF0s6cImw?3n)mhHbAjqL+MC|rFWKIb}v;af(+eCvG)?YF&Z@{ zn%KLECibq;*kXC_?#5`oKm6nM@!{DyGoRD$Ip>~xXKup^v~e-os*MSb363*yjaJx3 zb!^k-@q1XZEHn?zM~l#6v>dHK0a}IDq4j7Z+Jv^E3(yX<6YWJ8qD#?b=pecrU5&0m zH=rBQt>`v%C%OyWhklD5M88LWK#!uw(Vx*%=xOvkdI7zHUPZ5?zoWO%+vt7t0r~`e zioQVqLf@co(GTcH48<@E$9Nbe!)Qzc)5LT!J_Db%nGx{Y%x2`5p%-aFn4Su zHVX5`d@w(30yYKn$7WzNu|O;c3&X;(C@dO_#}cq)ECtKJGO=7N4=coqurjP1lVbp@ z#p+Sb_KhJUB_->x3GKIee5yz1bdFXz+Pi-u=m&pHo`{PTsF=o*%Vuy zt-;n|>#_~mMr<>-Ia|!OVh?BAvK`otY*)4$+lxJtJ(lgw9?$k;2e4>ze9 zJA@s|4r3>>CF~@2GCQ9wWf!mu*+uMPb_u(bUB|9x&t=bJcd$FzUF>dl54)GWkiCe# zioKe>hP{@(j=i3}fxVl(hrO4*kNqusKl?lO0rt=AU)U$uC)uair`c!NXW7@;zq9{f z-(cTl-(ufp-(f#z%U-bmV!z}d9F&7`*c=Xr%fUHX9BqycN0+0=(dQU&3^~>u8_qD! zaE>j$7$d+a=JL(oE}auX9Z^^XBB4~=Nry;&JLy)vmQfnX|8~4$Ti{`b4|Fzxwc$8 z?g*|u*MaNEb>fcUj^%oDeYn2daoq7-KW+eb7I!u`kQ>8|<;HR2nR}RnY%g~o_gn5y z++*D1+>6{x+{@f6+&kR6+a2>+ucvMtl>#8Q+3$#rNai;Ro=8`1klB z{4jn5KaHQk&*Ind-|;{28~79aDgF%q6aRpJv21Uy5Y5zm-6j5nNT%d_Ko z^1OH>d82rfc$0Zkc>cT)UMMe&Cky9EcuBluUJ6gjE8rFKs(97B8eT1L0k4h6@RstH z@%niEybZjKyiL5#ynVcHdHZ?a@ec3~^1kOC;+^E3;+^K5;a%fh=l#z6gZG&Cg!h#9 zj`yDTf%lP5@JT+!*XJAX4f#fV8~!lU&fd70l$i0&9C9t^6U6*e1_l7@8I|G`}qU>LH;KGX8sobxBUJ5@A$|0 zKl6X#U*=!oU*+HB-{arsKj6ROzvjOo*aU~*65515VL+G=ri6&FBy5Ob#0bKka3$P` zQN(CM=0i*-W)QQ9P$G(mBa(?!BAdu3N{BK75LLunVjj^zv=I!^PIM5R#4@6f=qCn< zHN-aJ8)7@LgV;&zB6bsph{MDY;s@dsahf=pSpMNu>*poEk;D%P`}8i6V&h2AJh%%CUuLt zP2HjH(rPq|Mrf4AXg1BExin7mXg*ER8nhm5M4Qnz^e}oj?MS=PW9YH8FFlc-L{Ft> z(zEGcdJY{)$I@|h5nW7|(4}-4T~1ffm9&hO(}1p_=hE$T2i-|`(cSbSdKE2OO>dw# z(wpdQ^mp_j`Y8P){S$qR{)Ik8pQSI*ztexvH|U%6pY(J31^pNOl723xP;rDG&>+1l9r@fxW<0;3n`E_y{HorU+&Vf(3H~34%mHnjlM%EhrO|3n~Pa z0-2yzP$y^-Gz%CrLA#(suu!m6uw1ZKuvM^4@Qq-f;9J3d!4HC?f*%DxF}HZ$td)$I z-=mWQ(0qUv1GERAy#Vb8tYz^3*0cO57HjraXYxB84y|UtQ5em`9M0qZ7UJp`;5fb|-%-T?#y2o4Yu zAOe7B0z?NO1^_Vuhy_5b05TjPBLLzE5LbYV28bU(CIe&|Kmq^~1dvF8!~!G%AV~m8 z1xO~4A-MpN0;C)ua)8VQNE1NX0n!bSMF1H9$O?dL0LXTLd<&2R067eh9|7_+Ku!YW z3_vaboQ~O9L&1L0Ov5)P3E$5HtQba?{XY}tU?Hx>toh4 zc5b@(a}_~@IXT;bDRr|VUaJUNF~W69m9CF7V5Ye1ljwWZ4V@U_4tQfNqwk?dAS#+( zjBpy%(wiys&?mSmnn8?ks*?7bhaOIh`2FGg$PUMjkM+b|Ww@5boiV^uUqDCH17UUlKj zn7B@3R{K-j(f_{8De3>H+^4;Z7y9C_f*(Mn#*Z495$8B4VD3 zFyj-KQ9&dVFo7cGtLOnS!UhOVV@^-7BN!EJ_UADk&g__IhNlC34$rEnKJ~)<9@5uC2PLgD|CS`u1Pu58FU>d+z5D z)5lF!vWCS_pG2^QhQRD}6Zf&=qSuocUmkp;Um8X;CJ7v^fP2a^*b zCOlOSwX16J8pc0V2Op!Nb;3lpVRDQOG&AE;v+Te04SO6D7dqRGr^Z(!*b~_PYW9Qd ziR?-2$?PeB)eBe)0c#OpEgob~WlvMHW6uDrC4jY5IiL423#1hR4j-$=TrvzRbF?#a zck`^kLlnOpE6%8?ScbEszsfR(9Sd2;1J(dw4JuhKmpd`@!Yv(A*qI7cDm#sx&dva= zm4LMhuvP=snn89JI~$^M0c$N_ty7}bLq*D&un4h38C(8U9AH;LTn%7t1guR;+-7DS z#5Ay56qrVK6T6u`AF#Fp);7TU2C%jdvRl~;Acg^~9e}k{iP@!;RX)eUVKKX3fm^~} z%3j9q1FSuOwHL7V0oJ#J>;d*5#6f562dwXuIHj`~|43c?jqGg-+$Q#B_7*l2b`Y?> z2dqPYb$F2d4SPGpK}kmd>jx!{X_ZzoRFs9oLG}*{*!S#1?89t$`zOFU23W@d>*qoC zQTC4z1}}dBtP@JuNvLKeb2ZA$;T-$20(YK$fqjt;yQkBDbq27`0@k@f_7(P3i2Dt& z&I8s3CGMiqyp_=+hr8@YUp4PB`w7H71FXw{bw!E0%Djml?#O<{ey0GvX1`&-W&aIW zko7gdx(-;s53=90KR^)l$sd4qLka3*;tDF6Q!zRYJViS=$l-Gc4#}Ya>lR?$2CO@P zb$5^>;0V?1I2wR;53uelaSxPAw#8aG7;(%MIAe|p$CP6RSdRefFgo(H{!J0ibk1yr5i>Y5 zIRTtmfb|}*J^z^O5QG8;Q-mAnHmrHJA;Fb`A?U%f5=~4}}3eG0BahRU~`{g5kMI2Z!NC`!rJ zSmkA6*a=CGlC86=hnK>`Zf=mRlheOm`GIp>!FQ;#$oZM`3*-wIA*KL%e--n9^AKX-qQ)8^HcHGehDx@Qab9rV zd=>PT^S8?Vwg9nHfLN;0e|%@f>RAE*JNjIN%T=%%s*G@PE>FeE9v}{%SutCZ^&Es; zZ3S+q(#qB0>Z;js^#S4p5ND;{F3g+c5e}wYONil`am~3FT<8ZkfVcz110bG*Trt-Q zVr&571(1+HoAz}8Np6oB|EQB##6=g89g&@xID$?h=wpf0Y?P#3pP z5yws7rYN`$70KLGZW`p80g&kcnGvI83)iQLsz;pti{Ber=I*LU3ug}xH^|w;)5+~i zEAzNT3eH1KpWI?@3FKS`kXZnktz;a?l%?A;#c4v*V1U4K7z&WEL2f;FE_|*5AmIRs zP(C+DF+Lpoz+*SJ9A&Pj8#uIcd!U#hXBKWRcOir?1_<;_v=Sb}49l=_7~rl_V1_El z+|}GQs_=^gNW22W>SN>?Mh=^~+ZE;vm73fg+?^^+B0wbnV2))N$@X&(DKJBZ`C;x6 z6($)VDJqQ8x2!phXWMiB$rzV^$LVM8>94AMhIKmvCqIrR6Hc zJwGe{hWkMw|1I}#?mO;#fD{0v5FkYWDIVm0#MRX7a0DPF04Y_FP33EHk zN=D-93N?lbd0Ye6gjj8WQ~;z>iIpk+=kT%h+St7?1Km75VQjiMd%@Urc6Ir3M#c?s zGsqY>!i{kg9M)3+kSc&w1EgjUH^(g?V@rV4!dbFT$+=z;bn5oAI(!SB&na_tS4<3^ zPF{>zj+T=>?xL`-id)UC;no7=i}1x=aW}{cmYR70X;5-%gvtDCj=94ae4IjovA8$x zgTo@y43PN%X#q&6$>(gBc8 zC9dnAY@O?Nqb?fG=Z>x(&?hdAj?NWx;H8VJlgk$wi^t%J3g$z)TY@J+=1D04=>bTu zlKVm>cSn}NtY;CCWo{mhF#VmK-QA(T-95dWzT}*R=PS(3#&hso9L5by$t3_;3Xo-k zxD+o?#ZwW z8$eb9WEDVG17yu0-i~)bE4l!(79h%{1)^G7cuPeNefSCmt{)%32l3?q*$9wL0ND(X zEra+Y*QNljUprh-EFL!c()Y3ghkcK!_g7i?&RSCOaH&!AHEIWrQrJw zz8&9z?*zyWfWW`I0J3`!-;M8qeD?ul4?y7B0%q<$*fI~ue!vxdz>ozB{~3pU0Bp07 z{Q&t+Ww$chANAXIiBkS|s>RRYisiwO`G;S@;qn0g4Il>r0+%RI{2}F-GS!9Vj`&Ud z9z+i9{QLL=h-ef$mn7J}XZjS@Ohcc0AY)odw7_rQYWmgJNsh2%a;<3>h&z7oID`z_#HcK;WVb z+Im^(T6Y`sP4Q z-+yuxi zCFV9$Tw*IrwWIC@lsmAN{8xg)`=<28O&!6sg_O2H=p zdHNp(L$BUk^?$;tou_Cn{^K8`Px}&R5vTjQJJ{%utgeU&ObB7%PD40aRb< z1p}pr9o{?q$ldu*+wJ9|$Z&VZFB=fP9p4FZ9kRglo%t>*?Tr9x47UqDWzmjD1MAezORaxDL~Es@x;kWBZtZS=?ctHNz9+Yp9!s+1yBosij-DaGKMlM zhfsdxS20oiXo!ggD0GIk5(Bp*Wj3-Te)?B+%iw3KbQ=!eiG5+_x;3;9KS_~vm0K<`OWH9pE2PC^u9x@_*nTRbj>h)K7&`SiR%%MZYf7|IxvRpoR1 z^D0&o0XpdmR*bYt$C3Xl|BtUKaf5#odgwMlrvOyBZbhduyQ^#*`49QezH05C{O2lh z(*X*18)HA#dT!BT+&0QzN4J-+(V_4`r(8*Btl!2QbQ1dreo1VIw8 z_5=bn7@(m54F~9)<%EC`66%Bo0pIXO0W=1nNibW}0Gjom7r2C;a(^BTVs_N{;>N0r z5N1lPFK(f_2xHo6Jv9D(<4ahp$Pr8y1lXxABAKhz9_q^NcEW*xg`3kYANjTlFaSBqhX5XQtL zrnSygS0Y!#0U83(gfqkx!XLt>64Qw3%#pegGZIg$C8|k?nMA;x8G%!>5+%g}anh82 zVwPI!mju;QfkaS@P-lwYglXBye%T2HnUXB&wCsd9X=--X01-@tFuwI(vM?gzv%Yfx znk;uxDI2X&7Wy$|C}k~(B!H?k4FhQEe>4SX;y>>qh%_SOGxJP04)V**#NPKSWGMdXgNT;0NTyO z&&!@Rh>RR0mJ=(8mBcE5La|WNT!1#qw|}yCEwNF-YaOwk*Z|N}P$u3(>>mp99dUp-2+&%9LPP4AD-AZj#8Cn+LjEuJpNU_H z69AnD&<23Q^{mMIshaX%f|{8aQ8PPDoP|MjjyS*7uQ3ymHL0TGB5^5dM&R@UXlpBZJ#wAkAv=&GB%HXNv^=|OsuaPL!%CLPt_>lc7-0_bLdZh>`J4nMqI zBNxJ7TbHyNu?T~*GFRC2jBs&vWDYFQk@>?UBw^uNBe(q|eFhn(X1|)8Nd}O!$k}8d z8AJw?AtbD_TLHQapx*#=J3w~;bSFS}0dzM&_pBzvprNVd4>hUQ!5vBGA_WgWMHwC75~4PC*mdOwLy$$QHF2 zask=K1hh{xJ12+1@B=_Eo+sPM4tTSZ>>|7211;o2Vk)_a+1ozL;2c0N0QBfNatXN< zKC+DLBm2n#a*$lkSa$^TPXqKPfSv{Dk4#<1NaxjR?E~Z*axDpm;ut`G0q9A&?P+o& zL~bHCM@&hCwKg>=RgwqL;{g3RMrg0bq8}xHWJFyfSU)kdx*W!S;ScgS^vFWBt?)Z1IM(MCKD(e` z1}84^lp<0VKqo>Y6*CZdjyxYDv}Vk@MU1c;!~4k#YN?+IJdC_VUS^EDtugWnly2N@ z#bkAJHOODdYl__b4WO6%$m;;T!qj&=O(1WQw-sbqJFfPTcL4gU+$m@}d~g0KDj$$f z)HDXjhvXyjF+d^u8bGfPkWXPu{0Y$C6=Cp_dEcE7N4{1l3iI-h&msMe!qn_nlJCh6 zIfK>Bz`_Duc=d7#m>xe~UkrL*>JQ zPUTX00OJ6R+fPZU0)XKF<1s&(iI}d%28QL7?0=q-QvhHDz)0r!VpF1?nx_ys7hnSB zP05bxZ z>1ToasPF!V;Q{I(z>EQAB0nA=@~LZ8wg;cg`GNZB|2%$N`M4PjU7yHov;RDPlDY`< zi8@7{rp{1jsdLnM>H@$l044&MCBVc0vjUhkz-(4hm#E9s73wPWEA<<74Pe6n<`1x` z0Luax93L6LI+#-{jF{h+{+LGHqh6@luc7W!52%OKBkD2rgnCLnqyD6x18g|JYyoBm zun_>W2bcrE90BG8FlT_dtfBr=6H~9K*VG&8E&TghO+%SPM2hbrb<%+3lb6}`T65AvJ=u(S{TzNF+#z= zJzBg<-T-Y(o2sS$EB~KSq|IqdIEu6d4ToVQz()1cVj2#_Xn>8CACD0VX9Nbs<;5lE z#pR>~N=q^%u(O(&oh3a(+d@_C)M99R+JQMXU}!r=h^gc|(ateK-GAl(7d}9{(5{U7 zpp(Fz_Mkl>4=;KoJ&GR9%o-HCdIJo8YACRuR*(Q~os<^`mny+iRBXLzpMPZc)81;S zD&jbLJo9$B5qp64qbD%0mwN~%LzVdX;fCT_-7Qki$ED&HptJUo2EIONp zyRCE{oe%#OD2hK846qP@g(`nTe1`HzCKI#r*BMoa$1?dYWxb_q5syB)4q##OU7u8( zN5f^yAl*PW(oJ+Tz@U~90Gk7_$U(Y=ZlxE{Z2*e`SRBCO0hYitta5jvdtlwBdjS?L zx4ne4(TnLNG;C(*Wyk`$kM5@j=s|iAhW&E*HwOM*$vj(C$%zrd@)*k$tWLt{HT2rH zH3o1P*3s*k53A?O=*_Ufrndkr@!$GFe?#y7s%m@az4Si%TY$leodU2_fTjIYuIK{_ z8o(HL|>vW z(^mkN4X_-5N%E!n|7a@U2=IU50v^CB3W1@(NB|oZfhlaK zF&KC-z}l3CwJZE`Y<(mXvB8)rU#ZSmZ?JIvq|Gpa?Z0UwfWZ3CiaH1s+aLl*fX(d_ zz`8t-Ik3rC<}Mfs$AH{PED?AqyW4JnH4F$w3B2Gn)(9`)f4S4AycGBf{NP$0-i0+A z-fB|(gb%8o3JH;9C1&S+QpR5}?Xxm4E9ZY!CO`nc?}Ey}M1|VG{A~Gm@)m>$;0mXY zgzJ|ReS&a+wSK-8DTq->k0Lh#3^u#R|07-1!W&yETL6JXko;LVjC1C*@N_{2ldxVq zK1WahvqX?9$P?rXU_5mKtP5b!0X>5P_|=!7SWp5m<%?Sk1{8dW1`8%!9R*4^KrX0)Y6#%qF61S*Q)VpxXt4?@mt_QH(5J> zE&nM05B?K)dd&+?i|IriJa(oyXm*%536GbZCC)2$ao|489pWB5X7&gkFZ&aoADabF zfNdjxBOkz1U9ZWvm%gIWF5T6i35 zJ3Nkc7`A$6VVieR29H*Gz=?hoJX|%OUI^!xrEp$(qbOhkzJP?MnR4JcrV4n52?(m; zIi^-Y8$7}U14+;;SR~jfxGdBY+6gBJX9#Br1BJoDFkzf9RhT6#6IKapgmuEX!WQ8I zAtUS%b_q8Nj|y)Jp9%jIJ{SHa{GhI-Zm2G^QFl{!SNBl&QXi-8r#?$PQaxHdRy|%l zQ9VgLS3O_7K)p!4M7>PCLVd1!yZRpWbL#Ij*cw6&V+}J63k|V`wZE1kFjBL7H$cWit@~Q9v|eky(fV5(*VfdQX>03h>uVco8*AHW57)NSw%2ylcGh;) z4$#ii-lTm<`&aGT+IO_?X}{Edul-SnrGx75bx0jrN2sHrW2ZA-Cq^ejCsQX&Cr773 zr&33z19Td6x^((=26a~GtkPMdvrcD+&MuujI{S3?>m1PeUgsB`%epLGL)~$@Q+301 zB{JP4-DKTV-8|g_-6Gu*-8$WR-MP9Ax?Q^cx`VnabXV!F(OsvzLwA?%9^HMq`*jcK zey@8?kEQ3W7pRw^SD;s{SE^U8SE<*i*QVF5*QwX7w^XlBZ$NLk-b%d#dKdLx=(F?@ zeN>;Vuc5E0ucfb}Z>n#n@1gIdKT3a$zPG;2SAUxR4E+H8+4@2HA^KtZ3HsUko%);f zFX&&_f1>|R|GoYP12qGl0ck)R2n~!4Obkp7%nj@fybMMej4|*w@HH53FvB3gV75V! zL5M+^L4-k}LASwqgZqZ4A!#TuR5#Q#G%>U_bT)J~bT{-g^fvT09B(+maFSu1VXNgrRT4nT&(YHnijJ`KIZ1khid7}$P7mY3(-7&gv z^w8*u(KDmx#%jihF=os$jx#S)GpjMHGn;GHVAgH6*6f7YMYG?{Zks(fdu{gC z?43Ew95rW~bIoycb#q&DS91?@FY{66)67H6W6d+nv&?hM^US5@KboI5zh-{Z{I>aB z^GD`S%%7P*H~-rLvCy_~u<*19vxu>fS;#sp`Yi@51}#=tY!&5+q@qGmv8Ys3E~*sC zMOC62QJrY6s6o^unlEYy%T-3L@e2sxFsR8q%GAg zwJdcl4J?f<%`8QhR+hso?JONEoh{ugJuOFBj2EpRGQcv>GQ=|6GSV`} zGTu^RnPQo4nPr)4DYY!JEERi+M~l71+8Nml+=)2#xm0bJZ< z%BjYw&S{?0X{T#WzdPMec}4j^|kBYZfo3jxb1S=iZrjN=%WF%QPP9`kn0yD=ZfP8%COcFx$Sv9aFz-d5f=-ow2| zco%qAdDnQ?dC&8{=Kaw7vG-H&=RS@;V|=`Qe0}_U+I{+b27H$Ltn#_*^Ow&npEoj} zcfO;1C;R&QPV=4V+wHsDcct%Y-*w|c$0d$S8kaIIecYCD`^Oy^_x-pdUoyRK`hD5-SJU52|9kp}8QW$YnDPCL!!wS~RG(=w(`=^2O!3U5nfWsd zW){sXow;}Bk28W0RKW`-7qmV}mv%0jC`8$;)ZE(mQ8?F#J)-4=Q!^j?^Hm_e8*%reY2%rne4 z%r9(Gn19&xu#m9uu*k5Ou=ub}=TI;kb;C z$On;+BcDaSjeH-a7KKJ}qVOnDlz&v1EJ_|VFN%p;9JMBDYt;6rT~T|Z_D3C!Iu`Xy z)TyYmQRkzsMLmpq6^%tx(ZXoWXq{;NXp3lZv`w^aw0*Q=^wj9QXnFLi=*`i)qko7# z8GR=DeDtO0tI@Zj??yj}ejNQQ`gsf;;}$bMCOt+P17aFux?}oc24hyntch74vpq(( zD`s!Z{+NR?hhnb9a$>b&N5@Wxof#VzD~ZjD&5f1D7R8pvR>#)G&Wmk|ZHZkF+Z($o zc5Cc+u|LKhk3A84I`&-bZ?V6}-i*Bydq4JJ>|b$692ut{XAvikvx&2fvybzL8yPny z&L?hs+=RH;xcaz`xP5U);!eh0iMt#3EbfIY?p55|xcBj#cwRgiFNoKO*NQieA0F=( zKQ4Z1{EYZn@j>yS@v-p<@k#M;y&9htUld;(-x|L-entH1_;v9cVTj zAL4(EKOKK1{%-uU_!sf7;@`%wWFL5w&W#XE|t%=(ccO~vk+@E+rmiSZRg~Z+;wPCd36KOzLL}jmc!@-kB7s}Hk{q~btdg`yx+M!GOC)`gLCJc_ zCdpRGcF8Wu9?2oe&yvfM8qCCrOZGoHQ)SJ;^&sHZEyG z(&VJ6NkK`WNfAj=NwG=sNfk+hNn4UGC*4STnDkdNl1wDi$?C~k$-2pA$)aSd(PlUF2fOx~M(B>7D8)#Pi*ey-INC@k5itdyiIwZs+NkTa#HbB^;DD8VX5w^-l^kKC!|hJothey z8k!oB8kHKG8lPH`I+(g8^>XTs)Q72mr6FlV8l9$|rj@3fW|k&Ovq~G5W|wB4Ci6)1 zO`Db$o)(`bNlQsfPs>UxN-IsPNRy{kr`4v-PwPq>Ok1C}DQ#=o_OxAT2h$Fx9Zfry z_DkByv`cA!q&-P{o%VOyhjdmtmQJM$(>2p|()H5~(=F3Sq>oG=pFS~tO8T_)ndxEa zbJC;JC4ksrEgB(mcAo>cly5c{pm;3&!t~af0X_* z{Z0D2^p6=x29ZH$sAp(p=w|3=SY+5{cxL!!_+?DW@Xwf@5t0#}5t$K_5ucHmAMYxLLw1qb%zz*Q_yFK3U_lCT2~^nw=G#6_zz8D>^GS zD>W-Ot0b!~t0k)~t0SvBYhl)4*2=6kS?jYlWo^m&IqO9>l07`zC3|%C#O%QA$n2Qx z_-sjbN_K9xG`lFfG`k{OmOVGSD|+r z|C#+RhnJ&~qn)FdW0+%-W1TZRXGD%;j!TYPPC!mkPHoQmob5ULbB^bn%ek0yCFi%C z-*fKgJj!{R^E~He&g)!OE|qJLE6TOX9hPgC>yYc2J1Tcl> ze#kwQdnNa7?z7w%xvz5H=DyG4rbiUPxYIUQ=F2USHnYyq$Rm@($(w zkoQyG&w1zbF6CX#yO#Gy-p#y^`BwR^`H}gO{Ji{%{Dyoczcarle^LI@{8jmD^Ec#g z&fk{5J%4}x(fo7yzvcg)e>4A1{{8&t`7iU|$nxLif0VMMe5sbyL~1K_m3l}=O2H+$y+R@Sxyv z!Lx$r1@8)Zg_?zCg~JN%3LOfa3*8FG7Wx+Y6;3MjFPv5wT$oUpU07aNTR69{v2cFj zg2LXy#f8fX2MSjdt}5JIxV!NC!jpxU3a`ovuND4Lc&qSn;j_XQg|7BdjYaK6T}8b`i;I>O z^%t!z+E%o$=%=DHMdyny67;6>lltUwpLq zSn)5#r;5)O|5|*#_(t*V;(NspN;oAWN=B6=lw_2Yl+=_gDCsR(T(YcWpkzhK#*!^1 z-<0eu*;BHwGslHrF%*bmHtwC zvGi8yUS?1xDswFJDjQwqT{f<4LfMS6S!F?Gp=A+e zk!6xHS$0`zS$$b+8B^9-)>F2qY8j>g;RxV#h418it!Z_E2dOTtq7`!sz|HIuPCf2sVJ|IRm`nute9W1 zprXB^vto0_#fsaNbfs>kd8KWoXQgkYU*)7q|H|o=A(i2kk(Du(@s){{nU$rLHI)l0 zdn*@LE~}IcRIaGpSh=P0o64P)dn)%;9;rN8dAagV<_md3#$jKH&uUAeW3cM>KoPfYtS0~8mpRNHFh-)HO@7z zHDhXgYR1=0sEMeFs)?zItx2d!s>!b@tf{W4tC?5RRMS$kpr*HGam})t{+b;%yKDB= z?5p{%=KGqHHD_wB*W9SNU30JILCvFDUad~8eyvfh%(T{`*0R>Qc0%p!+R)mF+Nj$2 zT1jn6ZCY(*ZD;MC+Jm(xYR}hRs=Zo!t@d{9z1oMhPimjlS=M>ih1JE?rPXED<<#ZZ z71kBk)z&rDwbZrMb<{1YTUys&x4dp;-O0KS^~393>fP!`)laUUSwFizxIU~trar!2 zQlC%X8ygyX8W%QhXx!1byK!&hw~fade`!3`c((CEc^5ifM{( zk~F0?Wj5tB^YoAx&yY&z8RL(@-9$D1xSU1|EY z>3Xw!^Z4co&6Aq_n|qp9H1BBM-Mp{)yXNnkk2L?-e7yNY^Y!K%&9|HHH9u^A-2ANh zdGpKW*YmmaZRcmqpDUZcWB#T2f3*l(MzpxMc(sgf8QU_oCAuZ9C9x&BC9Ng1C8s67 zrLd)>rKzQ*WkCzm(%I6}vZ!TAOJB=i%Ziq5EkCqeZxyzRT4%RPTBWV>*5=k_tplwq zT35HOYu(torS+TEovnLXe`!6{dbag^>&4c~t-rQjZ@tlayY+7C^93?;fx!Z&1)~?t zUQn=L-hxF7)-O1?;Oc@`3*Ihxx8P$N(q`CZ*=F13(H7B`)Rxkg-j>yt+g8`s)3&N@ zPuqpI%Wc25U2nV5c8k$v%ouBCI5UEAWSp5%%tU4iGmS}O@|iZKi`mZXXAUxlnG?)K z<_hy0^Njh>&T7ZnIkI*^ySROLyHER+c4>P_`(XRN_V3ycwjXZ)q5Wk0nfCMT7u$dD zAUcE{BRj@-q;_O=w0HD%40f#SSktk-V`ImKj+-5KI_`Ho>Ui4mXUD5ftdr=ZJJmb2 zIt@EbI?Xyoo#M_}ovEFmv!S!Ov$eCWb7|*>&dr_MI(KyL?%dZY`>yl*&eNUeIxlu! z>HMwp_s*N0cRKHNzU)G}jJuq=rgx=x6?M()>g-zD)!((eYgO0Uu5DdAx^{Q%>-w(i zVArv(vt9SP9(FzH`m^h=u2LVCh_B6=cwqI=4FdV2Quoa_0c=T^_%o(DaTd!F`Uy;QG8 zuXe9quR*VMuU)T0uXC?Q@5tUUz23dkdt-Z3d-Hl_y*0h{y$!uBy=}d+j^3``zTP#x z>w0(f?(N;*d$9LV?~&fiy?1)=_TKA#(EGag@7@m!Sqrg+oP`DpMGOC5jrRVV;}rmK zTuRI(!x)`gwKAhFWip%2rIboTT&txnr8F*!Z0i!Twc|BgwOdzVW5qe=ea_{1&;2}R zA!{@mO{)xVI>w4^DmC2{HMWVnEtCE7{rnT(Az&Ct0;9kLkP6a3I+zLOfDDicmVq^3 zJtzPh!DdhdN==WwWPI5C_V zemlH7d@Ot^{KSj((!IG}j+g7b)cfAM;9c>WaTgqm6Y(gV zjHlrlcov?Evv4+Ef^%>_ejgX%?HJ-PCYWK1>u?nRh98j5q&w+L29jYUiM&c)Bh$!i zk`*G0NgmluiU}hXj;3SiSUQeQptI=` zx|HV7<@95^oifU)qL%KX-_V1!f>zQhdYYc44fG=Yg^^(Mp87F<=YI#47yK9fSiiSF!cX=m`sw~$ zf4;xK&-OR@yL|0e`KSG6|AGJ5e;SF2w2QQltckvfMC5R!m3QH>ybtfs2k_y1 z3?IwK@risgpU!9UIXr`Bh4@0A%k%hZzLsy}JNQoi75`IYi+oWa3dKgTRTPU)#TVjB zu}g%7Cx{5dK~W(pMU^-zj*F8bD(Xdx>?9Lpl1!H4Wr|FdX);}A$Sj#Hm&j#uh0K$M zvRqck6Y_#=me=GBc}w1rf2sB=)I;@By;VOIr{dK}HD0BtRF$UE)pWH$EmBL>aO6EusWh@RGm7m8r5wbuT%6a{hki#ExJgT=u%y#_iCiE zW}0iMEA??*ug~an`n+z`P5P?-J)~Rob$w4iHf>B>)4_B$T}(F~pr0ecpDny=^}`zz(#7>|mR0b8YBdTWojQa=X_ei!HSWY_+Ym$Lt9kwe_~i z-m-V>-}at;U>~_2Zm4_1z3C>nNp6ZubMst=oA0vRO1H|bb?e+aF5eZnkK8A&*nQ?c zcY9pe5$8MQtlQ`IyIL1@7hR)ka#vl8yXpRLcT$5kLC2t1&^L$+5`tHP#9(-k6ig1L c2it=nI1(HUPX2cZw26s%X4m>>{@)<_50cq;#Q*>R diff --git a/demo_osx/STTwitterDemoOSX.xcodeproj/xcuserdata/nst.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/demo_osx/STTwitterDemoOSX.xcodeproj/xcuserdata/nst.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index eb1a525..1ed6c96 100644 --- a/demo_osx/STTwitterDemoOSX.xcodeproj/xcuserdata/nst.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/demo_osx/STTwitterDemoOSX.xcodeproj/xcuserdata/nst.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -13,5 +13,67 @@ moduleName = ""> + + + + + + + + + + + + + + diff --git a/demo_osx/STTwitterDemoOSX/STClientVC.m b/demo_osx/STTwitterDemoOSX/STClientVC.m index 4d76171..e5bdc41 100644 --- a/demo_osx/STTwitterDemoOSX/STClientVC.m +++ b/demo_osx/STTwitterDemoOSX/STClientVC.m @@ -25,30 +25,33 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil - (IBAction)postTweet:(id)sender { - + self.twitterPostTweetStatus = @"-"; - + if(_twitterPostMediaURL) { - + [_twitter postStatusUpdate:_twitterPostTweetText inReplyToStatusID:nil mediaURL:_twitterPostMediaURL placeID:nil latitude:_twitterPostLatitude longitude:_twitterPostLongitude - successBlock:^(NSDictionary *status) { - - self.twitterPostTweetText = @""; - self.twitterPostTweetStatus = @"OK"; - self.twitterPostLatitude = nil; - self.twitterPostLongitude = nil; - self.twitterPostMediaURL = nil; - } errorBlock:^(NSError *error) { - self.twitterPostTweetStatus = error ? [error localizedDescription] : @"Unknown error"; - }]; - + + uploadProgressBlock:^(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) { + NSLog(@"%lu %lu %lu", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } successBlock:^(NSDictionary *status) { + + self.twitterPostTweetText = @""; + self.twitterPostTweetStatus = @"OK"; + self.twitterPostLatitude = nil; + self.twitterPostLongitude = nil; + self.twitterPostMediaURL = nil; + } errorBlock:^(NSError *error) { + self.twitterPostTweetStatus = error ? [error localizedDescription] : @"Unknown error"; + }]; + } else { - + [_twitter postStatusUpdate:_twitterPostTweetText inReplyToStatusID:nil latitude:_twitterPostLatitude @@ -57,7 +60,7 @@ - (IBAction)postTweet:(id)sender { displayCoordinates:@(YES) trimUser:nil successBlock:^(NSDictionary *status) { - + self.twitterPostTweetText = @""; self.twitterPostTweetStatus = @"OK"; self.twitterPostLatitude = nil; @@ -101,7 +104,7 @@ - (IBAction)chooseMedia:(id)sender { [panel setAllowedFileTypes:@[ @"png", @"PNG", @"jpg", @"JPG", @"jpeg", @"JPEG", @"gif", @"GIF"] ]; NSWindow *mainWindow = [[NSApplication sharedApplication] mainWindow]; - + [panel beginSheetModalForWindow:mainWindow completionHandler:^(NSInteger result) { if (result != NSFileHandlingPanelOKButton) return; diff --git a/demo_osx/STTwitterDemoOSX/STConsoleVC.m b/demo_osx/STTwitterDemoOSX/STConsoleVC.m index c3e9dc2..afa25dd 100644 --- a/demo_osx/STTwitterDemoOSX/STConsoleVC.m +++ b/demo_osx/STTwitterDemoOSX/STConsoleVC.m @@ -43,16 +43,16 @@ - (IBAction)changeHTTPMethodAction:(id)sender { } - (NSString *)curlDescriptionWithEndpoint:(NSString *)endPoint baseURLString:(NSString *)baseURLString parameters:(NSDictionary *)parameters requestHeaders:(NSDictionary *)requestHeaders { -/* - $ curl -i -H "Authorization: OAuth oauth_consumer_key="7YBPrscvh0RIThrWYVeGg", \ - oauth_nonce="DA5E6B1E-E98D-4AFB-9AAB-18A463F2", \ - oauth_signature_method="HMAC-SHA1", \ - oauth_timestamp="1381908706", \ - oauth_version="1.0", \ - oauth_token="1294332967-UsaIUBcsC4JcHv9tIYxk5EktsVisAtCLNVGKghP", \ - oauth_signature="gnmc02ohamTvTmkTppz%2FbH8OjAs%3D"" \ - "https://api.twitter.com/1.1/statuses/home_timeline.json?count=10" - */ + /* + $ curl -i -H "Authorization: OAuth oauth_consumer_key="7YBPrscvh0RIThrWYVeGg", \ + oauth_nonce="DA5E6B1E-E98D-4AFB-9AAB-18A463F2", \ + oauth_signature_method="HMAC-SHA1", \ + oauth_timestamp="1381908706", \ + oauth_version="1.0", \ + oauth_token="1294332967-UsaIUBcsC4JcHv9tIYxk5EktsVisAtCLNVGKghP", \ + oauth_signature="gnmc02ohamTvTmkTppz%2FbH8OjAs%3D"" \ + "https://api.twitter.com/1.1/statuses/home_timeline.json?count=10" + */ if([baseURLString hasSuffix:@"/"]) baseURLString = [baseURLString substringToIndex:[baseURLString length]-1]; @@ -70,7 +70,7 @@ - (NSString *)curlDescriptionWithEndpoint:(NSString *)endPoint baseURLString:(NS [urlString appendFormat:@"?%@", parameterString]; } - + return [NSString stringWithFormat:@"curl -i -H \"Authorization: %@\" \"%@\"", [requestHeaders valueForKey:@"Authorization"], urlString]; } @@ -95,50 +95,56 @@ - (IBAction)sendRequestAction:(id)sender { self.rootNode = nil; [_outlineView reloadData]; - [_twitter fetchResource:_genericAPIEndpoint HTTPMethod:_genericHTTPMethod baseURLString:_genericBaseURLString parameters:parameters progressBlock:nil successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response) { - - NSString *curlDescription = [self curlDescriptionWithEndpoint:_genericAPIEndpoint baseURLString:_genericBaseURLString parameters:parameters requestHeaders:requestHeaders]; - - self.curlTextViewAttributedString = [[NSAttributedString alloc] initWithString:curlDescription attributes:attributes]; - self.responseHeadersTextViewAttributedString = [[NSAttributedString alloc] initWithString:[responseHeaders description] attributes:attributes]; - - JSONSyntaxHighlight *jsh = [[JSONSyntaxHighlight alloc] initWithJSON:response]; - - NSMutableDictionary *keyAttributes = [jsh.keyAttributes mutableCopy]; - [keyAttributes addEntriesFromDictionary:attributes]; - - NSMutableDictionary *stringAttributes = [jsh.stringAttributes mutableCopy]; - [stringAttributes addEntriesFromDictionary:attributes]; - - NSMutableDictionary *nonStringAttributes = [jsh.nonStringAttributes mutableCopy]; - [nonStringAttributes addEntriesFromDictionary:attributes]; - - jsh.keyAttributes = keyAttributes; - jsh.stringAttributes = stringAttributes; - jsh.nonStringAttributes = nonStringAttributes; - - self.bodyTextViewAttributedString = [jsh highlightJSONWithPrettyPrint:YES]; - - self.rootNode = [BAVPlistNode plistNodeFromObject:response key:@"Root"]; - - [_outlineView reloadData]; - - } errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { - NSString *s = @"error"; - if(error) { - s = [error localizedDescription]; - } - - NSString *requestHeadersDescription = requestHeaders ? [requestHeaders description] : @""; - NSString *responseHeadersDescription = responseHeaders ? [responseHeaders description] : @""; - self.curlTextViewAttributedString = [[NSAttributedString alloc] initWithString:requestHeadersDescription attributes:attributes]; - self.responseHeadersTextViewAttributedString = [[NSAttributedString alloc] initWithString:responseHeadersDescription attributes:attributes]; - - self.bodyTextViewAttributedString = [[NSAttributedString alloc] initWithString:s attributes:attributes]; - - self.rootNode = nil; - [_outlineView reloadData]; - }]; + [_twitter fetchResource:_genericAPIEndpoint + HTTPMethod:_genericHTTPMethod + baseURLString:_genericBaseURLString + parameters:parameters + uploadProgressBlock:nil + downloadProgressBlock:nil + successBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, id response) { + + NSString *curlDescription = [self curlDescriptionWithEndpoint:_genericAPIEndpoint baseURLString:_genericBaseURLString parameters:parameters requestHeaders:requestHeaders]; + + self.curlTextViewAttributedString = [[NSAttributedString alloc] initWithString:curlDescription attributes:attributes]; + self.responseHeadersTextViewAttributedString = [[NSAttributedString alloc] initWithString:[responseHeaders description] attributes:attributes]; + + JSONSyntaxHighlight *jsh = [[JSONSyntaxHighlight alloc] initWithJSON:response]; + + NSMutableDictionary *keyAttributes = [jsh.keyAttributes mutableCopy]; + [keyAttributes addEntriesFromDictionary:attributes]; + + NSMutableDictionary *stringAttributes = [jsh.stringAttributes mutableCopy]; + [stringAttributes addEntriesFromDictionary:attributes]; + + NSMutableDictionary *nonStringAttributes = [jsh.nonStringAttributes mutableCopy]; + [nonStringAttributes addEntriesFromDictionary:attributes]; + + jsh.keyAttributes = keyAttributes; + jsh.stringAttributes = stringAttributes; + jsh.nonStringAttributes = nonStringAttributes; + + self.bodyTextViewAttributedString = [jsh highlightJSONWithPrettyPrint:YES]; + + self.rootNode = [BAVPlistNode plistNodeFromObject:response key:@"Root"]; + + [_outlineView reloadData]; + + } errorBlock:^(id request, NSDictionary *requestHeaders, NSDictionary *responseHeaders, NSError *error) { + NSString *s = @"error"; + if(error) { + s = [error localizedDescription]; + } + + NSString *requestHeadersDescription = requestHeaders ? [requestHeaders description] : @""; + NSString *responseHeadersDescription = responseHeaders ? [responseHeaders description] : @""; + self.curlTextViewAttributedString = [[NSAttributedString alloc] initWithString:requestHeadersDescription attributes:attributes]; + self.responseHeadersTextViewAttributedString = [[NSAttributedString alloc] initWithString:responseHeadersDescription attributes:attributes]; + + self.bodyTextViewAttributedString = [[NSAttributedString alloc] initWithString:s attributes:attributes]; + + self.rootNode = nil; + [_outlineView reloadData]; + }]; } #pragma mark - NSOutlineViewDataSource