hotfixing ios apps with javascript

78
Hotfixing iOS apps with Javascript Sergio Padrino Recio

Upload: sergio-padrino-recio

Post on 12-Apr-2017

175 views

Category:

Software


3 download

TRANSCRIPT

Page 1: Hotfixing iOS apps with Javascript

Hotfixing iOS appswith Javascript

Sergio Padrino Recio

Page 2: Hotfixing iOS apps with Javascript

About me…

• Sergio Padrino (@sergiou87)

• Started working on iOS on 2010

• Worked at Tuenti as iOS engineer (2012-2013)

• Worked at Fever as iOS Lead engineer (2014)

• Working at Plex since July 2014 – Current iOS Team Lead

Page 3: Hotfixing iOS apps with Javascript

About Plex

Page 4: Hotfixing iOS apps with Javascript

About Plex

Page 5: Hotfixing iOS apps with Javascript

Example case

Page 6: Hotfixing iOS apps with Javascript

Example case

• Submit to Apple.

• Wait for review: 7 days.

• In review: 3 hours.

• Release!

Page 7: Hotfixing iOS apps with Javascript

Example caseWTF??

Page 8: Hotfixing iOS apps with Javascript
Page 9: Hotfixing iOS apps with Javascript

8 days later…

Page 10: Hotfixing iOS apps with Javascript
Page 11: Hotfixing iOS apps with Javascript
Page 12: Hotfixing iOS apps with Javascript

We are all Peter

Page 13: Hotfixing iOS apps with Javascript

Example case• Extreme case, workflow full of flaws:

• Coder failed.

• Code reviewer failed.

• Testers failed.

• Apple… didn’t say anything.

Page 14: Hotfixing iOS apps with Javascript

Plex: The Monster

• Too many moving parts:

• Plex Media Server version

• User network setup

• Interoperability with other Plex players

• Audio/Video/Subtitle format

Page 15: Hotfixing iOS apps with Javascript
Page 16: Hotfixing iOS apps with Javascript

Fixes are usually quick, but…

Page 17: Hotfixing iOS apps with Javascript

Apple App Review process• Used to be 1 week.

• Now reduced to 1-3 days.

• Still not reliable…

• Reviewer testing the wrong binary.

• App rejected because… Apple 😒

• Christmas holidays.

• Sometimes just gets longer.

Page 18: Hotfixing iOS apps with Javascript

appreviewtimes.com

Page 19: Hotfixing iOS apps with Javascript

Can we improve this?

Page 20: Hotfixing iOS apps with Javascript

Can we improve this?

• Android and Web apps can be updated at any time.

• Native iOS apps need to bypass Apple review:

• Use a remote configuration (GroundControl).

• Embedded web pages can be updated.

• Or…

Page 21: Hotfixing iOS apps with Javascript

rollout.io• Service to hotfix apps without Apple review:

• Surround method with try…catch

• Replace method argument or return value

• No-op a method

• Basic scripting

Page 22: Hotfixing iOS apps with Javascript
Page 23: Hotfixing iOS apps with Javascript

rollout.io• Main problems (for us):

• Their platform has the ability to change our app remotely. If they’re compromised… 😱

• “Expensive” post-build script to upload dSym.

• Our dSym is massive and they had trouble processing it 😆

Page 24: Hotfixing iOS apps with Javascript

DIY• Can we do it ourselves… better?

• Recent pain working on Apple TV app:

• Too much Javascript 😖

• Objective-C magic

• My own solution… SPHotPatch

Page 25: Hotfixing iOS apps with Javascript

plex.tv

DIY

Configuration file

(hotfixes)

Plex for iOS

MAGIC✨

Page 26: Hotfixing iOS apps with Javascript

But… how??

Page 27: Hotfixing iOS apps with Javascript

But… how??

Method Swizzling!

Page 28: Hotfixing iOS apps with Javascript

Method SwizzlingAn Example

- (void)doSomethingWrong { self.array[self.array.count];}

Page 29: Hotfixing iOS apps with Javascript

Method SwizzlingAn Example

- (void)doSomethingWrong { self.array[self.array.count];} - (void)safe_doSomethingWrong { @try { [self safe_doSomethingWrong]; } @catch(…) { } }

Page 30: Hotfixing iOS apps with Javascript

Method SwizzlingAn Example

- (void)doSomethingWrong { self.array[self.array.count];} - (void)safe_doSomethingWrong { @try { [self safe_doSomethingWrong]; } @catch(…) { } }

INFINITERECURSION?!

Page 31: Hotfixing iOS apps with Javascript

Method SwizzlingAn Example

- (void)doSomethingWrong { self.array[self.array.count];} - (void)safe_doSomethingWrong { @try { [self safe_doSomethingWrong]; } @catch(…) { } }

INFINITERECURSION?!

Page 32: Hotfixing iOS apps with Javascript

doSomethingWrong

Method SwizzlingAn Example

array[array.count]

safe_doSomethingWrong

@try { [self safe_doSomethingWrong];} @catch(…) {}

Page 33: Hotfixing iOS apps with Javascript

doSomethingWrong

Method SwizzlingAn Example

array[array.count]

safe_doSomethingWrong

@try { [self safe_doSomethingWrong];} @catch(…) {}

Page 34: Hotfixing iOS apps with Javascript

safe_doSomethingWrongdoSomethingWrong

@try { [self safe_doSomethingWrong];} @catch(…) {}

Method SwizzlingAn Example

array[array.count]

Page 35: Hotfixing iOS apps with Javascript

doSomethingWrong

@try { [self safe_doSomethingWrong];} @catch(…) {}

Method SwizzlingAn Example

array[array.count]

safe_doSomethingWrong

Page 36: Hotfixing iOS apps with Javascript
Page 37: Hotfixing iOS apps with Javascript

Where is my Javascript??

• JavascriptCore since iOS 7.

• Run JS scripts from Objective-C.

• Bridging: call Objective-C code from JS.

• More Objective-C runtime magic.

Page 38: Hotfixing iOS apps with Javascript

Javascript Core

• Run Javascript scripts from Objective-C:

Page 39: Hotfixing iOS apps with Javascript

Bridging

• Invoke Objective-C code from Javascript:

Page 40: Hotfixing iOS apps with Javascript

MyCrashyClass

doSomethingWrong

Combine all that…

array[array.count]

Page 41: Hotfixing iOS apps with Javascript

MyCrashyClass

safe_doSomethingWrongdoSomethingWrong

JSContext *context = …[context evaluate:hotfixString]

Combine all that…

array[array.count]

Page 42: Hotfixing iOS apps with Javascript

doSomethingWrong

MyCrashyClass

JSContext *context = …[context evaluate:hotfixString]

…fixed!

array[array.count]

safe_doSomethingWrong

Page 43: Hotfixing iOS apps with Javascript

More Objective-C runtime?

• “Proxy” object that gives access to Obj-C stuff from JS.

Page 44: Hotfixing iOS apps with Javascript

More Objective-C runtime?

• Box method parameters to JSValue.

• Unbox return value from JSValue ⚠

• A lot of boilerplate to support as many types as possible.

• Took some “inspiration” from OCMockito.

Page 45: Hotfixing iOS apps with Javascript

Unboxing return value…IMP newImp = imp_implementationWithBlock(^(id self, ...) { va_list args; // Box parameters from args and prepare script NSString *script = ...; JSValue *result = [context evaluateScript:script]; if ([result isString]) return [result toString]; else if ([result isNumber]) return [result toInt32];}

Page 46: Hotfixing iOS apps with Javascript

Unboxing return value…

• Method parameters are easy: variadic arguments.

• There is no “wild card” for return types.

• The only option… override forwardInvocation:

• NSInvocation is the key!

Page 47: Hotfixing iOS apps with Javascript

Unboxing return value…

IMP newImp = imp_implementationWithBlock(^(id self, NSInvocation *inv) { // Box parameters from invocation and prepare script NSString *script = ...; JSValue *result = [context evaluateScript:script]; if (inv.methodSignature.methodReturnType == ‘@’) [inv setReturnValue:[result toObject]]; else if (inv.methodSignature.methodReturnType == ‘i’) [inv setReturnValue:[result toInt32]];}

Page 48: Hotfixing iOS apps with Javascript

MyCrashyClass

doSomethingWrong

Real Life™

array[array.count]

forwardInvocation:

original implementation

Page 49: Hotfixing iOS apps with Javascript

MyCrashyClass

ORIGdoSomethingWrongdoSomethingWrong

_objc_msgForward

Real Life™

forwardInvocation:

original implementation

array[array.count]

Page 50: Hotfixing iOS apps with Javascript

MyCrashyClass

ORIGdoSomethingWrongdoSomethingWrong

_objc_msgForward

Real Life™

forwardInvocation:

original implementation

array[array.count]

Page 51: Hotfixing iOS apps with Javascript

MyCrashyClass

ORIGdoSomethingWrongdoSomethingWrong

_objc_msgForward

Real Life™

forwardInvocation:

original implementation

array[array.count]

ORIGforwardInvocation:

JSContext *context = …[context evaluate:hotfixString]

Page 52: Hotfixing iOS apps with Javascript

MyCrashyClass

ORIGdoSomethingWrongdoSomethingWrong

_objc_msgForward

Real Life™

array[array.count]

ORIGforwardInvocation:forwardInvocation:

JSContext *context = …[context evaluate:hotfixString] original implementation

Page 53: Hotfixing iOS apps with Javascript
Page 54: Hotfixing iOS apps with Javascript
Page 55: Hotfixing iOS apps with Javascript

DEMO

Page 56: Hotfixing iOS apps with Javascript

SPHotPatch• Share it with friends to show off…

• …until someone tells me about JSPatch

• Open Source

• Better JS syntax (pre-processing)

• Extensions to use C stuff (CoreGraphics…)

Page 57: Hotfixing iOS apps with Javascript

JSPatch

Page 58: Hotfixing iOS apps with Javascript

JSPatch in Plex• Remote configuration file declares available patches.

• Patches belong to a:

• App version.

• App build.

• Patch channel (default: production).

Page 59: Hotfixing iOS apps with Javascript

JSPatch in Plex

• Multiple “channels” allow:

• Testing hotfixes before “releasing” them.

• Create custom patches for users if needed.

Page 60: Hotfixing iOS apps with Javascript

JSPatch in Plex

Page 61: Hotfixing iOS apps with Javascript

JSPatch in Plex• More features:

• Safe patching: if the app crashes before the patch can be downloaded and applied, next time the whole patching process will be synchronous.

• Skip patching in the next run.

• Clear last patch.

Page 62: Hotfixing iOS apps with Javascript

Things you can do

Page 63: Hotfixing iOS apps with Javascript

Hotfixingof course

Page 64: Hotfixing iOS apps with Javascript

Gather data

• For bugs hard/impossible to reproduce.

• Create specific patch channel for affected users.

• Deploy patches for those users.

• Ask them for feedback in the forums.

Page 65: Hotfixing iOS apps with Javascript

Gather data

• Example 1:

• Video stalls and stuttering.

• Patches to log more info.

• Patches to change different settings of the video player.

Page 66: Hotfixing iOS apps with Javascript

Gather data

• Example 2:

• Weird AutoLayout crash on old devices.

• Crash stacktrace impossible to read: all Apple code.

• Patches to change different bits of broken layout.

Page 67: Hotfixing iOS apps with Javascript

Rewrite the whole app in JS

Page 68: Hotfixing iOS apps with Javascript

Rewrite the whole app in JS

Page 69: Hotfixing iOS apps with Javascript

Things you CAN’T fix

Page 70: Hotfixing iOS apps with Javascript

Things you CAN’T fixseriously…

• Virtually nothing?

• You REALLY can write your whole app with JSPatch!

• Create extensions for C stuff you need.

• Apply patches as soon as you can.

• At Plex, we leave out +load methods.

Page 71: Hotfixing iOS apps with Javascript

What about Swift?

• Depends on Objective-C runtime so…

• Only works with NSObject.

• No structs or primitive types.

• No classes not inheriting from NSObject.

Page 72: Hotfixing iOS apps with Javascript

What about Apple?3.3.2 Except as set forth in the next paragraph, an Application may not download or install executable code. Interpreted code may only be used in an Application if all scripts, code and interpreters are packaged in the Application and not downloaded. The only exceptions to the foregoing are scripts and code downloaded and run by Apple's built-in WebKit framework or JavascriptCore, provided that such scripts and code do not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store

Page 73: Hotfixing iOS apps with Javascript

What about Apple?

• Just Javascript code that runs in JavascriptCore.

• Small fixes, not changing the whole app.

Page 74: Hotfixing iOS apps with Javascript

Security

• Avoid downloading patches from unknown sources.

• Don’t run JS scripts without a valid signature.

• Only allow to sign patches to a handful of people.

Page 75: Hotfixing iOS apps with Javascript

Good practices

• Don’t abuse JS patching. It’s just your safe net.

• Establish a proper workflow to catch bugs before the release.

• Test, test, test. Automate as much as you can.

Page 76: Hotfixing iOS apps with Javascript

Good practices• QA should find nothing. If they do, it should be a big

thing.

• If all the above fails and the bug has a huge impact, hotfix it.

• JS hotfixes should be reviewed and tested too.

• Immediately after, submit another build. Never rely on those hotfixes.

Page 77: Hotfixing iOS apps with Javascript

Questions?

Page 78: Hotfixing iOS apps with Javascript

Thank you!