diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bce6af --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +xcuserdata diff --git a/ViewSource.xcodeproj/project.pbxproj b/ViewSource.xcodeproj/project.pbxproj index dc3d868..b6593cd 100644 --- a/ViewSource.xcodeproj/project.pbxproj +++ b/ViewSource.xcodeproj/project.pbxproj @@ -19,6 +19,9 @@ 7B00EC841654BBEB00531FBD /* VSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B00EC831654BBEB00531FBD /* VSViewController.m */; }; 7B00EC871654BBEB00531FBD /* VSViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B00EC851654BBEB00531FBD /* VSViewController_iPhone.xib */; }; 7B00EC8A1654BBEB00531FBD /* VSViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7B00EC881654BBEB00531FBD /* VSViewController_iPad.xib */; }; + 7B00EC9A1654BCBC00531FBD /* MMHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B00EC971654BCBC00531FBD /* MMHTTPClient.m */; }; + 7B00EC9B1654BCBC00531FBD /* MMHTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B00EC991654BCBC00531FBD /* MMHTTPRequest.m */; }; + 7B00EC9E1654C0A900531FBD /* VSSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B00EC9D1654C0A900531FBD /* VSSource.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -39,6 +42,12 @@ 7B00EC831654BBEB00531FBD /* VSViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VSViewController.m; sourceTree = ""; }; 7B00EC861654BBEB00531FBD /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/VSViewController_iPhone.xib; sourceTree = ""; }; 7B00EC891654BBEB00531FBD /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/VSViewController_iPad.xib; sourceTree = ""; }; + 7B00EC961654BCBC00531FBD /* MMHTTPClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMHTTPClient.h; sourceTree = ""; }; + 7B00EC971654BCBC00531FBD /* MMHTTPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMHTTPClient.m; sourceTree = ""; }; + 7B00EC981654BCBC00531FBD /* MMHTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMHTTPRequest.h; sourceTree = ""; }; + 7B00EC991654BCBC00531FBD /* MMHTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMHTTPRequest.m; sourceTree = ""; }; + 7B00EC9C1654C0A900531FBD /* VSSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VSSource.h; sourceTree = ""; }; + 7B00EC9D1654C0A900531FBD /* VSSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VSSource.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,6 +67,7 @@ 7B00EC5B1654BBEB00531FBD = { isa = PBXGroup; children = ( + 7B00ECA11654C1AA00531FBD /* 3rd Party */, 7B00EC701654BBEB00531FBD /* ViewSource */, 7B00EC691654BBEB00531FBD /* Frameworks */, 7B00EC671654BBEB00531FBD /* Products */, @@ -87,6 +97,8 @@ children = ( 7B00EC791654BBEB00531FBD /* VSAppDelegate.h */, 7B00EC7A1654BBEB00531FBD /* VSAppDelegate.m */, + 7B00EC9C1654C0A900531FBD /* VSSource.h */, + 7B00EC9D1654C0A900531FBD /* VSSource.m */, 7B00EC821654BBEB00531FBD /* VSViewController.h */, 7B00EC831654BBEB00531FBD /* VSViewController.m */, 7B00EC851654BBEB00531FBD /* VSViewController_iPhone.xib */, @@ -110,6 +122,18 @@ name = "Supporting Files"; sourceTree = ""; }; + 7B00ECA11654C1AA00531FBD /* 3rd Party */ = { + isa = PBXGroup; + children = ( + 7B00EC961654BCBC00531FBD /* MMHTTPClient.h */, + 7B00EC971654BCBC00531FBD /* MMHTTPClient.m */, + 7B00EC981654BCBC00531FBD /* MMHTTPRequest.h */, + 7B00EC991654BCBC00531FBD /* MMHTTPRequest.m */, + ); + name = "3rd Party"; + path = ViewSource; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -181,6 +205,9 @@ 7B00EC771654BBEB00531FBD /* main.m in Sources */, 7B00EC7B1654BBEB00531FBD /* VSAppDelegate.m in Sources */, 7B00EC841654BBEB00531FBD /* VSViewController.m in Sources */, + 7B00EC9A1654BCBC00531FBD /* MMHTTPClient.m in Sources */, + 7B00EC9B1654BCBC00531FBD /* MMHTTPRequest.m in Sources */, + 7B00EC9E1654C0A900531FBD /* VSSource.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ViewSource.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ViewSource.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..49fef95 --- /dev/null +++ b/ViewSource.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ViewSource/MMHTTPClient.h b/ViewSource/MMHTTPClient.h new file mode 100644 index 0000000..142fcf4 --- /dev/null +++ b/ViewSource/MMHTTPClient.h @@ -0,0 +1,50 @@ +// +// MMHTTPClient.h +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import +#import "MMHTTPRequest.h" + +@interface MMHTTPClient : NSObject + ++ (MMHTTPClient *) sharedClient; ++ (id) client; ++ (id) clientWithBaseURL: (NSString *)baseURL; ++ (id) clientWithBaseURL: (NSString *)baseURL timeout: (NSUInteger)timeout; + ++ (NSString *) pathFor: (NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; ++ (NSString *) urlFor: (NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; ++ (NSString *) urlWithPath: (NSString *)path; ++ (MMHTTPRequest *) request: (NSDictionary *)options then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) get: (NSString *)url then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) getImage: (NSString *)url then: (MMHTTPImageCallback)callback; ++ (MMHTTPRequest *) getText: (NSString *)url then: (MMHTTPTextCallback)callback; ++ (MMHTTPRequest *) post: (NSString *)url then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) post: (NSString *)url fields: (NSDictionary *)fields then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) post: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) put: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback; ++ (MMHTTPRequest *) delete: (NSString *)url then: (MMHTTPCallback)callback; + +@property (strong, nonatomic) NSString *baseURL; +@property NSUInteger timeout; + +- (id) initWithBaseURL: (NSString *)baseURl; +- (id) initWithBaseURL: (NSString *)baseURl timeout: (NSUInteger)timeout; +- (NSString *) pathFor: (NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; +- (NSString *) urlFor: (NSString *)first, ... NS_REQUIRES_NIL_TERMINATION; +- (NSString *) urlWithPath: (NSString *)path; +- (MMHTTPRequest *) request: (NSDictionary *)options then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) get: (NSString *)url then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) getImage: (NSString *)url then: (MMHTTPImageCallback)callback; +- (MMHTTPRequest *) getText: (NSString *)url then: (MMHTTPTextCallback)callback; +- (MMHTTPRequest *) post: (NSString *)url fields: (NSDictionary *)fields then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) post: (NSString *)url then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) post: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) put: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback; +- (MMHTTPRequest *) delete: (NSString *)url then: (MMHTTPCallback)callback; + +@end diff --git a/ViewSource/MMHTTPClient.m b/ViewSource/MMHTTPClient.m new file mode 100644 index 0000000..ee5e1f7 --- /dev/null +++ b/ViewSource/MMHTTPClient.m @@ -0,0 +1,288 @@ +// +// MMHTTPClient.m +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import "MMHTTPClient.h" + +// Encode a string to embed in an URL. +NSString* MMHTTPURLEncode(NSString *string) { + CFStringRef urlStringRef = CFURLCreateStringByAddingPercentEscapes(NULL, + (CFStringRef) string, + NULL, + (CFStringRef) @"!*'();:@&=+$,/?%#[]", + kCFStringEncodingUTF8); + return (__bridge NSString *)urlStringRef; +} + +MMHTTPClient *_sharedMMHTTPClient; + +NSString *JoinURLComponents(NSString *first, va_list args) +{ + NSMutableString *url = [NSMutableString string]; + NSString *slash = @""; + for (NSString *arg = first; arg != nil; arg = va_arg(args, NSString *)) { + [url appendFormat: @"%@%@", slash, MMHTTPURLEncode(arg)]; + slash = @"/"; + } + return [NSString stringWithString: url]; +} + +@implementation MMHTTPClient + +@synthesize baseURL = _baseURL; +@synthesize timeout = _timeout; + ++ (MMHTTPClient *) sharedClient +{ + if (!_sharedMMHTTPClient) { + _sharedMMHTTPClient = [[self alloc] init]; + } + return _sharedMMHTTPClient; +} + ++ (id) client +{ + return [[self alloc] init]; +} + ++ (id) clientWithBaseURL: (NSString *)baseURL +{ + return [[self alloc] initWithBaseURL: baseURL]; +} + ++ (id) clientWithBaseURL: (NSString *)baseURL timeout: (NSUInteger)timeout +{ + return [[self alloc] initWithBaseURL: baseURL timeout: timeout]; +} + ++ (NSString *) pathFor: (NSString *)first, ... +{ + va_list args; + va_start(args, first); + NSString *url = JoinURLComponents(first, args); + va_end(args); + return url; +} + ++ (NSString *) urlFor: (NSString *)first, ... +{ + va_list args; + va_start(args, first); + NSString *url = [[[self sharedClient] baseURL] stringByAppendingString: JoinURLComponents(first, args)]; + va_end(args); + return url; +} + ++ (NSString *) urlWithPath: (NSString *)path +{ + return [[[self sharedClient] baseURL] stringByAppendingPathComponent: path]; +} + ++ (MMHTTPRequest *) request: (NSDictionary *)options then: (MMHTTPCallback)callback +{ + return [[self sharedClient] request: options then: callback]; +} + ++ (MMHTTPRequest *) get: (NSString *)url then: (MMHTTPCallback)callback +{ + return [[self sharedClient] get: url then: callback]; +} + ++ (MMHTTPRequest *) getImage: (NSString *)url then: (MMHTTPImageCallback)callback +{ + return [[self sharedClient] getImage: url then: callback]; +} + ++ (MMHTTPRequest *) getText: (NSString *)url then: (MMHTTPTextCallback)callback +{ + return [[self sharedClient] getText: url then: callback]; +} + ++ (MMHTTPRequest *) post: (NSString *)url then: (MMHTTPCallback)callback +{ + return [[self sharedClient] post: url then: callback]; +} + ++ (MMHTTPRequest *) post: (NSString *)url fields: (NSDictionary *)fields then: (MMHTTPCallback)callback +{ + return [[self sharedClient] post: url fields: fields then: callback]; +} + ++ (MMHTTPRequest *) post: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback +{ + return [[self sharedClient] post: url data: data then: callback]; +} + ++ (MMHTTPRequest *) put: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback +{ + return [[self sharedClient] put: url data: data then: callback]; +} + ++ (MMHTTPRequest *) delete: (NSString *)url then: (MMHTTPCallback)callback +{ + return [[self sharedClient] delete: url then: callback]; +} + +- (id) init +{ + return [self initWithBaseURL: nil timeout: MMHTTPRequestDefaultTimeout]; +} + +- (id) initWithBaseURL: (NSString *)baseURL +{ + return [self initWithBaseURL: baseURL timeout: MMHTTPRequestDefaultTimeout]; +} + +- (id) initWithBaseURL: (NSString *)baseURL timeout: (NSUInteger)timeout +{ + self = [super init]; + if (self) { + _baseURL = [baseURL copy]; + _timeout = timeout; + } + return self; +} + +- (NSString *) pathFor: (NSString *)first, ... +{ + va_list args; + va_start(args, first); + NSString *url = JoinURLComponents(first, args); + va_end(args); + return url; +} + +- (NSString *) urlFor: (NSString *)first, ... +{ + va_list args; + va_start(args, first); + NSString *url = [_baseURL stringByAppendingString: JoinURLComponents(first, args)]; + va_end(args); + return url; +} + +- (NSString *) urlWithPath: (NSString *)path +{ + return [_baseURL stringByAppendingPathComponent: path]; +} + +- (MMHTTPRequest *) getImage: (NSString *)url then: (MMHTTPImageCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + url, @"url", + @"image", @"type", + nil]; + return [self request: options then: (MMHTTPCallback)callback]; +} + +- (MMHTTPRequest *) getText: (NSString *)url then: (MMHTTPTextCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + url, @"url", + @"text", @"type", + nil]; + return [self request: options then: (MMHTTPCallback)callback]; +} + +- (MMHTTPRequest *) get: (NSString *)url then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + url, @"url", + @"image", @"type", + nil]; + return [self request: options then: (MMHTTPCallback)callback]; +} + +- (MMHTTPRequest *) post: (NSString *)url then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"POST", @"method", + url, @"url", + nil]; + return [self request: options then: callback]; +} + +- (NSString *) encodeFields: (NSDictionary *)fields withPrefix: (NSString *)prefix +{ + NSString *suffix = @""; + if (prefix) { + prefix = [NSString stringWithFormat: @"%@[", prefix]; + suffix = @"]"; + } + else { + prefix = @""; + } + NSMutableArray *parts = [NSMutableArray array]; + NSString *value; + for (NSString *key in [fields keyEnumerator]) { + value = [fields objectForKey: key]; + if ([value isKindOfClass: [NSDictionary class]]) { + [parts addObject: [self encodeFields: (NSDictionary *)value withPrefix: [NSString stringWithFormat: @"%@%@", prefix, key]]]; + } + else { + [parts addObject: [NSString stringWithFormat: @"%@%@%@=%@", prefix, MMHTTPURLEncode(key), suffix, MMHTTPURLEncode(value)]]; + } + } + return [parts componentsJoinedByString: @"&"]; +} + +- (NSString *) encodeFields: (NSDictionary *)fields +{ + return [self encodeFields: fields withPrefix: nil]; +} + +- (MMHTTPRequest *) post: (NSString *)url fields: (NSDictionary *)fields then: (MMHTTPCallback)callback +{ + NSString *body = [self encodeFields: fields]; + return [self post: url data: [body dataUsingEncoding: NSUTF8StringEncoding] then: callback]; +} + +- (MMHTTPRequest *) post: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"POST", @"method", + url, @"url", + data, @"data", + nil]; + return [self request: options then: callback]; +} + +- (MMHTTPRequest *) put: (NSString *)url data: (NSData *)data then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"PUT", @"method", + url, @"url", + data, @"data", + nil]; + return [self request: options then: callback]; +} + +- (MMHTTPRequest *) delete: (NSString *)url then: (MMHTTPCallback)callback +{ + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"DELETE", @"method", + url, @"url", + nil]; + return [self request: options then: callback]; +} + +- (MMHTTPRequest *) request: (NSDictionary *)options then: (MMHTTPCallback)callback +{ + NSString *url = [options objectForKey: @"url"]; + NSMutableDictionary *mutableOptions = [options mutableCopy]; + if (_baseURL && !([url hasPrefix: @"http:"] || [url hasPrefix: @"https:"])) { + [mutableOptions setObject: [self urlWithPath: url] forKey: @"url"]; + } + NSUInteger timeout = [[options objectForKey: @"timeout"] unsignedIntValue]; + if (timeout == 0) { + [mutableOptions setValue: [NSNumber numberWithUnsignedInt: self.timeout] forKey: @"timeout"]; + } + options = [NSDictionary dictionaryWithDictionary: mutableOptions]; + return [MMHTTPRequest requestWithOptions: options callback: callback]; +} + +@end diff --git a/ViewSource/MMHTTPRequest.h b/ViewSource/MMHTTPRequest.h new file mode 100644 index 0000000..eb9e64d --- /dev/null +++ b/ViewSource/MMHTTPRequest.h @@ -0,0 +1,43 @@ +// +// MMHTTPRequest.h +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import +#import + +#define MMHTTPRequestStatusError -1 +#define MMHTTPRequestDefaultTimeout 120 + +typedef void (^MMHTTPCallback)(NSInteger status, id data); +typedef void (^MMHTTPTextCallback)(NSInteger status, NSString *text); +typedef void (^MMHTTPImageCallback)(NSInteger status, UIImage *image); + +@interface MMHTTPRequest : NSObject +{ + NSMutableData *_responseData; +} + +@property (nonatomic, retain) NSURLConnection *connection; +@property (nonatomic, retain) NSMutableURLRequest *request; +@property (nonatomic, retain) NSString *method; +@property (nonatomic, retain) NSString *url; +@property (nonatomic, retain) NSMutableDictionary *headers; +@property (nonatomic, retain) NSData *data; +@property (nonatomic, retain) NSString *type; +@property (nonatomic, copy) MMHTTPCallback callback; +@property NSUInteger timeout; +@property (readonly) NSInteger statusCode; +@property (strong, readonly) NSDictionary *responseHeaders; +@property (readonly) NSData *responseData; +@property (readonly) NSString *responseText; +@property (readonly) UIImage *responseImage; + ++ (id) requestWithOptions: (NSDictionary *)options callback: (MMHTTPCallback)callback; +- (id) initWithOptions: (NSDictionary *)options callback: (MMHTTPCallback)callback; +- (void) cancel; + +@end diff --git a/ViewSource/MMHTTPRequest.m b/ViewSource/MMHTTPRequest.m new file mode 100644 index 0000000..a077db5 --- /dev/null +++ b/ViewSource/MMHTTPRequest.m @@ -0,0 +1,147 @@ +// +// MMHTTPRequest.m +// Marshmallows +// +// Created by Sami Samhuri on 11-09-03. +// Copyright 2011 Guru Logic. All rights reserved. +// + +#import "MMHTTPRequest.h" + +@interface MMHTTPRequest () +- (void) _start; +@end + + +@implementation MMHTTPRequest + +@synthesize connection = _connection; +@synthesize request = _request; +@synthesize method = _method; +@synthesize url = _url; +@synthesize headers = _headers; +@synthesize data = _data; +@synthesize type = _type; +@synthesize callback = _callback; +@synthesize timeout = _timeout; +@synthesize statusCode = _statusCode; +@synthesize responseHeaders = _responseHeaders; + ++ (id) requestWithOptions: (NSDictionary *)options callback: (MMHTTPCallback)callback +{ + return [[self alloc] initWithOptions: options callback: callback]; +} + +- (id) initWithOptions: (NSDictionary *)options callback: (MMHTTPCallback)callback +{ + self = [super init]; + if (self) { + self.callback = callback; + self.timeout = MMHTTPRequestDefaultTimeout; + self.method = [options objectForKey: @"method"]; + self.url = [options objectForKey: @"url"]; + self.headers = [options objectForKey: @"headers"]; + self.data = [options objectForKey: @"data"]; + self.type = [options objectForKey: @"type"]; + if (!self.method) self.method = @"GET"; + [self _start]; + } + return self; +} + +- (void) cancel +{ + self.callback = nil; + [_connection cancel]; +} + +- (NSData *) responseData +{ + return [NSData dataWithData: _responseData]; +} + +- (NSString *) responseText +{ + return [[NSString alloc] initWithBytes: _responseData.bytes + length: _responseData.length + encoding: NSUTF8StringEncoding]; +} + +- (UIImage *) responseImage +{ + return [UIImage imageWithData: _responseData]; +} + +- (void) _start +{ + self.request = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: self.url] + cachePolicy: NSURLRequestUseProtocolCachePolicy + timeoutInterval: self.timeout]; + [self.request setHTTPMethod: self.method]; + + if (self.data && ([self.method isEqualToString: @"POST"] || [self.method isEqualToString: @"PUT"])) { + [self.request setHTTPBody: self.data]; + } + + if (self.headers) { + for (NSString *key in self.headers) { + [self.request setValue: [self.headers objectForKey: key] forHTTPHeaderField: key]; + } + } + + self.connection = [NSURLConnection connectionWithRequest: self.request delegate: self]; + [self.connection start]; +} + +#pragma mark - NSURLConnection delegate methods + +- (void) connection: (NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)response +{ + if ([response respondsToSelector: @selector(statusCode)]) + { + _statusCode = [(NSHTTPURLResponse *)response statusCode]; + _responseHeaders = [(NSHTTPURLResponse *)response allHeaderFields]; + } + else { + _statusCode = 500; + _responseHeaders = [[NSDictionary alloc] init]; + } + + _responseData = [[NSMutableData alloc] init]; +} + +- (void) connection: (NSURLConnection *)connection didReceiveData: (NSData *)data +{ + [_responseData appendData: data]; +} + +- (void) connection: (NSURLConnection *)connection didFailWithError: (NSError *)error +{ + _responseData = nil; + _statusCode = MMHTTPRequestStatusError; + if (self.callback) { + self.callback(self.statusCode, error); + } +} + +- (void) connectionDidFinishLoading: (NSURLConnection *)connection +{ + id data = nil; + if (self.callback && self.statusCode == 200) { + if ([self.type isEqualToString: @"text"]) { + data = self.responseText; + } + else if ([self.type isEqualToString: @"image"]) { + data = self.responseImage; + } + else { + data = self.responseData; + } + } + + if (self.callback) { + self.callback(self.statusCode, data); + } +} + +@end diff --git a/ViewSource/VSAppDelegate.m b/ViewSource/VSAppDelegate.m index 35d05cb..e88aa50 100644 --- a/ViewSource/VSAppDelegate.m +++ b/ViewSource/VSAppDelegate.m @@ -7,8 +7,8 @@ // #import "VSAppDelegate.h" - #import "VSViewController.h" +#import "VSSource.h" @implementation VSAppDelegate @@ -21,6 +21,12 @@ } else { self.viewController = [[VSViewController alloc] initWithNibName:@"VSViewController_iPad" bundle:nil]; } + + NSString *url = @"http://samhuri.net"; + NSRange scrollRange = NSMakeRange(0, 0); + VSSource *source = [VSSource sourceWithURL: url scrollRange: scrollRange]; + [self.viewController loadSource: source]; + self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; diff --git a/ViewSource/VSSource.h b/ViewSource/VSSource.h new file mode 100644 index 0000000..5d30fbf --- /dev/null +++ b/ViewSource/VSSource.h @@ -0,0 +1,27 @@ +// +// VSSource.h +// ViewSource +// +// Created by Sami Samhuri on 2012-11-14. +// Copyright (c) 2012 Sami Samhuri. All rights reserved. +// + +#import + +typedef void (^VSSourceCallback)(BOOL ok, NSString *text); + +@interface VSSource : NSObject + +@property (strong, nonatomic) NSString *url; +@property (assign, nonatomic) NSRange scrollRange; +@property (strong, nonatomic) NSString *text; + ++ (id) sourceWithURL: (NSString *)url scrollRange: (NSRange)scrollRange; ++ (id) sourceWithURL: (NSString *)url; + +- (id) initWithURL: (NSString *)url scrollRange: (NSRange)scrollRange; +- (id) initWithURL: (NSString *)url; +- (void) fetch: (VSSourceCallback)callback; +- (void) cancel; + +@end diff --git a/ViewSource/VSSource.m b/ViewSource/VSSource.m new file mode 100644 index 0000000..a26b511 --- /dev/null +++ b/ViewSource/VSSource.m @@ -0,0 +1,73 @@ +// +// VSSource.m +// ViewSource +// +// Created by Sami Samhuri on 2012-11-14. +// Copyright (c) 2012 Sami Samhuri. All rights reserved. +// + +#import "VSSource.h" +#import "MMHTTPClient.h" + +@interface VSSource () +@property (strong, nonatomic) MMHTTPRequest *request; +@property (copy, nonatomic) VSSourceCallback callback; +@end + + +@implementation VSSource + +@synthesize scrollRange = _scrollRange; +@synthesize text = _text; +@synthesize url = _url; + ++ (id) sourceWithURL: (NSString *)url scrollRange: (NSRange)scrollRange +{ + return [[self alloc] initWithURL: url scrollRange: scrollRange]; +} + ++ (id) sourceWithURL: (NSString *)url +{ + return [self sourceWithURL: url scrollRange: NSMakeRange(0, 0)]; +} + +- (id) initWithURL: (NSString *)url scrollRange: (NSRange)scrollRange +{ + self = [super init]; + if (self) { + self.url = url; + self.scrollRange = scrollRange; + } + return self; +} + +- (id) initWithURL: (NSString *)url +{ + return [self initWithURL: url scrollRange: NSMakeRange(0, 0)]; +} + +- (void) fetch: (VSSourceCallback)callback; +{ + if (self.text) { + callback(YES, self.text); + } + else { + self.callback = callback; + self.request = [MMHTTPClient getText: self.url then: ^(NSInteger status, NSString *text) { + self.text = text; + if (self.callback) { + self.callback(status == 200, self.text); + } + }]; + } +} + +- (void) cancel +{ + [self.request cancel]; + self.request = nil; + self.callback = nil; + self.text = nil; +} + +@end diff --git a/ViewSource/VSViewController.h b/ViewSource/VSViewController.h index d63c52e..30f3dd2 100644 --- a/ViewSource/VSViewController.h +++ b/ViewSource/VSViewController.h @@ -7,7 +7,13 @@ // #import +#import "VSSource.h" @interface VSViewController : UIViewController +@property (strong, nonatomic) VSSource *source; +@property (strong, nonatomic) IBOutlet UITextView *sourceView; + +- (void) loadSource: (VSSource *)source; + @end diff --git a/ViewSource/VSViewController.m b/ViewSource/VSViewController.m index 1bfb683..56135e4 100644 --- a/ViewSource/VSViewController.m +++ b/ViewSource/VSViewController.m @@ -9,21 +9,50 @@ #import "VSViewController.h" @interface VSViewController () - +- (void) update; @end @implementation VSViewController -- (void)viewDidLoad +@synthesize source = _source; + +- (void) viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. + if (self.source) { + [self update]; + } } -- (void)didReceiveMemoryWarning +- (void) loadSource: (VSSource *)source +{ + self.source = source; + [self update]; +} + +- (void) update +{ + if (self.sourceView && self.source) { + if (self.source.text) { + self.sourceView.text = self.source.text; + [self.sourceView scrollRangeToVisible: self.source.scrollRange]; + } + else { + [self.source fetch: ^(BOOL ok, NSString *text) { + if (ok) { + [self update]; + } + else { + NSLog(@"error fetching %@", self.source.url); + } + }]; + } + } +} + +- (void) didReceiveMemoryWarning { [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. } @end diff --git a/ViewSource/en.lproj/VSViewController_iPad.xib b/ViewSource/en.lproj/VSViewController_iPad.xib index 727eee5..d2a952d 100644 --- a/ViewSource/en.lproj/VSViewController_iPad.xib +++ b/ViewSource/en.lproj/VSViewController_iPad.xib @@ -2,16 +2,18 @@ 1536 - 12A206j - 2519 - 1172.1 - 613.00 + 12C60 + 2840 + 1187.34 + 625.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1856 + 1926 + IBNSLayoutConstraint IBProxyObject + IBUITextView IBUIView @@ -33,6 +35,38 @@ 274 + + + + 274 + {768, 1004} + + + _NS:9 + + 1 + MSAxIDEAA + + YES + YES + IBIPadFramework + NO + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + 2 + IBCocoaTouchFramework + + + 1 + 14 + + + Helvetica + 14 + 16 + + + {{0, 20}, {768, 1004}} @@ -59,6 +93,14 @@ 3 + + + sourceView + + + + 9 + @@ -82,8 +124,100 @@ 2 + + + + + 3 + 0 + + 3 + 1 + + 0.0 + + 1000 + + 8 + 29 + 3 + + + + 5 + 0 + + 5 + 1 + + 0.0 + + 1000 + + 8 + 29 + 3 + + + + 4 + 0 + + 4 + 1 + + 0.0 + + 1000 + + 8 + 29 + 3 + + + + 6 + 0 + + 6 + 1 + + 0.0 + + 1000 + + 8 + 29 + 3 + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + @@ -92,18 +226,49 @@ UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 3 + 9 + + NSLayoutConstraint + NSObject + + IBProjectSource + ./Classes/NSLayoutConstraint.h + + VSViewController UIViewController + + sourceView + UITextView + + + sourceView + + sourceView + UITextView + + IBProjectSource ./Classes/VSViewController.h @@ -116,6 +281,6 @@ YES 3 YES - 1856 + 1926 diff --git a/ViewSource/en.lproj/VSViewController_iPhone.xib b/ViewSource/en.lproj/VSViewController_iPhone.xib index 149d359..00f6f34 100644 --- a/ViewSource/en.lproj/VSViewController_iPhone.xib +++ b/ViewSource/en.lproj/VSViewController_iPhone.xib @@ -2,16 +2,18 @@ 1536 - 12A269 - 2835 - 1187 - 624.00 + 12C60 + 2840 + 1187.34 + 625.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1919 + 1926 + IBNSLayoutConstraint IBProxyObject + IBUITextView IBUIView @@ -33,10 +35,38 @@ 274 + + + + 274 + {320, 548} + + _NS:9 + + 1 + MSAxIDEAA + + YES + YES + IBCocoaTouchFramework + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + 2 + IBCocoaTouchFramework + + + 1 + 14 + + + Helvetica + 14 + 16 + + + {{0, 20}, {320, 548}} - - 3 MC43NQA @@ -76,6 +106,14 @@ 7 + + + sourceView + + + + 14 + @@ -99,8 +137,100 @@ 6 + + + + + 6 + 0 + + 6 + 1 + + 0.0 + + 1000 + + 8 + 29 + 3 + + + + 5 + 0 + + 5 + 1 + + 0.0 + + 1000 + + 8 + 29 + 3 + + + + 4 + 0 + + 4 + 1 + + 0.0 + + 1000 + + 8 + 29 + 3 + + + + 3 + 0 + + 3 + 1 + + 0.0 + + 1000 + + 8 + 29 + 3 + + + + 8 + + + + + 9 + + + + + 10 + + + + + 11 + + + + + 12 + + + @@ -108,31 +238,32 @@ com.apple.InterfaceBuilder.IBCocoaTouchPlugin UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 7 - - - - - VSViewController - UIViewController - - IBProjectSource - ./Classes/VSViewController.h - - - + 14 + 0 IBCocoaTouchFramework YES 3 YES - 1919 + 1926