Share
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
Transcript
132 slides
-
@skentphd For common topics: • Illustrate similarities by comparing code.
• Discuss differences and mitigation strategies. Recommend resources & tactics. Q&As. How? -
@skentphd • Application lifecycle • Networking • Continuous Integration •
Distribution • Soft Skills ❤ Not Topics -
@skentphd Principles • Safe • Expressive • Concise • Multi-paradigm
• Fast • Interoperable -
@skentphd Principles • Safe • Expressive • Concise • Multi-paradigm
• Fast • Interoperable -
@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 -
@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 -
@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 -
@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 -
@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 -
@skentphd Swift let stuart = User(first: "stuart", last: "kent") Kotlin
val stuart = User(first = "stuart", last = "kent") -
@skentphd Swift let stuart = User(first: "stuart", last: "kent") stuart.first
// "stuart" Kotlin val stuart = User(first = "stuart", last = "kent") stuart.first // "stuart" -
@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" -
@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" -
@skentphd Swift let eminem = User(first: "eminem") Kotlin val eminem
= User(first = "eminem") -
@skentphd Swift let eminem = User(first: "eminem") eminem.first // "eminem"
Kotlin val eminem = User(first = "eminem") eminem.first // "eminem" -
@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 -
@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" -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@skentphd Swift enum Type { case visa case masterCard case
discover } enum class Type { VISA, MASTERCARD, DISCOVER } Kotlin -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@skentphd Swift enum Method { case cash case card(String) }
sealed class Method { class Cash : Method() class Card(val num: String):Method() } Kotlin -
@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 -
@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 -
@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 -
@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 -
@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 -
@skentphd Swift struct Name { let first: String let last:
String? } data class Name( val first: String, val last: String? = null) Kotlin -
@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 -
@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 -
@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 -
@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 -
@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 -
@skentphd Swift protocol InitialsProvider { func initials() -> String }
interface InitialsProvider { fun initials(): String } Kotlin -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@skentphd Swift protocol InitialsProvider { func initials() -> String }
class User {/**/} Kotlin -
@skentphd Swift protocol InitialsProvider { func initials() -> String }
class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } Kotlin -
@skentphd Swift protocol InitialsProvider { func initials() -> String }
class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } Kotlin -
@skentphd Swift protocol InitialsProvider { func initials() -> String }
class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } Kotlin -
@skentphd Swift protocol InitialsProvider { func initials() -> String }
class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } stuart is InitialsProvider // true Kotlin -
@skentphd Swift protocol InitialsProvider { func initials() -> String }
class User {/**/} extension User: InitialsProvider { func initials() -> String {/**/} } stuart is InitialsProvider // true ⛔ Kotlin -
@skentphd Swift extension ProgressIndicatable where Self: UIViewController {/**/} extension Stack
where Element: Equatable {/**/} Kotlin -
@skentphd Swift extension ProgressIndicatable where Self: UIViewController {/**/} extension Stack
where Element: Equatable {/**/} ⛔ Kotlin -
@skentphd iOS // Mockable protocol UserView: class { // View
updating methods } // Mockable interface UserView { // View updating methods } Android -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@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 -
@skentphd 2008 iPhone 2010 iPad 2012 iPhone 5; iPad mini;
AutoLayout iOS -
@skentphd 2008 iPhone 2010 iPad 2012 iPhone 5; iPad mini;
AutoLayout 2014 iPhone 6; iPhone 6 Plus iOS -
@skentphd 2008 iPhone 2010 iPad 2012 iPhone 5; iPad mini;
AutoLayout 2014 iPhone 6; iPhone 6 Plus 2015 UIStackView iOS -
@skentphd 2009 Android 1.0 (internal) 2009 AbsoluteLayout replaced by LinearLayout,
RelativeLayout Android -
@skentphd 2009 Android 1.0 (internal) 2009 AbsoluteLayout replaced by LinearLayout,
RelativeLayout 2009 Android 1.6 (public) Android -
@skentphd 2009 Android 1.0 (internal) 2009 AbsoluteLayout replaced by LinearLayout,
RelativeLayout 2009 Android 1.6 (public) 2017 ConstraintLayout released Android -
@skentphd iOS vs Android Similarities: • Precise → adaptive •
General, performant option • Specialized, maintainable option(s) -
@skentphd AutoLayout vs ConstraintLayout Similarities: • Powerful • Design via
GUI • Based on Cassowary algorithm • Simple slick animations -
@skentphd UIStackView vs LinearLayout Similarities: • Maintainable • Orientation control
• Graceful dynamic behaviors -
@skentphd Use AutoLayout* or ConstraintLayout when #PerfMatters. Use UIStackView or
LinearLayout when #SanityMatters. -
@skentphd Tactics • Ride the wave • Read Android PRs;
ask questions -
@skentphd Tactics • Ride the wave • Read Android PRs;
ask questions • Build a small Android app -
@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.