Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lessons Learned From Porting To Swift And Kotlin - Mobile Era Oslo, October 2017

Lessons Learned From Porting To Swift And Kotlin - Mobile Era Oslo, October 2017

Video:
https://vimeo.com/237875498

Abstract:
SpotHero has been moving its entire iOS application to Swift and started moving its Android app to Kotlin. Here's some lessons learned in the process in terms of selling management on using a new languages, how to get started, and some tips on things you should do along the way.

(Big thanks to my old crew at SpotHero!)

Ellen Shapiro

October 05, 2017
Tweet

More Decks by Ellen Shapiro

Other Decks in Technology

Transcript

  1. Lessons learned from porting to
    Swift and Kotlin
    by Ellen Shapiro
    Mobile Era | Oslo, Norway | October 2017
    @designatednerd | justhum.com | designatednerd.com

    View full-size slide

  2. (this is awkward)

    View full-size slide

  3. iOS (excluding dependencies)

    View full-size slide

  4. Step 1
    Selling it to The Bosses

    View full-size slide

  5. Identify specific problems
    addressed by the new language

    View full-size slide

  6. Objective-C
    if (![paymentNetworks containsObject:PKPaymentNetworkDiscover]) {
    [paymentNetworks addObject:PKPaymentNetworkDiscover];
    }

    View full-size slide

  7. Objective-C
    if (![paymentNetworks containsObject:PKPaymentNetworkDiscover]) {
    [paymentNetworks addObject:PKPaymentNetworkDiscover];
    }
    iOS 9 = !

    View full-size slide

  8. Objective-C
    if (![paymentNetworks containsObject:PKPaymentNetworkDiscover]) {
    [paymentNetworks addObject:PKPaymentNetworkDiscover];
    }
    iOS 8 = !

    View full-size slide

  9. Swift
    if !paymentNetworks.contains(.discover) {
    paymentNetworks.append(.discover)
    }

    View full-size slide

  10. Swift
    if !paymentNetworks.contains(.discover) {
    paymentNetworks.append(.discover)
    }
    !" Does Not Compile

    View full-size slide

  11. Swift
    if @available(iOS 9.0, *) {
    if !paymentNetworks.contains(.discover) {
    paymentNetworks.append(.discover)
    }
    }

    View full-size slide

  12. Swift
    if @available(iOS 9.0, *) {
    if !paymentNetworks.contains(.discover) {
    paymentNetworks.append(.discover)
    }
    }
    Safe at compile time and run time

    View full-size slide

  13. Be honest
    about potential drawbacks

    View full-size slide

  14. Be honest
    (with yourself)
    about potential drawbacks

    View full-size slide

  15. Step 2
    Start small

    View full-size slide

  16. Step 2
    Start small

    View full-size slide

  17. Learn the
    new language

    View full-size slide

  18. Fight New System APIs
    or A New Language
    not both at once

    View full-size slide

  19. Stop writing old language patterns
    in a new language

    View full-size slide

  20. Objective-C
    NSMutableArray *capitalizedStrings = [NSMutableArray array];
    for (NSString *string in arrayOfStrings) {
    NSString *thing = [string capitalized];
    [capitalizedStrings addObject: thing];
    }

    View full-size slide

  21. Objective-C, but written in Swift
    var capitalizedStrings = [String]()
    for string in arrayOfStrings {
    let thing = string.capitalized
    capitalizedStrings.append(thing)
    }

    View full-size slide

  22. Swiftier
    let capitalizedStrings = arrayOfStrings.map {
    string in
    let thing = string.capitalized
    return thing
    }

    View full-size slide

  23. Swiftier
    let capitalizedStrings = arrayOfStrings.map {
    string in
    let thing = string.capitalized
    return thing
    }
    Swiftiest
    let capitalizedStrings = arrayOfStrings.map { $0.capitalized }

    View full-size slide

  24. Limit your risk

    View full-size slide

  25. A parable of going
    too far, too fast

    View full-size slide

  26. Be willing to pause
    and re-evaluate
    (before everything goes wrong)

    View full-size slide

  27. Be willing to pause
    and re-evaluate
    (before everything goes wrong)

    View full-size slide

  28. Step 3
    Biggest ! For Your "

    View full-size slide

  29. Step 3
    Biggest ! For Your "

    View full-size slide

  30. Look at your
    bugs and crashes

    View full-size slide

  31. NPE + GoogleMaps

    View full-size slide

  32. Look at your most
    error-prone code

    View full-size slide

  33. Objective-C JSON Parsing
    - (instancetype)fromJSONData:(NSData *)data
    {
    NSDictionary* jsonObject = [NSJSONSerialization jsonObjectWithData:data error:nil];
    if (jsonObject == nil]) {
    return nil;
    }
    user.firstName = dictionary[@"first_name"];
    user.lastName = dictionary[@"last_name"];
    user.age = [dictionary[@"age"] integerValue];
    return user;
    }

    View full-size slide

  34. Objective-C JSON Parsing
    - (instancetype)fromJSONData:(NSData *)data
    {
    NSDictionary* jsonObject = [NSJSONSerialization jsonObjectWithData:data error:nil];
    if (jsonObject == nil || ![jsonObject isKindOfClass:[NSDictionary class]]) {
    return nil;
    }
    if ([dictionary[@"first_name"] isKindOfClass: [NSString class]]) {
    user.firstName = dictionary[@"first_name"];
    } else {
    return nil;
    }
    if ([dictionary[@"last_name"] isKindOfClass: [NSString class]]) {
    user.lastName = dictionary[@"last_name"];
    } else {
    return nil;
    }
    if ([dictionary[@"age"] isKindOfClass: [NSNumber class]]) {
    user.age = [dictionary[@"age"] integerValue];
    } else {
    return nil;
    }
    return user;
    }

    View full-size slide

  35. Swift 4 JSON Parsing
    static func from(jsonData: Data) -> User? {
    let decoder = JSONDecoder()
    return try? decoder.decode(User.self, for: jsonData)
    }

    View full-size slide

  36. (This does require some setup)
    struct User: Codable {
    let firstName: String
    let lastName: String
    let age: Int
    enum CodingKeys: String, CodingKey {
    case
    firstName = "first_name",
    lastName = "last_name",
    age
    }
    }

    View full-size slide

  37. Step 4
    Advanced Features
    for Fun and Profit

    View full-size slide

  38. Step 4
    Advanced Features
    for Fun, Maintainability, and Profit

    View full-size slide

  39. Make your code more
    reusable

    View full-size slide

  40. Swift: Protocols!

    View full-size slide

  41. Kotlin: Extensions!

    View full-size slide

  42. Make your code
    safer

    View full-size slide

  43. Swift: Version handling!

    View full-size slide

  44. Kotlin: XML ids
    Become automatic variables

    View full-size slide

  45. activity_profile.xml
    android:id="@+id/textview_username"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    />
    ProfileActivity.java
    TextView mUsernameTextView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.profile_activity);
    mUsernameTextView = (TextView) findViewById(R.id.textview_username);
    mUsernameTextView.setText("Hello, User");
    }

    View full-size slide

  46. activity_profile.xml
    android:id="@+id/textview_username"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    />
    ProfileActivity.java
    @BindView(R.id.textview_username)
    TextView mUsernameTextView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.profile_activity);
    mUsernameTextView.setText("Hello, User");
    }

    View full-size slide

  47. activity_profile.xml
    android:id="@+id/textview_username"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    />
    ProfileActivity.kt
    import kotlinx.android.synthetic.main.activity_profile.*
    public override fun onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_profile)
    textview_username.text = "Hello, User"
    }

    View full-size slide

  48. activity_profile.xml
    android:id="@+id/usernameTextView"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    />
    ProfileActivity.kt
    import kotlinx.android.synthetic.main.activity_profile.*
    public override fun onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_profile)
    usernameTextView = "Hello, User"
    }

    View full-size slide

  49. Things You Should Do
    at Every Step

    View full-size slide

  50. Seriously though,
    Test

    View full-size slide

  51. What was I even
    trying
    to do here?

    View full-size slide

  52. Share strategies

    View full-size slide

  53. Share benefits

    View full-size slide

  54. Share problems

    View full-size slide

  55. Learn by reading

    View full-size slide

  56. Keep code clear

    View full-size slide

  57. Swiftiest
    let capitalizedStrings = arrayOfStrings.map { $0.capitalized }

    View full-size slide

  58. Unnecessarily One-Liniest
    let studentNames = students.reduce("") { $0 + "\n" + ($1.firstName ?? "") + " " + ($1.lastName ?? "") }

    View full-size slide

  59. ! Borderline Incomprehensible !
    array.flatMap { $0 }
    .map { $0.doSomething() }
    .flatMap { $0 }
    .filter { $0.isSomething || $0.isSomethingElse }
    .reduce("") { $0 + $1.description }

    View full-size slide

  60. "I don't understand this code"
    !=
    "I don't understand this language"

    View full-size slide

  61. Reducing confusion

    View full-size slide

  62. Listen
    to the creators

    View full-size slide

  63. Listen
    skeptically
    to the creators

    View full-size slide

  64. Watch out for
    Yaks

    View full-size slide

  65. Never let the perfect
    be the enemy of the shipping

    View full-size slide

  66. Obligatory Summary Slide
    → Honestly assess the pros and cons
    → Start small, then go bonkers once it works
    → Test the crap out of stuff in the new language
    → Share your code, your joy, and your pain
    → Resist desire to rewrite your whole app at once
    → Ship it!

    View full-size slide

  67. Links
    → Concurrency In Swift: One Approach
    https://gist.github.com/lattner/
    31ed37682ef1576b16bca1432ea9f782
    → Apple's Swift Blog
    https://developer.apple.com/swift/blog/
    → Jetbrains' Kotlin Blog
    https://blog.jetbrains.com/kotlin/

    View full-size slide

  68. Links
    → Kotlin Android Extensions
    https://kotlinlang.org/docs/tutorials/android-
    plugin.html

    View full-size slide

  69. Videos
    → Anything You Can Do, I Can Do Better
    (Kickstarter team on FP in Kotlin vs. Swift)
    https://www.youtube.com/watch?
    v=_DuGaAkQSnM
    → iOS & Android & Swift & Kotlin
    (Stuart Kent on Kotlin for iOS Devs)
    http://www.stkent.com/2017/07/27/ios-and-
    android-and-swift-and-kotlin.html

    View full-size slide

  70. Photo Credits
    → Paperwork, by Keith Williamson
    https://www.flickr.com/photos/elwillo/4729801304
    → Simpsons screenshots + memes, Frinkiac
    https://frinkiac.com/

    View full-size slide