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!)

C4861b1dfdf3bbb21faec4a1acdf183d?s=128

Ellen Shapiro

October 05, 2017
Tweet

Transcript

  1. Lessons learned from porting to Swift and Kotlin by Ellen

    Shapiro Mobile Era | Oslo, Norway | October 2017 @designatednerd | justhum.com | designatednerd.com
  2. None
  3. (this is awkward)

  4. !

  5. !

  6. !""

  7. !"#

  8. !"#

  9. !

  10. !

  11. None
  12. None
  13. ANYWAY

  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. None
  21. Android

  22. iOS

  23. iOS (excluding dependencies)

  24. None
  25. Step 1 Selling it to The Bosses

  26. Identify specific problems addressed by the new language

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

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

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

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

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

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

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

    } } Safe at compile time and run time
  35. Be honest about potential drawbacks

  36. Be honest (with yourself) about potential drawbacks

  37. None
  38. None
  39. None
  40. None
  41. Step 2 Start small

  42. Step 2 Start small

  43. Learn the new language

  44. Fight New System APIs or A New Language not both

    at once
  45. Stop writing old language patterns in a new language

  46. Objective-C NSMutableArray *capitalizedStrings = [NSMutableArray array]; for (NSString *string in

    arrayOfStrings) { NSString *thing = [string capitalized]; [capitalizedStrings addObject: thing]; }
  47. Objective-C, but written in Swift var capitalizedStrings = [String]() for

    string in arrayOfStrings { let thing = string.capitalized capitalizedStrings.append(thing) }
  48. Swiftier let capitalizedStrings = arrayOfStrings.map { string in let thing

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

    = string.capitalized return thing } Swiftiest let capitalizedStrings = arrayOfStrings.map { $0.capitalized }
  50. Limit your risk

  51. A parable of going too far, too fast

  52. None
  53. None
  54. None
  55. None
  56. None
  57. None
  58. None
  59. Be willing to pause and re-evaluate (before everything goes wrong)

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

  61. Step 3 Biggest ! For Your "

  62. Step 3 Biggest ! For Your "

  63. Look at your bugs and crashes

  64. NPE

  65. NPE + GoogleMaps

  66. Look at your most error-prone code

  67. 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; }
  68. 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; }
  69. Swift 4 JSON Parsing static func from(jsonData: Data) -> User?

    { let decoder = JSONDecoder() return try? decoder.decode(User.self, for: jsonData) }
  70. (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 } }
  71. Step 4 Advanced Features for Fun and Profit

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

  73. Make your code more reusable

  74. Swift: Protocols!

  75. Kotlin: Extensions!

  76. Make your code safer

  77. Swift: Version handling!

  78. Kotlin: XML ids Become automatic variables

  79. activity_profile.xml <TextView 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"); }
  80. activity_profile.xml <TextView 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"); }
  81. activity_profile.xml <TextView 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" }
  82. activity_profile.xml <TextView 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" }
  83. Things You Should Do at Every Step

  84. Test

  85. None
  86. Seriously though, Test

  87. What was I even trying to do here?

  88. Share

  89. Share strategies

  90. Share benefits

  91. Share problems

  92. Review

  93. Learn by reading

  94. Keep code clear

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

  96. Unnecessarily One-Liniest let studentNames = students.reduce("") { $0 + "\n"

    + ($1.firstName ?? "") + " " + ($1.lastName ?? "") }
  97. ! Borderline Incomprehensible ! array.flatMap { $0 } .map {

    $0.doSomething() } .flatMap { $0 } .filter { $0.isSomething || $0.isSomethingElse } .reduce("") { $0 + $1.description }
  98. "I don't understand this code" != "I don't understand this

    language"
  99. Reducing confusion

  100. Listen to the creators

  101. None
  102. None
  103. None
  104. None
  105. None
  106. None
  107. None
  108. Listen skeptically to the creators

  109. None
  110. None
  111. None
  112. None
  113. Watch out for Yaks

  114. None
  115. Never let the perfect be the enemy of the shipping

  116. 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!
  117. 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/
  118. Links → Kotlin Android Extensions https://kotlinlang.org/docs/tutorials/android- plugin.html

  119. 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
  120. Photo Credits → Paperwork, by Keith Williamson https://www.flickr.com/photos/elwillo/4729801304 → Simpsons

    screenshots + memes, Frinkiac https://frinkiac.com/