Slide 1

Slide 1 text

May 15, 2017 | Marin Usalj Launch argumentsthe mysteries

Slide 2

Slide 2 text

May 15, 2017 | MARIN USALJ @supermarin supermar.in Marin Usalj

Slide 3

Slide 3 text

May 15, 2017 | MARIN USALJ Author of Alcatraz, xcpretty

Slide 4

Slide 4 text

May 15, 2017 | MARIN USALJ

Slide 5

Slide 5 text

May 15, 2017 | MARIN USALJ Agenda

Slide 6

Slide 6 text

May 15, 2017 | MARIN USALJ Process Arguments Agenda

Slide 7

Slide 7 text

May 15, 2017 | MARIN USALJ Process Arguments Advanced usage Agenda

Slide 8

Slide 8 text

May 15, 2017 | MARIN USALJ Process Arguments Advanced usage Gotchas Agenda

Slide 9

Slide 9 text

Process arguments

Slide 10

Slide 10 text

May 15, 2017 | MARIN USALJ $ ls -la $ git commit -a -m 'yolo' Process arguments

Slide 11

Slide 11 text

May 15, 2017 | MARIN USALJ $ ls -la $ git commit -a -m 'yolo' Process arguments

Slide 12

Slide 12 text

May 15, 2017 | MARIN USALJ $ ls -la $ git commit -a -m 'yolo' Process arguments

Slide 13

Slide 13 text

May 15, 2017 | MARIN USALJ $ ls -la $ git commit -a -m 'yolo' Process arguments

Slide 14

Slide 14 text

May 15, 2017 | MARIN USALJ $ ls -la $ git commit -a -m 'yolo' Process arguments

Slide 15

Slide 15 text

May 15, 2017 | MARIN USALJ $ ls -la $ git commit -a -m 'yolo' Process arguments

Slide 16

Slide 16 text

May 15, 2017 | MARIN USALJ Used by exec() family of functions (execve on OSX) Process Arguments

Slide 17

Slide 17 text

May 15, 2017 | MARIN USALJ Used by exec() family of functions (execve on OSX) Launching binaries, scripts with #! Process Arguments

Slide 18

Slide 18 text

May 15, 2017 | MARIN USALJ Used by exec() family of functions (execve on OSX) Launching binaries, scripts with #! In C-family of languages, main(argc, argv) Process Arguments

Slide 19

Slide 19 text

May 15, 2017 | MARIN USALJ +(id)launchParametersWithSchemeIde ntifier: launcherIdentifier: debuggerIdentifier: launchStyle: runnableLocation: debugProcessAsUID: workingDirectory: commandLineArgs: environmentVariables: architecture: platformIdentifier: buildConfiguration: buildableProduct: Launching, Xcode style deviceAppDataPackage: allowLocationSimulation: locationScenarioReference: routingCoverageFileReference: enableOpenGLFrameCaptureMode: enableOpenGLPerformanceAnalysisMo de: simulatorIPhoneDisplay: simulatorIPadDisplay: debugXPCServices: additionalXPCServicesToDebug: internalIOSLaunchStyle: internalIOSSubstitutionApp: launchAutomaticallySubstyle:

Slide 20

Slide 20 text

May 15, 2017 | MARIN USALJ Demo ⏲

Slide 21

Slide 21 text

May 15, 2017 | MARIN USALJ There are a number of options that can be passed into a target’s scheme to enable useful debugging behavior, but like a fast food secret menu, they’re obscure and widely unknown. MATTT THOMPSON, NSHIPSTER

Slide 22

Slide 22 text

May 15, 2017 | MARIN USALJ How does this work?

Slide 23

Slide 23 text

May 15, 2017 | MARIN USALJ In most programming environments, you handle arguments by yourself How does this work?

Slide 24

Slide 24 text

May 15, 2017 | MARIN USALJ In most programming environments, you handle arguments by yourself Bash: getopt, getopts How does this work?

Slide 25

Slide 25 text

May 15, 2017 | MARIN USALJ In most programming environments, you handle arguments by yourself Bash: getopt, getopts Ruby: OptionParser (and n^2345 others) How does this work?

Slide 26

Slide 26 text

May 15, 2017 | MARIN USALJ In most programming environments, you handle arguments by yourself Bash: getopt, getopts Ruby: OptionParser (and n^2345 others) Python: argparse How does this work?

Slide 27

Slide 27 text

May 15, 2017 | MARIN USALJ How does this work?

Slide 28

Slide 28 text

May 15, 2017 | MARIN USALJ In Cocoa, there's a little bit of

Slide 29

Slide 29 text

May 15, 2017 | MARIN USALJ Arguments are parsed by Cocoa Parsing in Cocoa

Slide 30

Slide 30 text

May 15, 2017 | MARIN USALJ Arguments are parsed by Cocoa Injected on top of NSUserDefaults Parsing in Cocoa

Slide 31

Slide 31 text

May 15, 2017 | MARIN USALJ Arguments are parsed by Cocoa Injected on top of NSUserDefaults No need to store a value into NSUserDefaults to read it from args Parsing in Cocoa

Slide 32

Slide 32 text

May 15, 2017 | MARIN USALJ Arguments are parsed by Cocoa Injected on top of NSUserDefaults No need to store a value into NSUserDefaults to read it from args Anything stored in NSUserDefaults can be overridden Parsing in Cocoa

Slide 33

Slide 33 text

May 15, 2017 | MARIN USALJ NSUserDefaults lookup app

Slide 34

Slide 34 text

May 15, 2017 | MARIN USALJ NSUserDefaults lookup NSUserDefaults app conference = NSSpain speaker = Marin

Slide 35

Slide 35 text

May 15, 2017 | MARIN USALJ NSUserDefaults lookup NSUserDefaults app Launch args conference = SwiftSummit conference = NSSpain speaker = Marin

Slide 36

Slide 36 text

May 15, 2017 | MARIN USALJ NSUserDefaults lookup NSUserDefaults app Launch args conference = SwiftSummit conference = NSSpain speaker = Marin objectForKey: "speaker"

Slide 37

Slide 37 text

May 15, 2017 | MARIN USALJ NSUserDefaults lookup NSUserDefaults app Launch args conference = SwiftSummit conference = NSSpain speaker = Marin objectForKey: "speaker" "Marin"

Slide 38

Slide 38 text

May 15, 2017 | MARIN USALJ NSUserDefaults lookup NSUserDefaults app Launch args conference = SwiftSummit conference = NSSpain speaker = Marin objectForKey: "conference" objectForKey: "speaker" "Marin"

Slide 39

Slide 39 text

May 15, 2017 | MARIN USALJ NSUserDefaults lookup NSUserDefaults app Launch args conference = SwiftSummit conference = NSSpain speaker = Marin objectForKey: "conference" "SwiftSummit" objectForKey: "speaker" "Marin"

Slide 40

Slide 40 text

May 15, 2017 | MARIN USALJ What do I do with this?

Slide 41

Slide 41 text

May 15, 2017 | MARIN USALJ Changing app behavior without recompiling

Slide 42

Slide 42 text

May 15, 2017 | MARIN USALJ Lyft's use case

Slide 43

Slide 43 text

May 15, 2017 | MARIN USALJ Lyft's use case

Slide 44

Slide 44 text

May 15, 2017 | MARIN USALJ Lyft's use case

Slide 45

Slide 45 text

May 15, 2017 | MARIN USALJ Lyft's use case

Slide 46

Slide 46 text

May 15, 2017 | MARIN USALJ Lyft's use case ? ? ? ? ? ? ? ?

Slide 47

Slide 47 text

May 15, 2017 | MARIN USALJ First approach: UIAutomation with a hidden server selector Bring up UI, type URI letter by letter Lyft's use case

Slide 48

Slide 48 text

May 15, 2017 | MARIN USALJ First approach: UIAutomation with a hidden server selector Bring up UI, type URI letter by letter Lyft's use case

Slide 49

Slide 49 text

May 15, 2017 | MARIN USALJ Lyft's use case

Slide 50

Slide 50 text

May 15, 2017 | MARIN USALJ Lyft's use case

Slide 51

Slide 51 text

May 15, 2017 | MARIN USALJ Recompile the app with hardcoded server address Given each compilation is ~5 minutes, this would waste at least 50 CI minutes for each developer push. Recompile?

Slide 52

Slide 52 text

May 15, 2017 | MARIN USALJ Inject a file in the bundle after compilation Almost a viable solution, requires adding specific code for reading the bundle and overriding our servers. Inject a file?

Slide 53

Slide 53 text

May 15, 2017 | MARIN USALJ Launch the app with arguments overriding server endpoints Challenge: server environment stored as a nested Dictionary More than one endpoint to override (analytics for example) Launch arguments?

Slide 54

Slide 54 text

May 15, 2017 | MARIN USALJ "navigation": "Waze", "servers": { "api": "https://api.dev.lyft.com", "analytics": "https://analytics.dev.lyft.com" } Launch arguments?

Slide 55

Slide 55 text

Advanced usage

Slide 56

Slide 56 text

May 15, 2017 | MARIN USALJ $ lyft -servers.api "http://..." \ -servers.analytics "http://..." \ Key paths?

Slide 57

Slide 57 text

May 15, 2017 | MARIN USALJ $ lyft -navigation Waze -servers '{ "foo": "bar" }' JSON?

Slide 58

Slide 58 text

May 15, 2017 | MARIN USALJ Quiz ⏲

Slide 59

Slide 59 text

May 15, 2017 | MARIN USALJ

Slide 60

Slide 60 text

May 15, 2017 | MARIN USALJ

Slide 61

Slide 61 text

May 15, 2017 | MARIN USALJ $ lyft -servers '{ api = https://api.staging.lyft.com; analytics = https://analytics.staging.lyft.com; lastOpened = 2016-09-18T01:29:01Z; }' -conference FrenchKit -speaker 'Marin Usalj' NeXTSTEP plists

Slide 62

Slide 62 text

May 15, 2017 | MARIN USALJ $ lyft -servers '{ api = https://api.staging.lyft.com; analytics = https://analytics.staging.lyft.com; lastOpened = 2016-09-18T01:29:01Z; }' -conference FrenchKit -speaker 'Marin Usalj' NeXTSTEP plists

Slide 63

Slide 63 text

May 15, 2017 | MARIN USALJ $ lyft -servers 'yolo' Built in XML reader 1. Try to parse XML from 'yolo' 2. Try to parse Plist from 'yolo' 3. Just String('yolo')

Slide 64

Slide 64 text

May 15, 2017 | MARIN USALJ $ lyft -servers 'yolo' Built in XML reader 1. Try to parse XML from 'yolo' 2. Try to parse Plist from 'yolo' 3. Just String('yolo')

Slide 65

Slide 65 text

May 15, 2017 | MARIN USALJ $ lyft -servers 'yolo' Built in XML reader 1. Try to parse XML from 'yolo' 2. Try to parse Plist from 'yolo' 3. Just String('yolo')

Slide 66

Slide 66 text

Gotchas

Slide 67

Slide 67 text

May 15, 2017 | MARIN USALJ Overwriting values after launch won't work after app launch Watch out on places where you're reading / writing to NSUserDefaults Gotchas

Slide 68

Slide 68 text

May 15, 2017 | MARIN USALJ UserDefaults is a key-value Store with String keys Value types: String, Data, Number, Date, Array, and Dictionary Gotchas

Slide 69

Slide 69 text

May 15, 2017 | MARIN USALJ open func string(forKey defaultName: String) -> String? open func array(forKey defaultName: String) -> [Any]? open func dictionary(forKey defaultName: String) -> [String : Any]? open func data(forKey defaultName: String) -> Data? open func stringArray(forKey defaultName: String) -> [String]? open func integer(forKey defaultName: String) -> Int open func float(forKey defaultName: String) -> Float open func double(forKey defaultName: String) -> Double open func bool(forKey defaultName: String) -> Bool open func url(forKey defaultName: String) -> URL? UserDefaults helpers

Slide 70

Slide 70 text

May 15, 2017 | MARIN USALJ /*! -integerForKey: is equivalent to -objectForKey:, except that it converts the returned value to an NSInteger. If the value is an NSNumber, the result of - integerValue will be returned. If the value is an NSString, it will be converted to NSInteger if possible. If the value is a boolean, it will be converted to either 1 for YES or 0 for NO. If the value is absent or can't be converted to an integer, 0 will be returned. */ Gotchas

Slide 71

Slide 71 text

May 15, 2017 | MARIN USALJ XML tags CoreFoundation type XML Tag Storage format CFString UTF-8 encoded string CFNumber , Decimal string CFBoolean , No data (tag only) CFDate ISO 8601 formatted string CFData Base64 encoded data CFArray Can contain any number of child elements CFDictionary Alternating tags and plist element tags

Slide 72

Slide 72 text

May 15, 2017 | MARIN USALJ Thanks LYFT, IDA & BEREN

Slide 73

Slide 73 text

May 15, 2017 | MARIN USALJ We are hiring!

Slide 74

Slide 74 text

May 15, 2017 | MARIN USALJ https://developer.apple.com/library/ios/recipes/xcode_help-scheme_editor/Articles/ SchemeRun.html https://en.wikipedia.org/wiki/Property_list http://linux.die.net/man/2/execve http://nshipster.com/launch-arguments-and-environment-variables/ http://useyourloaf.com/blog/using-launch-arguments-to-test-localizations/#disqus_thread http://www.manpagez.com/man/3/execv/ References