Ben Copsey

First public release

//
// ASIHTTPRequest.h
//
// Created by Ben Copsey on 04/10/2007.
// Copyright 2007-2008 All-Seeing Interactive. All rights reserved.
//
// Portions are based on the ImageClient example from Apple:
// See: http://developer.apple.com/samplecode/ImageClient/listing37.html
#import <Cocoa/Cocoa.h>
#import "ASIProgressDelegate.h"
@interface ASIHTTPRequest : NSOperation {
//The url for this operation, should include get params in the query string where appropriate
NSURL *url;
//The delegate, you need to manage setting and talking to your delegate in your subclasses
id delegate;
//Parameters that will be POSTed to the url
NSMutableDictionary *postData;
//Files that will be POSTed to the url
NSMutableDictionary *fileData;
//Dictionary for custom request headers
NSMutableDictionary *requestHeaders;
//If usesKeychain 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 usesKeychain;
//When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location
//If downloadDestinationPath is not set, download data will be stored in memory
NSString *downloadDestinationPath;
//Used for writing data to a file when downloadDestinationPath is set
NSOutputStream *outputStream;
//When the request fails or completes successfully, complete will be true
BOOL complete;
//If an error occurs, error will contain an NSError
NSError *error;
//If an authentication error occurs, we give the delegate a chance to handle it, ignoreError will be set to true
BOOL ignoreError;
//Username and password used for authentication
NSString *username;
NSString *password;
//Delegate for displaying upload progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself)
NSObject <ASIProgressDelegate> *uploadProgressDelegate;
//Delegate for displaying download progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself)
NSObject <ASIProgressDelegate> *downloadProgressDelegate;
// Whether we've seen the headers of the response yet
BOOL haveExaminedHeaders;
//Data we receive will be stored here
CFMutableDataRef receivedData;
//Used for sending and receiving data
CFHTTPMessageRef request;
CFReadStreamRef readStream;
// Authentication currently being used for prompting and resuming
CFHTTPAuthenticationRef authentication;
// Credentials associated with the authentication (reused until server says no)
CFMutableDictionaryRef credentials;
//Size of the response
double contentLength;
//Size of the POST payload
double postLength;
//Timer used to update the progress delegates
NSTimer *progressTimer;
//The total amount of downloaded data
double totalBytesRead;
//Realm for authentication when credentials are required
NSString *authenticationRealm;
//Called on the delegate when the request completes successfully
SEL didFinishSelector;
//Called on the delegate when the request fails
SEL didFailSelector;
//This lock will block the request until the delegate supplies authentication info
NSConditionLock *authenticationLock;
}
// Should be an HTTP or HTTPS url, may include username and password if appropriate
- (id)initWithURL:(NSURL *)newURL;
//Add a POST variable to the request
- (void)setPostValue:(id)value forKey:(NSString *)key;
//Add the contents of a local file as a POST variable to the request
- (void)setFile:(NSString *)filePath forKey:(NSString *)key;
//Add a custom header to the request
- (void)addRequestHeader:(NSString *)header value:(NSString *)value;
//the results of this request will be saved to downloadDestinationPath, if it is set
- (void)setDownloadDestinationPath:(NSString *)newDestinationPath;
- (NSString *)downloadDestinationPath;
// When set, username and password will be presented for HTTP authentication
- (void)setUsername:(NSString *)newUsername andPassword:(NSString *)newPassword;
// Delegate will get messages when the request completes, fails or when authentication is required
- (void)setDelegate:(id)newDelegate;
// Called on the delegate when the request completes successfully
- (void)setDidFinishSelector:(SEL)selector;
// Called on the delegate when the request fails
- (void)setDidFailSelector:(SEL)selector;
// upload progress delegate (usually an NSProgressIndicator) is sent information on upload progress
- (void)setUploadProgressDelegate:(id)newDelegate;
// download progress delegate (usually an NSProgressIndicator) is sent information on download progress
- (void)setDownloadProgressDelegate:(id)newDelegate;
// When true, authentication information will automatically be stored in (and re-used from) the keychain
- (void)setUsesKeychain:(BOOL)shouldUseKeychain;
// Will be true when the request is complete (success or failure)
- (BOOL)complete;
// Returns the contents of the result as an NSString (not appropriate for binary data!)
- (NSString *)dataString;
// Accessors for getting information about the request (useful for auth dialogs)
- (NSString *)authenticationRealm;
- (NSString *)host;
// Contains a description of the error that occurred if the request failed
- (NSError *)error;
// CFnetwork event handlers
- (void)handleStreamComplete;
- (void)handleStreamError;
- (void)handleBytesAvailable;
- (void)handleNetworkEvent:(CFStreamEventType)type;
// Start loading the request
- (void)loadRequest;
// 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)isAuthorizationFailure;
// Apply authentication information and resume the request after an authentication challenge
- (void)applyCredentialsAndResume;
// 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;
// Cancel loading and clean up
- (void)cancelLoad;
// Called from timer on main thread to update progress delegates
- (void)updateUploadProgress;
- (void)updateDownloadProgress;
#pragma mark keychain stuff
//Save credentials to the keychain
+ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
//Return credentials from the keychain
+ (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;
//Called when a request completes successfully
- (void)requestFinished;
//Called when a request fails
- (void)failWithProblem:(NSString *)problem;
@end
... ...
//
// ASIHTTPRequest.m
//
// Created by Ben Copsey on 04/10/2007.
// Copyright 2007-2008 All-Seeing Interactive. All rights reserved.
//
// Portions are based on the ImageClient example from Apple:
// See: http://developer.apple.com/samplecode/ImageClient/listing37.html
#import "ASIHTTPRequest.h"
#import "AppDelegate.h"
const NSTimeInterval PROGRESS_INDICATOR_TIMER_INTERVAL = 0.05; // seconds between progress updates
const double PROGRESS_INDICATOR_CHUNK_SIZE = 1024; //Each progress step will be 1KB
static NSString *NetworkRequestErrorDomain = @"com.All-SeeingInteractive.MemexTrails.NetworkError.";
static const CFOptionFlags kNetworkEvents = kCFStreamEventOpenCompleted |
kCFStreamEventHasBytesAvailable |
kCFStreamEventEndEncountered |
kCFStreamEventErrorOccurred;
static CFMutableDictionaryRef sharedCredentials = NULL;
static CFHTTPAuthenticationRef sharedAuthentication = NULL;
static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventType type, void *clientCallBackInfo) {
[((ASIHTTPRequest*)clientCallBackInfo) handleNetworkEvent: type];
}
@implementation ASIHTTPRequest
#pragma mark init / dealloc
- (id)initWithURL:(NSURL *)newURL
{
[super init];
url = [newURL retain];
postData = nil;
fileData = nil;
username = nil;
password = nil;
requestHeaders = nil;
authenticationRealm = nil;
outputStream = nil;
authentication = NULL;
credentials = NULL;
request = NULL;
usesKeychain = NO;
return self;
}
- (void)dealloc
{
if (authentication) {
CFRelease(authentication);
}
if (credentials) {
CFRelease(credentials);
}
if (request) {
CFRelease(request);
}
[self cancelLoad];
[error release];
[delegate release];
[uploadProgressDelegate release];
[downloadProgressDelegate release];
[postData release];
[fileData release];
[requestHeaders release];
[downloadDestinationPath release];
[outputStream release];
[username release];
[password release];
[authenticationRealm release];
[url release];
[authenticationLock release];
[super dealloc];
}
#pragma mark delegate configuration
- (void)setDelegate:(id)newDelegate
{
[delegate release];
delegate = [newDelegate retain];
}
- (void)setUploadProgressDelegate:(id)newDelegate
{
[uploadProgressDelegate release];
uploadProgressDelegate = [newDelegate retain];
}
- (void)setDownloadProgressDelegate:(id)newDelegate
{
[downloadProgressDelegate release];
downloadProgressDelegate = [newDelegate retain];
}
#pragma mark setup request
- (void)addRequestHeader:(NSString *)header value:(NSString *)value
{
if (!requestHeaders) {
requestHeaders = [[NSMutableDictionary alloc] init];
}
[requestHeaders setObject:value forKey:header];
}
- (void)setPostValue:(id)value forKey:(NSString *)key
{
if (!postData) {
postData = [[NSMutableDictionary alloc] init];
}
[postData setValue:value forKey:key];
}
- (void)setFile:(NSString *)filePath forKey:(NSString *)key
{
if (!fileData) {
fileData = [[NSMutableDictionary alloc] init];
}
[fileData setValue:filePath forKey:key];
}
- (void)setUsername:(NSString *)newUsername andPassword:(NSString *)newPassword
{
[username release];
username = [newUsername retain];
[password release];
password = [newPassword retain];
}
- (void)setUsesKeychain:(BOOL)shouldUseKeychain
{
usesKeychain = shouldUseKeychain;
}
- (void)setDownloadDestinationPath:(NSString *)newDestinationPath
{
[downloadDestinationPath release];
downloadDestinationPath = [newDestinationPath retain];
}
- (NSString *)downloadDestinationPath
{
return downloadDestinationPath;
}
- (void)setDidFinishSelector:(SEL)selector
{
didFinishSelector = selector;
}
- (void)setDidFailSelector:(SEL)selector
{
didFinishSelector = selector;
}
- (NSString *)authenticationRealm
{
return authenticationRealm;
}
- (NSString *)host
{
return [url host];
}
- (NSError *)error
{
return error;
}
- (BOOL)isFinished
{
return complete;
}
#pragma mark request logic
- (void)main
{
complete = NO;
// We'll make a post request only if the user specified post data
NSString *method = @"GET";
if ([postData count] > 0 || [fileData count] > 0) {
method = @"POST";
}
// Create a new HTTP request.
request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, (CFStringRef)method, (CFURLRef)url, kCFHTTPVersion1_1);
if (!request) {
[self failWithProblem:@"Unable to create request"];
return;
}
if (sharedAuthentication && sharedCredentials) {
CFHTTPMessageApplyCredentialDictionary(request, sharedAuthentication, sharedCredentials, NULL);
}
NSString *stringBoundary = @"0xKhTmLbOuNdArY";
//Add custom headers
NSString *header;
for (header in requestHeaders) {
CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[requestHeaders objectForKey:header]);
}
CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)@"Content-Type", (CFStringRef)[NSString stringWithFormat:@"multipart/form-data; boundary=%@",stringBoundary]);
if ([postData count] > 0) {
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
//Adds post data
NSData *endItemBoundary = [[NSString stringWithFormat:@"\r\n--%@\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding];
NSEnumerator *e = [postData keyEnumerator];
NSString *key;
while (key = [e nextObject]) {
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[postData objectForKey:key] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:endItemBoundary];
}
//Adds files to upload
NSData *contentTypeHeader = [[NSString stringWithString:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding];
e = [fileData keyEnumerator];
while (key = [e nextObject]) {
NSString *filePath = [fileData objectForKey:key];
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",key,[filePath lastPathComponent]] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:contentTypeHeader];
[postBody appendData:[NSData dataWithContentsOfMappedFile:filePath]];
[postBody appendData:endItemBoundary];
}
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Set the body.
CFHTTPMessageSetBody(request, (CFDataRef)postBody);
postLength = [postBody length];
}
[self loadRequest];
}
- (BOOL)complete
{
return complete;
}
- (NSString *)dataString
{
if (!receivedData) {
return nil;
}
NSString *theData = [[[NSString alloc] initWithBytes:[(NSData *)receivedData bytes] length:[(NSData *)receivedData length] encoding:NSUTF8StringEncoding] autorelease];
return theData;
}
//Subclasses can override this method to process the result in the same thread
//If not overidden, it will call the didFinishSelector on the delegate, if one has been setup
- (void)requestFinished
{
if (didFinishSelector) {
if ([delegate respondsToSelector:didFinishSelector]) {
[delegate performSelectorOnMainThread:didFinishSelector withObject:self waitUntilDone:YES];
}
}
}
//Subclasses can override this method to perform error handling in the same thread
//If not overidden, it will call the didFailSelector on the delegate, if one has been setup
- (void)failWithProblem:(NSString *)problem
{
complete = YES;
error =[[NSError errorWithDomain:NetworkRequestErrorDomain
code:1
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"An error occurred",@"Title",
problem,@"Description",nil]] retain];
NSLog(problem);
if (didFailSelector) {
if ([delegate respondsToSelector:didFailSelector]) {
[delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:YES];
}
}
}
//Called by delegate to resume loading once authentication info has been populated
- (void)retryWithAuthentication
{
[authenticationLock lockWhenCondition:1];
[authenticationLock unlockWithCondition:2];
}
- (void)loadRequest
{
//Callled twice during authentication test - fix this
[authenticationLock release];
authenticationLock = [[NSConditionLock alloc] initWithCondition:1];
complete = NO;
totalBytesRead = 0;
contentLength = 0;
haveExaminedHeaders = NO;
receivedData = CFDataCreateMutable(NULL, 0);
// Create the stream for the request.
readStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,readStream);
if (!readStream) {
[self failWithProblem:@"Unable to create read stream"];
return;
}
// Set the client
CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL};
if (!CFReadStreamSetClient(readStream, kNetworkEvents, ReadStreamClientCallBack, &ctxt)) {
CFRelease(readStream);
readStream = NULL;
[self failWithProblem:@"Unable to setup read stream"];
return;
}
// Schedule the stream
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
// Start the HTTP connection
if (!CFReadStreamOpen(readStream)) {
CFReadStreamSetClient(readStream, 0, NULL, NULL);
CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFRelease(readStream);
readStream = NULL;
[self failWithProblem:@"Unable to start http connection"];
return;
}
if (uploadProgressDelegate) {
[self performSelectorOnMainThread:@selector(resetUploadProgress:) withObject:[NSNumber numberWithDouble:postLength] waitUntilDone:YES];
}
[self performSelectorOnMainThread:@selector(setupProgressTimer) withObject:nil waitUntilDone:YES];
// Wait for the request to finish
NSDate* endDate = [NSDate distantFuture];
while (!complete) {
// See if our NSOperationQueue told us to cancel
if ([self isCancelled]) {
[self failWithProblem:@"The request was cancelled"];
[self cancelLoad];
complete = YES;
break;
}
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:endDate];
}
}
- (void)cancelLoad
{
if (readStream) {
CFReadStreamClose(readStream);
CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFRelease(readStream);
readStream = NULL;
}
if (receivedData) {
CFRelease(receivedData);
receivedData = NULL;
//If we were downloading to a file, let's remove it
} else if (downloadDestinationPath) {
[outputStream close];
[[NSFileManager defaultManager] removeFileAtPath:downloadDestinationPath handler:nil];
}
haveExaminedHeaders = NO;
}
#pragma mark upload/download progress
- (void)setupProgressTimer
{
progressTimer = [NSTimer
timerWithTimeInterval:PROGRESS_INDICATOR_TIMER_INTERVAL
target:self
selector:@selector(updateProgressIndicators)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:progressTimer forMode:NSDefaultRunLoopMode];
}
- (void)updateProgressIndicators
{
[self updateUploadProgress];
[self updateDownloadProgress];
}
- (void)resetUploadProgress:(NSNumber *)max
{
[uploadProgressDelegate setMaxValue:[max doubleValue]/PROGRESS_INDICATOR_CHUNK_SIZE];
[uploadProgressDelegate setDoubleValue:0];
}
- (void)updateUploadProgress
{
if (complete) {
[progressTimer invalidate];
progressTimer = nil;
[uploadProgressDelegate setDoubleValue:postLength];
} else if (uploadProgressDelegate) {
CFNumberRef byteCount = (CFNumberRef)CFReadStreamCopyProperty (readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount);
[uploadProgressDelegate setDoubleValue:[(NSNumber *)byteCount doubleValue]/PROGRESS_INDICATOR_CHUNK_SIZE];
}
}
- (void)updateDownloadProgress
{
if (complete) {
[progressTimer invalidate];
progressTimer = nil;
[downloadProgressDelegate setDoubleValue:contentLength];
} else if (downloadProgressDelegate) {
[downloadProgressDelegate setDoubleValue:totalBytesRead/PROGRESS_INDICATOR_CHUNK_SIZE];
}
}
- (void)resetDownloadProgress:(NSNumber *)max
{
[downloadProgressDelegate setMaxValue:[max doubleValue]/PROGRESS_INDICATOR_CHUNK_SIZE];
[downloadProgressDelegate setDoubleValue:0];
}
#pragma mark http authentication
// Parse the response headers to get the content-length, and check to see if we need to authenticate
- (BOOL)isAuthorizationFailure
{
CFHTTPMessageRef responseHeaders = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
BOOL isAuthenticationChallenge = NO;
if (responseHeaders) {
if (CFHTTPMessageIsHeaderComplete(responseHeaders)) {
CFStringRef cLength = CFHTTPMessageCopyHeaderFieldValue(responseHeaders,CFSTR("Content-Length"));
if (cLength) {
contentLength = CFStringGetDoubleValue(cLength);
if (downloadProgressDelegate) {
[self performSelectorOnMainThread:@selector(resetDownloadProgress:) withObject:[NSNumber numberWithDouble:contentLength] waitUntilDone:YES];
}
CFRelease(cLength);
}
// Is the server response a challenge for credentials?
isAuthenticationChallenge = (CFHTTPMessageGetResponseStatusCode(responseHeaders) == 401);
}
CFRelease(responseHeaders);
}
return isAuthenticationChallenge;
}
- (void)applyCredentialsAndResume {
// Apply whatever credentials we've built up to the old request
if (!CFHTTPMessageApplyCredentialDictionary(request, authentication, credentials, NULL)) {
[self failWithProblem:@"Failed to apply credentials to request"];
} else {
//If we have credentials and they're ok, let's save them to the keychain
if (usesKeychain) {
NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:(NSString *)CFDictionaryGetValue(credentials, kCFHTTPAuthenticationUsername)
password:(NSString *)CFDictionaryGetValue(credentials, kCFHTTPAuthenticationPassword)
persistence:NSURLCredentialPersistencePermanent];
if (authenticationCredentials) {
[ASIHTTPRequest saveCredentials:authenticationCredentials forHost:[url host] port:[[url port] intValue] protocol:[url scheme] realm:authenticationRealm];
}
}
// Now that we've updated our request, retry the load
[self loadRequest];
}
}
- (void)applyCredentialsLoad
{
// Get the authentication information
if (!authentication) {
CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream,kCFStreamPropertyHTTPResponseHeader);
authentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
CFRelease(responseHeader);
}
CFStreamError err;
if (!authentication) {
// the newly created authentication object is bad, must return
[self failWithProblem:@"Failed to get authentication object from response headers"];
return;
//Authentication is not valid, we need to get new ones
} else if (!CFHTTPAuthenticationIsValid(authentication, &err)) {
// destroy authentication and credentials
if (credentials) {
CFRelease(credentials);
credentials = NULL;
}
CFRelease(authentication);
authentication = NULL;
// check for bad credentials (to be treated separately)
if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) {
ignoreError = YES;
if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) {
[delegate performSelectorOnMainThread:@selector(authorizationNeededForRequest:) withObject:self waitUntilDone:YES];
[authenticationLock lockWhenCondition:2];
[authenticationLock unlock];
[self applyCredentialsLoad];
return;
}
[self failWithProblem:@"Waiting for authentication"];
complete = YES;
return;
} else {
[self failWithProblem:@"An authentication problem occurred"];
return;
}
} else {
[self cancelLoad];
if (credentials) {
[self applyCredentialsAndResume];
// are a user name & password needed?
} else if (CFHTTPAuthenticationRequiresUserNameAndPassword(authentication)) {
// Build the credentials dictionary
credentials = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
[authenticationRealm release];
authenticationRealm = nil;
// Get the authentication realm
if (!CFHTTPAuthenticationRequiresAccountDomain(authentication)) {
authenticationRealm = (NSString *)CFHTTPAuthenticationCopyRealm(authentication);
}
//First, let's look at the url to see if the username and password were included
CFStringRef user = (CFStringRef)[url user];
CFStringRef pass = (CFStringRef)[url password];
//If the username and password weren't in the url, let's try to use the ones set in this object
if ((!user || !pass) && username && password) {
user = (CFStringRef)username;
pass = (CFStringRef)password;
}
//Ok, that didn't work, let's try the keychain
if ((!user || !pass) && usesKeychain) {
NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForHost:[url host] port:[[url port] intValue] protocol:[url scheme] realm:authenticationRealm];
if (authenticationCredentials) {
user = (CFStringRef)[authenticationCredentials user];
pass = (CFStringRef)[authenticationCredentials password];
}
}
//If we have a username and password, let's apply them to the request and continue
if (user && pass) {
CFDictionarySetValue(credentials, kCFHTTPAuthenticationUsername, user);
CFDictionarySetValue(credentials, kCFHTTPAuthenticationPassword, pass);
[self applyCredentialsAndResume];
return;
}
if (credentials) {
CFRelease(credentials);
credentials = NULL;
}
//We've got no credentials, let's ask the delegate to sort this out
ignoreError = YES;
if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) {
[delegate performSelectorOnMainThread:@selector(authorizationNeededForRequest:) withObject:self waitUntilDone:YES];
[authenticationLock lockWhenCondition:2];
[authenticationLock unlock];
[self applyCredentialsLoad];
return;
}
[self failWithProblem:@"Waiting for authentication"];
complete = YES;
return;
//We don't need a username or password, let's carry on
} else {
[self applyCredentialsAndResume];
}
}
}
#pragma mark stream status handlers
- (void)handleNetworkEvent:(CFStreamEventType)type
{
// Dispatch the stream events.
switch (type) {
case kCFStreamEventHasBytesAvailable:
[self handleBytesAvailable];
break;
case kCFStreamEventEndEncountered:
[self handleStreamComplete];
break;
case kCFStreamEventErrorOccurred:
[self handleStreamError];
break;
default:
break;
}
}
- (void)handleBytesAvailable
{
if (!haveExaminedHeaders) {
haveExaminedHeaders = YES;
if ([self isAuthorizationFailure]) {
[self applyCredentialsLoad];
return;
}
}
UInt8 buffer[2048];
CFIndex bytesRead = CFReadStreamRead(readStream, buffer, sizeof(buffer));
// Less than zero is an error
if (bytesRead < 0) {
[self handleStreamError];
// If zero bytes were read, wait for the EOF to come.
} else if (bytesRead) {
totalBytesRead += bytesRead;
// Are we downloading to a file?
if (downloadDestinationPath) {
if (!outputStream) {
outputStream = [[NSOutputStream alloc] initToFileAtPath:downloadDestinationPath append:NO];
[outputStream open];
}
[outputStream write:buffer maxLength:bytesRead];
//Otherwise, let's add the data to our in-memory store
} else {
CFDataAppendBytes(receivedData, buffer, bytesRead);
}
}
}
- (void)handleStreamComplete
{
complete = YES;
[self updateUploadProgress];
[self updateDownloadProgress];
if (readStream) {
CFReadStreamClose(readStream);
CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFRelease(readStream);
readStream = NULL;
}
//close the output stream as we're done writing to the file
if (downloadDestinationPath) {
[outputStream close];
}
[self requestFinished];
}
- (void)handleStreamError
{
complete = YES;
NSError *err = [(NSError *)CFReadStreamCopyError(readStream) autorelease];
[self cancelLoad];
if (!error) { //We may already have handled this error
[self failWithProblem:[NSString stringWithFormat: @"An error occurred: %@",[err localizedDescription]]];
}
}
#pragma mark keychain storage
+ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
{
NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host
port:port
protocol:protocol
realm:realm
authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
NSURLCredentialStorage *storage = [NSURLCredentialStorage sharedCredentialStorage];
[storage setDefaultCredential:credentials forProtectionSpace:protectionSpace];
}
+ (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
{
NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host
port:port
protocol:protocol
realm:realm
authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
NSURLCredentialStorage *storage = [NSURLCredentialStorage sharedCredentialStorage];
return [storage defaultCredentialForProtectionSpace:protectionSpace];
}
@end
... ...
//
// ASIProgressDelegate.h
//
// Created by Ben Copsey on 28/03/2008.
// Copyright 2008 All-Seeing Interactive. All rights reserved
//
#import <Cocoa/Cocoa.h>
@protocol ASIProgressDelegate
- (void)incrementProgress;
- (void)setDoubleValue:(double)newValue;
- (void)incrementBy:(double)amount;
- (void)setMaxValue:(double)newMax;
@end
\ No newline at end of file
... ...
//
// AppDelegate.h
//
// Created by Ben Copsey on 09/07/2008.
// Copyright 2008 All-Seeing Interactive Ltd. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class ASIHTTPRequest;
@interface AppDelegate : NSObject {
NSOperationQueue *networkQueue;
IBOutlet NSProgressIndicator *progressIndicator;
IBOutlet NSTextView *htmlSource;
IBOutlet NSTextField *fileLocation;
IBOutlet NSWindow *window;
IBOutlet NSWindow *loginWindow;
IBOutlet NSTextField *host;
IBOutlet NSTextField *realm;
IBOutlet NSTextField *username;
IBOutlet NSTextField *password;
IBOutlet NSTextField *topSecretInfo;
IBOutlet NSButton *keychainCheckbox;
IBOutlet NSImageView *imageView1;
IBOutlet NSImageView *imageView2;
IBOutlet NSImageView *imageView3;
}
- (IBAction)simpleURLFetch:(id)sender;
- (IBAction)URLFetchWithProgress:(id)sender;
- (IBAction)fetchThreeImages:(id)sender;
- (void)authorizationNeededForRequest:(ASIHTTPRequest *)request;
- (IBAction)dismissAuthSheet:(id)sender;
- (IBAction)fetchTopSecretInformation:(id)sender;
- (IBAction)postWithProgress:(id)sender;
@end
... ...
//
// AppDelegate.m
//
// Created by Ben Copsey on 09/07/2008.
// Copyright 2008 All-Seeing Interactive Ltd. All rights reserved.
//
#import "AppDelegate.h"
#import "ASIHTTPRequest.h"
@implementation AppDelegate
- (id)init
{
[super init];
networkQueue = [[NSOperationQueue alloc] init];
return self;
}
- (void)dealloc
{
[networkQueue release];
[super dealloc];
}
- (IBAction)simpleURLFetch:(id)sender
{
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]] autorelease];
[request start];
if ([request dataString]) {
[htmlSource setString:[request dataString]];
}
}
- (IBAction)URLFetchWithProgress:(id)sender
{
[networkQueue cancelAllOperations];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://trails-network.net/Downloads/MemexTrails_1.0b1.zip"]] autorelease];
[request setDelegate:self];
[request setDownloadProgressDelegate:progressIndicator];
[request setDidFinishSelector:@selector(URLFetchWithProgressComplete:)];
[request setDownloadDestinationPath:[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"MemexTrails_1.0b1.zip"]];
[networkQueue addOperation:request];
}
- (void)URLFetchWithProgressComplete:(ASIHTTPRequest *)request
{
if ([request error]) {
[fileLocation setStringValue:[NSString stringWithFormat:@"An error occurred: %@",[[[request error] userInfo] objectForKey:@"Title"]]];
} else {
[fileLocation setStringValue:[NSString stringWithFormat:@"File downloaded to %@",[request downloadDestinationPath]]];
}
}
- (IBAction)fetchThreeImages:(id)sender
{
[imageView1 setImage:nil];
[imageView2 setImage:nil];
[imageView3 setImage:nil];
[networkQueue cancelAllOperations];
[progressIndicator setDoubleValue:0];
[progressIndicator setMaxValue:3];
ASIHTTPRequest *request;
request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/logo.png"]] autorelease];
[request setDelegate:self];
[request setDidFinishSelector:@selector(imageFetchComplete:)];
[request setDownloadDestinationPath:[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"1.png"]];
[networkQueue addOperation:request];
request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/trailsnetwork.png"]] autorelease];
[request setDelegate:self];
[request setDidFinishSelector:@selector(imageFetchComplete:)];
[request setDownloadDestinationPath:[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"2.png"]];
[networkQueue addOperation:request];
request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com/i/sharedspace20.png"]] autorelease];
[request setDelegate:self];
[request setDidFinishSelector:@selector(imageFetchComplete:)];
[request setDownloadDestinationPath:[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"3.png"]];
[networkQueue addOperation:request];
}
- (void)imageFetchComplete:(ASIHTTPRequest *)request
{
NSImage *img = [[[NSImage alloc] initWithContentsOfFile:[request downloadDestinationPath]] autorelease];
if (img) {
if ([imageView1 image]) {
if ([imageView2 image]) {
[imageView3 setImage:img];
} else {
[imageView2 setImage:img];
}
} else {
[imageView1 setImage:img];
}
}
[progressIndicator incrementBy:1];
}
- (IBAction)fetchTopSecretInformation:(id)sender
{
[networkQueue cancelAllOperations];
[progressIndicator setDoubleValue:0];
ASIHTTPRequest *request;
request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com/top_secret/"]] autorelease];
[request setDelegate:self];
[request setDidFinishSelector:@selector(topSecretFetchComplete:)];
[request setUsesKeychain:[keychainCheckbox state]];
[networkQueue addOperation:request];
}
- (IBAction)topSecretFetchComplete:(ASIHTTPRequest *)request
{
if (![request error]) {
[topSecretInfo setStringValue:[request dataString]];
[topSecretInfo setFont:[NSFont boldSystemFontOfSize:13]];
}
}
- (void)authorizationNeededForRequest:(ASIHTTPRequest *)request
{
[realm setStringValue:[request authenticationRealm]];
[host setStringValue:[request host]];
[NSApp beginSheet: loginWindow
modalForWindow: window
modalDelegate: self
didEndSelector: @selector(authSheetDidEnd:returnCode:contextInfo:)
contextInfo: request];
}
- (IBAction)dismissAuthSheet:(id)sender {
[[NSApplication sharedApplication] endSheet: loginWindow returnCode: [(NSControl*)sender tag]];
}
- (void)authSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
ASIHTTPRequest *request = (ASIHTTPRequest *)contextInfo;
if (returnCode == NSOKButton) {
[request setUsername:[[[username stringValue] copy] autorelease] andPassword:[[[password stringValue] copy] autorelease]];
[request retryWithAuthentication];
} else {
[request cancelLoad];
}
[loginWindow orderOut: self];
}
- (IBAction)postWithProgress:(id)sender
{
//Create a 1mb file
NSMutableData *data = [NSMutableData dataWithLength:1024*1024];
NSString *path = [[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"bigfile"];
[data writeToFile:path atomically:NO];
[networkQueue cancelAllOperations];
[progressIndicator setDoubleValue:0];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ignore"]] autorelease];
[request setDelegate:self];
[request setUploadProgressDelegate:progressIndicator];
[request setPostValue:@"test" forKey:@"value1"];
[request setPostValue:@"test" forKey:@"value2"];
[request setPostValue:@"test" forKey:@"value3"];
[request setFile:path forKey:@"file"];
[networkQueue addOperation:request];
}
@end
... ...
B/* Localized versions of Info.plist keys */
... ...
This diff could not be displayed because it is too large.
No preview for this file type
<?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>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.allseeing-i.asi-http-request</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
... ...
* Copyright (c) 2007-2008, All-Seeing Interactive
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the All-Seeing Interactive nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY All-Seeing Interactive ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
... ...
<?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>ActivePerspectiveName</key>
<string>Project</string>
<key>AllowedModules</key>
<array>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>PBXSmartGroupTreeModule</string>
<key>Name</key>
<string>Groups and Files Outline View</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Name</key>
<string>Editor</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>XCTaskListModule</string>
<key>Name</key>
<string>Task List</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>XCDetailModule</string>
<key>Name</key>
<string>File and Smart Group Detail Viewer</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>1</string>
<key>Module</key>
<string>PBXBuildResultsModule</string>
<key>Name</key>
<string>Detailed Build Results Viewer</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>1</string>
<key>Module</key>
<string>PBXProjectFindModule</string>
<key>Name</key>
<string>Project Batch Find Tool</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>XCProjectFormatConflictsModule</string>
<key>Name</key>
<string>Project Format Conflicts List</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>PBXBookmarksModule</string>
<key>Name</key>
<string>Bookmarks Tool</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>PBXClassBrowserModule</string>
<key>Name</key>
<string>Class Browser</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>PBXCVSModule</string>
<key>Name</key>
<string>Source Code Control Tool</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>PBXDebugBreakpointsModule</string>
<key>Name</key>
<string>Debug Breakpoints Tool</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>XCDockableInspector</string>
<key>Name</key>
<string>Inspector</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>PBXOpenQuicklyModule</string>
<key>Name</key>
<string>Open Quickly Tool</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>1</string>
<key>Module</key>
<string>PBXDebugSessionModule</string>
<key>Name</key>
<string>Debugger</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>1</string>
<key>Module</key>
<string>PBXDebugCLIModule</string>
<key>Name</key>
<string>Debug Console</string>
</dict>
<dict>
<key>BundleLoadPath</key>
<string></string>
<key>MaxInstances</key>
<string>n</string>
<key>Module</key>
<string>XCSnapshotModule</string>
<key>Name</key>
<string>Snapshots Tool</string>
</dict>
</array>
<key>BundlePath</key>
<string>/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources</string>
<key>Description</key>
<string>DefaultDescriptionKey</string>
<key>DockingSystemVisible</key>
<false/>
<key>Extension</key>
<string>mode1v3</string>
<key>FavBarConfig</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>B5ABC7B40E24C52A0072F422</string>
<key>XCBarModuleItemNames</key>
<dict/>
<key>XCBarModuleItems</key>
<array/>
</dict>
<key>FirstTimeWindowDisplayed</key>
<false/>
<key>Identifier</key>
<string>com.apple.perspectives.project.mode1v3</string>
<key>MajorVersion</key>
<integer>33</integer>
<key>MinorVersion</key>
<integer>0</integer>
<key>Name</key>
<string>Default</string>
<key>Notifications</key>
<array/>
<key>OpenEditors</key>
<array>
<dict>
<key>Content</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>B513D4520E2BE8A9000A50C6</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequest.m</string>
<key>PBXSplitModuleInNavigatorKey</key>
<dict>
<key>Split0</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>B513D4530E2BE8A9000A50C6</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequest.m</string>
<key>_historyCapacity</key>
<integer>0</integer>
<key>bookmark</key>
<string>B513D49B0E2BF854000A50C6</string>
<key>history</key>
<array>
<string>B513D4110E2BE42A000A50C6</string>
</array>
</dict>
<key>SplitCount</key>
<string>1</string>
</dict>
<key>StatusBarVisibility</key>
<true/>
</dict>
<key>Geometry</key>
<dict>
<key>Frame</key>
<string>{{0, 20}, {1475, 777}}</string>
<key>PBXModuleWindowStatusBarHidden2</key>
<false/>
<key>RubberWindowFrame</key>
<string>190 -332 1475 818 0 0 1440 878 </string>
</dict>
</dict>
</array>
<key>PerspectiveWidths</key>
<array>
<integer>-1</integer>
<integer>-1</integer>
</array>
<key>Perspectives</key>
<array>
<dict>
<key>ChosenToolbarItems</key>
<array>
<string>active-target-popup</string>
<string>active-buildstyle-popup</string>
<string>action</string>
<string>NSToolbarFlexibleSpaceItem</string>
<string>buildOrClean</string>
<string>build-and-goOrGo</string>
<string>com.apple.ide.PBXToolbarStopButton</string>
<string>get-info</string>
<string>toggle-editor</string>
<string>NSToolbarFlexibleSpaceItem</string>
<string>com.apple.pbx.toolbar.searchfield</string>
</array>
<key>ControllerClassBaseName</key>
<string></string>
<key>IconName</key>
<string>WindowOfProjectWithEditor</string>
<key>Identifier</key>
<string>perspective.project</string>
<key>IsVertical</key>
<false/>
<key>Layout</key>
<array>
<dict>
<key>ContentConfiguration</key>
<dict>
<key>PBXBottomSmartGroupGIDs</key>
<array>
<string>1C37FBAC04509CD000000102</string>
<string>1C37FAAC04509CD000000102</string>
<string>1C08E77C0454961000C914BD</string>
<string>1C37FABC05509CD000000102</string>
<string>1C37FABC05539CD112110102</string>
<string>E2644B35053B69B200211256</string>
<string>1C37FABC04509CD000100104</string>
<string>1CC0EA4004350EF90044410B</string>
<string>1CC0EA4004350EF90041110B</string>
</array>
<key>PBXProjectModuleGUID</key>
<string>1CE0B1FE06471DED0097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>Files</string>
<key>PBXProjectStructureProvided</key>
<string>yes</string>
<key>PBXSmartGroupTreeModuleColumnData</key>
<dict>
<key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
<array>
<real>186</real>
</array>
<key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
<array>
<string>MainColumn</string>
</array>
</dict>
<key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
<dict>
<key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
<array>
<string>29B97314FDCFA39411CA2CEA</string>
<string>080E96DDFE201D6D7F000001</string>
<string>29B97317FDCFA39411CA2CEA</string>
<string>1C37FABC05509CD000000102</string>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>5</integer>
<integer>1</integer>
<integer>0</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
<string>{{0, 0}, {186, 681}}</string>
</dict>
<key>PBXTopSmartGroupGIDs</key>
<array/>
<key>XCIncludePerspectivesSwitch</key>
<true/>
<key>XCSharingToken</key>
<string>com.apple.Xcode.GFSharingToken</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {203, 699}}</string>
<key>GroupTreeTableConfiguration</key>
<array>
<string>MainColumn</string>
<real>186</real>
</array>
<key>RubberWindowFrame</key>
<string>308 138 1121 740 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXSmartGroupTreeModule</string>
<key>Proportion</key>
<string>203pt</string>
</dict>
<dict>
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CE0B20306471E060097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequest.m</string>
<key>PBXSplitModuleInNavigatorKey</key>
<dict>
<key>Split0</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CE0B20406471E060097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequest.m</string>
<key>_historyCapacity</key>
<integer>0</integer>
<key>bookmark</key>
<string>B513D49A0E2BF854000A50C6</string>
<key>history</key>
<array>
<string>B513D3E90E2BD48A000A50C6</string>
<string>B513D3EA0E2BD48A000A50C6</string>
<string>B513D4170E2BE8A9000A50C6</string>
<string>B513D41B0E2BE8A9000A50C6</string>
<string>B513D4930E2BF854000A50C6</string>
<string>B513D4940E2BF854000A50C6</string>
<string>B513D4950E2BF854000A50C6</string>
</array>
<key>prevStack</key>
<array>
<string>B5ABC8250E24CDE70072F422</string>
<string>B5ABC8260E24CDE70072F422</string>
<string>B5ABC8280E24CDE70072F422</string>
<string>B5ABC82F0E24CDE70072F422</string>
<string>B5ABC8300E24CDE70072F422</string>
<string>B513D25D0E2B507F000A50C6</string>
<string>B513D25E0E2B507F000A50C6</string>
<string>B513D2750E2B5152000A50C6</string>
<string>B513D2760E2B5152000A50C6</string>
<string>B513D2770E2B5152000A50C6</string>
<string>B513D2850E2B522F000A50C6</string>
<string>B513D2860E2B522F000A50C6</string>
<string>B513D2870E2B522F000A50C6</string>
<string>B513D2880E2B522F000A50C6</string>
<string>B513D2890E2B522F000A50C6</string>
<string>B513D28F0E2B524B000A50C6</string>
<string>B513D2900E2B524B000A50C6</string>
<string>B513D2950E2B5259000A50C6</string>
<string>B513D2960E2B5259000A50C6</string>
<string>B513D2A00E2B52F3000A50C6</string>
<string>B513D2A10E2B52F3000A50C6</string>
<string>B513D2AA0E2B542B000A50C6</string>
<string>B513D2C60E2B562C000A50C6</string>
<string>B513D2C70E2B562C000A50C6</string>
<string>B513D2CF0E2B56C6000A50C6</string>
<string>B513D2D60E2B56FF000A50C6</string>
<string>B513D2D70E2B56FF000A50C6</string>
<string>B513D2D80E2B56FF000A50C6</string>
<string>B513D2DD0E2B575C000A50C6</string>
<string>B513D2DE0E2B575C000A50C6</string>
<string>B513D2E40E2B57B7000A50C6</string>
<string>B513D2E50E2B57B7000A50C6</string>
<string>B513D2E60E2B57B7000A50C6</string>
<string>B513D2E70E2B57B7000A50C6</string>
<string>B513D2EC0E2B5841000A50C6</string>
<string>B513D2F40E2B5A2E000A50C6</string>
<string>B513D2F50E2B5A2E000A50C6</string>
<string>B513D2F60E2B5A2E000A50C6</string>
<string>B513D2F70E2B5A2E000A50C6</string>
<string>B513D2FD0E2B5A3F000A50C6</string>
<string>B513D2FE0E2B5A3F000A50C6</string>
<string>B513D30A0E2B5A62000A50C6</string>
<string>B513D3100E2B5C2F000A50C6</string>
<string>B513D3110E2B5C2F000A50C6</string>
<string>B513D3190E2B5CDB000A50C6</string>
<string>B513D3210E2B5D23000A50C6</string>
<string>B513D3220E2B5D23000A50C6</string>
<string>B513D3230E2B5D23000A50C6</string>
<string>B513D3520E2B5F47000A50C6</string>
<string>B513D3530E2B5F47000A50C6</string>
<string>B513D3540E2B5F47000A50C6</string>
<string>B513D3550E2B5F47000A50C6</string>
<string>B513D3560E2B5F47000A50C6</string>
<string>B513D3570E2B5F47000A50C6</string>
<string>B513D3580E2B5F47000A50C6</string>
<string>B513D35F0E2B5F47000A50C6</string>
<string>B513D3760E2B61A1000A50C6</string>
<string>B513D3770E2B61A1000A50C6</string>
<string>B513D3780E2B61A1000A50C6</string>
<string>B513D3790E2B61A1000A50C6</string>
<string>B513D37A0E2B61A1000A50C6</string>
<string>B513D3800E2B61BA000A50C6</string>
<string>B513D3810E2B61BA000A50C6</string>
<string>B513D3820E2B61BA000A50C6</string>
<string>B513D38F0E2B62C1000A50C6</string>
<string>B513D3900E2B62C1000A50C6</string>
<string>B513D3910E2B62C1000A50C6</string>
<string>B513D3930E2B62C1000A50C6</string>
<string>B513D3940E2B62C1000A50C6</string>
<string>B513D3950E2B62C1000A50C6</string>
<string>B513D39F0E2B6303000A50C6</string>
<string>B513D3A00E2B6303000A50C6</string>
<string>B513D3A10E2B6303000A50C6</string>
<string>B513D3A20E2B6303000A50C6</string>
<string>B513D3AE0E2BD1BC000A50C6</string>
<string>B513D3AF0E2BD1BC000A50C6</string>
<string>B513D3B00E2BD1BC000A50C6</string>
<string>B513D3B10E2BD1BC000A50C6</string>
<string>B513D3B20E2BD1BC000A50C6</string>
<string>B513D3B30E2BD1BC000A50C6</string>
<string>B513D3B40E2BD1BC000A50C6</string>
<string>B513D3B50E2BD1BC000A50C6</string>
<string>B513D3B60E2BD1BC000A50C6</string>
<string>B513D3B70E2BD1BC000A50C6</string>
<string>B513D3B80E2BD1BC000A50C6</string>
<string>B513D3BA0E2BD1BC000A50C6</string>
<string>B513D3BB0E2BD1BC000A50C6</string>
<string>B513D3C30E2BD1BC000A50C6</string>
<string>B513D3C40E2BD1BC000A50C6</string>
<string>B513D3CE0E2BD213000A50C6</string>
<string>B513D3CF0E2BD213000A50C6</string>
<string>B513D3D00E2BD213000A50C6</string>
<string>B513D3D10E2BD213000A50C6</string>
<string>B513D3D20E2BD213000A50C6</string>
<string>B513D3D30E2BD213000A50C6</string>
<string>B513D3D40E2BD213000A50C6</string>
<string>B513D3D50E2BD213000A50C6</string>
<string>B513D3D60E2BD213000A50C6</string>
<string>B513D3D70E2BD213000A50C6</string>
<string>B513D3F00E2BD48A000A50C6</string>
<string>B513D3F10E2BD48A000A50C6</string>
<string>B513D3F20E2BD48A000A50C6</string>
<string>B513D3F30E2BD48A000A50C6</string>
<string>B513D3F40E2BD48A000A50C6</string>
<string>B513D3F80E2BD48A000A50C6</string>
<string>B513D3F90E2BD48A000A50C6</string>
<string>B513D3FA0E2BD48A000A50C6</string>
<string>B513D3FB0E2BD48A000A50C6</string>
<string>B513D3FC0E2BD48A000A50C6</string>
<string>B513D3FD0E2BD48A000A50C6</string>
<string>B513D3FE0E2BD48A000A50C6</string>
<string>B513D3FF0E2BD48A000A50C6</string>
<string>B513D4000E2BD48A000A50C6</string>
<string>B513D4010E2BD48A000A50C6</string>
<string>B513D4020E2BD48A000A50C6</string>
<string>B513D4030E2BD48A000A50C6</string>
<string>B513D4040E2BD48A000A50C6</string>
<string>B513D4050E2BD48A000A50C6</string>
<string>B513D4060E2BD48A000A50C6</string>
<string>B513D4070E2BD48A000A50C6</string>
<string>B513D4080E2BD48A000A50C6</string>
<string>B513D40E0E2BD48A000A50C6</string>
<string>B513D40F0E2BD48A000A50C6</string>
<string>B513D41E0E2BE8A9000A50C6</string>
<string>B513D41F0E2BE8A9000A50C6</string>
<string>B513D4200E2BE8A9000A50C6</string>
<string>B513D4210E2BE8A9000A50C6</string>
<string>B513D4220E2BE8A9000A50C6</string>
<string>B513D4230E2BE8A9000A50C6</string>
<string>B513D4240E2BE8A9000A50C6</string>
<string>B513D4250E2BE8A9000A50C6</string>
<string>B513D4260E2BE8A9000A50C6</string>
<string>B513D4270E2BE8A9000A50C6</string>
<string>B513D4280E2BE8A9000A50C6</string>
<string>B513D4290E2BE8A9000A50C6</string>
<string>B513D42D0E2BE8A9000A50C6</string>
<string>B513D42E0E2BE8A9000A50C6</string>
<string>B513D42F0E2BE8A9000A50C6</string>
<string>B513D4300E2BE8A9000A50C6</string>
<string>B513D4310E2BE8A9000A50C6</string>
<string>B513D4320E2BE8A9000A50C6</string>
<string>B513D4350E2BE8A9000A50C6</string>
<string>B513D4360E2BE8A9000A50C6</string>
<string>B513D4370E2BE8A9000A50C6</string>
<string>B513D4380E2BE8A9000A50C6</string>
<string>B513D4390E2BE8A9000A50C6</string>
<string>B513D43A0E2BE8A9000A50C6</string>
<string>B513D43E0E2BE8A9000A50C6</string>
<string>B513D43F0E2BE8A9000A50C6</string>
<string>B513D4400E2BE8A9000A50C6</string>
<string>B513D4410E2BE8A9000A50C6</string>
<string>B513D4420E2BE8A9000A50C6</string>
<string>B513D4430E2BE8A9000A50C6</string>
<string>B513D4440E2BE8A9000A50C6</string>
<string>B513D4450E2BE8A9000A50C6</string>
<string>B513D4460E2BE8A9000A50C6</string>
<string>B513D4470E2BE8A9000A50C6</string>
<string>B513D4480E2BE8A9000A50C6</string>
<string>B513D4490E2BE8A9000A50C6</string>
<string>B513D44A0E2BE8A9000A50C6</string>
<string>B513D44B0E2BE8A9000A50C6</string>
<string>B513D44C0E2BE8A9000A50C6</string>
<string>B513D44D0E2BE8A9000A50C6</string>
<string>B513D44E0E2BE8A9000A50C6</string>
<string>B513D44F0E2BE8A9000A50C6</string>
<string>B513D4500E2BE8A9000A50C6</string>
<string>B513D4590E2BEDE4000A50C6</string>
<string>B513D45A0E2BEDE4000A50C6</string>
<string>B513D45B0E2BEDE4000A50C6</string>
<string>B513D45C0E2BEDE4000A50C6</string>
<string>B513D45D0E2BEDE4000A50C6</string>
<string>B513D4860E2BF2C4000A50C6</string>
<string>B513D4870E2BF2C4000A50C6</string>
<string>B513D4960E2BF854000A50C6</string>
<string>B513D4970E2BF854000A50C6</string>
<string>B513D4980E2BF854000A50C6</string>
<string>B513D4990E2BF854000A50C6</string>
</array>
</dict>
<key>SplitCount</key>
<string>1</string>
</dict>
<key>StatusBarVisibility</key>
<true/>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {913, 581}}</string>
<key>RubberWindowFrame</key>
<string>308 138 1121 740 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>581pt</string>
</dict>
<dict>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CE0B20506471E060097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>Detail</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 586}, {913, 113}}</string>
<key>RubberWindowFrame</key>
<string>308 138 1121 740 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>XCDetailModule</string>
<key>Proportion</key>
<string>113pt</string>
</dict>
</array>
<key>Proportion</key>
<string>913pt</string>
</dict>
</array>
<key>Name</key>
<string>Project</string>
<key>ServiceClasses</key>
<array>
<string>XCModuleDock</string>
<string>PBXSmartGroupTreeModule</string>
<string>XCModuleDock</string>
<string>PBXNavigatorGroup</string>
<string>XCDetailModule</string>
</array>
<key>TableOfContents</key>
<array>
<string>B513D2600E2B507F000A50C6</string>
<string>1CE0B1FE06471DED0097A5F4</string>
<string>B513D2610E2B507F000A50C6</string>
<string>1CE0B20306471E060097A5F4</string>
<string>1CE0B20506471E060097A5F4</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.defaultV3</string>
</dict>
<dict>
<key>ControllerClassBaseName</key>
<string></string>
<key>IconName</key>
<string>WindowOfProject</string>
<key>Identifier</key>
<string>perspective.morph</string>
<key>IsVertical</key>
<integer>0</integer>
<key>Layout</key>
<array>
<dict>
<key>BecomeActive</key>
<integer>1</integer>
<key>ContentConfiguration</key>
<dict>
<key>PBXBottomSmartGroupGIDs</key>
<array>
<string>1C37FBAC04509CD000000102</string>
<string>1C37FAAC04509CD000000102</string>
<string>1C08E77C0454961000C914BD</string>
<string>1C37FABC05509CD000000102</string>
<string>1C37FABC05539CD112110102</string>
<string>E2644B35053B69B200211256</string>
<string>1C37FABC04509CD000100104</string>
<string>1CC0EA4004350EF90044410B</string>
<string>1CC0EA4004350EF90041110B</string>
</array>
<key>PBXProjectModuleGUID</key>
<string>11E0B1FE06471DED0097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>Files</string>
<key>PBXProjectStructureProvided</key>
<string>yes</string>
<key>PBXSmartGroupTreeModuleColumnData</key>
<dict>
<key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
<array>
<real>186</real>
</array>
<key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
<array>
<string>MainColumn</string>
</array>
</dict>
<key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
<dict>
<key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
<array>
<string>29B97314FDCFA39411CA2CEA</string>
<string>1C37FABC05509CD000000102</string>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>0</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
<string>{{0, 0}, {186, 337}}</string>
</dict>
<key>PBXTopSmartGroupGIDs</key>
<array/>
<key>XCIncludePerspectivesSwitch</key>
<integer>1</integer>
<key>XCSharingToken</key>
<string>com.apple.Xcode.GFSharingToken</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {203, 355}}</string>
<key>GroupTreeTableConfiguration</key>
<array>
<string>MainColumn</string>
<real>186</real>
</array>
<key>RubberWindowFrame</key>
<string>373 269 690 397 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXSmartGroupTreeModule</string>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Name</key>
<string>Morph</string>
<key>PreferredWidth</key>
<integer>300</integer>
<key>ServiceClasses</key>
<array>
<string>XCModuleDock</string>
<string>PBXSmartGroupTreeModule</string>
</array>
<key>TableOfContents</key>
<array>
<string>11E0B1FE06471DED0097A5F4</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.default.shortV3</string>
</dict>
</array>
<key>PerspectivesBarVisible</key>
<false/>
<key>ShelfIsVisible</key>
<false/>
<key>SourceDescription</key>
<string>file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode1.xcperspec'</string>
<key>StatusbarIsVisible</key>
<true/>
<key>TimeStamp</key>
<real>0.0</real>
<key>ToolbarDisplayMode</key>
<integer>1</integer>
<key>ToolbarIsVisible</key>
<true/>
<key>ToolbarSizeMode</key>
<integer>1</integer>
<key>Type</key>
<string>Perspectives</string>
<key>UpdateMessage</key>
<string>The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature). You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature. Do you wish to update to the latest Workspace defaults for project '%@'?</string>
<key>WindowJustification</key>
<integer>5</integer>
<key>WindowOrderList</key>
<array>
<string>1C530D57069F1CE1000CFCEE</string>
<string>B513D2650E2B507F000A50C6</string>
<string>B513D2660E2B507F000A50C6</string>
<string>B5ABC8410E24CDE70072F422</string>
<string>B513D4520E2BE8A9000A50C6</string>
<string>1C78EAAD065D492600B07095</string>
<string>1CD10A99069EF8BA00B06720</string>
<string>/Users/ben/asi-http-request/asi-http-request.xcodeproj</string>
</array>
<key>WindowString</key>
<string>308 138 1121 740 0 0 1440 878 </string>
<key>WindowToolsV3</key>
<array>
<dict>
<key>FirstTimeWindowDisplayed</key>
<false/>
<key>Identifier</key>
<string>windowTool.build</string>
<key>IsVertical</key>
<true/>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CD0528F0623707200166675</string>
<key>PBXProjectModuleLabel</key>
<string>ASIHTTPRequest.m</string>
<key>StatusBarVisibility</key>
<true/>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {1440, 536}}</string>
<key>RubberWindowFrame</key>
<string>0 60 1440 818 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>536pt</string>
</dict>
<dict>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>XCMainBuildResultsModuleGUID</string>
<key>PBXProjectModuleLabel</key>
<string>Build</string>
<key>XCBuildResultsTrigger_Collapse</key>
<integer>1021</integer>
<key>XCBuildResultsTrigger_Open</key>
<integer>1011</integer>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 541}, {1440, 236}}</string>
<key>RubberWindowFrame</key>
<string>0 60 1440 818 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXBuildResultsModule</string>
<key>Proportion</key>
<string>236pt</string>
</dict>
</array>
<key>Proportion</key>
<string>777pt</string>
</dict>
</array>
<key>Name</key>
<string>Build Results</string>
<key>ServiceClasses</key>
<array>
<string>PBXBuildResultsModule</string>
</array>
<key>StatusbarIsVisible</key>
<true/>
<key>TableOfContents</key>
<array>
<string>B5ABC8410E24CDE70072F422</string>
<string>B513D2620E2B507F000A50C6</string>
<string>1CD0528F0623707200166675</string>
<string>XCMainBuildResultsModuleGUID</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.buildV3</string>
<key>WindowString</key>
<string>0 60 1440 818 0 0 1440 878 </string>
<key>WindowToolGUID</key>
<string>B5ABC8410E24CDE70072F422</string>
<key>WindowToolIsVisible</key>
<false/>
</dict>
<dict>
<key>FirstTimeWindowDisplayed</key>
<false/>
<key>Identifier</key>
<string>windowTool.debugger</string>
<key>IsVertical</key>
<true/>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>ContentConfiguration</key>
<dict>
<key>Debugger</key>
<dict>
<key>HorizontalSplitView</key>
<dict>
<key>_collapsingFrameDimension</key>
<real>0.0</real>
<key>_indexOfCollapsedView</key>
<integer>0</integer>
<key>_percentageOfCollapsedView</key>
<real>0.0</real>
<key>isCollapsed</key>
<string>yes</string>
<key>sizes</key>
<array>
<string>{{0, 0}, {713, 338}}</string>
<string>{{713, 0}, {851, 338}}</string>
</array>
</dict>
<key>VerticalSplitView</key>
<dict>
<key>_collapsingFrameDimension</key>
<real>0.0</real>
<key>_indexOfCollapsedView</key>
<integer>0</integer>
<key>_percentageOfCollapsedView</key>
<real>0.0</real>
<key>isCollapsed</key>
<string>yes</string>
<key>sizes</key>
<array>
<string>{{0, 0}, {1564, 338}}</string>
<string>{{0, 338}, {1564, 297}}</string>
</array>
</dict>
</dict>
<key>LauncherConfigVersion</key>
<string>8</string>
<key>PBXProjectModuleGUID</key>
<string>1C162984064C10D400B95A72</string>
<key>PBXProjectModuleLabel</key>
<string>Debug - GLUTExamples (Underwater)</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>DebugConsoleVisible</key>
<string>None</string>
<key>DebugConsoleWindowFrame</key>
<string>{{200, 200}, {500, 300}}</string>
<key>DebugSTDIOWindowFrame</key>
<string>{{200, 200}, {500, 300}}</string>
<key>Frame</key>
<string>{{0, 0}, {1564, 635}}</string>
<key>PBXDebugSessionStackFrameViewKey</key>
<dict>
<key>DebugVariablesTableConfiguration</key>
<array>
<string>Name</string>
<real>248</real>
<string>Type</string>
<real>84</real>
<string>Value</string>
<real>85</real>
<string>Summary</string>
<real>406</real>
</array>
<key>Frame</key>
<string>{{713, 0}, {851, 338}}</string>
<key>RubberWindowFrame</key>
<string>-221 202 1564 676 0 0 1440 878 </string>
</dict>
<key>RubberWindowFrame</key>
<string>-221 202 1564 676 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXDebugSessionModule</string>
<key>Proportion</key>
<string>635pt</string>
</dict>
</array>
<key>Proportion</key>
<string>635pt</string>
</dict>
</array>
<key>Name</key>
<string>Debugger</string>
<key>ServiceClasses</key>
<array>
<string>PBXDebugSessionModule</string>
</array>
<key>StatusbarIsVisible</key>
<true/>
<key>TableOfContents</key>
<array>
<string>1CD10A99069EF8BA00B06720</string>
<string>B513D2530E2B5029000A50C6</string>
<string>1C162984064C10D400B95A72</string>
<string>B513D2540E2B5029000A50C6</string>
<string>B513D2550E2B5029000A50C6</string>
<string>B513D2560E2B5029000A50C6</string>
<string>B513D2570E2B5029000A50C6</string>
<string>B513D2580E2B5029000A50C6</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.debugV3</string>
<key>WindowString</key>
<string>-221 202 1564 676 0 0 1440 878 </string>
<key>WindowToolGUID</key>
<string>1CD10A99069EF8BA00B06720</string>
<key>WindowToolIsVisible</key>
<true/>
</dict>
<dict>
<key>FirstTimeWindowDisplayed</key>
<false/>
<key>Identifier</key>
<string>windowTool.find</string>
<key>IsVertical</key>
<true/>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CDD528C0622207200134675</string>
<key>PBXProjectModuleLabel</key>
<string>&lt;No Editor&gt;</string>
<key>StatusBarVisibility</key>
<true/>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {781, 212}}</string>
<key>RubberWindowFrame</key>
<string>329 385 781 470 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>781pt</string>
</dict>
</array>
<key>Proportion</key>
<string>212pt</string>
</dict>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CD0528E0623707200166675</string>
<key>PBXProjectModuleLabel</key>
<string>Project Find</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 217}, {781, 212}}</string>
<key>RubberWindowFrame</key>
<string>329 385 781 470 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXProjectFindModule</string>
<key>Proportion</key>
<string>212pt</string>
</dict>
</array>
<key>Proportion</key>
<string>429pt</string>
</dict>
</array>
<key>Name</key>
<string>Project Find</string>
<key>ServiceClasses</key>
<array>
<string>PBXProjectFindModule</string>
</array>
<key>StatusbarIsVisible</key>
<true/>
<key>TableOfContents</key>
<array>
<string>1C530D57069F1CE1000CFCEE</string>
<string>B513D3460E2B5F3E000A50C6</string>
<string>B513D3470E2B5F3E000A50C6</string>
<string>1CDD528C0622207200134675</string>
<string>1CD0528E0623707200166675</string>
</array>
<key>WindowString</key>
<string>329 385 781 470 0 0 1440 878 </string>
<key>WindowToolGUID</key>
<string>1C530D57069F1CE1000CFCEE</string>
<key>WindowToolIsVisible</key>
<false/>
</dict>
<dict>
<key>Identifier</key>
<string>MENUSEPARATOR</string>
</dict>
<dict>
<key>FirstTimeWindowDisplayed</key>
<false/>
<key>Identifier</key>
<string>windowTool.debuggerConsole</string>
<key>IsVertical</key>
<true/>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1C78EAAC065D492600B07095</string>
<key>PBXProjectModuleLabel</key>
<string>Debugger Console</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {629, 511}}</string>
<key>RubberWindowFrame</key>
<string>49 209 629 552 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXDebugCLIModule</string>
<key>Proportion</key>
<string>511pt</string>
</dict>
</array>
<key>Proportion</key>
<string>511pt</string>
</dict>
</array>
<key>Name</key>
<string>Debugger Console</string>
<key>ServiceClasses</key>
<array>
<string>PBXDebugCLIModule</string>
</array>
<key>StatusbarIsVisible</key>
<true/>
<key>TableOfContents</key>
<array>
<string>1C78EAAD065D492600B07095</string>
<string>B513D2630E2B507F000A50C6</string>
<string>1C78EAAC065D492600B07095</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.consoleV3</string>
<key>WindowString</key>
<string>49 209 629 552 0 0 1440 878 </string>
<key>WindowToolGUID</key>
<string>1C78EAAD065D492600B07095</string>
<key>WindowToolIsVisible</key>
<true/>
</dict>
<dict>
<key>Identifier</key>
<string>windowTool.snapshots</string>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>Module</key>
<string>XCSnapshotModule</string>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Name</key>
<string>Snapshots</string>
<key>ServiceClasses</key>
<array>
<string>XCSnapshotModule</string>
</array>
<key>StatusbarIsVisible</key>
<string>Yes</string>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.snapshots</string>
<key>WindowString</key>
<string>315 824 300 550 0 0 1440 878 </string>
<key>WindowToolIsVisible</key>
<string>Yes</string>
</dict>
<dict>
<key>Identifier</key>
<string>windowTool.scm</string>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1C78EAB2065D492600B07095</string>
<key>PBXProjectModuleLabel</key>
<string>&lt;No Editor&gt;</string>
<key>PBXSplitModuleInNavigatorKey</key>
<dict>
<key>Split0</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1C78EAB3065D492600B07095</string>
</dict>
<key>SplitCount</key>
<string>1</string>
</dict>
<key>StatusBarVisibility</key>
<integer>1</integer>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {452, 0}}</string>
<key>RubberWindowFrame</key>
<string>743 379 452 308 0 0 1280 1002 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>0pt</string>
</dict>
<dict>
<key>BecomeActive</key>
<integer>1</integer>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CD052920623707200166675</string>
<key>PBXProjectModuleLabel</key>
<string>SCM</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>ConsoleFrame</key>
<string>{{0, 259}, {452, 0}}</string>
<key>Frame</key>
<string>{{0, 7}, {452, 259}}</string>
<key>RubberWindowFrame</key>
<string>743 379 452 308 0 0 1280 1002 </string>
<key>TableConfiguration</key>
<array>
<string>Status</string>
<real>30</real>
<string>FileName</string>
<real>199</real>
<string>Path</string>
<real>197.09500122070312</real>
</array>
<key>TableFrame</key>
<string>{{0, 0}, {452, 250}}</string>
</dict>
<key>Module</key>
<string>PBXCVSModule</string>
<key>Proportion</key>
<string>262pt</string>
</dict>
</array>
<key>Proportion</key>
<string>266pt</string>
</dict>
</array>
<key>Name</key>
<string>SCM</string>
<key>ServiceClasses</key>
<array>
<string>PBXCVSModule</string>
</array>
<key>StatusbarIsVisible</key>
<integer>1</integer>
<key>TableOfContents</key>
<array>
<string>1C78EAB4065D492600B07095</string>
<string>1C78EAB5065D492600B07095</string>
<string>1C78EAB2065D492600B07095</string>
<string>1CD052920623707200166675</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.scm</string>
<key>WindowString</key>
<string>743 379 452 308 0 0 1280 1002 </string>
</dict>
<dict>
<key>Identifier</key>
<string>windowTool.breakpoints</string>
<key>IsVertical</key>
<integer>0</integer>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<integer>1</integer>
<key>ContentConfiguration</key>
<dict>
<key>PBXBottomSmartGroupGIDs</key>
<array>
<string>1C77FABC04509CD000000102</string>
</array>
<key>PBXProjectModuleGUID</key>
<string>1CE0B1FE06471DED0097A5F4</string>
<key>PBXProjectModuleLabel</key>
<string>Files</string>
<key>PBXProjectStructureProvided</key>
<string>no</string>
<key>PBXSmartGroupTreeModuleColumnData</key>
<dict>
<key>PBXSmartGroupTreeModuleColumnWidthsKey</key>
<array>
<real>168</real>
</array>
<key>PBXSmartGroupTreeModuleColumnsKey_v4</key>
<array>
<string>MainColumn</string>
</array>
</dict>
<key>PBXSmartGroupTreeModuleOutlineStateKey_v7</key>
<dict>
<key>PBXSmartGroupTreeModuleOutlineStateExpansionKey</key>
<array>
<string>1C77FABC04509CD000000102</string>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>0</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
<string>{{0, 0}, {168, 350}}</string>
</dict>
<key>PBXTopSmartGroupGIDs</key>
<array/>
<key>XCIncludePerspectivesSwitch</key>
<integer>0</integer>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {185, 368}}</string>
<key>GroupTreeTableConfiguration</key>
<array>
<string>MainColumn</string>
<real>168</real>
</array>
<key>RubberWindowFrame</key>
<string>315 424 744 409 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXSmartGroupTreeModule</string>
<key>Proportion</key>
<string>185pt</string>
</dict>
<dict>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
<string>1CA1AED706398EBD00589147</string>
<key>PBXProjectModuleLabel</key>
<string>Detail</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{190, 0}, {554, 368}}</string>
<key>RubberWindowFrame</key>
<string>315 424 744 409 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>XCDetailModule</string>
<key>Proportion</key>
<string>554pt</string>
</dict>
</array>
<key>Proportion</key>
<string>368pt</string>
</dict>
</array>
<key>MajorVersion</key>
<integer>3</integer>
<key>MinorVersion</key>
<integer>0</integer>
<key>Name</key>
<string>Breakpoints</string>
<key>ServiceClasses</key>
<array>
<string>PBXSmartGroupTreeModule</string>
<string>XCDetailModule</string>
</array>
<key>StatusbarIsVisible</key>
<integer>1</integer>
<key>TableOfContents</key>
<array>
<string>1CDDB66807F98D9800BB5817</string>
<string>1CDDB66907F98D9800BB5817</string>
<string>1CE0B1FE06471DED0097A5F4</string>
<string>1CA1AED706398EBD00589147</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.breakpointsV3</string>
<key>WindowString</key>
<string>315 424 744 409 0 0 1440 878 </string>
<key>WindowToolGUID</key>
<string>1CDDB66807F98D9800BB5817</string>
<key>WindowToolIsVisible</key>
<integer>1</integer>
</dict>
<dict>
<key>Identifier</key>
<string>windowTool.debugAnimator</string>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Name</key>
<string>Debug Visualizer</string>
<key>ServiceClasses</key>
<array>
<string>PBXNavigatorGroup</string>
</array>
<key>StatusbarIsVisible</key>
<integer>1</integer>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.debugAnimatorV3</string>
<key>WindowString</key>
<string>100 100 700 500 0 0 1280 1002 </string>
</dict>
<dict>
<key>Identifier</key>
<string>windowTool.bookmarks</string>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>Module</key>
<string>PBXBookmarksModule</string>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Name</key>
<string>Bookmarks</string>
<key>ServiceClasses</key>
<array>
<string>PBXBookmarksModule</string>
</array>
<key>StatusbarIsVisible</key>
<integer>0</integer>
<key>WindowString</key>
<string>538 42 401 187 0 0 1280 1002 </string>
</dict>
<dict>
<key>Identifier</key>
<string>windowTool.projectFormatConflicts</string>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>Module</key>
<string>XCProjectFormatConflictsModule</string>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Name</key>
<string>Project Format Conflicts</string>
<key>ServiceClasses</key>
<array>
<string>XCProjectFormatConflictsModule</string>
</array>
<key>StatusbarIsVisible</key>
<integer>0</integer>
<key>WindowContentMinSize</key>
<string>450 300</string>
<key>WindowString</key>
<string>50 850 472 307 0 0 1440 877</string>
</dict>
<dict>
<key>Identifier</key>
<string>windowTool.classBrowser</string>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<integer>1</integer>
<key>ContentConfiguration</key>
<dict>
<key>OptionsSetName</key>
<string>Hierarchy, all classes</string>
<key>PBXProjectModuleGUID</key>
<string>1CA6456E063B45B4001379D8</string>
<key>PBXProjectModuleLabel</key>
<string>Class Browser - NSObject</string>
</dict>
<key>GeometryConfiguration</key>
<dict>
<key>ClassesFrame</key>
<string>{{0, 0}, {374, 96}}</string>
<key>ClassesTreeTableConfiguration</key>
<array>
<string>PBXClassNameColumnIdentifier</string>
<real>208</real>
<string>PBXClassBookColumnIdentifier</string>
<real>22</real>
</array>
<key>Frame</key>
<string>{{0, 0}, {630, 331}}</string>
<key>MembersFrame</key>
<string>{{0, 105}, {374, 395}}</string>
<key>MembersTreeTableConfiguration</key>
<array>
<string>PBXMemberTypeIconColumnIdentifier</string>
<real>22</real>
<string>PBXMemberNameColumnIdentifier</string>
<real>216</real>
<string>PBXMemberTypeColumnIdentifier</string>
<real>97</real>
<string>PBXMemberBookColumnIdentifier</string>
<real>22</real>
</array>
<key>PBXModuleWindowStatusBarHidden2</key>
<integer>1</integer>
<key>RubberWindowFrame</key>
<string>385 179 630 352 0 0 1440 878 </string>
</dict>
<key>Module</key>
<string>PBXClassBrowserModule</string>
<key>Proportion</key>
<string>332pt</string>
</dict>
</array>
<key>Proportion</key>
<string>332pt</string>
</dict>
</array>
<key>Name</key>
<string>Class Browser</string>
<key>ServiceClasses</key>
<array>
<string>PBXClassBrowserModule</string>
</array>
<key>StatusbarIsVisible</key>
<integer>0</integer>
<key>TableOfContents</key>
<array>
<string>1C0AD2AF069F1E9B00FABCE6</string>
<string>1C0AD2B0069F1E9B00FABCE6</string>
<string>1CA6456E063B45B4001379D8</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.classbrowser</string>
<key>WindowString</key>
<string>385 179 630 352 0 0 1440 878 </string>
<key>WindowToolGUID</key>
<string>1C0AD2AF069F1E9B00FABCE6</string>
<key>WindowToolIsVisible</key>
<integer>0</integer>
</dict>
<dict>
<key>Identifier</key>
<string>windowTool.refactoring</string>
<key>IncludeInToolsMenu</key>
<integer>0</integer>
<key>Layout</key>
<array>
<dict>
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<integer>1</integer>
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{0, 0}, {500, 335}</string>
<key>RubberWindowFrame</key>
<string>{0, 0}, {500, 335}</string>
</dict>
<key>Module</key>
<string>XCRefactoringModule</string>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Proportion</key>
<string>100%</string>
</dict>
</array>
<key>Name</key>
<string>Refactoring</string>
<key>ServiceClasses</key>
<array>
<string>XCRefactoringModule</string>
</array>
<key>WindowString</key>
<string>200 200 500 356 0 0 1920 1200 </string>
</dict>
</array>
</dict>
</plist>
... ...
This diff could not be displayed because it is too large.
This diff was suppressed by a .gitattributes entry.
//
// Prefix header for all source files of the 'asi-http-request' target in the 'asi-http-request' project
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif
... ...
//
// main.m
// asi-http-request
//
// Created by Ben Copsey on 09/07/2008.
// Copyright __MyCompanyName__ 2008. All rights reserved.
//
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **) argv);
}
... ...