Ben Copsey

Added basic unit tests

Added responseStatusCode property
Various cleanups
... ... @@ -26,9 +26,12 @@
//Files that will be POSTed to the url
NSMutableDictionary *fileData;
//Dictionary for custom request headers
//Dictionary for custom HTTP request headers
NSMutableDictionary *requestHeaders;
//Will be populate with HTTP response headers from the server
NSDictionary *responseHeaders;
//If useKeychainPersistance is true, network requests will attempt to read credentials from the keychain, and will save them in the keychain when they are successfully presented
BOOL useKeychainPersistance;
... ... @@ -72,11 +75,15 @@
CFReadStreamRef readStream;
// Authentication currently being used for prompting and resuming
CFHTTPAuthenticationRef authentication;
CFHTTPAuthenticationRef requestAuthentication;
NSMutableDictionary *requestCredentials;
// Credentials associated with the authentication (reused until server says no)
//CFMutableDictionaryRef credentials;
// HTTP status code, eg: 200 = OK, 404 = Not found etc
int responseStatusCode;
//Size of the response
double contentLength;
... ... @@ -103,8 +110,6 @@
//Called on the delegate when the request fails
SEL didFailSelector;
NSDictionary *responseHeaders;
NSMutableDictionary *requestCredentials;
}
... ... @@ -113,9 +118,6 @@
// Should be an HTTP or HTTPS url, may include username and password if appropriate
- (id)initWithURL:(NSURL *)newURL;
#pragma mark delegate configuration
#pragma mark setup request
//Add a custom header to the request
... ... @@ -159,10 +161,10 @@
#pragma mark handling request complete / failure
//Called when a request completes successfully - defaults to: @selector(requestFinished:)
// Called when a request completes successfully - defaults to: @selector(requestFinished:)
- (void)requestFinished;
//Called when a request fails - defaults to: @selector(requestFailed:)
// Called when a request fails - defaults to: @selector(requestFailed:)
- (void)failWithProblem:(NSString *)problem;
#pragma mark http authentication stuff
... ... @@ -170,6 +172,12 @@
// Reads the response headers to find the content length, and returns true if the request needs a username and password (or if those supplied were incorrect)
- (BOOL)readResponseHeadersReturningAuthenticationFailure;
// Apply credentials to this request
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials;
// Attempt to obtain credentials for this request from the URL, username and password or keychain
- (NSMutableDictionary *)findCredentials;
// Unlock (unpause) the request thread so it can resume the request
// Should be called by delegates when they have populated the authentication information after an authentication challenge
- (void)retryWithAuthentication;
... ... @@ -188,16 +196,23 @@
- (void)handleStreamComplete;
- (void)handleStreamError;
#pragma mark managing the session
+ (void)setSessionCredentials:(NSMutableDictionary *)newCredentials;
+ (void)setSessionAuthentication:(CFHTTPAuthenticationRef)newAuthentication;
#pragma mark keychain storage
//Save credentials to the keychain
// Save credentials for this request to the keychain
- (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials;
// Save creddentials to the keychain
+ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
//Return credentials from the keychain
// Return credentials from the keychain
+ (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
//Remove credentials from the keychain
// Remove credentials from the keychain
+ (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
... ... @@ -215,9 +230,7 @@
@property (assign,readonly) BOOL complete;
@property (retain) NSDictionary *responseHeaders;
@property (retain) NSDictionary *requestCredentials;
- (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials;
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials;
@property (assign) int responseStatusCode;
@end
... ...
... ... @@ -33,12 +33,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
- (id)initWithURL:(NSURL *)newURL
{
[self init];
url = [newURL retain];
return self;
}
- (id)init {
[super init];
lastBytesSent = 0;
postData = nil;
... ... @@ -48,7 +42,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
requestHeaders = nil;
authenticationRealm = nil;
outputStream = nil;
authentication = NULL;
requestAuthentication = NULL;
//credentials = NULL;
request = NULL;
responseHeaders = nil;
... ... @@ -57,13 +51,14 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
didFinishSelector = @selector(requestFinished:);
didFailSelector = @selector(requestFailed:);
delegate = nil;
return self;
url = [newURL retain];
return self;
}
- (void)dealloc
{
if (authentication) {
CFRelease(authentication);
if (requestAuthentication) {
CFRelease(requestAuthentication);
}
if (request) {
CFRelease(request);
... ... @@ -146,12 +141,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
#pragma mark request logic
+ (void)setSessionCredentials:(NSMutableDictionary *)newCredentials
{
[sessionCredentials release];
sessionCredentials = [newCredentials retain];
}
// Create the request
- (void)main
{
... ... @@ -173,8 +162,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
//If we've already talked to this server and have valid credentials, let's apply them to the request
if (useSessionPersistance && sessionCredentials && sessionAuthentication) {
if (!CFHTTPMessageApplyCredentialDictionary(request, sessionAuthentication, (CFMutableDictionaryRef)sessionCredentials, NULL)) {
CFRelease(sessionAuthentication);
sessionAuthentication = NULL;
[ASIHTTPRequest setSessionAuthentication:NULL];
[ASIHTTPRequest setSessionCredentials:nil];
}
}
... ... @@ -415,9 +403,10 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
CFHTTPMessageRef headers = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
if (CFHTTPMessageIsHeaderComplete(headers)) {
responseHeaders = (NSDictionary *)CFHTTPMessageCopyAllHeaderFields(headers);
responseStatusCode = CFHTTPMessageGetResponseStatusCode(headers);
// Is the server response a challenge for credentials?
isAuthenticationChallenge = (CFHTTPMessageGetResponseStatusCode(headers) == 401);
isAuthenticationChallenge = (responseStatusCode == 401);
//We won't reset the download progress delegate if we got an authentication challenge
if (!isAuthenticationChallenge) {
... ... @@ -438,13 +427,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
}
// Called by delegate to resume loading once authentication info has been populated
- (void)retryWithAuthentication
{
[authenticationLock lockWhenCondition:1];
[authenticationLock unlockWithCondition:2];
}
- (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials
{
NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationUsername]
... ... @@ -458,20 +440,16 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials
{
if (newCredentials && authentication && request) {
if (newCredentials && requestAuthentication && request) {
// Apply whatever credentials we've built up to the old request
if (CFHTTPMessageApplyCredentialDictionary(request, authentication, (CFMutableDictionaryRef)newCredentials, NULL)) {
if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) {
//If we have credentials and they're ok, let's save them to the keychain
if (useKeychainPersistance) {
[self saveCredentialsToKeychain:newCredentials];
}
if (useSessionPersistance) {
if (sessionAuthentication) {
CFRelease(sessionAuthentication);
}
sessionAuthentication = authentication;
CFRetain(sessionAuthentication);
[ASIHTTPRequest setSessionAuthentication:requestAuthentication];
[ASIHTTPRequest setSessionCredentials:newCredentials];
}
[self setRequestCredentials:newCredentials];
... ... @@ -481,15 +459,15 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
return FALSE;
}
- (NSMutableDictionary *)getCredentials
- (NSMutableDictionary *)findCredentials
{
NSMutableDictionary *newCredentials = [[[NSMutableDictionary alloc] init] autorelease];
// Get the authentication realm
[authenticationRealm release];
authenticationRealm = nil;
if (!CFHTTPAuthenticationRequiresAccountDomain(authentication)) {
authenticationRealm = (NSString *)CFHTTPAuthenticationCopyRealm(authentication);
if (!CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) {
authenticationRealm = (NSString *)CFHTTPAuthenticationCopyRealm(requestAuthentication);
}
//First, let's look at the url to see if the username and password were included
... ... @@ -519,30 +497,37 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
[newCredentials setObject:pass forKey:(NSString *)kCFHTTPAuthenticationPassword];
return newCredentials;
}
return NULL;
return nil;
}
// Called by delegate to resume loading once authentication info has been populated
- (void)retryWithAuthentication
{
[authenticationLock lockWhenCondition:1];
[authenticationLock unlockWithCondition:2];
}
- (void)attemptToApplyCredentialsAndResume
{
//Read authentication data
if (!authentication) {
if (!requestAuthentication) {
CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream,kCFStreamPropertyHTTPResponseHeader);
authentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
CFRelease(responseHeader);
}
if (!authentication) {
if (!requestAuthentication) {
[self failWithProblem:@"Failed to get authentication object from response headers"];
return;
}
//See if authentication is valid
CFStreamError err;
if (!CFHTTPAuthenticationIsValid(authentication, &err)) {
if (!CFHTTPAuthenticationIsValid(requestAuthentication, &err)) {
CFRelease(authentication);
authentication = NULL;
CFRelease(requestAuthentication);
requestAuthentication = NULL;
// check for bad credentials, so we can give the delegate a chance to replace them
if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) {
... ... @@ -572,9 +557,9 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
}
// are a user name & password needed?
} else if (CFHTTPAuthenticationRequiresUserNameAndPassword(authentication)) {
} else if (CFHTTPAuthenticationRequiresUserNameAndPassword(requestAuthentication)) {
NSMutableDictionary *newCredentials = [self getCredentials];
NSMutableDictionary *newCredentials = [self findCredentials];
//If we have some credentials to use let's apply them to the request and continue
if (newCredentials) {
... ... @@ -714,6 +699,24 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
}
}
#pragma mark managing the session
+ (void)setSessionCredentials:(NSMutableDictionary *)newCredentials
{
[sessionCredentials release];
sessionCredentials = [newCredentials retain];
}
+ (void)setSessionAuthentication:(CFHTTPAuthenticationRef)newAuthentication
{
if (sessionAuthentication) {
CFRelease(sessionAuthentication);
}
sessionAuthentication = newAuthentication;
if (newAuthentication) {
CFRetain(sessionAuthentication);
}
}
#pragma mark keychain storage
... ... @@ -774,4 +777,5 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
@synthesize complete;
@synthesize responseHeaders;
@synthesize requestCredentials;
@synthesize responseStatusCode;
@end
... ...
//
// ASIHTTPRequestTests.h
// asi-http-request
//
// Created by Ben Copsey on 01/08/2008.
// Copyright 2008 All-Seeing Interactive. All rights reserved.
//
#import <SenTestingKit/SenTestingKit.h>
@interface ASIHTTPRequestTests : SenTestCase {
}
- (void)testBasicDownload;
@end
... ...
//
// ASIHTTPRequestTests.m
// asi-http-request
//
// Created by Ben Copsey on 01/08/2008.
// Copyright 2008 All-Seeing Interactive. All rights reserved.
//
#import "ASIHTTPRequestTests.h"
#import "ASIHTTPRequest.h"
@implementation ASIHTTPRequestTests
- (void)testBasicDownload
{
//Grab data
NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request start];
NSString *html = [request dataString];
STAssertNotNil(html,@"Basic synchronous request failed");
//Check we're getting the correct response headers
NSString *pingBackHeader = [[request responseHeaders] objectForKey:@"X-Pingback"];
BOOL success = [pingBackHeader isEqualToString:@"http://allseeing-i.com/Ping-Back"];
STAssertTrue(success,@"Failed to populate response headers");
//Check we're getting back the correct status code
url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/a-page-that-does-not-exist"] autorelease];
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request start];
success = ([request responseStatusCode] == 404);
STAssertTrue(success,@"Didn't get correct status code");
//Check data
NSRange notFound = NSMakeRange(NSNotFound, 0);
success = !NSEqualRanges([html rangeOfString:@"All-Seeing Interactive"],notFound);
STAssertTrue(success,@"Failed to download the correct data");
//Attempt to grab from bad url (astonishingly, there is a website at http://aaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com !)
url = [[[NSURL alloc] initWithString:@"http://aaaaaaaaaaaaaaaaaaaaaaaaaaaaab.com"] autorelease];
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request start];
NSError *error = [request error];
STAssertNotNil(error,@"Failed to generate an error for a bad host");
}
@end
... ...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>
... ...
... ... @@ -255,7 +255,7 @@
<dict>
<key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
<array>
<real>186</real>
<real>312</real>
</array>
<key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
<array>
... ... @@ -267,20 +267,28 @@
<key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
<array>
<string>29B97314FDCFA39411CA2CEA</string>
<string>B5731AF70E430B020008024F</string>
<string>080E96DDFE201D6D7F000001</string>
<string>29B97315FDCFA39411CA2CEA</string>
<string>29B97317FDCFA39411CA2CEA</string>
<string>29B97323FDCFA39411CA2CEA</string>
<string>1058C7A0FEA54F0111CA2CBB</string>
<string>1C37FBAC04509CD000000102</string>
<string>B5731BB50E4318C20008024F</string>
<string>B5731C080E431A3C0008024F</string>
<string>B5731BBD0E4319180008024F</string>
<string>B5731C320E431B3F0008024F</string>
<string>1C37FABC05509CD000000102</string>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>4</integer>
<integer>1</integer>
<integer>2</integer>
<integer>0</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
<string>{{0, 0}, {186, 760}}</string>
<string>{{0, 0}, {312, 760}}</string>
</dict>
<key>PBXTopSmartGroupGIDs</key>
<array/>
... ... @@ -292,19 +300,19 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {203, 778}}</string>
<string>{{0, 0}, {329, 778}}</string>
<key>GroupTreeTableConfiguration</key>
<array>
<string>MainColumn</string>
<real>186</real>
<real>312</real>
</array>
<key>RubberWindowFrame</key>
<string>353 264 1342 819 0 0 1920 1178 </string>
<string>483 359 1342 819 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXSmartGroupTreeModule</string>
<key>Proportion</key>
<string>203pt</string>
<string>329pt</string>
</dict>
<dict>
<key>Dock</key>
... ... @@ -317,7 +325,7 @@
<key>PBXProjectModuleGUID</key>
<string>1CE0B20306471E060097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequest.h</string>
<string>ASIHTTPRequestTests.m</string>
<key>PBXSplitModuleInNavigatorKey</key>
<dict>
<key>Split0</key>
... ... @@ -325,20 +333,26 @@
<key>PBXProjectModuleGUID</key>
<string>1CE0B20406471E060097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequest.h</string>
<string>ASIHTTPRequestTests.m</string>
<key>_historyCapacity</key>
<integer>0</integer>
<key>bookmark</key>
<string>B569CF4A0E41D94E00B57986</string>
<string>B5731DB10E433BDB0008024F</string>
<key>history</key>
<array>
<string>B513D3E90E2BD48A000A50C6</string>
<string>B513D3EA0E2BD48A000A50C6</string>
<string>B5AACA810E3F3D3400064080</string>
<string>B5127C400E41C09D00D266C2</string>
<string>B5127C540E41C0F300D266C2</string>
<string>B569CED90E41D71C00B57986</string>
<string>B569CEDA0E41D71C00B57986</string>
<string>B5731B350E430D310008024F</string>
<string>B5731B8B0E4310180008024F</string>
<string>B5731BBE0E4319180008024F</string>
<string>B5731BEE0E431A050008024F</string>
<string>B5731BEF0E431A050008024F</string>
<string>B5731BF00E431A050008024F</string>
<string>B5731C780E4333810008024F</string>
<string>B5731C790E4333810008024F</string>
<string>B5731D960E433A750008024F</string>
<string>B5731D970E433A750008024F</string>
<string>B5731DA30E433B550008024F</string>
<string>B5731DB00E433BDB0008024F</string>
</array>
<key>prevStack</key>
<array>
... ... @@ -349,41 +363,52 @@
<string>B5ABC8300E24CDE70072F422</string>
<string>B513D4020E2BD48A000A50C6</string>
<string>B513D4030E2BD48A000A50C6</string>
<string>B569CDCF0E41C1DC00B57986</string>
<string>B569CDD50E41C1EE00B57986</string>
<string>B569CE040E41C8E100B57986</string>
<string>B569CE050E41C8E100B57986</string>
<string>B569CE060E41C8E100B57986</string>
<string>B569CE070E41C8E100B57986</string>
<string>B569CE130E41CB6200B57986</string>
<string>B569CE140E41CB6200B57986</string>
<string>B569CE1C0E41CCC500B57986</string>
<string>B569CE1D0E41CCC500B57986</string>
<string>B569CE1E0E41CCC500B57986</string>
<string>B569CE1F0E41CCC500B57986</string>
<string>B569CE3A0E41D24C00B57986</string>
<string>B569CE3B0E41D24C00B57986</string>
<string>B569CE3C0E41D24C00B57986</string>
<string>B569CE3D0E41D24C00B57986</string>
<string>B569CE3E0E41D24C00B57986</string>
<string>B569CE3F0E41D24C00B57986</string>
<string>B569CE490E41D2D200B57986</string>
<string>B569CE4A0E41D2D200B57986</string>
<string>B569CE4B0E41D2D200B57986</string>
<string>B569CE510E41D30800B57986</string>
<string>B569CE5A0E41D3A800B57986</string>
<string>B569CE610E41D3E300B57986</string>
<string>B569CE6A0E41D41200B57986</string>
<string>B569CE730E41D5EB00B57986</string>
<string>B569CE740E41D5EB00B57986</string>
<string>B569CE750E41D5EB00B57986</string>
<string>B569CE760E41D5EB00B57986</string>
<string>B569CE770E41D5EB00B57986</string>
<string>B569CE780E41D5EB00B57986</string>
<string>B569CE790E41D5EB00B57986</string>
<string>B569CE7A0E41D5EB00B57986</string>
<string>B569CE800E41D63F00B57986</string>
<string>B569CEDB0E41D71C00B57986</string>
<string>B5731B390E430D310008024F</string>
<string>B5731B3A0E430D310008024F</string>
<string>B5731B8F0E4310180008024F</string>
<string>B5731BC00E4319180008024F</string>
<string>B5731BF30E431A050008024F</string>
<string>B5731BF40E431A050008024F</string>
<string>B5731BF50E431A050008024F</string>
<string>B5731BF60E431A050008024F</string>
<string>B5731BF70E431A050008024F</string>
<string>B5731BF80E431A050008024F</string>
<string>B5731BF90E431A050008024F</string>
<string>B5731BFA0E431A050008024F</string>
<string>B5731BFB0E431A050008024F</string>
<string>B5731BFC0E431A050008024F</string>
<string>B5731C0B0E431A3C0008024F</string>
<string>B5731C350E431B3F0008024F</string>
<string>B5731C360E431B3F0008024F</string>
<string>B5731C370E431B3F0008024F</string>
<string>B5731C380E431B3F0008024F</string>
<string>B5731C4D0E431B890008024F</string>
<string>B5731C4E0E431B890008024F</string>
<string>B5731C4F0E431B890008024F</string>
<string>B5731C500E431B890008024F</string>
<string>B5731C620E431CD80008024F</string>
<string>B5731C630E431CD80008024F</string>
<string>B5731C7C0E4333810008024F</string>
<string>B5731C7D0E4333810008024F</string>
<string>B5731C7E0E4333810008024F</string>
<string>B5731C7F0E4333810008024F</string>
<string>B5731C800E4333810008024F</string>
<string>B5731C810E4333810008024F</string>
<string>B5731C820E4333810008024F</string>
<string>B5731C830E4333810008024F</string>
<string>B5731C840E4333810008024F</string>
<string>B5731C9A0E4334260008024F</string>
<string>B5731C9B0E4334260008024F</string>
<string>B5731C9C0E4334260008024F</string>
<string>B5731C9D0E4334260008024F</string>
<string>B5731D840E4339F30008024F</string>
<string>B5731D8C0E433A1F0008024F</string>
<string>B5731D990E433A750008024F</string>
<string>B5731D9A0E433A750008024F</string>
<string>B5731D9B0E433A750008024F</string>
<string>B5731D9C0E433A750008024F</string>
<string>B5731DA50E433B550008024F</string>
<string>B5731DA60E433B550008024F</string>
</array>
</dict>
<key>SplitCount</key>
... ... @@ -395,9 +420,9 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {1134, 679}}</string>
<string>{{0, 0}, {1008, 679}}</string>
<key>RubberWindowFrame</key>
<string>353 264 1342 819 0 0 1920 1178 </string>
<string>483 359 1342 819 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
... ... @@ -415,9 +440,9 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 684}, {1134, 94}}</string>
<string>{{0, 684}, {1008, 94}}</string>
<key>RubberWindowFrame</key>
<string>353 264 1342 819 0 0 1920 1178 </string>
<string>483 359 1342 819 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>XCDetailModule</string>
... ... @@ -426,7 +451,7 @@
</dict>
</array>
<key>Proportion</key>
<string>1134pt</string>
<string>1008pt</string>
</dict>
</array>
<key>Name</key>
... ... @@ -441,9 +466,9 @@
</array>
<key>TableOfContents</key>
<array>
<string>B569CDBD0E41C18F00B57986</string>
<string>B5731BC20E4319180008024F</string>
<string>1CE0B1FE06471DED0097A5F4</string>
<string>B569CDBE0E41C18F00B57986</string>
<string>B5731BC30E4319180008024F</string>
<string>1CE0B20306471E060097A5F4</string>
<string>1CE0B20506471E060097A5F4</string>
</array>
... ... @@ -577,15 +602,15 @@
<integer>5</integer>
<key>WindowOrderList</key>
<array>
<string>B569CDC80E41C18F00B57986</string>
<string>B569CDC90E41C18F00B57986</string>
<string>1CD10A99069EF8BA00B06720</string>
<string>B5731C3B0E431B3F0008024F</string>
<string>B5731C3C0E431B3F0008024F</string>
<string>B5ABC8410E24CDE70072F422</string>
<string>1CD10A99069EF8BA00B06720</string>
<string>1C78EAAD065D492600B07095</string>
<string>/Users/ben/asi-http-request/asi-http-request.xcodeproj</string>
</array>
<key>WindowString</key>
<string>353 264 1342 819 0 0 1920 1178 </string>
<string>483 359 1342 819 0 0 1920 1178 </string>
<key>WindowToolsV3</key>
<array>
<dict>
... ... @@ -608,7 +633,7 @@
<key>PBXProjectModuleGUID</key>
<string>1CD0528F0623707200166675</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequest.m</string>
<string>RunPlatformUnitTests.include</string>
<key>StatusBarVisibility</key>
<true/>
</dict>
... ... @@ -617,7 +642,7 @@
<key>Frame</key>
<string>{{0, 0}, {1440, 536}}</string>
<key>RubberWindowFrame</key>
<string>396 341 1440 818 0 0 1920 1178 </string>
<string>257 107 1440 818 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
... ... @@ -641,7 +666,7 @@
<key>Frame</key>
<string>{{0, 541}, {1440, 236}}</string>
<key>RubberWindowFrame</key>
<string>396 341 1440 818 0 0 1920 1178 </string>
<string>257 107 1440 818 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXBuildResultsModule</string>
... ... @@ -664,14 +689,14 @@
<key>TableOfContents</key>
<array>
<string>B5ABC8410E24CDE70072F422</string>
<string>B569CDBF0E41C18F00B57986</string>
<string>B5731BC40E4319180008024F</string>
<string>1CD0528F0623707200166675</string>
<string>XCMainBuildResultsModuleGUID</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.buildV3</string>
<key>WindowString</key>
<string>396 341 1440 818 0 0 1920 1178 </string>
<string>257 107 1440 818 0 0 1920 1178 </string>
<key>WindowToolGUID</key>
<string>B5ABC8410E24CDE70072F422</string>
<key>WindowToolIsVisible</key>
... ... @@ -784,13 +809,13 @@
<key>TableOfContents</key>
<array>
<string>1CD10A99069EF8BA00B06720</string>
<string>B569CDC00E41C18F00B57986</string>
<string>B5731BE60E4319EE0008024F</string>
<string>1C162984064C10D400B95A72</string>
<string>B569CDC10E41C18F00B57986</string>
<string>B569CDC20E41C18F00B57986</string>
<string>B569CDC30E41C18F00B57986</string>
<string>B569CDC40E41C18F00B57986</string>
<string>B569CDC50E41C18F00B57986</string>
<string>B5731BE70E4319EE0008024F</string>
<string>B5731BE80E4319EE0008024F</string>
<string>B5731BE90E4319EE0008024F</string>
<string>B5731BEA0E4319EE0008024F</string>
<string>B5731BEB0E4319EE0008024F</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.debugV3</string>
... ... @@ -799,7 +824,7 @@
<key>WindowToolGUID</key>
<string>1CD10A99069EF8BA00B06720</string>
<key>WindowToolIsVisible</key>
<false/>
<true/>
</dict>
<dict>
<key>FirstTimeWindowDisplayed</key>
... ... @@ -923,7 +948,7 @@
<key>Frame</key>
<string>{{0, 0}, {629, 511}}</string>
<key>RubberWindowFrame</key>
<string>385 95 629 552 0 0 1920 1178 </string>
<string>67 588 629 552 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXDebugCLIModule</string>
... ... @@ -946,13 +971,13 @@
<key>TableOfContents</key>
<array>
<string>1C78EAAD065D492600B07095</string>
<string>B569CDC60E41C18F00B57986</string>
<string>B5731C0D0E431A3C0008024F</string>
<string>1C78EAAC065D492600B07095</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.consoleV3</string>
<key>WindowString</key>
<string>385 95 629 552 0 0 1920 1178 </string>
<string>67 588 629 552 0 0 1920 1178 </string>
<key>WindowToolGUID</key>
<string>1C78EAAD065D492600B07095</string>
<key>WindowToolIsVisible</key>
... ...
This diff could not be displayed because it is too large.
This diff was suppressed by a .gitattributes entry.