How do A/B testing platforms replace Objective-C assets on the fly?
ab-testing, ios, leanplum, objective-c
Solution
The question is, what techniques does these A/B testing platforms use to replace assets at runtime?
An educated guess is that they use method swizzling to swap the implementations of standard methods (e.g. `[NSBundle URLForResource:withExtension:]`) with those of their own versions of those methods (e.g. `[NSBundle(LeanplumExtension) leanplum_URLForResource:withExtension:]`). This means that your code uses the same method that you would otherwise, but you get different behavior -- in this case, the URL that's returned depends on which version of the resource the A/B testing framework decides to present to the user.
I wonder how they get this entire list of API covered?
By working carefully. The number of methods used to load resources isn't unmanageable.
What happens if I open an image with fopen or other C lib?
I expect that the framework wouldn't be able to swap resources in such a case. There are lots of ways to load data, and the framework can't possibly anticipate all of them. But if you're using an A/B framework, you presumably want the resources to be replaced as appropriate for your testing, so it doesn't make a lot of sense to try to defeat the framework.
How can I decipher the output of tool?
For the case you've shown, learn to read assembly language. (`otool` has a lot of options, though, and they don't all produce assembly.)
Problem
Leanplum, Apptimize and other A/B testing platforms for iOS have the ability to download assets (nib files ,images, etc...) from the web and replace them at runtime. The naive approach will be to download the new assets and replace them in the resource bundle directory, but it is impossible to write files to the resource directory because of permissions. The question is, what techniques does these A/B testing platforms use to replace assets at runtime? EDIT: After reading the symbols on leanplum static library file (using nm) it seems that they are Swizzling cocoa file system API's. for example: (an example line from `nm -m leanplum.a`) ``` -[NSBundle(LeanplumExtension) leanplum_URLForResource:withExtension:] ``` By using otool I can print the implementation: ``` -[NSBundle(LeanplumExtension) leanplum_URLForResource:withExtension:]: 0000000000000069 pushq %rbp 000000000000006a movq %rsp, %rbp 000000000000006d pushq %r15 000000000000006f pushq %r14 0000000000000071 pushq %r13 0000000000000073 pushq %r12 0000000000000075 pushq %rbx 0000000000000076 subq $0x18, %rsp 000000000000007a movq %rcx, %rbx 000000000000007d movq %rdi, 0xffffffffffffffc8(%rbp) 0000000000000081 movq %rdx, %rdi 0000000000000084 callq _objc_retain 0000000000000089 movq %rax, %r14 000000000000008c movq %rbx, %rdi 000000000000008f callq _objc_retain 0000000000000094 movq %rax, 0xffffffffffffffd0(%rbp) 0000000000000098 movq _originalMainBundle(%rip), %rcx 000000000000009f movq "+[NSBundle(LeanplumExtension) leanplum_mainBundle]"(%rcx), %rdi 00000000000000a2 movq 0x4487(%rip), %rsi 00000000000000a9 movq _objc_msgSend(%rip), %r12 00000000000000b0 movq %r14, %rdx 00000000000000b3 movq %rax, %rcx 00000000000000b6 callq *%r12 00000000000000b9 movq %rax, %rdi 00000000000000bc callq _objc_retainAutoreleasedReturnValue 00000000000000c1 movq %rax, %r13 00000000000000c4 movq _skippedFiles(%rip), %rax 00000000000000cb movq "+[NSBundle(LeanplumExtension) leanplum_mainBundle]"(%rax), %rbx 00000000000000ce movq 0x4463(%rip), %rsi 00000000000000d5 movq %r13, %rdi 00000000000000d8 callq *%r12 00000000000000db movq %rax, %rdi 00000000000000de callq _objc_retainAutoreleasedReturnValue 00000000000000e3 movq %rax, %r15 00000000000000e6 movq 0x4453(%rip), %rsi 00000000000000ed movq %rbx, %rdi 00000000000000f0 movq %r15, %rdx 00000000000000f3 callq *%r12 00000000000000f6 movb %al, %bl 00000000000000f8 movq %r15, %rdi 00000000000000fb callq _objc_release 0000000000000100 testb %bl, %bl 0000000000000102 je 0x115 0000000000000104 movq %r13, %rdi 0000000000000107 callq _objc_retain 000000000000010c movq %rax, %r15 000000000000010f movq 0xffffffffffffffd0(%rbp), %rbx 0000000000000113 jmp 0x13b 0000000000000115 movq 0x4414(%rip), %rsi 000000000000011c movq 0xffffffffffffffc8(%rbp), %rdi 0000000000000120 movq %r14, %rdx 0000000000000123 movq 0xffffffffffffffd0(%rbp), %rbx 0000000000000127 movq %rbx, %rcx 000000000000012a callq *_objc_msgSend(%rip) 0000000000000130 movq %rax, %rdi 0000000000000133 callq _objc_retainAutoreleasedReturnValue 0000000000000138 movq %rax, %r15 000000000000013b movq %r13, %rdi 000000000000013e callq _objc_release 0000000000000143 movq %rbx, %rdi 0000000000000146 callq _objc_release 000000000000014b movq %r14, %rdi 000000000000014e callq _objc_release 0000000000000153 movq %r15, %rdi 0000000000000156 addq $0x18, %rsp 000000000000015a popq %rbx 000000000000015b popq %r12 000000000000015d popq %r13 000000000000015f popq %r14 0000000000000161 popq %r15 0000000000000163 popq %rbp 0000000000000164 jmpq _objc_autoreleaseReturnValue ``` - Can someone verify my findings? - I wonder how they get this entire list of API covered? - What happens if I open an image with fopen or other C lib? - How can I decipher the output of otool?