Cocoa Kucha talk at @NSLondonMeetup about iOS backports. Whats that, why you should use them and example of base internationalisation backport to iOS5
#1
I am iOS developer from before official iOS SDK times and I have seen how it grow and evolves
#2
Time to time Apple release new version of iOS SDK. All those new features that will open up new possibilities
#3
But those other people - you know - like your boss for example, doesn’t understand that time you will waste to support multiple iOS versions will be more expensive than those 3 users that still didn’t updated
#4
So you stuck for a few years supporting 2 or 3 versions of SDK writing spaghetti code, checking if class or method exist to use different code paths
#5
Backports to the rescue! No spaghetti code, no checking for methods or class existence, no impact on users who already updated
#6
Good backport is a runtime hook and have no implications on SDK’s that supports functionality natively
#7
It’s not that hard to implement if you know what you doing and it will make your app code way cleaner and more maintainable in a long term
#8
Can everything be backported? Most likely yes, but sometimes it could be more complex than the app itself and it would be a waste of time. On the other hand it could be few lines of code and could do things that would be extremely difficult to do otherwise
#9
I made few of those, maybe you used some, maybe not, but nevertheless I think it’s insanely cool, and I use them in apps I write
#10
For example, base internationalisation backport. You know - that small checkbox in Xcode that makes you life as developer way brighter if you making multilingual applications
#11
If you launch App on iOS5 witch doesn’t support base internationalisation you will see NSUnarchyver error that NSLocalizableString doesn’t exist
#12
Fortunately NSKeyedUnarchiver has convenient method to replace classes
#13
Next, you have to extract info passed into -[RRLocalizableString initWithCoder:]. There is no easy way to find out -[NSCoder decodeObjectForKey:] keys, but the best way I found is to swizzle it
#14
NSString belongs to class cluster...
#15
by swizzling -[NSCoder decodeObjectForKey:] you will find NSKey and NSDev to use in -[NSBundle localizedStringForKey:value:table:], but we still missing “table”.
#16
Unfortunately there is no public method on NSCoder to wind out what file we unarchiving
#17
Most file operations use -[NSBundle pathForResource:ofType:] and sure enough swizzling it gives path we need. All we have to do is store it
#18
There is several ways to store information in categories
#19
All what was needed to make this complicated looking backport was several lines of code
#20
there is no reason to write hard maintainable code just to support older SDK’s, so I invite everyone to check out my backports on github and write some of their own
Hi, everyone, I am iOS developer from before official iOS SDK times and I have seen how it grow and evolves. 5 plus years later I can say that it’s a fun ride and WWDC is my xmas and NewYear :)
Time to time Apple release new version of iOS SDK. All those shiny new features that will open up new possibilities and make your life as developer less miserable. I bet every developer eager to use them.
But those other people - you know - like your boss for example, doesn’t understand that time you will waste to support multiple iOS versions will be more expensive than those 3 users that still didn’t updated, and will make code base less maintainable over time.
So you stuck for a few years supporting 2 or 3 versions of SDK. You stuck writing ugly spaghetti code, checking if class or method exist to use different code paths. Your code becomes complex and less maintainable.
But it doesn’t have to be like that. Backports to the rescue! No spaghetti code, no checking for methods or class existence, no impact on users who already updated, and no code to strip after you drop support for older SDK’s.
Good backport is a runtime hook and have no implications on SDK’s that supports functionality natively. ObjC runtime allows to check for class or method existence at runtime and only if they don’t backport dynamically injects them.
It’s not that hard to implement if you know what you doing and it will make your app code way cleaner and more maintainable in a long term. It’s just like another class or category you was writing all along.
Can everything be backported? Most likely yes, but sometimes it could be more complex than the app itself and it would be a waste of time. On the other hand it could be few lines of code and could do things that would be extremely difficult to do otherwise.
I made few of those, maybe you used some, maybe not, but nevertheless I think it’s insanely cool, and I use them in apps I write. None of them was rejected from AppStore for using private API so its totally safe.
For example, base internationalisation backport. You know - that small checkbox in Xcode that makes you life as developer way brighter if you making multilingual applications. Before iOS6 it was very difficult to localize interface builder files…
If you launch App on iOS5 witch doesn’t support base internationalisation you will see NSUnarchyver error that NSLocalizableString doesn’t exist, and thats what we need to backport.
Fortunately NSKeyedUnarchiver has convenient method to replace classes:
-[NSKeyedUnarchiver setClass: [RRLocalizableString class]
forClassName: @“NSLocalizableString"]
this will tell unarchiver to use our class instead of encoded one.
Next, you have to extract info passed into -[RRLocalizableString initWithCoder:]. There is no easy way to find out -[NSCoder decodeObjectForKey:] keys, but the best way I found is to swizzle it and add NSLog.
NSString belongs to class cluster so with -[NSString initWithCoder:] we have to implement -[NSString length] and -[NSString characterAtIndex:] also. They wont be used but they still have to be there.
by swizzling -[NSCoder decodeObjectForKey:] you will find NSKey and NSDev to use in -[NSBundle localizedStringForKey:value:table:], but we still missing “table”.
best way would be to check what file we currently unarchiving, but unfortunately there is no public method on NSCoder for that. So we will have to look for it somewhere else.
Most file operations use -[NSBundle pathForResource:ofType:] to find path to file by name. And sure enough swizzling it gives path we need. All we have to do is store it.
There is several ways to store information in categories, and because we know that only main bundle is used and we need that information only at unarchiving time, we can put it in a static NSString
great success! All what was needed to make this complicated looking backport was several lines of code, and it will save allot of headache for you as developer if you making multilingual applications.
there is no reason to write hard maintainable code just to support older SDK’s, so I invite everyone to check out my backports on github and write some of their own. Thank you.