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

iOS & Android & Swift & Kotlin (Video)

iOS & Android & Swift & Kotlin (Video)

Android and iOS development tools have consistently converged over the past few years. The arrival of Kotlin as a second officially supported language for Android development is perhaps the most significant example of this to date.

Kotlin and Swift were designed independently but share many foundational principles. In this talk, I compare and contrast these exciting languages and show you just how similar their implementations are. I also demonstrate how shared architectures and layout design tool evolution have further aided cross platform parity.

If you’re a cross-platform curious iOS developer, then this talk’s for you! There’s never been a better time to widen your horizons.

Video: https://youtu.be/hylwSuqSJ_E
Venue: Motor City CocoaHeads

3a6060bc7ace07fa75791cd5dac2d46a?s=128

Stuart Kent

July 20, 2017
Tweet

Transcript

  1. @skentphd iOS & Android & Swift & Kotlin

  2. @skentphd Who? Stuart Kent stkent.com @skentphd http://bit.ly/cocoaheads-july-2017

  3. @skentphd Who? Stuart Kent stkent.com @skentphd http://bit.ly/cocoaheads-july-2017

  4. @skentphd ! What?

  5. @skentphd $ What Not?

  6. @skentphd Why?

  7. @skentphd For common topics: • Illustrate similarities by comparing code.

    • Discuss differences and mitigation strategies. Recommend resources & tactics. Q&As. How?
  8. @skentphd • Languages • Architecture • Layouts Topics

  9. @skentphd • Application lifecycle • Networking • Continuous Integration •

    Distribution • Soft Skills ❤ Not Topics
  10. @skentphd • IDEs (mostly) • Frameworks Also Not Topics

  11. @skentphd Level Check

  12. @skentphd Me Android Java Kotlin iOS Objective-C Swift

  13. @skentphd You? iOS Objective-C Swift Android Java Kotlin

  14. @skentphd Languages

  15. @skentphd Principles • Safe • Expressive • Concise • Multi-paradigm

    • Fast • Interoperable
  16. @skentphd Principles • Safe • Expressive • Concise • Multi-paradigm

    • Fast • Interoperable
  17. @skentphd Syntax & Capabilities

  18. @skentphd Swift 4 class User { let first: String let

    last: String? init( first: String, last: String? = nil) { self.first = first self.last = last } } class User { val first: String val last: String? constructor( first: String, last: String? = null) { this.first = first this.last = last } } Kotlin 1.1.3
  19. @skentphd Swift class User { let first: String let last:

    String? init( first: String, last: String? = nil) { self.first = first self.last = last } } class User { val first: String val last: String? constructor( first: String, last: String? = null) { this.first = first this.last = last } } Kotlin
  20. @skentphd Swift class User { let first: String let last:

    String? init( first: String, last: String? = nil) { self.first = first self.last = last } } class User { val first: String val last: String? constructor( first: String, last: String? = null) { this.first = first this.last = last } } Kotlin
  21. @skentphd Swift class User { let first: String let last:

    String? init( first: String, last: String? = nil) { self.first = first self.last = last } } class User { val first: String val last: String? constructor( first: String, last: String? = null) { this.first = first this.last = last } } Kotlin
  22. @skentphd Swift class User { let first: String let last:

    String? init( first: String, last: String? = nil) { self.first = first self.last = last } } class User { val first: String val last: String? constructor( first: String, last: String? = null) { this.first = first this.last = last } } Kotlin
  23. @skentphd Swift let stuart = User(first: "stuart", last: "kent") Kotlin

    val stuart = User(first = "stuart", last = "kent")
  24. @skentphd Swift let stuart = User(first: "stuart", last: "kent") stuart.first

    // "stuart" Kotlin val stuart = User(first = "stuart", last = "kent") stuart.first // "stuart"
  25. @skentphd Swift let stuart = User(first: "stuart", last: "kent") stuart.first

    // "stuart" stuart.last // "kent" Kotlin val stuart = User(first = "stuart", last = "kent") stuart.first // "stuart" stuart.last // "kent"
  26. @skentphd Swift let stuart = User(first: "stuart", last: "kent") stuart.first

    // "stuart" stuart.last // "kent" "\(stuart.first) \(stuart.last)" // "stuart Optional("kent")" Kotlin val stuart = User(first = "stuart", last = "kent") stuart.first // "stuart" stuart.last // "kent" "${stuart.first} ${stuart.last}" // "stuart kent"
  27. @skentphd Swift let eminem = User(first: "eminem") Kotlin val eminem

    = User(first = "eminem")
  28. @skentphd Swift let eminem = User(first: "eminem") eminem.first // "eminem"

    Kotlin val eminem = User(first = "eminem") eminem.first // "eminem"
  29. @skentphd Swift let eminem = User(first: "eminem") eminem.first // "eminem"

    eminem.last // nil Kotlin val eminem = User(first = "eminem") eminem.first // "eminem" eminem.last // null
  30. @skentphd Swift let eminem = User(first: "eminem") eminem.first // "eminem"

    eminem.last // nil "\(eminem.first) \(eminem.last)" // "eminem nil" Kotlin val eminem = User(first = "eminem") eminem.first // "eminem" eminem.last // null "${eminem.first} ${eminem.last}" // "eminem null"
  31. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .joined(separator: " ") } } class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .joinToString(" ") } } Kotlin
  32. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .joined(separator: " ") } } class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .joinToString(" ") } } Kotlin
  33. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .joined(separator: " ") } } class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .joinToString(" ") } } Kotlin
  34. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .joined(separator: " ") } } class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .joinToString(" ") } } Kotlin
  35. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .joined(separator: " ") } } class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .joinToString(" ") } } Kotlin
  36. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .joined(separator: " ") } } stuart.fullName() // "stuart kent" eminem.fullName() // "eminem" class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .joinToString(" ") } } stuart.fullName() // "stuart kent" eminem.fullName() // "eminem" Kotlin
  37. @skentphd Swift extension String { func cap() -> String {

    guard !isEmpty else { return "" } return String(uppercased().first!) + dropFirst() } } fun String.cap(): String { if (isEmpty()) return "" return toUpperCase().first() + drop(1) } Kotlin
  38. @skentphd Swift extension String { func cap() -> String {

    guard !isEmpty else { return "" } return String(uppercased().first!) + dropFirst() } } fun String.cap(): String { if (isEmpty()) return "" return toUpperCase().first() + drop(1) } Kotlin
  39. @skentphd Swift extension String { func cap() -> String {

    guard !isEmpty else { return "" } return String(uppercased().first!) + dropFirst() } } fun String.cap(): String { if (isEmpty()) return "" return toUpperCase().first() + drop(1) } Kotlin
  40. @skentphd Swift extension String { func cap() -> String {

    guard !isEmpty else { return "" } return String(uppercased().first!) + dropFirst() } } fun String.cap(): String { if (isEmpty()) return "" return toUpperCase().first() + drop(1) } Kotlin
  41. @skentphd Swift extension String { func cap() -> String {

    guard !isEmpty else { return "" } return String(uppercased().first!) + dropFirst() } } fun String.cap(): String { if (isEmpty()) return "" return toUpperCase().first() + drop(1) } Kotlin
  42. @skentphd Swift extension String { func cap() -> String {

    guard !isEmpty else { return "" } return String(uppercased().first!) + dropFirst() } } "stuart".cap() // "Stuart" fun String.cap(): String { if (isEmpty()) return "" return toUpperCase().first() + drop(1) } "stuart".cap() // "Stuart" Kotlin
  43. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .joined(separator: " ") } } class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .joinToString(" ") } } Kotlin
  44. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .joined(separator: " ") } } class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .joinToString(" ") } } Kotlin
  45. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .map { $0!.cap() } .joined(separator: " ") } } class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .map { it.cap() } .joinToString(" ") } } Kotlin
  46. @skentphd Swift class User { // ... func fullName() ->

    String { return [first, last] .flatMap { $0 } .map { $0!.cap() } .joined(separator: " ") } } stuart.fullName() // "Stuart Kent" eminem.fullName() // "Eminem" class User { // ... fun fullName(): String { return arrayOf(first, last) .filterNotNull() .map { it.cap() } .joinToString(" ") } } stuart.fullName() // "Stuart Kent" eminem.fullName() // "Eminem" Kotlin
  47. @skentphd Swift class User { // ... var reversedName: String

    { return fullName() .split(separator: " ") .map { String($0) } .reversed() .joined(separator: ", ") } } class User { // ... val reversedName: String get() = fullName() .split(Regex(" ")) // No explicit cast needed. .asReversed() .joinToString(", ") } Kotlin
  48. @skentphd Swift class User { // ... var reversedName: String

    { return fullName() .split(separator: " ") .map { String($0) } .reversed() .joined(separator: ", ") } } class User { // ... val reversedName: String get() = fullName() .split(Regex(" ")) // No explicit cast needed. .asReversed() .joinToString(", ") } Kotlin
  49. @skentphd Swift class User { // ... var reversedName: String

    { return fullName() .split(separator: " ") .map { String($0) } .reversed() .joined(separator: ", ") } } class User { // ... val reversedName: String get() = fullName() .split(Regex(" ")) // No explicit cast needed. .asReversed() .joinToString(", ") } Kotlin
  50. @skentphd Swift class User { // ... var reversedName: String

    { return fullName() .split(separator: " ") .map { String($0) } .reversed() .joined(separator: ", ") } } stuart.reversedName // "Kent, Stuart" eminem.reversedName // "Eminem" class User { // ... val reversedName: String get() = fullName() .split(Regex(" ")) // No explicit cast needed. .asReversed() .joinToString(", ") } stuart.reversedName // "Kent, Stuart" eminem.reversedName // "Eminem" Kotlin
  51. @skentphd Swift enum Type { case visa case masterCard case

    discover } enum class Type { VISA, MASTERCARD, DISCOVER } Kotlin
  52. @skentphd Swift enum Type { case visa case masterCard case

    discover } func getUrl(t: Type) -> String { switch t { case .visa: return "/visa" case .masterCard: return "/mc" case .discover: return "/disc" // No default case required! } } enum class Type { VISA, MASTERCARD, DISCOVER } fun getUrl(t: Type): String { return when (t) { VISA -> "/visa" MASTERCARD -> "/mc" DISCOVER -> "/disc" // No default case required! } } Kotlin
  53. @skentphd Swift enum Type { case visa case masterCard case

    discover } func getUrl(t: Type) -> String { switch t { case .visa: return "/visa" case .masterCard: return "/mc" case .discover: return "/disc" // No default case required! } } enum class Type { VISA, MASTERCARD, DISCOVER } fun getUrl(t: Type): String { return when (t) { VISA -> "/visa" MASTERCARD -> "/mc" DISCOVER -> "/disc" // No default case required! } } Kotlin
  54. @skentphd Swift enum Type { case visa case masterCard case

    discover } func getUrl(t: Type) -> String { switch t { case .visa: return "/visa" case .masterCard: return "/mc" case .discover: return "/disc" // No default case required! } } enum class Type { VISA, MASTERCARD, DISCOVER } fun getUrl(t: Type): String { return when (t) { VISA -> "/visa" MASTERCARD -> "/mc" DISCOVER -> "/disc" // No default case required! } } Kotlin
  55. @skentphd Swift enum Type { case visa case masterCard case

    discover } func getUrl(t: Type) -> String { switch t { case .visa: return "/visa" case .masterCard: return "/mc" case .discover: return "/disc" // No default case required! } } enum class Type { VISA, MASTERCARD, DISCOVER } fun getUrl(t: Type): String { return when (t) { VISA -> "/visa" MASTERCARD -> "/mc" DISCOVER -> "/disc" // No default case required! } } Kotlin
  56. @skentphd Swift enum Type { case visa case masterCard case

    discover } func getUrl(t: Type) -> String { switch t { case .visa: return "/visa" case .masterCard: return "/mc" case .discover: return "/disc" // No default case required! } } enum class Type { VISA, MASTERCARD, DISCOVER } fun getUrl(t: Type): String { return when (t) { VISA -> "/visa" MASTERCARD -> "/mc" DISCOVER -> "/disc" // No default case required! } } Kotlin
  57. @skentphd Swift enum Type { case visa case masterCard case

    discover } func getUrl(t: Type) -> String { switch t { case .visa: return "/visa" case .masterCard: return "/mc" case .discover: return "/disc" // No default case required! } } getUrl(t: .discover) // "/disc" enum class Type { VISA, MASTERCARD, DISCOVER } fun getUrl(t: Type): String { return when (t) { VISA -> "/visa" MASTERCARD -> "/mc" DISCOVER -> "/disc" // No default case required! } } getUrl(DISCOVER) // "/disc" Kotlin
  58. @skentphd Swift enum Method { case cash case card(String) }

    sealed class Method { class Cash : Method() class Card(val num: String):Method() } Kotlin
  59. @skentphd Swift enum Method { case cash case card(String) }

    func getUrl(m: Method) -> String { switch method { case .cash: return "/cash" case .card(let num): return "/card/\(num)" // No default case required! } } sealed class Method { class Cash : Method() class Card(val num: String):Method() } fun getUrl(m: Method): String { return when (method) { is Cash -> "/cash" is Card -> "/card/${method.num}" // No default case required! } } Kotlin
  60. @skentphd Swift enum Method { case cash case card(String) }

    func getUrl(m: Method) -> String { switch method { case .cash: return "/cash" case .card(let num): return "/card/\(num)" // No default case required! } } sealed class Method { class Cash : Method() class Card(val num: String):Method() } fun getUrl(m: Method): String { return when (method) { is Cash -> "/cash" is Card -> "/card/${method.num}" // No default case required! } } Kotlin
  61. @skentphd Swift enum Method { case cash case card(String) }

    func getUrl(m: Method) -> String { switch method { case .cash: return "/cash" case .card(let num): return "/card/\(num)" // No default case required! } } sealed class Method { class Cash : Method() class Card(val num: String):Method() } fun getUrl(m: Method): String { return when (method) { is Cash -> "/cash" is Card -> "/card/${method.num}" // No default case required! } } Kotlin
  62. @skentphd Swift enum Method { case cash case card(String) }

    func getUrl(m: Method) -> String { switch method { case .cash: return "/cash" case .card(let num): return "/card/\(num)" // No default case required! } } sealed class Method { class Cash : Method() class Card(val num: String):Method() } fun getUrl(m: Method): String { return when (method) { is Cash -> "/cash" is Card -> "/card/${method.num}" // No default case required! } } Kotlin
  63. @skentphd Swift enum Method { case cash case card(String) }

    func getUrl(m: Method) -> String { switch method { case .cash: return "/cash" case .card(let num): return "/card/\(num)" // No default case required! } } // "/card/1234" getUrl(m: .card("1234")) sealed class Method { class Cash : Method() class Card(val num: String):Method() } fun getUrl(m: Method): String { return when (method) { is Cash -> "/cash" is Card -> "/card/${method.num}" // No default case required! } } // "/card/1234" getUrl(Card("1234")) Kotlin
  64. @skentphd So far, soooo good...

  65. @skentphd Swift struct Name { let first: String let last:

    String? } data class Name( val first: String, val last: String? = null) Kotlin
  66. @skentphd Swift struct Name { let first: String let last:

    String? } let stuartName = Name(first: "Stuart", last: "Kent") data class Name( val first: String, val last: String? = null) val stuartName = Name(first="Stuart", last="Kent") Kotlin
  67. @skentphd Swift struct Name { let first: String let last:

    String? } let stuartName = Name(first: "Stuart", last: "Kent") let eminemName = Name(first: "Eminem", last: null) data class Name( val first: String, val last: String? = null) val stuartName = Name(first="Stuart", last="Kent") val eminemName = Name(first="Eminem") Kotlin
  68. @skentphd Swift // Value type struct Name { let first:

    String let last: String? } let stuartName = Name(first: "Stuart", last: "Kent") let eminemName = Name(first: "Eminem", last: null) // Reference type data class Name( val first: String, val last: String? = null) val stuartName = Name(first="Stuart", last="Kent") val eminemName = Name(first="Eminem") Kotlin
  69. @skentphd Swift // Value type struct Name { let first:

    String let last: String? } let stuartName = Name(first: "Stuart", last: "Kent") let eminemName = Name(first: "Eminem", last: null) // Reference type data class Name( val first: String, val last: String? = null) val stuartName = Name(first="Stuart", last="Kent") val eminemName = Name(first="Eminem") val littleStuartName = stuartName.copy(last = "Little") Kotlin
  70. @skentphd Swift // Value type struct Name { let first:

    String let last: String? } let stuartName = Name(first: "Stuart", last: "Kent") let eminemName = Name(first: "Eminem", last: null) // Reference type data class Name( val first: String, val last: String? = null) val stuartName = Name(first="Stuart", last="Kent") val eminemName = Name(first="Eminem") val littleStuartName = stuartName.copy(last = "Little") Kotlin
  71. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    interface InitialsProvider { fun initials(): String } Kotlin
  72. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User: InitialsProvider { // ... func initials() -> String { return fullName() .split(separator: " ") .map { String($0.first!) } .joined(separator: "") } } interface InitialsProvider { fun initials(): String } class User : InitialsProvider { // ... override fun initials(): String { return fullName() .split(Regex(" ")) .map { it.first() } .joinToString("") } } Kotlin
  73. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User: InitialsProvider { // ... func initials() -> String { return fullName() .split(separator: " ") .map { String($0.first!) } .joined(separator: "") } } interface InitialsProvider { fun initials(): String } class User : InitialsProvider { // ... override fun initials(): String { return fullName() .split(Regex(" ")) .map { it.first() } .joinToString("") } } Kotlin
  74. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User: InitialsProvider { // ... func initials() -> String { return fullName() .split(separator: " ") .map { String($0.first!) } .joined(separator: "") } } interface InitialsProvider { fun initials(): String } class User : InitialsProvider { // ... override fun initials(): String { return fullName() .split(Regex(" ")) .map { it.first() } .joinToString("") } } Kotlin
  75. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User: InitialsProvider { // ... func initials() -> String { return fullName() .split(separator: " ") .map { String($0.first!) } .joined(separator: "") } } interface InitialsProvider { fun initials(): String } class User : InitialsProvider { // ... override fun initials(): String { return fullName() .split(Regex(" ")) .map { it.first() } .joinToString("") } } Kotlin
  76. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User: InitialsProvider { // ... func initials() -> String { return fullName() .split(separator: " ") .map { String($0.first!) } .joined(separator: "") } } interface InitialsProvider { fun initials(): String } class User : InitialsProvider { // ... override fun initials(): String { return fullName() .split(Regex(" ")) .map { it.first() } .joinToString("") } } Kotlin
  77. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User: InitialsProvider { // ... func initials() -> String { return fullName() .split(separator: " ") .map { String($0.first!) } .joined(separator: "") } } stuart.initials() // "SK" eminem.initials() // "E" interface InitialsProvider { fun initials(): String } class User : InitialsProvider { // ... override fun initials(): String { return fullName() .split(Regex(" ")) .map { it.first() } .joinToString("") } } stuart.initials() // "SK" eminem.initials() // "E" Kotlin
  78. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    Kotlin
  79. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User {/**/} Kotlin
  80. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } Kotlin
  81. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } Kotlin
  82. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } Kotlin
  83. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } stuart is InitialsProvider // true Kotlin
  84. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } stuart is InitialsProvider // true ⛔ Kotlin
  85. @skentphd Swift extension ProgressIndicatable where Self: UIViewController {/**/} Kotlin

  86. @skentphd Swift extension ProgressIndicatable where Self: UIViewController {/**/} extension Stack

    where Element: Equatable {/**/} Kotlin
  87. @skentphd Swift extension ProgressIndicatable where Self: UIViewController {/**/} extension Stack

    where Element: Equatable {/**/} ⛔ Kotlin
  88. @skentphd Architecture

  89. @skentphd Shared Architectures • ⛔ Protocol-oriented • ✅ Platform-agnostic: MVP,

    MVVM, etc.
  90. @skentphd iOS // Mockable protocol UserView: class { // View

    updating methods } // Mockable interface UserView { // View updating methods } Android
  91. @skentphd iOS // Mockable protocol UserView: class { // View

    updating methods } // (Unit) Testable class UserPresenter { weak var view: UserView init(_ view: UserView) {/**/} func start() {} func stop() {} // Other view update callbacks } // Mockable interface UserView { // View updating methods } // (Unit) Testable class UserPresenter { var view: UserView constructor(view: UserView) {/**/} fun start() {} fun stop() {} // Other view update callbacks } Android
  92. @skentphd iOS // Mockable protocol UserView: class { // View

    updating methods } // (Unit) Testable class UserPresenter { weak var view: UserView init(_ view: UserView) {/**/} func start() {} func stop() {} // Other view update callbacks } // Mockable interface UserView { // View updating methods } // (Unit) Testable class UserPresenter { var view: UserView constructor(view: UserView) {/**/} fun start() {} fun stop() {} // Other view update callbacks } Android
  93. @skentphd iOS // Mockable protocol UserView: class { // View

    updating methods } // (Unit) Testable class UserPresenter { weak var view: UserView init(_ view: UserView) {/**/} func start() {} func stop() {} // Other view update callbacks } // Mockable interface UserView { // View updating methods } // (Unit) Testable class UserPresenter { var view: UserView constructor(view: UserView) {/**/} fun start() {} fun stop() {} // Other view update callbacks } Android
  94. @skentphd iOS // Mockable protocol UserView: class { // View

    updating methods } // (Unit) Testable class UserPresenter { weak var view: UserView init(_ view: UserView) {/**/} func start() {} func stop() {} // Other view update callbacks } // Mockable interface UserView { // View updating methods } // (Unit) Testable class UserPresenter { var view: UserView constructor(view: UserView) {/**/} fun start() {} fun stop() {} // Other view update callbacks } Android
  95. @skentphd iOS // Mockable protocol UserView: class { // View

    updating methods } // (Unit) Testable class UserPresenter { weak var view: UserView init(_ view: UserView) {/**/} func start() {} func stop() {} // Other view update callbacks } // Mockable interface UserView { // View updating methods } // (Unit) Testable class UserPresenter { var view: UserView constructor(view: UserView) {/**/} fun start() {} fun stop() {} // Other view update callbacks } Android
  96. @skentphd iOS class UserVC: UIViewController, UserView { var userPresenter: UserPresenter?

    override func viewDidLoad() { super.viewDidLoad() userPresenter = UserPresenter(self) } override func viewWillAppear(/**/) { super.viewWillAppear(/**/) userPresenter?.start() } override func viewWillDisappear(/**/) { userPresenter?.stop() super.viewWillDisappear(/**/) } } class UserActivity: Activity(), UserView { var userPresenter: UserPresenter? = null override fun onCreate(/**/) { super.onCreate(/**/) userPresenter = UserPresenter(this) } override fun onStart() { super.onStart() userPresenter?.start() } override fun onStop() { userPresenter?.stop() super.onStop() } } Android
  97. @skentphd iOS class UserVC: UIViewController, UserView { var userPresenter: UserPresenter?

    override func viewDidLoad() { super.viewDidLoad() userPresenter = UserPresenter(self) } override func viewWillAppear(/**/) { super.viewWillAppear(/**/) userPresenter?.start() } override func viewWillDisappear(/**/) { userPresenter?.stop() super.viewWillDisappear(/**/) } } class UserActivity: Activity(), UserView { var userPresenter: UserPresenter? = null override fun onCreate(/**/) { super.onCreate(/**/) userPresenter = UserPresenter(this) } override fun onStart() { super.onStart() userPresenter?.start() } override fun onStop() { userPresenter?.stop() super.onStop() } } Android
  98. @skentphd iOS class UserVC: UIViewController, UserView { var userPresenter: UserPresenter?

    override func viewDidLoad() { super.viewDidLoad() userPresenter = UserPresenter(self) } override func viewWillAppear(/**/) { super.viewWillAppear(/**/) userPresenter?.start() } override func viewWillDisappear(/**/) { userPresenter?.stop() super.viewWillDisappear(/**/) } } class UserActivity: Activity(), UserView { var userPresenter: UserPresenter? = null override fun onCreate(/**/) { super.onCreate(/**/) userPresenter = UserPresenter(this) } override fun onStart() { super.onStart() userPresenter?.start() } override fun onStop() { userPresenter?.stop() super.onStop() } } Android
  99. @skentphd iOS class UserVC: UIViewController, UserView { var userPresenter: UserPresenter?

    override func viewDidLoad() { super.viewDidLoad() userPresenter = UserPresenter(self) } override func viewWillAppear(/**/) { super.viewWillAppear(/**/) userPresenter?.start() } override func viewWillDisappear(/**/) { userPresenter?.stop() super.viewWillDisappear(/**/) } } class UserActivity: Activity(), UserView { var userPresenter: UserPresenter? = null override fun onCreate(/**/) { super.onCreate(/**/) userPresenter = UserPresenter(this) } override fun onStart() { super.onStart() userPresenter?.start() } override fun onStop() { userPresenter?.stop() super.onStop() } } Android
  100. @skentphd iOS class UserVC: UIViewController, UserView { var userPresenter: UserPresenter?

    override func viewDidLoad() { super.viewDidLoad() userPresenter = UserPresenter(self) } override func viewWillAppear(/**/) { super.viewWillAppear(/**/) userPresenter?.start() } override func viewWillDisappear(/**/) { userPresenter?.stop() super.viewWillDisappear(/**/) } } class UserActivity: Activity(), UserView { var userPresenter: UserPresenter? = null override fun onCreate(/**/) { super.onCreate(/**/) userPresenter = UserPresenter(this) } override fun onStart() { super.onStart() userPresenter?.start() } override fun onStop() { userPresenter?.stop() super.onStop() } } Android
  101. @skentphd iOS class UserVC: UIViewController, UserView { var userPresenter: UserPresenter?

    override func viewDidLoad() { super.viewDidLoad() userPresenter = UserPresenter(self) } override func viewWillAppear(/**/) { super.viewWillAppear(/**/) userPresenter?.start() } override func viewWillDisappear(/**/) { userPresenter?.stop() super.viewWillDisappear(/**/) } } class UserActivity: Activity(), UserView { var userPresenter: UserPresenter? = null override fun onCreate(/**/) { super.onCreate(/**/) userPresenter = UserPresenter(this) } override fun onStart() { super.onStart() userPresenter?.start() } override fun onStop() { userPresenter?.stop() super.onStop() } } Android
  102. @skentphd iOS class UserVC: UIViewController, UserView { var userPresenter: UserPresenter?

    override func viewDidLoad() { super.viewDidLoad() userPresenter = UserPresenter(self) } override func viewWillAppear(/**/) { super.viewWillAppear(/**/) userPresenter?.start() } override func viewWillDisappear(/**/) { userPresenter?.stop() super.viewWillDisappear(/**/) } } class UserActivity: Activity(), UserView { var userPresenter: UserPresenter? = null override fun onCreate(/**/) { super.onCreate(/**/) userPresenter = UserPresenter(this) } override fun onStart() { super.onStart() userPresenter?.start() } override fun onStop() { userPresenter?.stop() super.onStop() } } Android
  103. @skentphd Layouts

  104. @skentphd 2008 iPhone iOS

  105. @skentphd 2008 iPhone 2010 iPad iOS

  106. @skentphd 2008 iPhone 2010 iPad 2012 iPhone 5; iPad mini;

    AutoLayout iOS
  107. @skentphd 2008 iPhone 2010 iPad 2012 iPhone 5; iPad mini;

    AutoLayout 2014 iPhone 6; iPhone 6 Plus iOS
  108. @skentphd 2008 iPhone 2010 iPad 2012 iPhone 5; iPad mini;

    AutoLayout 2014 iPhone 6; iPhone 6 Plus 2015 UIStackView iOS
  109. @skentphd 2009 Android 1.0 (internal) Android

  110. @skentphd 2009 Android 1.0 (internal) 2009 AbsoluteLayout replaced by LinearLayout,

    RelativeLayout Android
  111. @skentphd 2009 Android 1.0 (internal) 2009 AbsoluteLayout replaced by LinearLayout,

    RelativeLayout 2009 Android 1.6 (public) Android
  112. @skentphd 2009 Android 1.0 (internal) 2009 AbsoluteLayout replaced by LinearLayout,

    RelativeLayout 2009 Android 1.6 (public) 2017 ConstraintLayout released Android
  113. @skentphd iOS vs Android Similarities: • Precise → adaptive •

    General, performant option • Specialized, maintainable option(s)
  114. @skentphd AutoLayout vs ConstraintLayout Similarities: • Powerful • Design via

    GUI • Based on Cassowary algorithm • Simple slick animations
  115. @skentphd AutoLayout vs ConstraintLayout Differences: • Default behaviors • Fallback

    handling
  116. @skentphd UIStackView vs LinearLayout Similarities: • Maintainable • Orientation control

    • Graceful dynamic behaviors
  117. @skentphd UIStackView vs LinearLayout Differences: • Weighting

  118. @skentphd Use AutoLayout* or ConstraintLayout when #PerfMatters. Use UIStackView or

    LinearLayout when #SanityMatters.
  119. @skentphd

  120. @skentphd

  121. @skentphd

  122. @skentphd

  123. @skentphd Resources & Tactics

  124. @skentphd Resources • http://nilhcem.com/swift-is-like-kotlin/

  125. @skentphd Resources • http://nilhcem.com/swift-is-like-kotlin/ • https://kotlinlang.org/docs/tutorials/getting-started.html

  126. @skentphd Resources • http://nilhcem.com/swift-is-like-kotlin/ • https://kotlinlang.org/docs/tutorials/getting-started.html • https://kotlinlang.org/docs/tutorials/koans.html

  127. @skentphd Resources • http://nilhcem.com/swift-is-like-kotlin/ • https://kotlinlang.org/docs/tutorials/getting-started.html • https://kotlinlang.org/docs/tutorials/koans.html • https://kotlinlang.org/docs/tutorials/kotlin-android.html

  128. @skentphd Tactics • Ride the wave

  129. @skentphd Tactics • Ride the wave • Read Android PRs;

    ask questions
  130. @skentphd Tactics • Ride the wave • Read Android PRs;

    ask questions • Build a small Android app
  131. @skentphd Tactics • Ride the wave • Read Android PRs;

    ask questions • Build a small Android app • Use IntelliJ's auto-conversion to turn Java/ Android code samples into Kotlin.
  132. @skentphd Q&A