From 7926ee1c7a755bc1710519bddde4ef0141bb9af0 Mon Sep 17 00:00:00 2001 From: Alex Michaud Date: Tue, 20 Mar 2012 14:14:37 -0700 Subject: [PATCH 01/13] Fixed bug with JSON decodes not returning errors --- AFNetworking/AFJSONUtilities.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AFNetworking/AFJSONUtilities.m b/AFNetworking/AFJSONUtilities.m index b7bb02f..6dc2b5d 100644 --- a/AFNetworking/AFJSONUtilities.m +++ b/AFNetworking/AFJSONUtilities.m @@ -143,7 +143,7 @@ id AFJSONDecode(NSData *data, NSError **error) { NSUInteger parseOptionFlags = 0; [invocation setArgument:&parseOptionFlags atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation if (error != NULL) { - [invocation setArgument:error atIndex:3]; + [invocation setArgument:&error atIndex:3]; } [invocation invoke]; @@ -165,7 +165,7 @@ id AFJSONDecode(NSData *data, NSError **error) { NSUInteger yajlParserOptions = 0; [invocation setArgument:&yajlParserOptions atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation if (error != NULL) { - [invocation setArgument:error atIndex:3]; + [invocation setArgument:&error atIndex:3]; } [invocation invoke]; @@ -178,7 +178,7 @@ id AFJSONDecode(NSData *data, NSError **error) { [invocation setArgument:&data atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation if (error != NULL) { - [invocation setArgument:error atIndex:3]; + [invocation setArgument:&error atIndex:3]; } [invocation setArgument:&nullOption atIndex:4]; @@ -196,7 +196,7 @@ id AFJSONDecode(NSData *data, NSError **error) { NSUInteger readOptions = 0; [invocation setArgument:&readOptions atIndex:3]; if (error != NULL) { - [invocation setArgument:error atIndex:4]; + [invocation setArgument:&error atIndex:4]; } [invocation invoke]; From 66a123e21626a7e8ee45e50bbfb849d9ae93a623 Mon Sep 17 00:00:00 2001 From: Oliver Eikemeier Date: Tue, 20 Mar 2012 22:39:18 +0100 Subject: [PATCH 02/13] Use SBJsonParser/SBJsonWriter instead of NSObject+SBJson - SBJsonParser can be used standalone when JSON posting is not used - No NSString -> NSData conversion when encoding - Possibility to implement error handling (although [parser/writer error] returns a NSString...) - Documented interface: http://stig.github.com/json-framework/api/3.0/interfaceSBJsonParser.html http://stig.github.com/json-framework/api/3.0/interfaceSBJsonWriter.html --- AFNetworking/AFJSONUtilities.m | 37 +++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/AFNetworking/AFJSONUtilities.m b/AFNetworking/AFJSONUtilities.m index b7bb02f..af91e52 100644 --- a/AFNetworking/AFJSONUtilities.m +++ b/AFNetworking/AFJSONUtilities.m @@ -26,9 +26,11 @@ NSData * AFJSONEncode(id object, NSError **error) { NSData *data = nil; SEL _JSONKitSelector = NSSelectorFromString(@"JSONDataWithOptions:error:"); - SEL _SBJSONSelector = NSSelectorFromString(@"JSONRepresentation"); SEL _YAJLSelector = NSSelectorFromString(@"yajl_JSONString"); + id _SBJsonWriterClass = NSClassFromString(@"SBJsonWriter"); + SEL _SBJsonWriterSelector = NSSelectorFromString(@"dataWithObject:"); + id _NXJsonSerializerClass = NSClassFromString(@"NXJsonSerializer"); SEL _NXJsonSerializerSelector = NSSelectorFromString(@"serialize:"); @@ -54,16 +56,16 @@ NSData * AFJSONEncode(id object, NSError **error) { [invocation invoke]; [invocation getReturnValue:&data]; - } else if (_SBJSONSelector && [object respondsToSelector:_SBJSONSelector]) { - NSString *JSONString = nil; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[object methodSignatureForSelector:_SBJSONSelector]]; - invocation.target = object; - invocation.selector = _SBJSONSelector; + } else if (_SBJsonWriterClass && [_SBJsonWriterClass instancesRespondToSelector:_SBJsonWriterSelector]) { + id writer = [[_SBJsonWriterClass alloc] init]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[writer methodSignatureForSelector:_SBJsonWriterSelector]]; + invocation.target = writer; + invocation.selector = _SBJsonWriterSelector; + + [invocation setArgument:&object atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation [invocation invoke]; - [invocation getReturnValue:&JSONString]; - - data = [JSONString dataUsingEncoding:NSUTF8StringEncoding]; + [invocation getReturnValue:&data]; } else if (_YAJLSelector && [object respondsToSelector:_YAJLSelector]) { @try { NSString *JSONString = nil; @@ -119,9 +121,11 @@ id AFJSONDecode(NSData *data, NSError **error) { id JSON = nil; SEL _JSONKitSelector = NSSelectorFromString(@"objectFromJSONDataWithParseOptions:error:"); - SEL _SBJSONSelector = NSSelectorFromString(@"JSONValue"); SEL _YAJLSelector = NSSelectorFromString(@"yajl_JSONWithOptions:error:"); + id _SBJSONParserClass = NSClassFromString(@"SBJsonParser"); + SEL _SBJSONParserSelector = NSSelectorFromString(@"objectWithData:"); + id _NSJSONSerializationClass = NSClassFromString(@"NSJSONSerialization"); SEL _NSJSONSerializationSelector = NSSelectorFromString(@"JSONObjectWithData:options:error:"); @@ -148,13 +152,14 @@ id AFJSONDecode(NSData *data, NSError **error) { [invocation invoke]; [invocation getReturnValue:&JSON]; - } else if (_SBJSONSelector && [NSString instancesRespondToSelector:_SBJSONSelector]) { - // Create a string representation of JSON, to use SBJSON -`JSONValue` category method - NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[string methodSignatureForSelector:_SBJSONSelector]]; - invocation.target = string; - invocation.selector = _SBJSONSelector; + } else if (_SBJSONParserClass && [_SBJSONParserClass instancesRespondToSelector:_SBJSONParserSelector]) { + id parser = [[_SBJSONParserClass alloc] init]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[parser methodSignatureForSelector:_SBJSONParserSelector]]; + invocation.target = parser; + invocation.selector = _SBJSONParserSelector; + [invocation setArgument:&data atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + [invocation invoke]; [invocation getReturnValue:&JSON]; } else if (_YAJLSelector && [data respondsToSelector:_YAJLSelector]) { From 32fb8171bf670ef2454bc7352921f5084e5d4f4d Mon Sep 17 00:00:00 2001 From: Oliver Eikemeier Date: Tue, 20 Mar 2012 22:54:27 +0100 Subject: [PATCH 03/13] AFNetworking is still non-ARC --- AFNetworking/AFJSONUtilities.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AFNetworking/AFJSONUtilities.m b/AFNetworking/AFJSONUtilities.m index af91e52..820d362 100644 --- a/AFNetworking/AFJSONUtilities.m +++ b/AFNetworking/AFJSONUtilities.m @@ -66,6 +66,7 @@ NSData * AFJSONEncode(id object, NSError **error) { [invocation invoke]; [invocation getReturnValue:&data]; + [writer release]; } else if (_YAJLSelector && [object respondsToSelector:_YAJLSelector]) { @try { NSString *JSONString = nil; @@ -162,6 +163,7 @@ id AFJSONDecode(NSData *data, NSError **error) { [invocation invoke]; [invocation getReturnValue:&JSON]; + [parser release]; } else if (_YAJLSelector && [data respondsToSelector:_YAJLSelector]) { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_YAJLSelector]]; invocation.target = data; From 371d6406ea2139e97f45094f7373aaabcababb35 Mon Sep 17 00:00:00 2001 From: Oliver Eikemeier Date: Wed, 21 Mar 2012 00:17:59 +0100 Subject: [PATCH 04/13] merged e87b3c1 --- AFNetworking/AFJSONUtilities.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AFNetworking/AFJSONUtilities.m b/AFNetworking/AFJSONUtilities.m index 820d362..3377fc4 100644 --- a/AFNetworking/AFJSONUtilities.m +++ b/AFNetworking/AFJSONUtilities.m @@ -148,7 +148,7 @@ id AFJSONDecode(NSData *data, NSError **error) { NSUInteger parseOptionFlags = 0; [invocation setArgument:&parseOptionFlags atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation if (error != NULL) { - [invocation setArgument:error atIndex:3]; + [invocation setArgument:&error atIndex:3]; } [invocation invoke]; @@ -172,7 +172,7 @@ id AFJSONDecode(NSData *data, NSError **error) { NSUInteger yajlParserOptions = 0; [invocation setArgument:&yajlParserOptions atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation if (error != NULL) { - [invocation setArgument:error atIndex:3]; + [invocation setArgument:&error atIndex:3]; } [invocation invoke]; @@ -185,7 +185,7 @@ id AFJSONDecode(NSData *data, NSError **error) { [invocation setArgument:&data atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation if (error != NULL) { - [invocation setArgument:error atIndex:3]; + [invocation setArgument:&error atIndex:3]; } [invocation setArgument:&nullOption atIndex:4]; @@ -203,7 +203,7 @@ id AFJSONDecode(NSData *data, NSError **error) { NSUInteger readOptions = 0; [invocation setArgument:&readOptions atIndex:3]; if (error != NULL) { - [invocation setArgument:error atIndex:4]; + [invocation setArgument:&error atIndex:4]; } [invocation invoke]; From 8606378b690629282403c8ea13ea67994192b770 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Tue, 27 Mar 2012 09:09:03 -0700 Subject: [PATCH 05/13] [Issue #267] Fixing compiler warning on undeclared AFQueryStringComponentFromKeyAndValueWithEncoding --- AFNetworking/AFHTTPClient.m | 1 + 1 file changed, 1 insertion(+) diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index f0f07e0..ee10027 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -157,6 +157,7 @@ NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEn extern NSArray * AFQueryStringComponentsFromKeyAndValueWithEncoding(NSString *key, id value, NSStringEncoding stringEncoding); extern NSArray * AFQueryStringComponentsFromKeyAndDictionaryValueWithEncoding(NSString *key, NSDictionary *value, NSStringEncoding stringEncoding); extern NSArray * AFQueryStringComponentsFromKeyAndArrayValueWithEncoding(NSString *key, NSArray *value, NSStringEncoding stringEncoding); +extern AFQueryStringComponent * AFQueryStringComponentFromKeyAndValueWithEncoding(id key, id value, NSStringEncoding stringEncoding); NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) { return [[AFQueryStringComponentsFromKeyAndValueWithEncoding(nil, parameters, stringEncoding) valueForKeyPath:@"description"] componentsJoinedByString:@"&"]; From 9e5ff6dcf5eb80df624a600d94b7108935d0e9c8 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Wed, 28 Mar 2012 10:05:00 -0700 Subject: [PATCH 06/13] [Issue #266] Fixing GET and POST parameter encoding --- AFNetworking/AFHTTPClient.m | 51 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index ee10027..79133bc 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -120,7 +120,8 @@ NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEn @property (readwrite, nonatomic, retain) id key; @property (readwrite, nonatomic, retain) id value; -- (id)initWithKey:(NSString *)key value:(NSString *)value; +- (id)initWithKey:(id)key value:(id)value; +- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding; @end @@ -128,7 +129,7 @@ NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEn @synthesize key = _key; @synthesize value = _value; -- (id)initWithKey:(NSString *)key value:(NSString *)value { +- (id)initWithKey:(id)key value:(id)value { self = [super init]; if (!self) { return nil; @@ -146,56 +147,56 @@ NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEn [super dealloc]; } -- (NSString *)description { - return [NSString stringWithFormat:@"%@=%@", self.key, self.value]; +- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding { + return [NSString stringWithFormat:@"%@=%@", self.key, AFURLEncodedStringFromStringWithEncoding([self.value description], stringEncoding)]; } @end #pragma mark - -extern NSArray * AFQueryStringComponentsFromKeyAndValueWithEncoding(NSString *key, id value, NSStringEncoding stringEncoding); -extern NSArray * AFQueryStringComponentsFromKeyAndDictionaryValueWithEncoding(NSString *key, NSDictionary *value, NSStringEncoding stringEncoding); -extern NSArray * AFQueryStringComponentsFromKeyAndArrayValueWithEncoding(NSString *key, NSArray *value, NSStringEncoding stringEncoding); -extern AFQueryStringComponent * AFQueryStringComponentFromKeyAndValueWithEncoding(id key, id value, NSStringEncoding stringEncoding); +extern NSArray * AFQueryStringComponentsFromKeyAndValue(NSString *key, id value); +extern NSArray * AFQueryStringComponentsFromKeyAndDictionaryValue(NSString *key, NSDictionary *value); +extern NSArray * AFQueryStringComponentsFromKeyAndArrayValue(NSString *key, NSArray *value); -NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) { - return [[AFQueryStringComponentsFromKeyAndValueWithEncoding(nil, parameters, stringEncoding) valueForKeyPath:@"description"] componentsJoinedByString:@"&"]; +NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) { + NSMutableArray *mutableComponents = [NSMutableArray array]; + for (AFQueryStringComponent *component in AFQueryStringComponentsFromKeyAndValue(nil, parameters)) { + [mutableComponents addObject:[component URLEncodedStringValueWithEncoding:stringEncoding]]; + } + + return [mutableComponents componentsJoinedByString:@"&"]; } -AFQueryStringComponent * AFQueryStringComponentFromKeyAndValueWithEncoding(id key, id value, NSStringEncoding stringEncoding) { - return [[[AFQueryStringComponent alloc] initWithKey:AFURLEncodedStringFromStringWithEncoding([key description], stringEncoding) value:AFURLEncodedStringFromStringWithEncoding([value description], stringEncoding)] autorelease]; -} - -NSArray * AFQueryStringComponentsFromKeyAndValueWithEncoding(NSString *key, id value, NSStringEncoding stringEncoding) { +NSArray * AFQueryStringComponentsFromKeyAndValue(NSString *key, id value) { NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; if([value isKindOfClass:[NSDictionary class]]) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndDictionaryValueWithEncoding(key, value, stringEncoding)]; + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndDictionaryValue(key, value)]; } else if([value isKindOfClass:[NSArray class]]) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndArrayValueWithEncoding(key, value, stringEncoding)]; + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndArrayValue(key, value)]; } else { - [mutableQueryStringComponents addObject:AFQueryStringComponentFromKeyAndValueWithEncoding(key, value, stringEncoding)]; + [mutableQueryStringComponents addObject:[[[AFQueryStringComponent alloc] initWithKey:key value:value] autorelease]]; } return mutableQueryStringComponents; } -NSArray * AFQueryStringComponentsFromKeyAndDictionaryValueWithEncoding(NSString *key, NSDictionary *value, NSStringEncoding stringEncoding){ +NSArray * AFQueryStringComponentsFromKeyAndDictionaryValue(NSString *key, NSDictionary *value){ NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - [value enumerateKeysAndObjectsUsingBlock:^(id k, id v, BOOL *stop) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndValueWithEncoding((key ? [NSString stringWithFormat:@"%@[%@]", key, k] : k), v, stringEncoding)]; + [value enumerateKeysAndObjectsUsingBlock:^(id nestedKey, id nestedValue, BOOL *stop) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; }]; return mutableQueryStringComponents; } -NSArray * AFQueryStringComponentsFromKeyAndArrayValueWithEncoding(NSString *key, NSArray *value, NSStringEncoding stringEncoding) { +NSArray * AFQueryStringComponentsFromKeyAndArrayValue(NSString *key, NSArray *value) { NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - [value enumerateObjectsUsingBlock:^(id v, NSUInteger idx, BOOL *stop) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndValueWithEncoding([NSString stringWithFormat:@"%@[]", key], v, stringEncoding)]; + [value enumerateObjectsUsingBlock:^(id nestedValue, NSUInteger idx, BOOL *stop) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; }]; return mutableQueryStringComponents; @@ -433,7 +434,7 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN NSMutableURLRequest *request = [self requestWithMethod:method path:path parameters:nil]; __block AFMultipartFormData *formData = [[AFMultipartFormData alloc] initWithStringEncoding:self.stringEncoding]; - for (AFQueryStringComponent *component in AFQueryStringComponentsFromKeyAndValueWithEncoding(nil, parameters, self.stringEncoding)) { + for (AFQueryStringComponent *component in AFQueryStringComponentsFromKeyAndValue(nil, parameters)) { [formData appendPartWithFormData:[component.value dataUsingEncoding:self.stringEncoding] name:component.key]; } From 0e6b58ba60a9319b46aa4102b7338f41cc347f59 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Wed, 28 Mar 2012 10:08:49 -0700 Subject: [PATCH 07/13] [Issue #266] Re-adding conditional in multipartFormRequestWithMethod:... that does not doubly-encode NSData parameter values --- AFNetworking/AFHTTPClient.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index 79133bc..60b44a4 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -435,7 +435,14 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN __block AFMultipartFormData *formData = [[AFMultipartFormData alloc] initWithStringEncoding:self.stringEncoding]; for (AFQueryStringComponent *component in AFQueryStringComponentsFromKeyAndValue(nil, parameters)) { - [formData appendPartWithFormData:[component.value dataUsingEncoding:self.stringEncoding] name:component.key]; + NSData *data = nil; + if ([component.value isKindOfClass:[NSData class]]) { + data = component.value; + } else { + data = [[component.value description] dataUsingEncoding:self.stringEncoding]; + } + + [formData appendPartWithFormData:data name:[component.key description]]; } if (block) { From a0967f5d4f35bd9d2cd49063c1e6a7047ca82ea1 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Sat, 31 Mar 2012 13:02:06 -0700 Subject: [PATCH 08/13] Removing @gowalla.com e-mail address --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e4adbe1..a339a43 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,6 @@ Scott Raymond - http://github.com/sco - http://twitter.com/sco -- sco@gowalla.com ## License From 0470f4b29fc664009a57d960df01ea0a25925e5b Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Wed, 4 Apr 2012 16:31:52 -0700 Subject: [PATCH 09/13] [Issue #278] Adding thread safety to incrementing / decrementing and display of network activity indicator --- .../AFNetworkActivityIndicatorManager.m | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/AFNetworking/AFNetworkActivityIndicatorManager.m b/AFNetworking/AFNetworkActivityIndicatorManager.m index a376d81..bdcb69a 100644 --- a/AFNetworking/AFNetworkActivityIndicatorManager.m +++ b/AFNetworking/AFNetworkActivityIndicatorManager.m @@ -30,6 +30,7 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; @interface AFNetworkActivityIndicatorManager () @property (readwrite, nonatomic, assign) NSInteger activityCount; @property (readwrite, nonatomic, retain) NSTimer *activityIndicatorVisibilityTimer; +@property (readwrite, nonatomic, retain) NSRecursiveLock *lock; @property (readonly, getter = isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; - (void)updateNetworkActivityIndicatorVisibility; @@ -38,6 +39,7 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; @implementation AFNetworkActivityIndicatorManager @synthesize activityCount = _activityCount; @synthesize activityIndicatorVisibilityTimer = _activityIndicatorVisibilityTimer; +@synthesize lock = _lock; @synthesize enabled = _enabled; @dynamic networkActivityIndicatorVisible; @@ -56,6 +58,8 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; if (!self) { return nil; } + + self.lock = [[[NSRecursiveLock alloc] init] autorelease]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(incrementActivityCount) name:AFNetworkingOperationDidStartNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(decrementActivityCount) name:AFNetworkingOperationDidFinishNotification object:nil]; @@ -69,10 +73,13 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; [_activityIndicatorVisibilityTimer invalidate]; [_activityIndicatorVisibilityTimer release]; _activityIndicatorVisibilityTimer = nil; + [_lock release]; + [super dealloc]; } - (void)setActivityCount:(NSInteger)activityCount { + [self.lock lock]; [self willChangeValueForKey:@"activityCount"]; _activityCount = MAX(activityCount, 0); [self didChangeValueForKey:@"activityCount"]; @@ -87,6 +94,7 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; [self updateNetworkActivityIndicatorVisibility]; } } + [self.lock unlock]; } - (BOOL)isNetworkActivityIndicatorVisible { @@ -94,19 +102,21 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; } - (void)updateNetworkActivityIndicatorVisibility { - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]]; + }); } - (void)incrementActivityCount { - @synchronized(self) { - self.activityCount += 1; - } + [self.lock lock]; + self.activityCount += 1; + [self.lock unlock]; } - (void)decrementActivityCount { - @synchronized(self) { - self.activityCount -= 1; - } + [self.lock lock]; + self.activityCount -= 1; + [self.lock unlock]; } @end From 88920034e2770ab86fa39f5cce1f334c2455a342 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 4 Apr 2012 17:17:06 -0700 Subject: [PATCH 10/13] fixes a blocked reload button once we hit an error. Also shows an alert now. --- .../Classes/Controllers/PublicTimelineViewController.m | 6 ++++-- iOS Example/Classes/Models/Tweet.m | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/iOS Example/Classes/Controllers/PublicTimelineViewController.m b/iOS Example/Classes/Controllers/PublicTimelineViewController.m index 8da1159..966b607 100644 --- a/iOS Example/Classes/Controllers/PublicTimelineViewController.m +++ b/iOS Example/Classes/Controllers/PublicTimelineViewController.m @@ -42,8 +42,10 @@ self.navigationItem.rightBarButtonItem.enabled = NO; [Tweet publicTimelineTweetsWithBlock:^(NSArray *tweets) { - _tweets = tweets; - [self.tableView reloadData]; + if (tweets) { + _tweets = tweets; + [self.tableView reloadData]; + } [_activityIndicatorView stopAnimating]; self.navigationItem.rightBarButtonItem.enabled = YES; diff --git a/iOS Example/Classes/Models/Tweet.m b/iOS Example/Classes/Models/Tweet.m index f53359d..d6bc1ec 100644 --- a/iOS Example/Classes/Models/Tweet.m +++ b/iOS Example/Classes/Models/Tweet.m @@ -65,6 +65,12 @@ } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Error: %@", error); + + [[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Ok", nil] show]; + + if (block) { + block(nil); + } }]; } From 069615a20d4c0de41cc3927d8cae5a16f0710c64 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 5 Apr 2012 17:29:18 -0700 Subject: [PATCH 11/13] Use lightweight locking for activityCount Also exposes isNetworkActivityIndicatorVisible which now is KVO observable. --- .../AFNetworkActivityIndicatorManager.h | 12 +++--- .../AFNetworkActivityIndicatorManager.m | 41 ++++++++++--------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/AFNetworking/AFNetworkActivityIndicatorManager.h b/AFNetworking/AFNetworkActivityIndicatorManager.h index d15384a..2cee153 100644 --- a/AFNetworking/AFNetworkActivityIndicatorManager.h +++ b/AFNetworking/AFNetworkActivityIndicatorManager.h @@ -30,12 +30,7 @@ /** `AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a network request operation has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero. */ -@interface AFNetworkActivityIndicatorManager : NSObject { -@private - NSInteger _activityCount; - BOOL _enabled; - NSTimer *_activityIndicatorVisibilityTimer; -} +@interface AFNetworkActivityIndicatorManager : NSObject; /** A Boolean value indicating whether the manager is enabled. @@ -61,6 +56,11 @@ */ - (void)decrementActivityCount; +/** + Returns the network indicator visibility. This is more excact than polling [UIApplication sharedApplication] isNetworkActivityIndicatorVisible] since we add a slight delay while updating the indicator to avoid flickering. You can observe this via KVO. + */ +- (BOOL)isNetworkActivityIndicatorVisible; + @end #endif diff --git a/AFNetworking/AFNetworkActivityIndicatorManager.m b/AFNetworking/AFNetworkActivityIndicatorManager.m index bdcb69a..32dbbe0 100644 --- a/AFNetworking/AFNetworkActivityIndicatorManager.m +++ b/AFNetworking/AFNetworkActivityIndicatorManager.m @@ -23,6 +23,7 @@ #import "AFNetworkActivityIndicatorManager.h" #import "AFHTTPRequestOperation.h" +#import #if __IPHONE_OS_VERSION_MIN_REQUIRED static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; @@ -30,7 +31,6 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; @interface AFNetworkActivityIndicatorManager () @property (readwrite, nonatomic, assign) NSInteger activityCount; @property (readwrite, nonatomic, retain) NSTimer *activityIndicatorVisibilityTimer; -@property (readwrite, nonatomic, retain) NSRecursiveLock *lock; @property (readonly, getter = isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; - (void)updateNetworkActivityIndicatorVisibility; @@ -39,7 +39,6 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; @implementation AFNetworkActivityIndicatorManager @synthesize activityCount = _activityCount; @synthesize activityIndicatorVisibilityTimer = _activityIndicatorVisibilityTimer; -@synthesize lock = _lock; @synthesize enabled = _enabled; @dynamic networkActivityIndicatorVisible; @@ -59,8 +58,6 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; return nil; } - self.lock = [[[NSRecursiveLock alloc] init] autorelease]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(incrementActivityCount) name:AFNetworkingOperationDidStartNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(decrementActivityCount) name:AFNetworkingOperationDidFinishNotification object:nil]; @@ -73,17 +70,10 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; [_activityIndicatorVisibilityTimer invalidate]; [_activityIndicatorVisibilityTimer release]; _activityIndicatorVisibilityTimer = nil; - [_lock release]; - [super dealloc]; } -- (void)setActivityCount:(NSInteger)activityCount { - [self.lock lock]; - [self willChangeValueForKey:@"activityCount"]; - _activityCount = MAX(activityCount, 0); - [self didChangeValueForKey:@"activityCount"]; - +- (void)updateNetworkActivityIndicatorVisibilityDelayed { if (self.enabled) { // Delay hiding of activity indicator for a short interval, to avoid flickering if (![self isNetworkActivityIndicatorVisible]) { @@ -94,11 +84,10 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; [self updateNetworkActivityIndicatorVisibility]; } } - [self.lock unlock]; } - (BOOL)isNetworkActivityIndicatorVisible { - return self.activityCount > 0; + return _activityCount > 0; } - (void)updateNetworkActivityIndicatorVisibility { @@ -107,16 +96,28 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; }); } +// not actually exposed, but will be used if someone tries to set activityCount via KVC. +- (void)setActivityCount:(NSInteger)activityCount { + __sync_swap(&_activityCount, activityCount); + [self updateNetworkActivityIndicatorVisibilityDelayed]; +} + - (void)incrementActivityCount { - [self.lock lock]; - self.activityCount += 1; - [self.lock unlock]; + [self willChangeValueForKey:@"activityCount"]; + OSAtomicIncrement32((int32_t*)&_activityCount); + [self didChangeValueForKey:@"activityCount"]; + [self updateNetworkActivityIndicatorVisibilityDelayed]; } - (void)decrementActivityCount { - [self.lock lock]; - self.activityCount -= 1; - [self.lock unlock]; + [self willChangeValueForKey:@"activityCount"]; + OSAtomicDecrement32((int32_t*)&_activityCount); + [self didChangeValueForKey:@"activityCount"]; + [self updateNetworkActivityIndicatorVisibilityDelayed]; +} + ++ (NSSet *)keyPathsForValuesAffectingIsNetworkActivityIndicatorVisible { + return [NSSet setWithObject:@"activityCount"]; } @end From e2751e6ffc0c88e2c3b58418d708daee60b3c135 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 5 Apr 2012 17:49:08 -0700 Subject: [PATCH 12/13] protect decrementActivityCount against integer underflow. --- AFNetworking/AFNetworkActivityIndicatorManager.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AFNetworking/AFNetworkActivityIndicatorManager.m b/AFNetworking/AFNetworkActivityIndicatorManager.m index 32dbbe0..3e36ba6 100644 --- a/AFNetworking/AFNetworkActivityIndicatorManager.m +++ b/AFNetworking/AFNetworkActivityIndicatorManager.m @@ -111,7 +111,11 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; - (void)decrementActivityCount { [self willChangeValueForKey:@"activityCount"]; - OSAtomicDecrement32((int32_t*)&_activityCount); + bool success; + do { + int32_t orig = _activityCount; + success = OSAtomicCompareAndSwap32(orig, MIN(orig - 1, orig), &_activityCount); + } while(!success); [self didChangeValueForKey:@"activityCount"]; [self updateNetworkActivityIndicatorVisibilityDelayed]; } From 6923e31db5b94052c4fcdd031ada4036ba758dcf Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Sun, 8 Apr 2012 12:02:19 -0700 Subject: [PATCH 13/13] Minor code re-formatting and documentation changes --- AFNetworking/AFNetworkActivityIndicatorManager.h | 12 ++++++------ AFNetworking/AFNetworkActivityIndicatorManager.m | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/AFNetworking/AFNetworkActivityIndicatorManager.h b/AFNetworking/AFNetworkActivityIndicatorManager.h index 2cee153..4a5640c 100644 --- a/AFNetworking/AFNetworkActivityIndicatorManager.h +++ b/AFNetworking/AFNetworkActivityIndicatorManager.h @@ -30,7 +30,7 @@ /** `AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a network request operation has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero. */ -@interface AFNetworkActivityIndicatorManager : NSObject; +@interface AFNetworkActivityIndicatorManager : NSObject /** A Boolean value indicating whether the manager is enabled. @@ -39,6 +39,11 @@ */ @property (nonatomic, assign, getter = isEnabled) BOOL enabled; +/** + A Boolean value indicating whether the network activity indicator is currently displayed in the status bar. + */ +@property (readonly, nonatomic, assign) BOOL isNetworkActivityIndicatorVisible; + /** Returns the shared network activity indicator manager object for the system. @@ -56,11 +61,6 @@ */ - (void)decrementActivityCount; -/** - Returns the network indicator visibility. This is more excact than polling [UIApplication sharedApplication] isNetworkActivityIndicatorVisible] since we add a slight delay while updating the indicator to avoid flickering. You can observe this via KVO. - */ -- (BOOL)isNetworkActivityIndicatorVisible; - @end #endif diff --git a/AFNetworking/AFNetworkActivityIndicatorManager.m b/AFNetworking/AFNetworkActivityIndicatorManager.m index 3e36ba6..01a6851 100644 --- a/AFNetworking/AFNetworkActivityIndicatorManager.m +++ b/AFNetworking/AFNetworkActivityIndicatorManager.m @@ -96,7 +96,7 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; }); } -// not actually exposed, but will be used if someone tries to set activityCount via KVC. +// Not exposed, but used if activityCount is set via KVC. - (void)setActivityCount:(NSInteger)activityCount { __sync_swap(&_activityCount, activityCount); [self updateNetworkActivityIndicatorVisibilityDelayed]; @@ -113,8 +113,8 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25; [self willChangeValueForKey:@"activityCount"]; bool success; do { - int32_t orig = _activityCount; - success = OSAtomicCompareAndSwap32(orig, MIN(orig - 1, orig), &_activityCount); + int32_t currentCount = _activityCount; + success = OSAtomicCompareAndSwap32(currentCount, MIN(currentCount - 1, currentCount), &_activityCount); } while(!success); [self didChangeValueForKey:@"activityCount"]; [self updateNetworkActivityIndicatorVisibilityDelayed];