Compare commits
12 commits
ebe5c7af31
...
5266874bcc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5266874bcc | ||
|
|
e7bc1679c7 | ||
|
|
b3d44e2fc3 | ||
|
|
55ef458688 | ||
|
|
5c237a66eb | ||
|
|
d74e568cf2 | ||
|
|
ab08e149e2 | ||
|
|
4fa43bd41c | ||
|
|
24816f52a4 | ||
|
|
353163c09c | ||
|
|
6aa3346e98 | ||
|
|
a89fede6dd |
7 changed files with 86 additions and 43 deletions
|
|
@ -1,11 +1,11 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'AFNetworking'
|
s.name = 'AFNetworking'
|
||||||
s.version = '1.3.3'
|
s.version = '1.3.4'
|
||||||
s.license = 'MIT'
|
s.license = 'MIT'
|
||||||
s.summary = 'A delightful iOS and OS X networking framework.'
|
s.summary = 'A delightful iOS and OS X networking framework.'
|
||||||
s.homepage = 'https://github.com/AFNetworking/AFNetworking'
|
s.homepage = 'https://github.com/AFNetworking/AFNetworking'
|
||||||
s.authors = { 'Mattt Thompson' => 'm@mattt.me', 'Scott Raymond' => 'sco@gowalla.com' }
|
s.authors = { 'Mattt Thompson' => 'm@mattt.me', 'Scott Raymond' => 'sco@gowalla.com' }
|
||||||
s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => '1.3.3' }
|
s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => '1.3.4' }
|
||||||
s.source_files = 'AFNetworking'
|
s.source_files = 'AFNetworking'
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -272,9 +272,10 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
|
||||||
if (userAgent) {
|
if (userAgent) {
|
||||||
if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
|
if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
|
||||||
NSMutableString *mutableUserAgent = [userAgent mutableCopy];
|
NSMutableString *mutableUserAgent = [userAgent mutableCopy];
|
||||||
CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, kCFStringTransformToLatin, false);
|
if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, kCFStringTransformToLatin, false)) {
|
||||||
userAgent = mutableUserAgent;
|
userAgent = mutableUserAgent;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
[self setDefaultHeader:@"User-Agent" value:userAgent];
|
[self setDefaultHeader:@"User-Agent" value:userAgent];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -758,22 +759,22 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
static NSString * const kAFMultipartFormBoundary = @"Boundary+0xAbCdEfGbOuNdArY";
|
static NSString * AFCreateMultipartFormBoundary() {
|
||||||
|
return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()];
|
||||||
|
}
|
||||||
|
|
||||||
static NSString * const kAFMultipartFormCRLF = @"\r\n";
|
static NSString * const kAFMultipartFormCRLF = @"\r\n";
|
||||||
|
|
||||||
static NSInteger const kAFStreamToStreamBufferSize = 1024 * 1024; //1 meg default
|
static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) {
|
||||||
|
return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF];
|
||||||
static inline NSString * AFMultipartFormInitialBoundary() {
|
|
||||||
return [NSString stringWithFormat:@"--%@%@", kAFMultipartFormBoundary, kAFMultipartFormCRLF];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline NSString * AFMultipartFormEncapsulationBoundary() {
|
static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) {
|
||||||
return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF];
|
return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline NSString * AFMultipartFormFinalBoundary() {
|
static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) {
|
||||||
return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF];
|
return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
|
static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
|
||||||
|
|
@ -800,6 +801,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
@property (nonatomic, assign) unsigned long long bodyContentLength;
|
@property (nonatomic, assign) unsigned long long bodyContentLength;
|
||||||
@property (nonatomic, strong) NSInputStream *inputStream;
|
@property (nonatomic, strong) NSInputStream *inputStream;
|
||||||
|
|
||||||
|
@property (nonatomic, copy) NSString *boundary;
|
||||||
@property (nonatomic, assign) BOOL hasInitialBoundary;
|
@property (nonatomic, assign) BOOL hasInitialBoundary;
|
||||||
@property (nonatomic, assign) BOOL hasFinalBoundary;
|
@property (nonatomic, assign) BOOL hasFinalBoundary;
|
||||||
|
|
||||||
|
|
@ -826,6 +828,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
|
|
||||||
@interface AFStreamingMultipartFormData ()
|
@interface AFStreamingMultipartFormData ()
|
||||||
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
|
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
|
||||||
|
@property (nonatomic, copy) NSString *boundary;
|
||||||
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
|
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
|
||||||
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
|
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
|
||||||
@end
|
@end
|
||||||
|
|
@ -845,6 +848,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
|
|
||||||
self.request = urlRequest;
|
self.request = urlRequest;
|
||||||
self.stringEncoding = encoding;
|
self.stringEncoding = encoding;
|
||||||
|
self.boundary = AFCreateMultipartFormBoundary();
|
||||||
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
|
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|
@ -897,6 +901,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
||||||
bodyPart.stringEncoding = self.stringEncoding;
|
bodyPart.stringEncoding = self.stringEncoding;
|
||||||
bodyPart.headers = mutableHeaders;
|
bodyPart.headers = mutableHeaders;
|
||||||
|
bodyPart.boundary = self.boundary;
|
||||||
bodyPart.body = fileURL;
|
bodyPart.body = fileURL;
|
||||||
|
|
||||||
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:nil];
|
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:nil];
|
||||||
|
|
@ -926,6 +931,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
||||||
bodyPart.stringEncoding = self.stringEncoding;
|
bodyPart.stringEncoding = self.stringEncoding;
|
||||||
bodyPart.headers = mutableHeaders;
|
bodyPart.headers = mutableHeaders;
|
||||||
|
bodyPart.boundary = self.boundary;
|
||||||
bodyPart.body = inputStream;
|
bodyPart.body = inputStream;
|
||||||
|
|
||||||
bodyPart.bodyContentLength = length;
|
bodyPart.bodyContentLength = length;
|
||||||
|
|
@ -968,6 +974,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
||||||
bodyPart.stringEncoding = self.stringEncoding;
|
bodyPart.stringEncoding = self.stringEncoding;
|
||||||
bodyPart.headers = headers;
|
bodyPart.headers = headers;
|
||||||
|
bodyPart.boundary = self.boundary;
|
||||||
bodyPart.bodyContentLength = [body length];
|
bodyPart.bodyContentLength = [body length];
|
||||||
bodyPart.body = body;
|
bodyPart.body = body;
|
||||||
|
|
||||||
|
|
@ -989,7 +996,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
// Reset the initial and final boundaries to ensure correct Content-Length
|
// Reset the initial and final boundaries to ensure correct Content-Length
|
||||||
[self.bodyStream setInitialAndFinalBoundaries];
|
[self.bodyStream setInitialAndFinalBoundaries];
|
||||||
|
|
||||||
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"];
|
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
|
||||||
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
|
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
|
||||||
[self.request setHTTPBodyStream:self.bodyStream];
|
[self.request setHTTPBodyStream:self.bodyStream];
|
||||||
|
|
||||||
|
|
@ -1253,7 +1260,7 @@ typedef enum {
|
||||||
- (unsigned long long)contentLength {
|
- (unsigned long long)contentLength {
|
||||||
unsigned long long length = 0;
|
unsigned long long length = 0;
|
||||||
|
|
||||||
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary() : AFMultipartFormEncapsulationBoundary()) dataUsingEncoding:self.stringEncoding];
|
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
|
||||||
length += [encapsulationBoundaryData length];
|
length += [encapsulationBoundaryData length];
|
||||||
|
|
||||||
NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
|
NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
|
||||||
|
|
@ -1261,7 +1268,7 @@ typedef enum {
|
||||||
|
|
||||||
length += _bodyContentLength;
|
length += _bodyContentLength;
|
||||||
|
|
||||||
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary() dataUsingEncoding:self.stringEncoding] : [NSData data]);
|
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
|
||||||
length += [closingBoundaryData length];
|
length += [closingBoundaryData length];
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
|
|
@ -1297,7 +1304,7 @@ typedef enum {
|
||||||
NSUInteger totalNumberOfBytesRead = 0;
|
NSUInteger totalNumberOfBytesRead = 0;
|
||||||
|
|
||||||
if (_phase == AFEncapsulationBoundaryPhase) {
|
if (_phase == AFEncapsulationBoundaryPhase) {
|
||||||
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary() : AFMultipartFormEncapsulationBoundary()) dataUsingEncoding:self.stringEncoding];
|
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
|
||||||
totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
|
totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1322,7 +1329,7 @@ typedef enum {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_phase == AFFinalBoundaryPhase) {
|
if (_phase == AFFinalBoundaryPhase) {
|
||||||
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary() dataUsingEncoding:self.stringEncoding] : [NSData data]);
|
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
|
||||||
totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
|
totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,8 @@ static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL
|
||||||
[userInfo setValue:self.response forKey:AFNetworkingOperationFailingURLResponseErrorKey];
|
[userInfo setValue:self.response forKey:AFNetworkingOperationFailingURLResponseErrorKey];
|
||||||
|
|
||||||
if (![self hasAcceptableStatusCode]) {
|
if (![self hasAcceptableStatusCode]) {
|
||||||
NSUInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? (NSUInteger)[self.response statusCode] : 200;
|
NSInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? [self.response statusCode] : 200;
|
||||||
[userInfo setValue:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Expected status code in (%@), got %d", @"AFNetworking", nil), AFStringFromIndexSet([[self class] acceptableStatusCodes]), statusCode] forKey:NSLocalizedDescriptionKey];
|
[userInfo setValue:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Expected status code in (%@), got %ld", @"AFNetworking", nil), AFStringFromIndexSet([[self class] acceptableStatusCodes]), (long)statusCode] forKey:NSLocalizedDescriptionKey];
|
||||||
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo];
|
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo];
|
||||||
} else if (![self hasAcceptableContentType]) {
|
} else if (![self hasAcceptableContentType]) {
|
||||||
// Don't invalidate content type if there is no content
|
// Don't invalidate content type if there is no content
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,13 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
|
||||||
static NSData *AFSecKeyGetData(SecKeyRef key) {
|
static NSData *AFSecKeyGetData(SecKeyRef key) {
|
||||||
CFDataRef data = NULL;
|
CFDataRef data = NULL;
|
||||||
|
|
||||||
|
#if defined(NS_BLOCK_ASSERTIONS)
|
||||||
|
SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data);
|
||||||
|
#else
|
||||||
OSStatus status = SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data);
|
OSStatus status = SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data);
|
||||||
NSCAssert(status == errSecSuccess, @"SecItemExport error: %ld", (long int)status);
|
NSCAssert(status == errSecSuccess, @"SecItemExport error: %ld", (long int)status);
|
||||||
|
#endif
|
||||||
|
|
||||||
NSCParameterAssert(data);
|
NSCParameterAssert(data);
|
||||||
|
|
||||||
return (__bridge_transfer NSData *)data;
|
return (__bridge_transfer NSData *)data;
|
||||||
|
|
@ -587,6 +592,7 @@ willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challe
|
||||||
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
|
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
|
||||||
|
|
||||||
SecPolicyRef policy = SecPolicyCreateBasicX509();
|
SecPolicyRef policy = SecPolicyCreateBasicX509();
|
||||||
|
SecTrustEvaluate(serverTrust, NULL);
|
||||||
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
|
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
|
||||||
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:certificateCount];
|
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:certificateCount];
|
||||||
|
|
||||||
|
|
@ -733,16 +739,20 @@ didReceiveResponse:(NSURLResponse *)response
|
||||||
while (totalNumberOfBytesWritten < length) {
|
while (totalNumberOfBytesWritten < length) {
|
||||||
numberOfBytesWritten = [self.outputStream write:&dataBuffer[0] maxLength:length];
|
numberOfBytesWritten = [self.outputStream write:&dataBuffer[0] maxLength:length];
|
||||||
if (numberOfBytesWritten == -1) {
|
if (numberOfBytesWritten == -1) {
|
||||||
[self.connection cancel];
|
break;
|
||||||
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
totalNumberOfBytesWritten += numberOfBytesWritten;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalNumberOfBytesWritten += numberOfBytesWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self.outputStream.streamError) {
|
||||||
|
[self.connection cancel];
|
||||||
|
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
|
|
||||||
28
CHANGES
28
CHANGES
|
|
@ -1,3 +1,31 @@
|
||||||
|
= 1.3.4 (2014-04-15)
|
||||||
|
|
||||||
|
* Fix `AFHTTPMultipartBodyStream` to randomly generate form boundary, to
|
||||||
|
prevent attack based on a known value (Mathias Bynens, Tom Van Goethem, Mattt
|
||||||
|
Thompson)
|
||||||
|
|
||||||
|
* Fix potential non-terminating loop in `connection:didReceiveData:` (Mattt
|
||||||
|
Thompson)
|
||||||
|
|
||||||
|
* Fix SSL certificate validation to provide a human readable Warning when
|
||||||
|
SSL Pinning fails (Maximillian Dornseif)
|
||||||
|
|
||||||
|
* Fix SSL certificate validation to assert that no impossible pinning
|
||||||
|
configuration exists (Maximillian Dornseif)
|
||||||
|
|
||||||
|
* Fix to check `CFStringTransform()` call for success before using result
|
||||||
|
(Kevin Cassidy Jr)
|
||||||
|
|
||||||
|
* Fix to prevent unused assertion results with macros (Indragie Karunaratne)
|
||||||
|
|
||||||
|
* Fix to call call `SecTrustEvaluate` before calling
|
||||||
|
`SecTrustGetCertificateCount` in SSL certificate validation (Josh Chung)
|
||||||
|
|
||||||
|
* Fix to add explicit cast to `NSUInteger` in format string (Alexander
|
||||||
|
Kempgen)
|
||||||
|
|
||||||
|
* Remove unused variable `kAFStreamToStreamBufferSize` (Alexander Kempgen)
|
||||||
|
|
||||||
= 1.3.3 (2013-09-25)
|
= 1.3.3 (2013-09-25)
|
||||||
|
|
||||||
* Add stream error handling to `AFMultipartBodyStream` (Nicolas Bachschmidt,
|
* Add stream error handling to `AFMultipartBodyStream` (Nicolas Bachschmidt,
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com/)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
<img src="https://raw.github.com/AFNetworking/AFNetworking/assets/afnetworking-logo.png" alt="AFNetworking" title="AFNetworking">
|
<img src="https://raw.github.com/AFNetworking/AFNetworking/assets/afnetworking-logo.png" alt="AFNetworking" title="AFNetworking">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://travis-ci.org/AFNetworking/AFNetworking)
|
|
||||||
|
|
||||||
AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of [NSURLConnection](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html), [NSOperation](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html), and other familiar Foundation technologies. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use. For example, here's how easy it is to get JSON from a URL:
|
AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of [NSURLConnection](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html), [NSOperation](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html), and other familiar Foundation technologies. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use. For example, here's how easy it is to get JSON from a URL:
|
||||||
|
|
||||||
```objective-c
|
```objective-c
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue