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

Stuart Kent

July 20, 2017
Tweet

More Decks by Stuart Kent

Other Decks in Technology

Transcript

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

    • Discuss differences and mitigation strategies. Recommend resources & tactics. Q&As. How?
  2. @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
  3. @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
  4. @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
  5. @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
  6. @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
  7. @skentphd Swift let stuart = User(first: "stuart", last: "kent") Kotlin

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

    // "stuart" Kotlin val stuart = User(first = "stuart", last = "kent") stuart.first // "stuart"
  9. @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"
  10. @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"
  11. @skentphd Swift let eminem = User(first: "eminem") eminem.first // "eminem"

    Kotlin val eminem = User(first = "eminem") eminem.first // "eminem"
  12. @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
  13. @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"
  14. @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
  15. @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
  16. @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
  17. @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
  18. @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
  19. @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
  20. @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
  21. @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
  22. @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
  23. @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
  24. @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
  25. @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
  26. @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
  27. @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
  28. @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
  29. @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
  30. @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
  31. @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
  32. @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
  33. @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
  34. @skentphd Swift enum Type { case visa case masterCard case

    discover } enum class Type { VISA, MASTERCARD, DISCOVER } Kotlin
  35. @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
  36. @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
  37. @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
  38. @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
  39. @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
  40. @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
  41. @skentphd Swift enum Method { case cash case card(String) }

    sealed class Method { class Cash : Method() class Card(val num: String):Method() } Kotlin
  42. @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
  43. @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
  44. @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
  45. @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
  46. @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
  47. @skentphd Swift struct Name { let first: String let last:

    String? } data class Name( val first: String, val last: String? = null) Kotlin
  48. @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
  49. @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
  50. @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
  51. @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
  52. @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
  53. @skentphd Swift protocol InitialsProvider { func initials() -> String }

    interface InitialsProvider { fun initials(): String } Kotlin
  54. @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
  55. @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
  56. @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
  57. @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
  58. @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
  59. @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
  60. @skentphd Swift protocol InitialsProvider { func initials() -> String }

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

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

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

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

    class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } stuart is InitialsProvider // true ⛔ Kotlin
  65. @skentphd iOS // Mockable protocol UserView: class { // View

    updating methods } // Mockable interface UserView { // View updating methods } Android
  66. @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
  67. @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
  68. @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
  69. @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
  70. @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
  71. @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
  72. @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
  73. @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
  74. @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
  75. @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
  76. @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
  77. @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
  78. @skentphd 2008 iPhone 2010 iPad 2012 iPhone 5; iPad mini;

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

    AutoLayout 2014 iPhone 6; iPhone 6 Plus 2015 UIStackView iOS
  80. @skentphd 2009 Android 1.0 (internal) 2009 AbsoluteLayout replaced by LinearLayout,

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

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

    GUI • Based on Cassowary algorithm • Simple slick animations
  83. @skentphd Tactics • Ride the wave • Read Android PRs;

    ask questions • Build a small Android app
  84. @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.