KEMBAR78
Live Updating Swift Code | PDF
Live-updating Swift
code
Bartosz Polaczyk
Senior iOS Developer
Bartosz Polaczyk
Agenda
• Demo

• How does Injection work?

• Swift limitations
Demo
Injection for Xcode®
Runtime
Live code injection ObjC
Runtime
Live code injection ObjC
Injection
Bundle
Runtime
Live code injection ObjC
Injection
Bundle
Method swizzling
let selectorToReplace = #selector(methodName)
let originalMethod = class_getInstanceMethod(UIView.self, selectorToReplace)
let swizzledMethod = class_getInstanceMethod(OtherClass.self, selectorToReplace)
method_exchangeImplementations(originalMethod, swizzledMethod)
Method swizzling
let selectorToReplace = #selector(methodName)
let originalMethod = class_getInstanceMethod(UIView.self, selectorTo
let swizzledMethod = class_getInstanceMethod(OtherClass.self, select
method_exchangeImplementations(originalMethod, swizzledMethod)
Runtime
Live code injection ObjC
Injection
Bundle
Runtime
Live code injection ObjC
Injection
Bundle
Runtime
Live code injection ObjC
Dispatching in Swift
Dispatching in Swift
Swift class memory layout
class(isa)
…
…
Method 1
Method 2
Method 3
…
Swift class memory layout
class(isa)
…
…
Method 1
Method 2
Method 3
…
pushq %rbp
movq %rsp, %rbp
pushq %r13
subq $0x38, %rsp
movq %r12, %rcx
movq 0x10(%r13), %rdi
movq 0x18(%r13), %rdx
movq %rdi, -0x10(%rbp)
movq %rdx, %rdi
Swift class memory layout
class(isa)
…
…
Method 1
Method 2
Method 3
…
pushq %rbp
movq %rsp, %rbp
pushq %r13
subq $0x38, %rsp
movq %r12, %rcx
movq 0x10(%r13), %rdi
movq 0x18(%r13), %rdx
movq %rdi, -0x10(%rbp)
movq %rdx, %rdi
pushq %rbp
movq %rsp, %rbp
pushq %r14
subq $0x38, %rsp
movq %r11, %rcx
movq 0x10(%r13), %rdi
movq 0x18(%r13), %rdx
movq %rdi, -0x10(%rbp)
movq %rdx, %rdi
Swift class memory layout
class(isa)
…
…
Method 1
Method 2
Method 3
…
pushq %rbp
movq %rsp, %rbp
pushq %r13
subq $0x38, %rsp
movq %r12, %rcx
movq 0x10(%r13), %rdi
movq 0x18(%r13), %rdx
movq %rdi, -0x10(%rbp)
movq %rdx, %rdi
pushq %rbp
movq %rsp, %rbp
pushq %r14
subq $0x38, %rsp
movq %r11, %rcx
movq 0x10(%r13), %rdi
movq 0x18(%r13), %rdx
movq %rdi, -0x10(%rbp)
movq %rdx, %rdi
Swift vtable copy
// if swift language class, copy vtable
struct _in_objc_class *newclass = (struct _in_objc_class *)INJECTION_BRIDGE(void *)newClass;
if ( (uintptr_t)newclass->internal & 0x1 ) {
struct _in_objc_class *oldclass = (struct _in_objc_class *)INJECTION_BRIDGE(void *)oldClass;
size_t bytes = oldclass->mdsize - offsetof(struct _in_objc_class, dispatch) - 2*sizeof(IMP);
memcpy( oldclass->dispatch, newclass->dispatch, bytes );
}
Swift vtable copy
// if swift language class, copy vtable
struct _in_objc_class *newclass = (struct _in_ob
if ( (uintptr_t)newclass->internal & 0x1 ) {
struct _in_objc_class *oldclass = (struct _i
size_t bytes = oldclass->mdsize - offsetof(s
memcpy( oldclass->dispatch, newclass->dispat
}
Static dispatch
class
struct
Static dispatch
class
struct
Static dispatch
class
struct
InjectionForXcode Swift
limitations
InjectionForXcode Swift
limitations
• classes inherit from NSObject
InjectionForXcode Swift
limitations
• classes inherit from NSObject
• non-final classes
InjectionForXcode Swift
limitations
• classes inherit from NSObject
• non-final classes
• non-final methods
InjectionForXcode Swift
limitations
• classes inherit from NSObject
• non-final classes
• non-final methods
• no structs/enums, no extensions
InjectionForXcode Swift
limitations
• classes inherit from NSObject
• non-final classes
• non-final methods
• no structs/enums, no extensions
• no global functions
InjectionForXcode Swift
limitations
• classes inherit from NSObject
• non-final classes
• non-final methods
• no structs/enums, no extensions
• no global functions
• no methods/properties added
but…
InjectionTDD
Demo
InjectionTDD
InjectionTDD
• Live unit tests
InjectionTDD
• Live unit tests
• Zero configuration
InjectionTDD
• Live unit tests
• Zero configuration
• CocoaPods or .framework integration
InjectionTDD
• Live unit tests
• Zero configuration
• CocoaPods or .framework integration
• Standard Xcode interface
InjectionTDD
• Live unit tests
• Zero configuration
• CocoaPods or .framework integration
• Standard Xcode interface
• Swift only
build.log
build.log
build.logbuild.log
lldb
simulator
build.log
build.log
build.logbuild.log
lldb
simulator
build.log
build.log
build.logbuild.log
lldb
simulator
1
lldb
build.log
build.log
build.logbuild.log
lldb
simulator
1
lldb
TCP
socket
2
build.log
build.log
build.logbuild.log
lldb
simulator
TCP
socket
2
build.log
build.log
build.logbuild.log
lldb
simulator
3
TCP
socket
2
build.log
build.log
build.logbuild.log
lldb
simulator
TCP
socket
2
build.log
build.log
build.logbuild.log
lldb
simulator
TCP
socket
2
4
build.log
build.log
build.logbuild.log
lldb
simulator
TCP
socket
2
build.log
build.log
build.logbuild.log
lldb
simulator
TCP
socket
2
5
swiftc/swift
InjectionBundle
swiftc/swift
file1.o
file2.o
file3.o
InjectionBundle
swiftc/swift
file1.o
file2.o
file3.o
InjectionBundle
class
struct
class
struct
class
struct
file .swiftdeps
file .swiftdeps
## Swift dependencies file v0 ###
provides-top-level:
- "TrackingClientType"
provides-nominal:
- "7Library18TrackingClientTypeP"
provides-member:
- ["7Library18TrackingClientTypeP", ""]
- ["7Library18TrackingClientTypeP", "track"]
provides-dynamic-lookup:
depends-top-level:
- !private "!"
- !private "!="
...
- !private ["13ReactiveSwift14SignalProducerV", "uniqueValues"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "collect"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "concat"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "demoteErrors"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "filter"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "ksr_delay"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "map"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "take"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "uncollect"]
- !private ["13ReactiveSwift22SignalProducerProtocolP", "uniqueValues"]
- !private ["13ReactiveSwift14SignalProtocolP", "combineLatest"]
- !private ["13ReactiveSwift14SignalProtocolP", "filter"]
- !private ["13ReactiveSwift14SignalProtocolP", "flatMap"]
- !private ["13ReactiveSwift14SignalProtocolP", "ignoreValues"]
...
file .swiftdeps
- "TrackingClientType"
provides-nominal:
- "7Library18TrackingClientTypeP"
provides-member:
- ["7Library18TrackingClientTypeP", ""]
- ["7Library18TrackingClientTypeP", "track"]
provides-dynamic-lookup:
depends-top-level:
- !private "!"
- !private "!="
...
- !private ["13ReactiveSwift14SignalProducerV", "uniqueVal
- !private ["13ReactiveSwift22SignalProducerProtocolP", "c
- !private ["13ReactiveSwift22SignalProducerProtocolP", "c
- !private ["13ReactiveSwift22SignalProducerProtocolP", "d
- !private ["13ReactiveSwift22SignalProducerProtocolP", "f
- !private ["13ReactiveSwift22SignalProducerProtocolP", "k
- !private ["13ReactiveSwift22SignalProducerProtocolP", "m
InjectionTDD limitations
InjectionTDD limitations
• No Objective-C
InjectionTDD limitations
• No Objective-C
• No static libraries used
InjectionTDD limitations
• No Objective-C
• No static libraries used
• Host application
InjectionTDD limitations
• No Objective-C
• No static libraries used
• Host application
• ⌘+U, if new file added
Installation
• Download Injection For Xcode with TDD

• correct xcode-select -p
• Integrate InjectionTDD to your project

• e.g. pod 'InjectionTDD', '~> 0.4’
• ⌘+U
Optionally: install Xcode
breakpoints
Optionally: install Xcode
breakpoints
curl https://codeload.github.com/polac24/InjectionTDD/tar.gz/master | tar -xz --strip=3
InjectionTDD-master/scripts/update_breakpoints_source/
./update_breakpoints_source.sh
Summary
• Injection For Xcode for UI

• InjectionTDD for unit tests

• tests on ⌘+S

• Swift

• minimal configuration

• standard Xcode interface
References
• InjectionTDD: https://github.com/polac24/InjectionTDD

• Download Injection: http://johnholdsworth.com/
injection.html

• Method Dispatch in Swift: https://www.raizlabs.com/dev/
2016/12/swift-method-dispatch/

• Exploring Swift Memory Layout • Mike Ash: https://
www.youtube.com/watch?v=ERYNyrfXjlg
Q&A

Live Updating Swift Code