Merge branch 'master' into invalid-ssl

This commit is contained in:
Kevin Harwood 2013-03-25 17:11:42 -05:00
commit bcb8aea682
17 changed files with 719 additions and 329 deletions

View file

@ -1,22 +1,25 @@
Pod::Spec.new do |s|
s.name = 'AFNetworking'
s.version = '1.1.0'
s.version = '1.2.0'
s.license = 'MIT'
s.summary = 'A delightful iOS and OS X networking framework.'
s.homepage = 'https://github.com/AFNetworking/AFNetworking'
s.authors = { 'Mattt Thompson' => 'm@mattt.me', 'Scott Raymond' => 'sco@gowalla.com' }
s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => '1.1.0' }
s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => '1.2.0' }
s.source_files = 'AFNetworking'
s.requires_arc = true
s.ios.deployment_target = '5.0'
s.ios.frameworks = 'MobileCoreServices', 'SystemConfiguration'
s.ios.frameworks = 'MobileCoreServices', 'SystemConfiguration', 'Security'
s.osx.deployment_target = '10.7'
s.osx.frameworks = 'CoreServices', 'SystemConfiguration'
s.osx.frameworks = 'CoreServices', 'SystemConfiguration', 'Security'
s.prefix_header_contents = <<-EOS
#import <Availability.h>
#define _AFNETWORKING_PIN_SSL_CERTIFICATES_
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <SystemConfiguration/SystemConfiguration.h>
#import <MobileCoreServices/MobileCoreServices.h>

View file

@ -21,6 +21,7 @@
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "AFURLConnectionOperation.h"
#import <Availability.h>
@ -137,6 +138,13 @@ typedef enum {
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
#endif
/**
Default SSL pinning mode for each `AFHTTPRequestOperation` which will be enqueued with `enqueueHTTPRequestOperation:`.
*/
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
@property (nonatomic, assign) AFURLConnectionOperationSSLPinningMode defaultSSLPinningMode;
#endif
///---------------------------------------------
/// @name Creating and Initializing HTTP Clients
///---------------------------------------------
@ -440,7 +448,7 @@ typedef enum {
///----------------
/**
### Network Reachability
## Network Reachability
The following constants are provided by `AFHTTPClient` as possible network reachability statuses.
@ -471,7 +479,7 @@ typedef enum {
A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification.
The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status.
### Parameter Encoding
## Parameter Encoding
The following constants are provided by `AFHTTPClient` as possible methods for serializing parameters into query string or message body values.
@ -543,12 +551,44 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
@return `YES` if the file data was successfully appended, otherwise `NO`.
@discussion The filename and MIME type for this data in the form will be automatically generated, using `NSURLResponse` `-suggestedFilename` and `-MIMEType`, respectively.
@discussion The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively.
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
error:(NSError * __autoreleasing *)error;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary.
@param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`.
@param name The name to be associated with the specified data. This parameter must not be `nil`.
@param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`.
@param mimeType The declared MIME type of the file data. This parameter must not be `nil`.
@param error If an error occurs, upon return contains an `NSError` object that describes the problem.
@return `YES` if the file data was successfully appended otherwise `NO`.
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType
error:(NSError * __autoreleasing *)error;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary.
@param inputStream The input stream to be appended to the form data
@param name The name to be associated with the specified input stream. This parameter must not be `nil`.
@param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`.
@param length The length of the specified input stream in bytes.
@param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`.
*/
- (void)appendPartWithInputStream:(NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(unsigned long long)length
mimeType:(NSString *)mimeType;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary.
@ -572,6 +612,7 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
- (void)appendPartWithFormData:(NSData *)data
name:(NSString *)name;
/**
Appends HTTP headers, followed by the encoded data and the multipart form boundary.

View file

@ -81,7 +81,7 @@ static NSString * AFBase64EncodedStringFromString(NSString *string) {
}
static NSString * AFPercentEscapedQueryStringPairMemberFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
static NSString * const kAFCharactersToBeEscaped = @":/?&=;+!@#$()~'";
static NSString * const kAFCharactersToBeEscaped = @":/?&=;+!@#$()~',*";
static NSString * const kAFCharactersToLeaveUnescaped = @"[].";
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescaped, (__bridge CFStringRef)kAFCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(encoding));
@ -146,10 +146,11 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(caseInsensitiveCompare:)];
[[[value allKeys] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] enumerateObjectsUsingBlock:^(id nestedKey, __unused NSUInteger idx, __unused BOOL *stop) {
id nestedValue = [value objectForKey:nestedKey];
[[[dictionary allKeys] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] enumerateObjectsUsingBlock:^(id nestedKey, __unused NSUInteger idx, __unused BOOL *stop) {
id nestedValue = [dictionary objectForKey:nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
@ -159,6 +160,11 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
[array enumerateObjectsUsingBlock:^(id nestedValue, __unused NSUInteger idx, __unused BOOL *stop) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}];
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
[set enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}];
} else {
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
@ -234,8 +240,13 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
self.defaultHeaders = [NSMutableDictionary dictionary];
// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
NSString *preferredLanguageCodes = [[NSLocale preferredLanguages] componentsJoinedByString:@", "];
[self setDefaultHeader:@"Accept-Language" value:[NSString stringWithFormat:@"%@, en-us;q=0.8", preferredLanguageCodes]];
NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
[[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
float q = 1.0f - (idx * 0.1f);
[acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
*stop = q <= 0.5f;
}];
[self setDefaultHeader:@"Accept-Language" value:[acceptLanguagesComponents componentsJoinedByString:@", "]];
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
@ -304,7 +315,8 @@ static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused targ
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:status] forKey:AFNetworkingReachabilityNotificationStatusItem]];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:status] forKey:AFNetworkingReachabilityNotificationStatusItem]];
});
}
@ -505,12 +517,12 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
AFHTTPRequestOperation *operation = nil;
NSString *className = nil;
NSEnumerator *enumerator = [self.registeredHTTPOperationClassNames reverseObjectEnumerator];
while (!operation && (className = [enumerator nextObject])) {
Class op_class = NSClassFromString(className);
if (op_class && [op_class canProcessRequest:urlRequest]) {
operation = [(AFHTTPRequestOperation *)[op_class alloc] initWithRequest:urlRequest];
for (NSString *className in self.registeredHTTPOperationClassNames) {
Class operationClass = NSClassFromString(className);
if (operationClass && [operationClass canProcessRequest:urlRequest]) {
operation = [(AFHTTPRequestOperation *)[operationClass alloc] initWithRequest:urlRequest];
break;
}
}
@ -521,6 +533,9 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
[operation setCompletionBlockWithSuccess:success failure:failure];
operation.credential = self.defaultCredential;
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
operation.SSLPinningMode = self.defaultSSLPinningMode;
#endif
return operation;
}
@ -534,7 +549,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
- (void)cancelAllHTTPOperationsWithMethod:(NSString *)method
path:(NSString *)path
{
NSString *URLStringToMatched = [[[self requestWithMethod:(method ?: @"GET") path:path parameters:nil] URL] absoluteString];
NSString *pathToBeMatched = [[[self requestWithMethod:(method ?: @"GET") path:path parameters:nil] URL] path];
for (NSOperation *operation in [self.operationQueue operations]) {
if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) {
@ -542,9 +557,9 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
}
BOOL hasMatchingMethod = !method || [method isEqualToString:[[(AFHTTPRequestOperation *)operation request] HTTPMethod]];
BOOL hasMatchingURL = [[[[(AFHTTPRequestOperation *)operation request] URL] absoluteString] isEqualToString:URLStringToMatched];
BOOL hasMatchingPath = [[[[(AFHTTPRequestOperation *)operation request] URL] path] isEqual:pathToBeMatched];
if (hasMatchingMethod && hasMatchingURL) {
if (hasMatchingMethod && hasMatchingPath) {
[operation cancel];
}
}
@ -581,9 +596,10 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
for (AFHTTPRequestOperation *operation in operations) {
AFCompletionBlock originalCompletionBlock = [operation.completionBlock copy];
__weak AFHTTPRequestOperation *weakOperation = operation;
__weak __typeof(&*operation)weakOperation = operation;
operation.completionBlock = ^{
dispatch_queue_t queue = weakOperation.successCallbackQueue ?: dispatch_get_main_queue();
__strong __typeof(&*weakOperation)strongOperation = weakOperation;
dispatch_queue_t queue = strongOperation.successCallbackQueue ?: dispatch_get_main_queue();
dispatch_group_async(dispatchGroup, queue, ^{
if (originalCompletionBlock) {
originalCompletionBlock();
@ -729,7 +745,12 @@ static inline NSString * AFMultipartFormFinalBoundary() {
static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
#ifdef __UTTYPE__
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
return (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
if (!contentType) {
return @"application/octet-stream";
} else {
return contentType;
}
#else
return @"application/octet-stream";
#endif
@ -755,9 +776,10 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
maxLength:(NSUInteger)length;
@end
@interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
@interface AFMultipartBodyStreamProvider : NSObject
@property (nonatomic, assign) NSUInteger bufferLength;
@property (nonatomic, assign) NSTimeInterval delay;
@property (nonatomic, readonly) NSInputStream *inputStream;
@property (nonatomic, readonly) unsigned long long contentLength;
@property (nonatomic, readonly, getter = isEmpty) BOOL empty;
@ -770,7 +792,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
@interface AFStreamingMultipartFormData ()
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
@property (readwrite, nonatomic, strong) AFMultipartBodyStreamProvider *bodyStream;
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
@end
@ -789,7 +811,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
self.request = urlRequest;
self.stringEncoding = encoding;
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
self.bodyStream = [[AFMultipartBodyStreamProvider alloc] initWithStringEncoding:encoding];
return self;
}
@ -801,6 +823,23 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
NSParameterAssert(fileURL);
NSParameterAssert(name);
NSString *fileName = [fileURL lastPathComponent];
NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]);
return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error];
}
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType
error:(NSError * __autoreleasing *)error
{
NSParameterAssert(fileURL);
NSParameterAssert(name);
NSParameterAssert(fileName);
NSParameterAssert(mimeType);
if (![fileURL isFileURL]) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil) forKey:NSLocalizedFailureReasonErrorKey];
if (error != NULL) {
@ -818,8 +857,8 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
}
NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, [fileURL lastPathComponent]] forKey:@"Content-Disposition"];
[mutableHeaders setValue:AFContentTypeForPathExtension([fileURL pathExtension]) forKey:@"Content-Type"];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding;
@ -834,6 +873,32 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
return YES;
}
- (void)appendPartWithInputStream:(NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(unsigned long long)length
mimeType:(NSString *)mimeType
{
NSParameterAssert(name);
NSParameterAssert(fileName);
NSParameterAssert(mimeType);
NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = mutableHeaders;
bodyPart.body = inputStream;
bodyPart.bodyContentLength = length;
[self.bodyStream appendHTTPBodyPart:bodyPart];
}
- (void)appendPartWithFileData:(NSData *)data
name:(NSString *)name
fileName:(NSString *)fileName
@ -878,7 +943,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
delay:(NSTimeInterval)delay
{
self.bodyStream.numberOfBytesInPacket = numberOfBytes;
self.bodyStream.bufferLength = numberOfBytes;
self.bodyStream.delay = delay;
}
@ -892,7 +957,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"];
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
[self.request setHTTPBodyStream:self.bodyStream];
[self.request setHTTPBodyStream:self.bodyStream.inputStream];
return self.request;
}
@ -901,24 +966,31 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
#pragma mark -
@interface AFMultipartBodyStream () <NSCopying>
@property (nonatomic, assign) NSStreamStatus streamStatus;
@property (nonatomic, strong) NSError *streamError;
@interface AFMultipartBodyStreamProvider () <NSCopying, NSStreamDelegate>
@property (nonatomic, assign) NSStringEncoding stringEncoding;
@property (nonatomic, strong) NSMutableArray *HTTPBodyParts;
@property (nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
@property (nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, strong) NSMutableData *buffer;
@end
@implementation AFMultipartBodyStream
@synthesize streamStatus = _streamStatus;
@synthesize streamError = _streamError;
static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
@implementation AFMultipartBodyStreamProvider {
@private
// Workaround for stream delegates being weakly referenced, but otherwise unowned
__strong id _self;
}
@synthesize stringEncoding = _stringEncoding;
@synthesize HTTPBodyParts = _HTTPBodyParts;
@synthesize HTTPBodyPartEnumerator = _HTTPBodyPartEnumerator;
@synthesize currentHTTPBodyPart = _currentHTTPBodyPart;
@synthesize numberOfBytesInPacket = _numberOfBytesInPacket;
@synthesize inputStream = _inputStream;
@synthesize outputStream = _outputStream;
@synthesize buffer = _buffer;
@synthesize bufferLength = _numberOfBytesInPacket;
@synthesize delay = _delay;
- (id)initWithStringEncoding:(NSStringEncoding)encoding {
@ -929,11 +1001,18 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
self.stringEncoding = encoding;
self.HTTPBodyParts = [NSMutableArray array];
self.numberOfBytesInPacket = NSIntegerMax;
self.bufferLength = NSIntegerMax;
self.buffer = [[NSMutableData alloc] init];
self.bufferLength = AFMultipartBodyStreamProviderDefaultBufferLength;
return self;
}
- (void)dealloc {
_outputStream.delegate = nil;
}
- (void)setInitialAndFinalBoundaries {
if ([self.HTTPBodyParts count] > 0) {
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
@ -950,80 +1029,95 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
[self.HTTPBodyParts addObject:bodyPart];
}
- (NSInputStream *)inputStream {
if (_inputStream == nil) {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreateBoundPair(NULL, &readStream, &writeStream, self.bufferLength);
_inputStream = CFBridgingRelease(readStream);
_outputStream = CFBridgingRelease(writeStream);
_outputStream.delegate = self;
if ([NSThread isMainThread]) {
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
});
}
[_outputStream open];
_self = self;
}
return _inputStream;
}
- (BOOL)isEmpty {
return [self.HTTPBodyParts count] == 0;
}
#pragma mark - NSInputStream
#pragma mark - NSStreamDelegate
- (NSInteger)read:(uint8_t *)buffer
maxLength:(NSUInteger)length
- (void)stream:(NSStream *)stream
handleEvent:(NSStreamEvent)eventCode
{
if ([self streamStatus] == NSStreamStatusClosed) {
return 0;
if (eventCode & NSStreamEventHasSpaceAvailable) {
[self handleOutputStreamSpaceAvailable];
}
}
NSInteger bytesRead = 0;
while ((NSUInteger)bytesRead < MIN(length, self.numberOfBytesInPacket)) {
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
break;
- (void)handleOutputStreamSpaceAvailable {
while ([_outputStream hasSpaceAvailable]) {
if ([_buffer length] > 0) {
NSInteger numberOfBytesWritten = [_outputStream write:[_buffer bytes] maxLength:[_buffer length]];
if (numberOfBytesWritten < 0) {
[self close];
return;
}
[_buffer replaceBytesInRange:NSMakeRange(0, numberOfBytesWritten) withBytes:NULL length:0];
} else {
bytesRead += [self.currentHTTPBodyPart read:&buffer[bytesRead] maxLength:(length - (NSUInteger)bytesRead)];
if (!self.currentHTTPBodyPart) {
if (!self.HTTPBodyPartEnumerator) {
self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
}
self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject];
}
if (!self.currentHTTPBodyPart) {
[self close];
return;
}
[_buffer setLength:self.bufferLength];
NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:[_buffer mutableBytes] maxLength:[_buffer length]];
if (numberOfBytesRead < 0) {
[self close];
return;
}
[_buffer setLength:numberOfBytesRead];
if(numberOfBytesRead == 0) {
self.currentHTTPBodyPart = nil;
}
if (self.delay > 0.0f) {
[NSThread sleepForTimeInterval:self.delay];
}
}
}
return bytesRead;
}
- (BOOL)getBuffer:(__unused uint8_t **)buffer
length:(__unused NSUInteger *)len
{
return NO;
}
- (BOOL)hasBytesAvailable {
return [self streamStatus] == NSStreamStatusOpen;
}
#pragma mark - NSStream
- (void)open {
if (self.streamStatus == NSStreamStatusOpen) {
return;
}
self.streamStatus = NSStreamStatusOpen;
[self setInitialAndFinalBoundaries];
self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
}
- (void)close {
self.streamStatus = NSStreamStatusClosed;
[_outputStream close];
_outputStream.delegate = nil;
_self = nil;
}
- (id)propertyForKey:(__unused NSString *)key {
return nil;
}
- (BOOL)setProperty:(__unused id)property
forKey:(__unused NSString *)key
{
return NO;
}
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
- (unsigned long long)contentLength {
unsigned long long length = 0;
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
@ -1033,26 +1127,10 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
return length;
}
#pragma mark - Undocumented CFReadStream Bridged Methods
- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop
forMode:(__unused CFStringRef)aMode
{}
- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop
forMode:(__unused CFStringRef)aMode
{}
- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
callback:(__unused CFReadStreamClientCallBack)inCallback
context:(__unused CFStreamClientContext *)inContext {
return NO;
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
AFMultipartBodyStreamProvider *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
[bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
@ -1068,10 +1146,12 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
#pragma mark -
typedef enum {
AFInitialPhase = 0,
AFEncapsulationBoundaryPhase = 1,
AFHeaderPhase = 2,
AFBodyPhase = 3,
AFFinalBoundaryPhase = 4,
AFCompletedPhase = 5,
} AFHTTPBodyPartReadPhase;
@interface AFHTTPBodyPart () <NSCopying> {
@ -1118,6 +1198,8 @@ typedef enum {
_inputStream = [NSInputStream inputStreamWithData:self.body];
} else if ([self.body isKindOfClass:[NSURL class]]) {
_inputStream = [NSInputStream inputStreamWithURL:self.body];
} else if ([self.body isKindOfClass:[NSInputStream class]]) {
_inputStream = self.body;
}
}
@ -1152,11 +1234,13 @@ typedef enum {
}
- (BOOL)hasBytesAvailable {
// Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the avaiable buffer
// Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer
if (_phase == AFFinalBoundaryPhase) {
return YES;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
switch (self.inputStream.streamStatus) {
case NSStreamStatusNotOpen:
case NSStreamStatusOpening:
@ -1170,6 +1254,8 @@ typedef enum {
default:
return NO;
}
#pragma clang diagnostic pop
}
- (NSInteger)read:(uint8_t *)buffer
@ -1227,7 +1313,12 @@ typedef enum {
return YES;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
switch (_phase) {
case AFInitialPhase:
_phase = AFEncapsulationBoundaryPhase;
break;
case AFEncapsulationBoundaryPhase:
_phase = AFHeaderPhase;
break;
@ -1241,10 +1332,13 @@ typedef enum {
_phase = AFFinalBoundaryPhase;
break;
case AFFinalBoundaryPhase:
_phase = AFEncapsulationBoundaryPhase;
case AFCompletedPhase:
default:
_phase = AFCompletedPhase;
break;
}
_phaseReadOffset = 0;
#pragma clang diagnostic pop
return YES;
}

View file

@ -30,7 +30,6 @@
#define AF_CAST_TO_BLOCK __bridge void *
#endif
// We do a little bit of duck typing in this file which can trigger this warning. Turn it off for this source file.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-selector-match"
@ -108,18 +107,12 @@ static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL
@property (readwrite, nonatomic, strong) NSURLRequest *request;
@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response;
@property (readwrite, nonatomic, strong) NSError *HTTPError;
@property (readwrite, nonatomic, copy) NSString *HTTPResponseString;
@property (readwrite, nonatomic, assign) long long totalContentLength;
@property (readwrite, nonatomic, assign) long long offsetContentLength;
@end
@implementation AFHTTPRequestOperation
@synthesize HTTPError = _HTTPError;
@synthesize HTTPResponseString = _HTTPResponseString;
@synthesize successCallbackQueue = _successCallbackQueue;
@synthesize failureCallbackQueue = _failureCallbackQueue;
@synthesize totalContentLength = _totalContentLength;
@synthesize offsetContentLength = _offsetContentLength;
@dynamic request;
@dynamic response;
@ -169,23 +162,19 @@ static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL
}
}
- (NSString *)responseString {
- (NSStringEncoding)responseStringEncoding {
// When no explicit charset parameter is provided by the sender, media subtypes of the "text" type are defined to have a default charset value of "ISO-8859-1" when received via HTTP. Data in character sets other than "ISO-8859-1" or its subsets MUST be labeled with an appropriate charset value.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.4.1
if (!self.HTTPResponseString && self.response && !self.response.textEncodingName && self.responseData) {
if (self.response && !self.response.textEncodingName && self.responseData) {
NSString *type = nil;
AFGetMediaTypeAndSubtypeWithString([[self.response allHeaderFields] valueForKey:@"Content-Type"], &type, nil);
if ([type isEqualToString:@"text"]) {
self.HTTPResponseString = [[NSString alloc] initWithData:self.responseData encoding:NSISOLatin1StringEncoding];
return NSISOLatin1StringEncoding;
}
}
if (self.HTTPResponseString) {
return self.HTTPResponseString;
} else {
return [super responseString];
}
return [super responseStringEncoding];
}
- (void)pause {
@ -324,46 +313,6 @@ static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL
return [[self acceptableContentTypes] intersectsSet:AFContentTypesFromHTTPHeader([request valueForHTTPHeaderField:@"Accept"])];
}
#pragma mark - NSURLConnectionDelegate
- (void)connection:(__unused NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
self.response = (NSHTTPURLResponse *)response;
// Set Content-Range header if status code of response is 206 (Partial Content)
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7
long long totalContentLength = self.response.expectedContentLength;
long long fileOffset = 0;
NSUInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? (NSUInteger)[self.response statusCode] : 200;
if (statusCode == 206) {
NSString *contentRange = [self.response.allHeaderFields valueForKey:@"Content-Range"];
if ([contentRange hasPrefix:@"bytes"]) {
NSArray *byteRanges = [contentRange componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" -/"]];
if ([byteRanges count] == 4) {
fileOffset = [[byteRanges objectAtIndex:1] longLongValue];
totalContentLength = [[byteRanges objectAtIndex:2] longLongValue] ?: -1; // if this is "*", it's converted to 0, but -1 is default.
}
}
} else {
if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) {
[self.outputStream setProperty:[NSNumber numberWithInteger:0] forKey:NSStreamFileCurrentOffsetKey];
} else {
if ([[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length] > 0) {
self.outputStream = [NSOutputStream outputStreamToMemory];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
}
}
}
self.offsetContentLength = MAX(fileOffset, 0);
self.totalContentLength = totalContentLength;
[self.outputStream open];
}
@end
#pragma clang diagnostic pop

View file

@ -22,11 +22,12 @@
#import "AFImageRequestOperation.h"
static dispatch_queue_t af_image_request_operation_processing_queue;
static dispatch_queue_t image_request_operation_processing_queue() {
if (af_image_request_operation_processing_queue == NULL) {
af_image_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.image-request.processing", 0);
}
static dispatch_queue_t af_image_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_image_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.image-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_image_request_operation_processing_queue;
}

View file

@ -22,11 +22,12 @@
#import "AFJSONRequestOperation.h"
static dispatch_queue_t af_json_request_operation_processing_queue;
static dispatch_queue_t json_request_operation_processing_queue() {
if (af_json_request_operation_processing_queue == NULL) {
af_json_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.json-request.processing", 0);
}
static dispatch_queue_t af_json_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_json_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.json-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_json_request_operation_processing_queue;
}
@ -34,12 +35,14 @@ static dispatch_queue_t json_request_operation_processing_queue() {
@interface AFJSONRequestOperation ()
@property (readwrite, nonatomic, strong) id responseJSON;
@property (readwrite, nonatomic, strong) NSError *JSONError;
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
@end
@implementation AFJSONRequestOperation
@synthesize responseJSON = _responseJSON;
@synthesize JSONReadingOptions = _JSONReadingOptions;
@synthesize JSONError = _JSONError;
@dynamic lock;
+ (instancetype)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON))success
@ -61,6 +64,7 @@ static dispatch_queue_t json_request_operation_processing_queue() {
- (id)responseJSON {
[self.lock lock];
if (!_responseJSON && [self.responseData length] > 0 && [self isFinished] && !self.JSONError) {
NSError *error = nil;
@ -77,6 +81,7 @@ static dispatch_queue_t json_request_operation_processing_queue() {
self.JSONError = error;
}
[self.lock unlock];
return _responseJSON;
}

View file

@ -62,8 +62,8 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.17;
return nil;
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(incrementActivityCount) name:AFNetworkingOperationDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(decrementActivityCount) name:AFNetworkingOperationDidFinishNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkingOperationDidStart:) name:AFNetworkingOperationDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkingOperationDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil];
return self;
}
@ -126,6 +126,20 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.17;
[self updateNetworkActivityIndicatorVisibilityDelayed];
}
- (void)networkingOperationDidStart:(NSNotification *)notification {
AFURLConnectionOperation *connectionOperation = [notification object];
if (connectionOperation.request.URL) {
[self incrementActivityCount];
}
}
- (void)networkingOperationDidFinish:(NSNotification *)notification {
AFURLConnectionOperation *connectionOperation = [notification object];
if (connectionOperation.request.URL) {
[self decrementActivityCount];
}
}
@end
#endif

View file

@ -22,11 +22,12 @@
#import "AFPropertyListRequestOperation.h"
static dispatch_queue_t af_property_list_request_operation_processing_queue;
static dispatch_queue_t property_list_request_operation_processing_queue() {
if (af_property_list_request_operation_processing_queue == NULL) {
af_property_list_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.property-list-request.processing", 0);
}
static dispatch_queue_t af_property_list_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_property_list_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.property-list-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_property_list_request_operation_processing_queue;
}

View file

@ -60,6 +60,14 @@
Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/).
## SSL Pinning
Relying on the CA trust model to validate SSL certificates exposes your app to security vulnerabilities, such as man-in-the-middle attacks. For applications that connect to known servers, SSL certificate pinning provides an increased level of security, by checking server certificate validity against those specified in the app bundle.
SSL with certificate pinning is strongly recommended for any application that transmits sensitive information to an external webservice.
When `_AFNETWORKING_PIN_SSL_CERTIFICATES_` is defined and the Security framework is linked, connections will be validated on all matching certificates with a `.cer` extension in the bundle root.
## NSCoding & NSCopying Conformance
`AFURLConnectionOperation` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. However, because of the intrinsic limitations of capturing the exact state of an operation at a particular moment, there are some important caveats to keep in mind:
@ -75,7 +83,21 @@
- A copy of an operation will not include the `outputStream` of the original.
- Operation copies do not include `completionBlock`. `completionBlock` often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ operation when copied.
*/
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, NSCoding, NSCopying>
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
typedef enum {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
} AFURLConnectionOperationSSLPinningMode;
#endif
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate,
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_5_0) || \
(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_8)
NSURLConnectionDataDelegate,
#endif
NSCoding, NSCopying>
///-------------------------------
/// @name Accessing Run Loop Modes
@ -131,7 +153,6 @@
*/
@property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding;
///-------------------------------
/// @name Managing URL Credentials
///-------------------------------
@ -150,6 +171,15 @@
*/
@property (nonatomic, strong) NSURLCredential *credential;
/**
The pinning mode which will be used for SSL connections. `AFSSLPinningModePublicKey` by default.
@discussion To enable SSL Pinning, `#define _AFNETWORKING_PIN_SSL_CERTIFICATES_` in `Prefix.pch`. Also, make sure that the Security framework is linked with the binary. See the "SSL Pinning" section in the `AFURLConnectionOperation`" header for more information.
*/
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
@property (nonatomic, assign) AFURLConnectionOperationSSLPinningMode SSLPinningMode;
#endif
///------------------------
/// @name Accessing Streams
///------------------------
@ -290,6 +320,25 @@
///----------------
/**
## SSL Pinning Options
The following constants are provided by `AFURLConnectionOperation` as possible SSL Pinning options.
enum {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
}
`AFSSLPinningModeNone`
Do not pin SSL connections
`AFSSLPinningModePublicKey`
Pin SSL connections to certificate public key (SPKI).
`AFSSLPinningModeCertificate`
Pin SSL connections to exact certificate. This may cause problems when your certificate expires and needs re-issuance.
## User info dictionary keys
These keys may exist in the user info dictionary, in addition to those defined for NSError.

View file

@ -145,6 +145,9 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
@dynamic inputStream;
@synthesize outputStream = _outputStream;
@synthesize credential = _credential;
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
@synthesize SSLPinningMode = _SSLPinningMode;
#endif
@synthesize shouldUseCredentialStorage = _shouldUseCredentialStorage;
@synthesize userInfo = _userInfo;
@synthesize backgroundTaskIdentifier = _backgroundTaskIdentifier;
@ -167,7 +170,6 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
@ -176,24 +178,61 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
return _networkRequestThread;
}
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
+ (NSArray *)pinnedCertificates {
static NSArray *_pinnedCertificates = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableArray *certificates = [NSMutableArray array];
NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
_pinnedCertificates = [[NSArray alloc] initWithArray:certificates];
});
return _pinnedCertificates;
}
+ (NSArray *)pinnedPublicKeys {
static NSArray *_pinnedPublicKeys = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSArray *pinnedCertificates = [self pinnedCertificates];
NSMutableArray *publicKeys = [NSMutableArray arrayWithCapacity:[pinnedCertificates count]];
for (NSData *data in pinnedCertificates) {
SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data);
NSCParameterAssert(allowedCertificate);
SecCertificateRef allowedCertificates[] = {allowedCertificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef allowedTrust = NULL;
OSStatus status = SecTrustCreateWithCertificates(certificates, policy, &allowedTrust);
NSAssert(status == noErr, @"SecTrustCreateWithCertificates error: %ld", (long int)status);
SecKeyRef allowedPublicKey = SecTrustCopyPublicKey(allowedTrust);
[publicKeys addObject:(__bridge_transfer id)allowedPublicKey];
CFRelease(allowedTrust);
CFRelease(policy);
CFRelease(certificates);
CFRelease(allowedCertificate);
}
_pinnedPublicKeys = [[NSArray alloc] initWithArray:publicKeys];
});
return _pinnedPublicKeys;
}
#endif
- (id)initWithRequest:(NSURLRequest *)urlRequest {
self = [super init];
if (!self) {
@ -344,19 +383,6 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
[self didChangeValueForKey:oldStateKey];
[self didChangeValueForKey:newStateKey];
[self.lock unlock];
dispatch_async(dispatch_get_main_queue(), ^{
switch (state) {
case AFOperationExecutingState:
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
break;
case AFOperationFinishedState:
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
break;
default:
break;
}
});
}
- (NSString *)responseString {
@ -371,7 +397,7 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
- (NSStringEncoding)responseStringEncoding {
[self.lock lock];
if (!_responseStringEncoding) {
if (!_responseStringEncoding && self.response) {
NSStringEncoding stringEncoding = NSUTF8StringEncoding;
if (self.response.textEncodingName) {
CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName);
@ -398,7 +424,8 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
[self.connection performSelector:@selector(cancel) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
});
}
@ -453,9 +480,7 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
- (void)operationDidStart {
[self.lock lock];
if ([self isCancelled]) {
[self finish];
} else {
if (! [self isCancelled]) {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
@ -467,10 +492,22 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
[self.connection start];
}
[self.lock unlock];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
});
if ([self isCancelled]) {
[self finish];
}
}
- (void)finish {
self.state = AFOperationFinishedState;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
});
}
- (void)cancel {
@ -516,13 +553,58 @@ willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challe
if ([[[self class] pinnedCertificates] containsObject:certificateData]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
switch (self.SSLPinningMode) {
case AFSSLPinningModePublicKey: {
id publicKey = (__bridge_transfer id)SecTrustCopyPublicKey(serverTrust);
if ([[self.class pinnedPublicKeys] containsObject:publicKey]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
break;
}
case AFSSLPinningModeCertificate: {
SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
NSData *serverCertificateData = (__bridge_transfer NSData *)SecCertificateCopyData(serverCertificate);
if ([[[self class] pinnedCertificates] containsObject:serverCertificateData]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
break;
}
case AFSSLPinningModeNone: {
#ifdef _AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
#else
SecTrustResultType result = 0;
OSStatus status = SecTrustEvaluate(serverTrust, &result);
NSAssert(status == noErr, @"SecTrustEvaluate error: %ld", (long int)status);
if (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
#endif
break;
}
}
}
}
}
#endif
- (BOOL)connection:(NSURLConnection *)connection
canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
@ -631,18 +713,18 @@ didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection __unused *)connection
didReceiveData:(NSData *)data
{
self.totalBytesRead += [data length];
if ([self.outputStream hasSpaceAvailable]) {
const uint8_t *dataBuffer = (uint8_t *) [data bytes];
[self.outputStream write:&dataBuffer[0] maxLength:[data length]];
}
if (self.downloadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
self.totalBytesRead += [data length];
if (self.downloadProgress) {
self.downloadProgress([data length], self.totalBytesRead, self.response.expectedContentLength);
});
}
});
}
- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {

View file

@ -24,11 +24,12 @@
#include <Availability.h>
static dispatch_queue_t af_xml_request_operation_processing_queue;
static dispatch_queue_t xml_request_operation_processing_queue() {
if (af_xml_request_operation_processing_queue == NULL) {
af_xml_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.xml-request.processing", 0);
}
static dispatch_queue_t af_xml_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_xml_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.xml-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_xml_request_operation_processing_queue;
}

View file

@ -87,7 +87,6 @@ static char kAFImageRequestOperationObjectKey;
placeholderImage:(UIImage *)placeholderImage
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPShouldHandleCookies:NO];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
@ -102,21 +101,22 @@ static char kAFImageRequestOperationObjectKey;
UIImage *cachedImage = [[[self class] af_sharedImageCache] cachedImageForRequest:urlRequest];
if (cachedImage) {
self.image = cachedImage;
self.af_imageRequestOperation = nil;
if (success) {
success(nil, nil, cachedImage);
} else {
self.image = cachedImage;
}
self.af_imageRequestOperation = nil;
} else {
self.image = placeholderImage;
AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if ([[urlRequest URL] isEqual:[[self.af_imageRequestOperation request] URL]]) {
if ([urlRequest isEqual:[self.af_imageRequestOperation request]]) {
if (success) {
success(operation.request, operation.response, responseObject);
} else {
} else if (responseObject) {
self.image = responseObject;
}
@ -127,7 +127,7 @@ static char kAFImageRequestOperationObjectKey;
[[[self class] af_sharedImageCache] cacheImage:responseObject forRequest:urlRequest];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if ([[urlRequest URL] isEqual:[[self.af_imageRequestOperation request] URL]]) {
if ([urlRequest isEqual:[self.af_imageRequestOperation request]]) {
if (failure) {
failure(operation.request, operation.response, error);
}

178
CHANGES
View file

@ -1,67 +1,195 @@
= 1.2.0 (2013-03-24)
* Add `SSLPinningMode` property to `AFHTTPClient` (Oliver Letterer, Kevin
Harwood, Adam Becevello, Dustin Barker, Mattt Thompson)
* Add single quote ("'"), comma (","), and asterix ("*") to escaped URL
encoding characters (Eric Florenzano, Marc Nijdam, Garrett Murray)
* Add `credential` property to `AFURLConnectionOperation` (Mattt Thompson)
* Add `-setDefaultCredential:` to `AFHTTPClient`
* Add `shouldUseCredentialStorage` property to `AFURLConnectionOperation`
(Mattt Thompson)
* Add support for repeated key value pairs in `AFHTTPClient` URL query string
(Nick Dawson)
* Add `AFMultipartFormData -
appendPartWithFileURL:name:fileName:mimeType:error` (Daniel Rodríguez Troitiño)
* Add `AFMultipartFormData -
appendPartWithInputStream:name:fileName:mimeType:` (@joein3d)
* Change SSL pinning to be runtime property on `AFURLConnectionOperation`
rather than defined by macro (Oliver Letterer)
* Change `AFMultipartBodyStream` to `AFMultipartBodyStreamProvider`, vending
one side of a bound CFStream pair rather than subclassing `NSInputStream` (Mike
Ash)
* Change default `Accept-Language` header in `AFHTTPClient` (@therigu, Mattt
Thompson)
* Change `AFHTTPClient` operation cancellation to be based on request URL path
rather than absolute URL string (Mattt Thompson)
* Change request operation subclass processing queues to use
`DISPATCH_QUEUE_CONCURRENT` (Mattt Thompson)
* Change `UIImageView+AFNetworking` to resolve asymmetry in cached image case
between success block provided and not provided (@Eveets, Mattt Thompson)
* Change `UIImageView+AFNetworking` to compare `NSURLRequest` instead of
`NSURL` to determine if previous request was equivalent (Cédric Luthi)
* Change `UIImageView+AFNetworking` to only set image if non-`nil` (Sean
Kovacs)
* Change indentation settings to four spaces at the project level (Cédric
Luthi)
* Change `AFNetworkActivityIndicatorManager` to only update if requests have a
non-`nil` URL (Cédric Luthi)
* Change `UIImageView+AFNetworking` to not do `setHTTPShouldHandleCookies`
(Konstantinos Vaggelakos)
* Fix request stream exhaustion error on authentication challenges (Alex
Burgel)
* Fix implementation to use `NSURL` methods instead of `CFURL` functions where
applicable (Cédric Luthi)
* Fix race condition in `UIImageView+AFNetworking` (Peyman)
* Fix `responseJSON`, `responseString`, and `responseStringEncoding` to be
threadsafe (Jon Parise, Mattt Thompson)
* Fix `AFContentTypeForPathExtension` to ensure non-`NULL` content return
value (Zach Waugh)
* Fix documentation for `appendPartWithFileURL:name:error:`
(Daniel Rodríguez Troitiño)
* Fix request operation subclass processing queues to initialize with
`dispatch_once` (Sasmito Adibowo)
* Fix posting of `AFNetworkingOperationDidStartNotification` and
`AFNetworkingOperationDidFinishNotification` to avoid crashes when logging in
response to notifications (Blake Watters)
* Fix ordering of registered operation consultation in `AFHTTPClient` (Joel
Parsons)
* Fix warning: multiple methods named 'postNotificationName:object:' found
[-Wstrict-selector-match] (Oliver Jones)
* Fix warning: multiple methods named 'objectForKey:' found
[-Wstrict-selector-match] (Oliver Jones)
* Fix warning: weak receiver may be unpredictably set to nil
[-Wreceiver-is-weak] (Oliver Jones)
* Fix missing #pragma clang diagnostic pop (Steven Fisher)
= 1.1.0 (2012-12-27)
* Add optional SSL certificate pinning with `#define _AFNETWORKING_PIN_SSL_CERTIFICATES_` (Dustin Barker)
* Add optional SSL certificate pinning with `#define
_AFNETWORKING_PIN_SSL_CERTIFICATES_` (Dustin Barker)
* Add `responseStringEncoding` property to `AFURLConnectionOperation` (Mattt Thompson)
* Add `responseStringEncoding` property to `AFURLConnectionOperation` (Mattt
Thompson)
* Add `userInfo` property to `AFURLConnectionOperation` (Mattt Thompson, Steven Fisher)
* Add `userInfo` property to `AFURLConnectionOperation` (Mattt Thompson,
Steven Fisher)
* Change behavior to cause a failure when an operation is cancelled (Daniel Tull)
* Change behavior to cause a failure when an operation is cancelled (Daniel
Tull)
* Change return type of class constructors to `instancetype` (@guykogus)
* Change notifications to always being posted on an asynchronously-dispatched block run on the main queue (Evadne Wu, Mattt Thompson)
* Change notifications to always being posted on an asynchronously-dispatched
block run on the main queue (Evadne Wu, Mattt Thompson)
* Change from NSLocalizedString to NSLocalizedStringFromTable with AFNetworking.strings table for localized strings (Cédric Luthi)
* Change from NSLocalizedString to NSLocalizedStringFromTable with
AFNetworking.strings table for localized strings (Cédric Luthi)
* Change `-appendPartWithHeaders:body:` to add assertion handler for existence of body data parameter (Jonathan Beilin)
* Change `-appendPartWithHeaders:body:` to add assertion handler for existence
of body data parameter (Jonathan Beilin)
* Change `AFHTTPRequestOperation -responseString` to follow guidelines from RFC 2616 regarding the use of string encoding when none is specified in the response (Jorge Bernal)
* Change `AFHTTPRequestOperation -responseString` to follow guidelines from
RFC 2616 regarding the use of string encoding when none is specified in the
response (Jorge Bernal)
* Change AFHTTPClient parameter serialization dictionary keys with `caseInsensitiveCompare:` to ensure
* Change AFHTTPClient parameter serialization dictionary keys with
`caseInsensitiveCompare:` to ensure
deterministic ordering of query string parameters, which may otherwise
cause ambiguous representations of nested parameters (James Coleman,
Mattt Thompson)
* Fix -Wstrict-selector-match warnings raised by Xcode 4.6DP3 (Jesse Collis, Cédric Luthi)
* Fix -Wstrict-selector-match warnings raised by Xcode 4.6DP3 (Jesse Collis,
Cédric Luthi)
* Fix NSJSONSerialization crash with Unicode character escapes in JSON response (Mathijs Kadijk)
* Fix NSJSONSerialization crash with Unicode character escapes in JSON
response (Mathijs Kadijk)
* Fix issue with early return in -startMonitoringNetworkReachability if network reachability object could not be created (i.e. invalid hostnames) (Basil Shkara)
* Fix issue with early return in -startMonitoringNetworkReachability if
network reachability object could not be created (i.e. invalid hostnames)
(Basil Shkara)
* Fix retain cycles in AFImageRequestOperation.m and AFHTTPClient.m caused by strong references within blocks (Nick Forge)
* Fix retain cycles in AFImageRequestOperation.m and AFHTTPClient.m caused by
strong references within blocks (Nick Forge)
* Fix issue caused by Rails behavior of returning a single space in head :ok responses, which is interpreted as invalid (Sebastian Ludwig)
* Fix issue caused by Rails behavior of returning a single space in head :ok
responses, which is interpreted as invalid (Sebastian Ludwig)
* Fix issue in streaming multipart upload, where final encapsulation boundary would not be appended if it was larger than the available buffer, causing a potential timeout (Tomohisa Takaoka, David Kasper)
* Fix issue in streaming multipart upload, where final encapsulation boundary
would not be appended if it was larger than the available buffer, causing a
potential timeout (Tomohisa Takaoka, David Kasper)
* Fix memory leak of network reachability callback block (Mattt Thompson)
* Fix `-initWithCoder:` for `AFURLConnectionOperation` and `AFHTTPClient` to cast scalar types (Mattt Thompson)
* Fix `-initWithCoder:` for `AFURLConnectionOperation` and `AFHTTPClient` to
cast scalar types (Mattt Thompson)
* Fix bug in `-enqueueBatchOfHTTPRequestOperations:...` to by using `addOperations:waitUntilFinished:` instead of adding each operation individually. (Mattt Thompson)
* Fix bug in `-enqueueBatchOfHTTPRequestOperations:...` to by using
`addOperations:waitUntilFinished:` instead of adding each operation
individually. (Mattt Thompson)
* Change `#warning` messages of checks for `CoreServices` and `MobileCoreServices` to message according to the build target platform (Mattt Thompson)
* Change `#warning` messages of checks for `CoreServices` and
`MobileCoreServices` to message according to the build target platform (Mattt
Thompson)
* Change `AFQueryStringFromParametersWithEncoding` to create keys string representations using the description method as specified in documentation (Cédric Luthi)
* Change `AFQueryStringFromParametersWithEncoding` to create keys string
representations using the description method as specified in documentation
(Cédric Luthi)
* Fix __unused keywords for better Xcode indexing (Christian Rasmussen)
* Fix warning: unused parameter 'x' [-Werror,-Wunused-parameter] (Oliver Jones)
* Fix warning: property is assumed atomic by default [-Werror,-Wimplicit-atomic-properties] (Oliver Jones)
* Fix warning: property is assumed atomic by default
[-Werror,-Wimplicit-atomic-properties] (Oliver Jones)
* Fix warning: weak receiver may be unpredictably null in ARC mode [-Werror,-Wreceiver-is-weak] (Oliver Jones)
* Fix warning: weak receiver may be unpredictably null in ARC mode
[-Werror,-Wreceiver-is-weak] (Oliver Jones)
* Fix warning: multiple methods named 'selector' found [-Werror,-Wstrict-selector-match] (Oliver Jones)
* Fix warning: multiple methods named 'selector' found
[-Werror,-Wstrict-selector-match] (Oliver Jones)
* Fix warning: 'macro' is not defined, evaluates to 0 (Oliver Jones)
* Fix warning: atomic by default property 'X' has a user (Oliver Jones)defined getter (property should be marked 'atomic' if this is intended) [-Werror, -Wcustom-atomic-properties] (Oliver Jones)
* Fix warning: atomic by default property 'X' has a user (Oliver Jones)defined
getter (property should be marked 'atomic' if this is intended) [-Werror,
-Wcustom-atomic-properties] (Oliver Jones)
* Fix warning: 'response' was marked unused but was used [-Werror,-Wused-but-marked-unused] (Oliver Jones)
* Fix warning: 'response' was marked unused but was used
[-Werror,-Wused-but-marked-unused] (Oliver Jones)
* Fix warning: enumeration value 'AFFinalBoundaryPhase' not explicitly handled in switch [-Werror,-Wswitch-enum] (Oliver Jones)
* Fix warning: enumeration value 'AFFinalBoundaryPhase' not explicitly handled
in switch [-Werror,-Wswitch-enum] (Oliver Jones)
= 1.0.1 / 2012-11-01

View file

@ -21,6 +21,7 @@
F82EB080159A172000B10B56 /* AFPropertyListRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB077159A172000B10B56 /* AFPropertyListRequestOperation.m */; };
F82EB081159A172000B10B56 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB079159A172000B10B56 /* AFURLConnectionOperation.m */; };
F82EB082159A172000B10B56 /* AFXMLRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB07B159A172000B10B56 /* AFXMLRequestOperation.m */; };
F88812F216C533E9003C8B8C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F88812F116C533E9003C8B8C /* Security.framework */; };
F8A847CF161F55A500940F39 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8A847CE161F55A500940F39 /* CoreServices.framework */; };
F8A847D2161F55AC00940F39 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */; };
/* End PBXBuildFile section */
@ -57,6 +58,7 @@
F82EB07A159A172000B10B56 /* AFXMLRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFXMLRequestOperation.h; path = ../AFNetworking/AFXMLRequestOperation.h; sourceTree = "<group>"; };
F82EB07B159A172000B10B56 /* AFXMLRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFXMLRequestOperation.m; path = ../AFNetworking/AFXMLRequestOperation.m; sourceTree = "<group>"; };
F877018B159A1CE700B45C0D /* AFNetworking Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "AFNetworking Example.entitlements"; sourceTree = "<group>"; };
F88812F116C533E9003C8B8C /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
F8A847CE161F55A500940F39 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
@ -66,6 +68,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F88812F216C533E9003C8B8C /* Security.framework in Frameworks */,
F8A847D2161F55AC00940F39 /* SystemConfiguration.framework in Frameworks */,
F8A847CF161F55A500940F39 /* CoreServices.framework in Frameworks */,
F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */,
@ -78,6 +81,7 @@
F8129BF01591061B009BFE23 = {
isa = PBXGroup;
children = (
F88812F116C533E9003C8B8C /* Security.framework */,
F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */,
F8A847CE161F55A500940F39 /* CoreServices.framework */,
F877018B159A1CE700B45C0D /* AFNetworking Example.entitlements */,
@ -86,7 +90,10 @@
F8129BFE1591061B009BFE23 /* Frameworks */,
F8129BFC1591061B009BFE23 /* Products */,
);
indentWidth = 4;
sourceTree = "<group>";
tabWidth = 4;
usesTabs = 0;
};
F8129BFC1591061B009BFE23 /* Products */ = {
isa = PBXGroup;
@ -206,7 +213,7 @@
F8129BF21591061B009BFE23 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0450;
LastUpgradeCheck = 0460;
};
buildConfigurationList = F8129BF51591061B009BFE23 /* Build configuration list for PBXProject "AFNetworking Mac Example" */;
compatibilityVersion = "Xcode 3.2";
@ -265,6 +272,10 @@
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
@ -292,6 +303,10 @@
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;

View file

@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
F8129C7415910C37009BFE23 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C7215910C37009BFE23 /* AppDelegate.m */; };
F818101615E6A0C600EF93C2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */; };
F88812F016C533D6003C8B8C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E469E013957DF100DB05C8 /* Security.framework */; };
F8A847C1161F51A300940F39 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C0161F51A300940F39 /* Default-568h@2x.png */; };
F8A847C3161F523E00940F39 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C2161F523E00940F39 /* Default.png */; };
F8A847C5161F524200940F39 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C4161F524200940F39 /* Default@2x.png */; };
@ -95,6 +96,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F88812F016C533D6003C8B8C /* Security.framework in Frameworks */,
F8E469651395739D00DB05C8 /* UIKit.framework in Frameworks */,
F8E469671395739D00DB05C8 /* Foundation.framework in Frameworks */,
F8E469691395739D00DB05C8 /* CoreGraphics.framework in Frameworks */,
@ -152,7 +154,10 @@
F8E469631395739D00DB05C8 /* Frameworks */,
F8E469611395739C00DB05C8 /* Products */,
);
indentWidth = 4;
sourceTree = "<group>";
tabWidth = 4;
usesTabs = 0;
};
F8E469611395739C00DB05C8 /* Products */ = {
isa = PBXGroup;

View file

@ -26,9 +26,9 @@ extern NSString * const kUserProfileImageDidLoadNotification;
@interface User : NSObject
@property (readonly) NSUInteger userID;
@property (readonly) NSString *username;
@property (unsafe_unretained, readonly) NSURL *avatarImageURL;
@property (readonly, nonatomic) NSUInteger userID;
@property (readonly, nonatomic) NSString *username;
@property (readonly, nonatomic, unsafe_unretained) NSURL *avatarImageURL;
- (id)initWithAttributes:(NSDictionary *)attributes;

View file

@ -1,5 +1,7 @@
#import <Availability.h>
#define _AFNETWORKING_PIN_SSL_CERTIFICATES_
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."