Ben Copsey

Moved cookie support over to NSHTTPCookie

//
// ASIHTTPCookie.h
// asi-http-request
//
// Created by Ben Copsey on 25/08/2008.
// Copyright 2008 All-Seeing Interactive. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface ASIHTTPCookie : NSObject {
NSString *name;
NSString *value;
NSDate *expires;
NSString *path;
NSString *domain;
BOOL requiresHTTPS;
}
- (void)setValue:(NSString *)newValue forProperty:(NSString *)property;
+ (NSMutableArray *)cookiesFromHeader:(NSString *)header;
+ (NSString *)urlEncodedValue:(NSString *)string;
+ (NSString *)urlDecodedValue:(NSString *)string;
@property (retain) NSString *name;
@property (retain) NSString *value;
@property (retain) NSDate *expires;
@property (retain) NSString *path;
@property (retain) NSString *domain;
@property (assign) BOOL requiresHTTPS;
@end
//
// ASIHTTPCookie.m
// asi-http-request
//
// Created by Ben Copsey on 25/08/2008.
// Copyright 2008 All-Seeing Interactive. All rights reserved.
//
#import "ASIHTTPCookie.h"
@implementation ASIHTTPCookie
- (void)setValue:(NSString *)newValue forProperty:(NSString *)property
{
NSString *prop = [property lowercaseString];
if ([prop isEqualToString:@"expires"]) {
[self setExpires:[NSDate dateWithNaturalLanguageString:newValue]];
return;
} else if ([prop isEqualToString:@"domain"]) {
[self setDomain:newValue];
return;
} else if ([prop isEqualToString:@"path"]) {
[self setPath:newValue];
return;
} else if ([prop isEqualToString:@"secure"]) {
[self setRequiresHTTPS:[newValue isEqualToString:@"1"]];
return;
}
if (![self name] && ![self value]) {
[self setName:property];
[self setValue:newValue];
}
}
// I know this looks like a really ugly way to parse the Set-Cookie header, but I'd guess this is probably one of the simplest methods!
// You can't rely on a comma being a cookie delimeter, since it's quite likely that the expiry date for a cookie will contain a comma
+ (NSMutableArray *)cookiesFromHeader:(NSString *)header
{
NSMutableArray *cookies = [[[NSMutableArray alloc] init] autorelease];
ASIHTTPCookie *cookie = [[[ASIHTTPCookie alloc] init] autorelease];
NSArray *parts = [header componentsSeparatedByString:@"="];
int i;
NSString *name;
NSString *value;
NSArray *components;
NSString *newKey;
NSString *terminator;
for (i=0; i<[parts count]; i++) {
NSString *part = [parts objectAtIndex:i];
if (i == 0) {
name = part;
continue;
} else if (i == [parts count]-1) {
[cookie setValue:[ASIHTTPCookie urlDecodedValue:part] forProperty:name];
[cookies addObject:cookie];
continue;
}
components = [part componentsSeparatedByString:@" "];
newKey = [components lastObject];
value = [part substringWithRange:NSMakeRange(0,[part length]-[newKey length]-2)];
[cookie setValue:[ASIHTTPCookie urlDecodedValue:value] forProperty:name];
terminator = [part substringWithRange:NSMakeRange([part length]-[newKey length]-2,1)];
if ([terminator isEqualToString:@","]) {
[cookies addObject:cookie];
cookie = [[[ASIHTTPCookie alloc] init] autorelease];
}
name = newKey;
}
return cookies;
}
+ (NSString *)urlDecodedValue:(NSString *)string
{
NSMutableString *s = [NSMutableString stringWithString:[string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
//Also swap plus signs for spaces
[s replaceOccurrencesOfString:@"+" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, [s length])];
return [NSString stringWithString:s];
}
+ (NSString *)urlEncodedValue:(NSString *)string
{
return [string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
@synthesize name;
@synthesize value;
@synthesize expires;
@synthesize path;
@synthesize domain;
@synthesize requiresHTTPS;
@end
... ... @@ -39,7 +39,7 @@
NSMutableArray *requestCookies;
//Will be populated with Cookies
NSMutableArray *responseCookies;
NSArray *responseCookies;
//If use cokie persistance is true, network requests will present valid cookies from previous requests
BOOL useCookiePersistance;
... ... @@ -224,17 +224,13 @@
// Remove credentials from the keychain
+ (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
// Store cookies for a particular request in the session
+ (void)recordCookiesInSessionForRequest:(ASIHTTPRequest *)request;
// We keep track of any cookies we accept, so that we can remove them from the persistent store later
+ (void)setSessionCookies:(NSMutableArray *)newSessionCookies;
+ (NSMutableArray *)sessionCookies;
// Dump all session data (authentication and cookies)
+ (void)clearSession;
@property (retain) NSString *username;
@property (retain) NSString *password;
@property (retain) NSString *domain;
... ... @@ -253,7 +249,7 @@
@property (assign,readonly) BOOL complete;
@property (retain) NSDictionary *responseHeaders;
@property (retain) NSMutableArray *requestCookies;
@property (retain) NSMutableArray *responseCookies;
@property (retain) NSArray *responseCookies;
@property (assign) BOOL useCookiePersistance;
@property (retain) NSDictionary *requestCredentials;
@property (assign) int responseStatusCode;
... ...
... ... @@ -11,7 +11,7 @@
// See: http://developer.apple.com/samplecode/ImageClient/listing37.html
#import "ASIHTTPRequest.h"
#import "ASIHTTPCookie.h"
#import "NSHTTPCookieAdditions.h"
static NSString *NetworkRequestErrorDomain = @"com.Your-Company.Your-Product.NetworkError.";
... ... @@ -168,47 +168,23 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
//Set your own boundary string only if really obsessive. We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does.
NSString *stringBoundary = @"0xKhTmLbOuNdArY";
//Add cookies from session
if (useCookiePersistance && [[ASIHTTPRequest sessionCookies] count] > 0) {
ASIHTTPCookie *requestCookie;
ASIHTTPCookie *storedCookie;
for (storedCookie in sessionCookies) {
BOOL foundExistingCookie = NO;
//Look for existing cookies in the request - these will always take precedence over session stored cookies
for (requestCookie in requestCookies) {
if ([[requestCookie domain] isEqualToString:[storedCookie domain]]) {
if ([[requestCookie path] isEqualToString:[storedCookie path]] || (![requestCookie path] && ![storedCookie path])) {
if ([[requestCookie name] isEqualToString:[storedCookie name]]) {
foundExistingCookie = YES;
break;
}
}
}
}
if (!foundExistingCookie) {
[requestCookies addObject:storedCookie];
}
//Add cookies from the persistant (mac os global) store
if (useCookiePersistance) {
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];
if (cookies) {
[requestCookies addObjectsFromArray:cookies];
}
}
//Apply request cookies
if ([requestCookies count] > 0) {
ASIHTTPCookie *cookie;
NSHTTPCookie *cookie;
NSString *cookieHeader = nil;
for (cookie in requestCookies) {
//Ensure the cookie is valid for this request
if ([[[url host] substringWithRange:NSMakeRange([[url host] length]-[[cookie domain] length],[[cookie domain] length])] isEqualToString:[cookie domain]]) {
if ([[[url path] substringWithRange:NSMakeRange(0,[[cookie path] length])] isEqualToString:[cookie path]]) {
if (![cookie requiresHTTPS] || [[url port] intValue] == 443) {
if (![cookie expires] || [[cookie expires] timeIntervalSinceNow] > 0) {
if (!cookieHeader) {
cookieHeader = [NSString stringWithFormat: @"%@=%@",[cookie name],[ASIHTTPCookie urlEncodedValue:[cookie value]]];
} else {
cookieHeader = [NSString stringWithFormat: @"%@; %@=%@",cookieHeader,[cookie name],[ASIHTTPCookie urlEncodedValue:[cookie value]]];
}
}
}
}
if (!cookieHeader) {
cookieHeader = [NSString stringWithFormat: @"%@=%@",[cookie name],[cookie encodedValue]];
} else {
cookieHeader = [NSString stringWithFormat: @"%@; %@=%@",cookieHeader,[cookie name],[cookie encodedValue]];
}
}
if (cookieHeader) {
... ... @@ -475,11 +451,20 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
}
//Handle cookies
NSString *cookieHeader = [responseHeaders valueForKey:@"Set-Cookie"];
if (cookieHeader) {
[self setResponseCookies:[ASIHTTPCookie cookiesFromHeader:cookieHeader]];
if (useCookiePersistance) {
[ASIHTTPRequest recordCookiesInSessionForRequest:self];
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaders forURL:url];
[self setResponseCookies:cookies];
if (useCookiePersistance) {
//Store cookies in global persistent store
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:nil];
//We also keep any cookies in the sessionCookies array, so that we have a reference to them if we need to remove them later
if (!sessionCookies) {
[ASIHTTPRequest setSessionCookies:[[[NSMutableArray alloc] init] autorelease]];
NSHTTPCookie *cookie;
for (cookie in cookies) {
[[ASIHTTPRequest sessionCookies] addObject:cookie];
}
}
}
... ... @@ -829,37 +814,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
}
+ (void)recordCookiesInSessionForRequest:(ASIHTTPRequest *)request
{
if (!sessionCookies) {
[self setSessionCookies:[[[NSMutableArray alloc] init] autorelease]];
}
ASIHTTPCookie *newCookie;
ASIHTTPCookie *storedCookie;
for (newCookie in [request responseCookies]) {
//If we didn't get a domain for the cookie, let's add the one from this request, so we aren't sending cookies from the wrong server later on
if (![newCookie domain]) {
[newCookie setDomain:[[request url] host]];
}
int i = 0;
BOOL foundExistingCookie = NO;
for (storedCookie in sessionCookies) {
if ([[storedCookie domain] isEqualToString:[newCookie domain]]) {
if ([[storedCookie path] isEqualToString:[newCookie path]] || (![storedCookie path] && ![newCookie path])) {
if ([[storedCookie name] isEqualToString:[newCookie name]]) {
foundExistingCookie = YES;
[sessionCookies replaceObjectAtIndex:i withObject:newCookie];
break;
}
}
}
i++;
}
if (!foundExistingCookie) {
[sessionCookies addObject:newCookie];
}
}
}
+ (NSMutableArray *)sessionCookies
{
... ... @@ -868,6 +822,11 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
+ (void)setSessionCookies:(NSMutableArray *)newSessionCookies
{
//Remove existing cookies from the persistent store
NSHTTPCookie *cookie;
for (cookie in newSessionCookies) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
[sessionCookies release];
sessionCookies = [newSessionCookies retain];
}
... ... @@ -881,6 +840,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
}
@synthesize username;
@synthesize password;
@synthesize domain;
... ...
... ... @@ -8,7 +8,7 @@
#import "ASIHTTPRequestTests.h"
#import "ASIHTTPRequest.h"
#import "ASIHTTPCookie.h"
#import "NSHTTPCookieAdditions.h"
@implementation ASIHTTPRequestTests
... ... @@ -120,24 +120,6 @@ More tests needed for:
{
BOOL success;
//Firstly, let's make sure cocoa still parses cookie dates correctly using the three examples at http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
NSString *dte = @"Sun, 06 Nov 1994 08:49:37 GMT";
NSDate *date = [NSDate dateWithNaturalLanguageString:dte];
NSDate *referenceDate = [NSDate dateWithString:@"1994-11-06 08:49:37 +0000"];
success = [date isEqualToDate:referenceDate];
STAssertTrue(success,@"Date parse 1 failed");
dte = @"Sunday, 06-Nov-94 08:49:37 GMT";
date = [NSDate dateWithNaturalLanguageString:dte];
success = [date isEqualToDate:referenceDate];
STAssertTrue(success,@"Date parse 2 failed");
dte = @"Sun Nov 6 08:49:37 1994";
date = [NSDate dateWithNaturalLanguageString:dte];
success = [date isEqualToDate:referenceDate];
STAssertTrue(success,@"Date parse 3 failed");
NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/set_cookie"] autorelease];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseCookiePersistance:YES];
... ... @@ -149,14 +131,15 @@ More tests needed for:
NSArray *cookies = [request responseCookies];
STAssertNotNil(cookies,@"Failed to store cookie data in responseCookies");
ASIHTTPCookie *cookie = nil;
NSHTTPCookie *cookie = nil;
BOOL foundCookie = NO;
for (cookie in cookies) {
if ([[cookie name] isEqualToString:@"ASIHTTPRequestTestCookie"]) {
foundCookie = YES;
success = [[cookie value] isEqualToString:@"This is the value"];
NSLog(@"%@",cookie);
success = [[cookie decodedValue] isEqualToString:@"This is the value"];
STAssertTrue(success,@"Failed to store the correct value for a cookie");
success = [[cookie domain] isEqualToString:@"allseeing-i.com"];
success = [[cookie domain] isEqualToString:@".allseeing-i.com"];
STAssertTrue(success,@"Failed to store the correct domain for a cookie");
success = [[cookie path] isEqualToString:@"/asi-http-request/tests"];
STAssertTrue(success,@"Failed to store the correct path for a cookie");
... ...
... ... @@ -26,7 +26,7 @@
- (IBAction)simpleURLFetch:(id)sender
{
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]] autorelease];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://asi/"]] autorelease];
//Customise our user agent, for no real reason
[request addRequestHeader:@"User-Agent" value:@"ASIHTTPRequest"];
... ...
//
// NSHTTPCookieAdditions.h
// asi-http-request
//
// Created by Ben Copsey on 12/09/2008.
// Copyright 2008 All-Seeing Interactive. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NSHTTPCookie (ValueEncodingAdditions)
- (NSString *)encodedValue;
- (NSString *)decodedValue;
@end
... ...
//
// NSHTTPCookieAdditions.m
// asi-http-request
//
// Created by Ben Copsey on 12/09/2008.
// Copyright 2008 All-Seeing Interactive. All rights reserved.
//
#import "NSHTTPCookieAdditions.h"
@implementation NSHTTPCookie (ValueEncodingAdditions)
- (NSString *)decodedValue
{
NSMutableString *s = [NSMutableString stringWithString:[[self value] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
//Also swap plus signs for spaces
[s replaceOccurrencesOfString:@"+" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, [s length])];
return [NSString stringWithString:s];
}
- (NSString *)encodedValue
{
return [[self value] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
@end
... ...
... ... @@ -279,13 +279,13 @@
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>4</integer>
<integer>2</integer>
<integer>11</integer>
<integer>5</integer>
<integer>0</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
<string>{{0, 0}, {312, 861}}</string>
<string>{{0, 0}, {312, 917}}</string>
</dict>
<key>PBXTopSmartGroupGIDs</key>
<array/>
... ... @@ -297,14 +297,14 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {329, 879}}</string>
<string>{{0, 0}, {329, 935}}</string>
<key>GroupTreeTableConfiguration</key>
<array>
<string>MainColumn</string>
<real>312</real>
</array>
<key>RubberWindowFrame</key>
<string>120 130 1647 920 0 0 1920 1178 </string>
<string>224 81 1432 976 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXSmartGroupTreeModule</string>
... ... @@ -322,7 +322,7 @@
<key>PBXProjectModuleGUID</key>
<string>1CE0B20306471E060097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequestTests.m</string>
<string>ASIHTTPRequest.m</string>
<key>PBXSplitModuleInNavigatorKey</key>
<dict>
<key>Split0</key>
... ... @@ -330,11 +330,11 @@
<key>PBXProjectModuleGUID</key>
<string>1CE0B20406471E060097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequestTests.m</string>
<string>ASIHTTPRequest.m</string>
<key>_historyCapacity</key>
<integer>0</integer>
<key>bookmark</key>
<string>B500B5BA0E635E0E00744D82</string>
<string>B5CF37350E7A8D040050CBA7</string>
<key>history</key>
<array>
<string>B5731B8B0E4310180008024F</string>
... ... @@ -344,16 +344,17 @@
<string>B5F3B7370E43683600E001FD</string>
<string>B567EF5C0E4EE4FC001E238F</string>
<string>B567EF5D0E4EE4FC001E238F</string>
<string>B5B3BF120E63470F0071D39F</string>
<string>B5B3BF150E63470F0071D39F</string>
<string>B5B3BF190E63470F0071D39F</string>
<string>B5B3BF5D0E634B5D0071D39F</string>
<string>B500B54C0E635A3200744D82</string>
<string>B500B57E0E635BC700744D82</string>
<string>B500B57F0E635BC700744D82</string>
<string>B500B5800E635BC700744D82</string>
<string>B500B5810E635BC700744D82</string>
<string>B500B5910E635CE900744D82</string>
<string>B5CF35640E7A7B2C0050CBA7</string>
<string>B5CF36FC0E7A8C380050CBA7</string>
<string>B5CF36FE0E7A8C380050CBA7</string>
<string>B5CF36FF0E7A8C380050CBA7</string>
<string>B5CF37000E7A8C380050CBA7</string>
<string>B5CF37010E7A8C380050CBA7</string>
<string>B5CF37100E7A8C7F0050CBA7</string>
<string>B5CF37110E7A8C7F0050CBA7</string>
<string>B5CF37120E7A8C7F0050CBA7</string>
<string>B5CF36FB0E7A8C380050CBA7</string>
</array>
<key>prevStack</key>
<array>
... ... @@ -374,6 +375,87 @@
<string>B567EF630E4EE4FC001E238F</string>
<string>B5B3BC690E62DA0E0071D39F</string>
<string>B5B3BC6C0E62DA0E0071D39F</string>
<string>B5CF35450E7A73EC0050CBA7</string>
<string>B5CF35460E7A73EC0050CBA7</string>
<string>B5CF35470E7A73EC0050CBA7</string>
<string>B5CF35680E7A7B2C0050CBA7</string>
<string>B5CF35690E7A7B2C0050CBA7</string>
<string>B5CF356A0E7A7B2C0050CBA7</string>
<string>B5CF356B0E7A7B2C0050CBA7</string>
<string>B5CF356C0E7A7B2C0050CBA7</string>
<string>B5CF356D0E7A7B2C0050CBA7</string>
<string>B5CF356F0E7A7B2C0050CBA7</string>
<string>B5CF35700E7A7B2C0050CBA7</string>
<string>B5CF357D0E7A7C790050CBA7</string>
<string>B5CF358E0E7A7F470050CBA7</string>
<string>B5CF35A90E7A84A00050CBA7</string>
<string>B5CF35AA0E7A84A00050CBA7</string>
<string>B5CF35AB0E7A84A00050CBA7</string>
<string>B5CF35AC0E7A84A00050CBA7</string>
<string>B5CF35AD0E7A84A00050CBA7</string>
<string>B5CF35AE0E7A84A00050CBA7</string>
<string>B5CF35AF0E7A84A00050CBA7</string>
<string>B5CF35B00E7A84A00050CBA7</string>
<string>B5CF35B10E7A84A00050CBA7</string>
<string>B5CF35B20E7A84A00050CBA7</string>
<string>B5CF35B30E7A84A00050CBA7</string>
<string>B5CF35B40E7A84A00050CBA7</string>
<string>B5CF35B50E7A84A00050CBA7</string>
<string>B5CF35B60E7A84A00050CBA7</string>
<string>B5CF35B70E7A84A00050CBA7</string>
<string>B5CF35B80E7A84A00050CBA7</string>
<string>B5CF35B90E7A84A00050CBA7</string>
<string>B5CF35CC0E7A85360050CBA7</string>
<string>B5CF35CD0E7A85360050CBA7</string>
<string>B5CF35CE0E7A85360050CBA7</string>
<string>B5CF35E20E7A85A50050CBA7</string>
<string>B5CF35EC0E7A85E00050CBA7</string>
<string>B5CF36770E7A88A60050CBA7</string>
<string>B5CF36780E7A88A60050CBA7</string>
<string>B5CF36790E7A88A60050CBA7</string>
<string>B5CF367A0E7A88A60050CBA7</string>
<string>B5CF367B0E7A88A60050CBA7</string>
<string>B5CF367C0E7A88A60050CBA7</string>
<string>B5CF367D0E7A88A60050CBA7</string>
<string>B5CF367E0E7A88A60050CBA7</string>
<string>B5CF367F0E7A88A60050CBA7</string>
<string>B5CF36800E7A88A60050CBA7</string>
<string>B5CF36810E7A88A60050CBA7</string>
<string>B5CF36820E7A88A60050CBA7</string>
<string>B5CF36830E7A88A60050CBA7</string>
<string>B5CF36840E7A88A60050CBA7</string>
<string>B5CF36850E7A88A60050CBA7</string>
<string>B5CF36860E7A88A60050CBA7</string>
<string>B5CF36870E7A88A60050CBA7</string>
<string>B5CF36880E7A88A60050CBA7</string>
<string>B5CF36890E7A88A60050CBA7</string>
<string>B5CF368A0E7A88A60050CBA7</string>
<string>B5CF368B0E7A88A60050CBA7</string>
<string>B5CF368C0E7A88A60050CBA7</string>
<string>B5CF368D0E7A88A60050CBA7</string>
<string>B5CF368E0E7A88A60050CBA7</string>
<string>B5CF368F0E7A88A60050CBA7</string>
<string>B5CF36900E7A88A60050CBA7</string>
<string>B5CF36910E7A88A60050CBA7</string>
<string>B5CF36BE0E7A89F10050CBA7</string>
<string>B5CF36BF0E7A89F10050CBA7</string>
<string>B5CF36C80E7A8A610050CBA7</string>
<string>B5CF36EC0E7A8B440050CBA7</string>
<string>B5CF37030E7A8C380050CBA7</string>
<string>B5CF37040E7A8C380050CBA7</string>
<string>B5CF37050E7A8C380050CBA7</string>
<string>B5CF37060E7A8C380050CBA7</string>
<string>B5CF37070E7A8C380050CBA7</string>
<string>B5CF37080E7A8C380050CBA7</string>
<string>B5CF37090E7A8C380050CBA7</string>
<string>B5CF370A0E7A8C380050CBA7</string>
<string>B5CF370B0E7A8C380050CBA7</string>
<string>B5CF370C0E7A8C380050CBA7</string>
<string>B5CF370D0E7A8C380050CBA7</string>
<string>B5CF370E0E7A8C380050CBA7</string>
<string>B5CF37130E7A8C7F0050CBA7</string>
<string>B5CF37140E7A8C7F0050CBA7</string>
<string>B5CF37150E7A8C7F0050CBA7</string>
</array>
</dict>
<key>SplitCount</key>
... ... @@ -385,14 +467,14 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {1313, 780}}</string>
<string>{{0, 0}, {1098, 836}}</string>
<key>RubberWindowFrame</key>
<string>120 130 1647 920 0 0 1920 1178 </string>
<string>224 81 1432 976 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>780pt</string>
<string>836pt</string>
</dict>
<dict>
<key>ContentConfiguration</key>
... ... @@ -405,9 +487,9 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 785}, {1313, 94}}</string>
<string>{{0, 841}, {1098, 94}}</string>
<key>RubberWindowFrame</key>
<string>120 130 1647 920 0 0 1920 1178 </string>
<string>224 81 1432 976 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>XCDetailModule</string>
... ... @@ -416,7 +498,7 @@
</dict>
</array>
<key>Proportion</key>
<string>1313pt</string>
<string>1098pt</string>
</dict>
</array>
<key>Name</key>
... ... @@ -431,9 +513,9 @@
</array>
<key>TableOfContents</key>
<array>
<string>B500B5930E635CE900744D82</string>
<string>B5CF35490E7A73EC0050CBA7</string>
<string>1CE0B1FE06471DED0097A5F4</string>
<string>B500B5940E635CE900744D82</string>
<string>B5CF354A0E7A73EC0050CBA7</string>
<string>1CE0B20306471E060097A5F4</string>
<string>1CE0B20506471E060097A5F4</string>
</array>
... ... @@ -567,15 +649,16 @@
<integer>5</integer>
<key>WindowOrderList</key>
<array>
<string>B500B5A20E635CFE00744D82</string>
<string>B500B5A30E635CFE00744D82</string>
<string>1C530D57069F1CE1000CFCEE</string>
<string>B5CF35540E7A73EC0050CBA7</string>
<string>B5CF35550E7A73EC0050CBA7</string>
<string>B5ABC8410E24CDE70072F422</string>
<string>1CD10A99069EF8BA00B06720</string>
<string>1C78EAAD065D492600B07095</string>
<string>1CD10A99069EF8BA00B06720</string>
<string>/Users/ben/asi-http-request/asi-http-request.xcodeproj</string>
</array>
<key>WindowString</key>
<string>120 130 1647 920 0 0 1920 1178 </string>
<string>224 81 1432 976 0 0 1920 1178 </string>
<key>WindowToolsV3</key>
<array>
<dict>
... ... @@ -591,6 +674,8 @@
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
... ... @@ -603,18 +688,16 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {1440, 536}}</string>
<string>{{0, 0}, {1279, 533}}</string>
<key>RubberWindowFrame</key>
<string>259 150 1440 818 0 0 1920 1178 </string>
<string>329 218 1279 815 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>536pt</string>
<string>533pt</string>
</dict>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
... ... @@ -629,9 +712,9 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 541}, {1440, 236}}</string>
<string>{{0, 538}, {1279, 236}}</string>
<key>RubberWindowFrame</key>
<string>259 150 1440 818 0 0 1920 1178 </string>
<string>329 218 1279 815 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXBuildResultsModule</string>
... ... @@ -640,7 +723,7 @@
</dict>
</array>
<key>Proportion</key>
<string>777pt</string>
<string>774pt</string>
</dict>
</array>
<key>Name</key>
... ... @@ -654,14 +737,14 @@
<key>TableOfContents</key>
<array>
<string>B5ABC8410E24CDE70072F422</string>
<string>B500B5950E635CE900744D82</string>
<string>B5CF354B0E7A73EC0050CBA7</string>
<string>1CD0528F0623707200166675</string>
<string>XCMainBuildResultsModuleGUID</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.buildV3</string>
<key>WindowString</key>
<string>259 150 1440 818 0 0 1920 1178 </string>
<string>329 218 1279 815 0 0 1920 1178 </string>
<key>WindowToolGUID</key>
<string>B5ABC8410E24CDE70072F422</string>
<key>WindowToolIsVisible</key>
... ... @@ -740,6 +823,8 @@
<array>
<string>Name</string>
<real>151</real>
<string>Type</string>
<real>84</real>
<string>Value</string>
<real>85</real>
<string>Summary</string>
... ... @@ -748,10 +833,10 @@
<key>Frame</key>
<string>{{713, 0}, {851, 339}}</string>
<key>RubberWindowFrame</key>
<string>60 308 1564 676 0 0 1920 1178 </string>
<string>179 278 1564 676 0 0 1920 1178 </string>
</dict>
<key>RubberWindowFrame</key>
<string>60 308 1564 676 0 0 1920 1178 </string>
<string>179 278 1564 676 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXDebugSessionModule</string>
... ... @@ -774,18 +859,18 @@
<key>TableOfContents</key>
<array>
<string>1CD10A99069EF8BA00B06720</string>
<string>B500B5960E635CE900744D82</string>
<string>B5CF354C0E7A73EC0050CBA7</string>
<string>1C162984064C10D400B95A72</string>
<string>B500B5970E635CE900744D82</string>
<string>B500B5980E635CE900744D82</string>
<string>B500B5990E635CE900744D82</string>
<string>B500B59A0E635CE900744D82</string>
<string>B500B59B0E635CE900744D82</string>
<string>B5CF354D0E7A73EC0050CBA7</string>
<string>B5CF354E0E7A73EC0050CBA7</string>
<string>B5CF354F0E7A73EC0050CBA7</string>
<string>B5CF35500E7A73EC0050CBA7</string>
<string>B5CF35510E7A73EC0050CBA7</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.debugV3</string>
<key>WindowString</key>
<string>60 308 1564 676 0 0 1920 1178 </string>
<string>179 278 1564 676 0 0 1920 1178 </string>
<key>WindowToolGUID</key>
<string>1CD10A99069EF8BA00B06720</string>
<key>WindowToolIsVisible</key>
... ... @@ -807,14 +892,12 @@
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CDD528C0622207200134675</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPCookie.m</string>
<string></string>
<key>StatusBarVisibility</key>
<true/>
</dict>
... ... @@ -835,6 +918,8 @@
<string>212pt</string>
</dict>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
... ... @@ -870,8 +955,8 @@
<key>TableOfContents</key>
<array>
<string>1C530D57069F1CE1000CFCEE</string>
<string>B5B3BE4F0E633CE50071D39F</string>
<string>B5B3BE500E633CE50071D39F</string>
<string>B5CF35C20E7A84E30050CBA7</string>
<string>B5CF35C30E7A84E30050CBA7</string>
<string>1CDD528C0622207200134675</string>
<string>1CD0528E0623707200166675</string>
</array>
... ... @@ -911,18 +996,18 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {985, 866}}</string>
<string>{{0, 0}, {1211, 827}}</string>
<key>RubberWindowFrame</key>
<string>433 271 985 907 0 0 1920 1178 </string>
<string>562 242 1211 868 0 0 1920 1178 </string>
</dict>
<key>Module</key>
<string>PBXDebugCLIModule</string>
<key>Proportion</key>
<string>866pt</string>
<string>827pt</string>
</dict>
</array>
<key>Proportion</key>
<string>866pt</string>
<string>827pt</string>
</dict>
</array>
<key>Name</key>
... ... @@ -936,13 +1021,13 @@
<key>TableOfContents</key>
<array>
<string>1C78EAAD065D492600B07095</string>
<string>B500B5A00E635CFE00744D82</string>
<string>B5CF35520E7A73EC0050CBA7</string>
<string>1C78EAAC065D492600B07095</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.consoleV3</string>
<key>WindowString</key>
<string>433 271 985 907 0 0 1920 1178 </string>
<string>562 242 1211 868 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.