14

Has Apple blocked Method Swizzling in iOS 5?

I was doing a little playing around and discovered that an app with Method Swizzling works on iOS 4 but not on iOS 5.

NOTE: The app works on iOS 5 but not the part when Method Swizzling is used.

Jash Sayani
  • 1,241
  • 3
  • 12
  • 17
  • 4
    What class are you targeting? Method swizzling is fragile because of private implementation details that are beyond your control. – FluffulousChimp Oct 11 '11 at 03:05
  • @NSBum Could you elaborate a little over why method swizzling is fragile? I'm not aware of any ABI or other potentially fragile situations. – Nikolai Ruhe Jan 07 '13 at 13:57

3 Answers3

15

Apple sent an email a while ago to some devs that were found to be using method swizzling in App Store apps:

Your application, xxx, currently posted to the App Store is using method_exchangeImplementations to exchange the implementation of Apple provided APIs with your own implementations. Because of upcoming changes, this behavior in your application may cause a crash or cause user data loss on iPhone OS 4.0.

xxx uses method_exchangeImplementations to exchange the implementation of dealloc with your method ttdealloc. It also exchanges the implementation of the method popViewControllerAnimated: with your method popViewControllerAnimated2:.

Please resolve this issue immediately and upload your new binary to iTunes Connect. We may remove your application if we believe that doing so is prudent or necessary.

Looks like they wanted to get rid of it, so I'd say chances are pretty high that they've now blocked it completely.

Jordan Smith
  • 10,310
  • 7
  • 68
  • 114
  • 1
    So no method swizzling, or no method swizzling for Apple API classes? – Dan Rosenstark Dec 16 '11 at 17:23
  • 2
    @Yar good question, I'm not sure of the answer. (Although method swizzling seems like very hacky code if it's not to work around API limitations - are you sure there's not a better way to structure your code?) Shouldn't be too hard to test yourself though, if you post the result here I'll update the answer :) – Jordan Smith Dec 19 '11 at 00:57
  • Thanks @Jordan, if I do, I will. Personally I've never touched the stuff, but I thought it was everyday fare for the more intrepid Obj-C developers. I also thought, at one point, that it would allow me to add a PROPERTY (with ivar and synthesize) to all my UIView instances, for example... I determined that it cannot be used for this. – Dan Rosenstark Dec 19 '11 at 13:13
  • 1
    @Yar you can definitely still use e.g. associated objects and `class_addMethod` to add an extra property at runtime. `class_addIvar` stops working after `objc_registerClassPair` so you're locked out of that, sadly. – Tommy May 31 '12 at 01:41
  • @Tommy so there's no way to monkey-patch (so to speak) a full-blown property with iVar and everything onto an instance of `UIView`, unless it's a class of my choosing, right? – Dan Rosenstark May 31 '12 at 05:33
  • 1
    @Yar it wouldn't technically be an ivar, but have a fully-functioning getter and setter so would be indistinguishable from any other property. – Tommy May 31 '12 at 15:05
  • @Tommy when you say "associated objects" you mean that I would need to manage creation/finding/destruction of those associated objects (in something like a singleton `NSDictionary`)? Or is there a more automatic way to add a property to an arbitrary object at runtime? Like I've said, I'm assuming that I can NOT access the class definition itself (e.g., `UIView`)... thx! – Dan Rosenstark May 31 '12 at 15:18
  • 2
    @Yar oh, sorry, associated objects like `objc_setAssociatedObject`, `objc_getAssociatedObject` and `objc_removeAssociatedObjects`. The runtime can link one object to another at a low level, then you could write a setter and getter to smooth over the cracks. See http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocAssociativeReferences.html about the former. – Tommy May 31 '12 at 16:51
  • Thanks so much Tommy. You've helped open up the door for me to get here: http://compileyouidontevenknowyou.blogspot.com/2012/06/adding-properties-to-class-you-dont.html. This is amazing and something I've been needing for a long time. – Dan Rosenstark Jun 02 '12 at 01:27
  • If anyone is still confused, method swizzling is allowed. Apple are comfortable with it for as long as you are not exchanging their implementations with your own. Sounds silly right, because why would you swizzle your own code. There are a few cases where swizzling is necessary for code that is not Apple's, but that belongs to another discussion. – pnizzle Mar 19 '15 at 03:08
9

UPDATE: (My App uses this method and is in the appstore)

Method swizzling seems to be working as of May 30, 2012. this is my implementation.

(This is for those of you looking around and finding bad code on wiki pages and just want a quick implementation.)

Swizz.h

#import <Foundation/Foundation.h>

void ActivateAutoSwizz();

void Swizz(Class c, SEL orig, SEL replace);


@interface NSObject (swizz)

// This Method allows the class to Swizzle more methods within itself.
// And allows for an overridable init method in Class Extensions
// ###################### 
// //// To Swizzle a method, call Swizzle once on the class in question.
// //// dispatch_once is a good way to handle that.
//            static dispatch_once_t onceToken;
//            dispatch_once(&onceToken, ^{
//                Swizz([UITableViewCell class], @selector(reuseIdentifier), @selector(classReuseIdentifier));
//            });
- (void) swizzInit;

@end

Swizz.m

#import "Swizz.h"
#import <objc/runtime.h> 
#import <objc/message.h>
//....

void Swizz(Class c, SEL orig, SEL replace)
{
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, replace);
    if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
        class_replaceMethod(c, replace, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    else
        method_exchangeImplementations(origMethod, newMethod);
}


@implementation NSObject (swizz)

// Load gets called on every object that has it. Off Thread, before application start.
+ (void) load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Swizz([NSObject class], @selector(init), @selector(initIntercept));
    });
}

- (id) initIntercept{
    self = [self initIntercept]; // Calls the Original init method
    if(self){
        [self swizzInit];
    }
    return self;
}

- (void) swizzInit{
    //Do Nothing.. Gives an extension point for other classes.
}

@end

I built this to allow me to intercept the reuseIdentifier on the UITableViewCell with a UITableViewCell extension.

Here is that example.

UITableViewCell+ReuseIdentifier.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface UITableViewCell (ReuseIdentifier)

@end

UITableViewCell+ReuseIdentifier.m

#import "UITableViewCell+ReuseIdentifier.h"
#import "Swizz.h"

@implementation UITableViewCell(ReuseIdentifier)


// Edited to remove Class variable.
// Class variables in Categories are Global.
// And as it turns out, this method did not need the variable anyhow.
- (NSString *)classReuseIdentifier{
    NSString *reuseIdentifierResult = [self classReuseIdentifier]; // Gets the original reuseIdentifier
    if (reuseIdentifierResult == nil){
        reuseIdentifierResult = [[self class] description];
    }
    return reuseIdentifierResult;
}

// Alternatively you can use the +(void)load method on the class to achieve the same thing.
- (void)swizzInit{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Swizz([UITableViewCell class], @selector(reuseIdentifier), @selector(classReuseIdentifier));
    });
}

@end

As you can see, the ActivateAutoSwizz() as well as my swizzInit method both use dispatch_once to execute the swizzle once.

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    Swizz([NSObject class], @selector(init), @selector(initIntercept));
});

If you execute it twice. it reverses your method switch back to original. I hope this helps some of you iOS devs out there.

NOTE: I have determined that +(void) load is called once at app start and is a wonderful place to achieve the method swizzle. Unfortunately in some dev situations +(void)load is not called, You may want to test your app to make sure these methods are being called.

The Lazy Coder
  • 11,560
  • 4
  • 51
  • 69
3

Well, we received the OK about a mont hago (beggining of May 2012) for an app that made a lot of use of method Swizzling to customize standard UI components in iOS4 (iOS5 using appearance). Besides, method swizzling is a fully documented API that also gives very powerful features not related to Apple itself or the use of private APIs. I find it difficult to believe that they could reject such a thing!

Anyway, please, keep everyone informed if you see more rejections related to this! Thanks!

Angel G. Olloqui
  • 8,045
  • 3
  • 33
  • 31