Posts Tagged ‘iPhone’


Overriding HTTP user agent for calls to -initWithContentsOfURL:

Tuesday, December 20th, 2011

Perhaps you need to override the HTTP user agent whenever you call -initWithContentsOfURL: from classes such as NSString, NSDictionary or NSArray, or one of this method’s convenience wrappers such as +stringWithContentsOfURL:, +dictionaryWithContentsOfURL: or +arrayWithContentsOfURL:. So let’s consider how this can be accomplished under iOS.

From what I can see, there is no easy and “clean” way apart from adding a category on the classes where you need to support this and writing your own implementation of -initWithContentsOfURL: and convenience functions (with a slightly different name, of course). These implementations would use NSURLConnection‘s +sendSynchronousRequest:returningResponse:error:. Of course, as with -initWithContentsOfURL: you’d use this replacement method in a background thread to maintain UI responsiveness.

You’d have to write a reimplementation of -initWithContentsOfURL: because the first place you can change this is NSURLRequest, or more specifically, its mutable variant NSMutableURLRequest, using the -setValue:forHTTPHeaderField:. But, if you have tons of code, you probably can’t easily change it to use the new method.

So I dug in and, with a few smart tricks (such as feeding a broken non-NSURL as a NSURL to figure out which methods get called, then implementing them as necessary), I figured out which of several ways for fetching web content is actually used in NSString‘s implementation of -initWithContentsOfURL:. These could have been NSURLConnection or some low level messing with CFNetwork.

It turned out not to matter since NSURLRequest is generated out of the NSURL passed to the method. Customizing the user agent turned out to be just a matter of taking all NSURLRequests, forcing them to become mutable copies in form of instances of NSMutableURLRequest during the initializer and setting the user agent at that time. Specific initializer appearing in iOS implementation used in iOS 5 Simulator that ships with Xcode 4.2.1 appears to be -initWithURL:cachePolicy:timeoutInterval:.

It’s an enormous hack, but I decided to simply swizzle this method out. Swizzling NSURLConnection‘s class method +sendSynchronousRequest:returningResponse:error: did not appear to work – the original method still got called despite my best efforts to figure out what went wrong with swizzling, so I gave up on it. If you can see a mistake in my class swizzling code, please tell me about it in the comments section below.

I definitely have no idea whether or not your app will be rejected for this, but from what I know, method swizzling is not illegal.

//  NSURLRequest+UserAgentFix.m

#define YOUR_USER_AGENT @"Your User Agent"
#import "NSURLRequest+UserAgentFix.h"
#import "NSObject+ISSwizzling.h"
@implementation NSURLRequest (UserAgentFix)
+(void)load
{
    [self swizzleMethod:@selector(initWithURL:cachePolicy:timeoutInterval:)
             withMethod:@selector(initWithURL2:cachePolicy:timeoutInterval:)];
}
-(id)initWithURL2:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval
{
    self = [self initWithURL2:URL cachePolicy:cachePolicy timeoutInterval:timeoutInterval];

    if(!self)
        return nil;

    if([self class] == [NSURLRequest class])
        self = [self mutableCopy];

    if([self class] == [NSMutableURLRequest class])
    {
        NSMutableURLRequest * req = self;
        [req setValue:YOUR_USER_AGENT forHTTPHeaderField:@"User-Agent"];
    }

    return self;
}
@end
// NSURLRequest+UserAgentFix.h

#import <Foundation/Foundation.h>

@interface NSURLRequest (UserAgentFix)

@end
// NSObject+ISSwizzling.h

#import <Foundation/Foundation.h>

@interface NSObject (ISSwizzling)
+ (BOOL)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector;
+ (BOOL)swizzleClassMethod:(SEL)origSelector withMethod:(SEL)newSelector;

@end
// NSObject+ISSwizzling.m

#import <objc/runtime.h>
#import "NSObject+ISSwizzling.h"

@implementation NSObject (ISSwizzling)
+ (BOOL)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
    Method origMethod = class_getInstanceMethod(self, origSelector);
    Method newMethod = class_getInstanceMethod(self, newSelector);

    if (origMethod && newMethod) {
        if (class_addMethod(self, origSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
            class_replaceMethod(self, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, newMethod);
        }
        return YES;
    }
    return NO;
}
+ (BOOL)swizzleClassMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
    Method origMethod = class_getClassMethod(self, origSelector);
    Method newMethod = class_getClassMethod(self, newSelector);

    Class class = object_getClass((id)self);

    if (origMethod && newMethod) {
        if (class_addMethod(class, origSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
            class_replaceMethod(class, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, newMethod);
        }
        return YES;
    }
    return NO;
}

@end

Tested on iOS 5 Simulator with NSString‘s +stringWithContentsOfURL:.

Compiling Lua for iOS

Friday, March 4th, 2011

To compile Lua for use on iPhone and iPad, I’ve grabbed the original source code for 5.1.4, and I have grabbed build_for_iphoneos.sh shell script by Christopher Stawarz. Both were slightly modified since Lua does not use autoconf, and build_for_iphoneos.sh is slightly dated. Read more to see what did I do.
(more…)

Correct check for Game Center availability

Tuesday, February 22nd, 2011

Apple’s example code for Game Center does something strange — in fact, we can simply go out and call those things bugs. Keep reading to see what I’m talking about.
(more…)

GKTapper – Apple’s buggy example

Tuesday, February 1st, 2011

I’ve caught a few mistakes in GKTapper. Read on to see how the sample performs invalid caching of achievements and to see how it shows incorrect string as leaderboard description in one place.
(more…)

Getting started with Objective-C?

Saturday, November 20th, 2010

If you want to get started with Objective-C, and you have some background in C/C++, read this neat post I randomly stumbled upon: “Why Objective-C is cool“. It’s a pretty nice description and hopefully removes a lot of WTFs from a newbie.

Then, read CocoaLab‘s free e-book “Become an Xcoder“.

Finally, this is the longest route, but the one I went with: watch all 18 hours + extras of CS193P iPhone Application Development 2009 (and then go watch some of the 2010 lessons, too). It takes a long time to watch, but it’s worth it.

Single Xcode project for iOS and Mac OS X

Thursday, September 16th, 2010

In Xcode 3.2.4, it’s trivial to create same project for iOS and Mac OS X. Just add a new target into your existing project; if your project is for OS X, then create a new Cocoa Touch Application target. If your project is for iPhone, obviously, craete a new Cocoa Application target. Then do a Get Info on your new target, and choose the appropriate Base SDK. For simplicity, let’s presume you’re adding an OS X target to an iPhone project.

However, after doing this, you’ll quite probably find that despite the choice of Base SDK in your target (you used Get Info on it, didn’t you?), Xcode has locked the target SDK onto whatever your project originally used. That is, now you’ll find it locked onto iPhone, despite switching to the OS X target using the Overview dropdown (in the top left of your Xcode project).

So how do you actually switched the now-locked SDK? Quite simple. Hold the option key while clicking in the Overview box. Instead of only two-entries device list (if you have an iPhone target selected), and then Active Configuration, Active Target, Active Executable and Active Architecture, by holding the option key while clicking on Overview you’ll also find the Active SDKs list. By switching it to the appropriate OS, you’ll be able to compile the application.

Of course, now comes the hard part: actually porting the code to the new platform.
E49PEQSG669E