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

Kotlin & Swift: Convergent Evolution? (Video)

Kotlin & Swift: Convergent Evolution? (Video)

At Google I/O this year, the Android team announced first-class support for Kotlin! Kotlin is a modern and compact language that looks and feels very similar to Apple’s Swift. Both are rapidly rising in popularity. Once you’ve learned one of these two languages, the barriers to becoming a cross-platform (native) mobile developer are significantly reduced. Join me for a journey through the two languages, and leave inspired to learn one, then both, then to conquer the entire (mobile) world!

Venue: Android Summit 2017, http://androidsummit.org/

Stuart Kent

August 25, 2017
Tweet

More Decks by Stuart Kent

Other Decks in Technology

Transcript

  1. @skentphd You • 90% heard of Kotlin • 50% used

    Kotlin • 15% written Kotlin on Android • 10% written Kotlin in production
  2. @skentphd You • 90% heard of Kotlin • 50% used

    Kotlin • 15% written Kotlin on Android • 10% written Kotlin in production
  3. @skentphd Convergences • Platform-agnostic architectures: MVP, MVVM • Layout components:

    • LinearLayout <-> UIStackView • ConstraintLayout <-> AutoLayout
  4. @skentphd Convergences • Platform-agnostic architectures: MVP, MVVM • Layout components:

    • LinearLayout <-> UIStackView • ConstraintLayout <-> AutoLayout • Development languages
  5. @skentphd Claim It's never been easier for a native Android

    developer to learn native iOS development.
  6. @skentphd Calls To Action As Android developers, you should consider

    • learning Kotlin; then • learning Swift & iOS $
  7. @skentphd Kotlin & Swift • Strikingly similar principles and implementations:

    • Accessibility • Expressivity • Safety • Developed ~simultaneously but independently
  8. @skentphd Kotlin & Swift: Accessibility • Both: "vanilla" syntax •

    Both: gradual adoption via interoperability • Both: excellent documentation • Kotlin: semi-automatic migration • Swift: manual migration
  9. @skentphd Kotlin & Swift: Accessibility • Both: "vanilla" syntax •

    Both: gradual adoption via interoperability • Both: excellent documentation • Kotlin: semi-automatic migration • Swift: manual migration
  10. @skentphd Kotlin 1.1.4 // Declarations val summit = "Android Summit"

    // Declarations let summit = "Android Summit" Swift 4
  11. @skentphd Kotlin 1.1.4 // Declarations val summit = "Android Summit"

    fun greet(name: String): String { return "Hello, \{name}!" } // Declarations let summit = "Android Summit" func greet(name: String) -> String { return "Hello, \(name)!" } Swift 4
  12. @skentphd Kotlin 1.1.4 // Declarations val summit = "Android Summit"

    fun greet(name: String): String { return "Hello, \{name}!" } // Usage greet(name = summit) // "Hello, Android Summit!" // Declarations let summit = "Android Summit" func greet(name: String) -> String { return "Hello, \(name)!" } // Usage greet(name: summit) // "Hello, Android Summit!" Swift 4
  13. @skentphd Kotlin // Declarations val summit = "Android Summit" var

    spaceCount = 0 var letterCount = 0 // Usage for (char in summit) { if (char == ' ') { spaceCount += 1 } else { letterCount += 1 } } spaceCount // 1 letterCount // 13 // Declarations let summit = "Android Summit" var spaceCount = 0 var letterCount = 0 // Usage for char in summit { if char == " " { spaceCount += 1 } else { letterCount += 1 } } spaceCount // 1 letterCount // 13 Swift
  14. @skentphd Kotlin // Declarations val summit = "Android Summit" var

    spaceCount = 0 var letterCount = 0 // Usage for (char in summit) { if (char == ' ') { spaceCount += 1 } else { letterCount += 1 } } spaceCount // 1 letterCount // 13 // Declarations let summit = "Android Summit" var spaceCount = 0 var letterCount = 0 // Usage for char in summit { if char == " " { spaceCount += 1 } else { letterCount += 1 } } spaceCount // 1 letterCount // 13 Swift
  15. @skentphd Kotlin // Declarations interface Listener { fun notify() }

    // Declarations protocol Listener { func notify() } Swift
  16. @skentphd Kotlin // Declarations interface Listener { fun notify() }

    class AnyClass : Listener { override fun notify() { // Logic } } // Declarations protocol Listener { func notify() } class AnyClass : Listener { func notify() { // Logic } } Swift
  17. @skentphd Kotlin // Declarations interface Listener { fun notify() }

    class AnyClass : Listener { override fun notify() { // Logic } } // Usage val anyClass: Listener = AnyClass() anyClass.notify() // Declarations protocol Listener { func notify() } class AnyClass : Listener { func notify() { // Logic } } // Usage let anyClass: Listener = AnyClass() anyClass.notify() Swift
  18. @skentphd Kotlin & Swift: Expressivity • Both: extension of closed

    source types • Both: fluent collection operators • Both: defaults shrink API surface areas • Both: algebraic data types • Both: first class functions • Both: amplify signal; reduce noise (boilerplate--)
  19. @skentphd Kotlin & Swift: Expressivity • Both: extension of closed

    source types • Both: fluent collection operators • Both: defaults shrink API surface areas • Both: algebraic data types • Both: first class functions • Both: amplify signal; reduce noise (boilerplate--)
  20. @skentphd Kotlin & Swift: Expressivity • Both: extension of closed

    source types • Both: fluent collection operators • Both: defaults shrink API surface areas • Both: algebraic data types • Both: first class functions • Both: amplify signal; reduce noise (boilerplate--)
  21. @skentphd Extensions • Both: more discoverable utility methods • Both:

    fluent expression of domain concepts • Both: completeness of standard library • Both: minimize cross-platform differences
  22. @skentphd Extensions • Both: more discoverable utility methods • Both:

    fluent expression of domain concepts • Both: completeness of standard library • Both: minimize cross-platform differences
  23. @skentphd Java 7 // Declarations public final class PasswordUtil {

    public static boolean isValidPassword(String candidate) { // Logic } private PasswordUtil() { // No instances } }
  24. @skentphd Java 7 // Declarations public final class PasswordUtil {

    public static boolean isValidPassword(String candidate) { // Logic } private PasswordUtil() { // No instances } } // Usage if (PasswordUtil.isValidPassword("swordfish")) { // I have to *know* this exists! // Conditional logic }
  25. @skentphd Kotlin // Declarations fun String.isValidPassword():Boolean { return this ==

    "swordfish" } // Declarations extension String { func isValidPassword() -> Bool { return self == "swordfish" } } Swift
  26. @skentphd Kotlin // Declarations fun String.isValidPassword():Boolean { return this ==

    "swordfish" } // Declarations extension String { func isValidPassword() -> Bool { return self == "swordfish" } } Swift
  27. @skentphd Kotlin // Declarations fun String.isValidPassword():Boolean { return this ==

    "swordfish" } // Declarations extension String { func isValidPassword() -> Bool { return self == "swordfish" } } Swift
  28. @skentphd Kotlin // Declarations fun String.isValidPassword():Boolean { return this ==

    "swordfish" } // Usage if ("swordfish".isValidPassword()) { // Conditional logic } // Declarations extension String { func isValidPassword() -> Bool { return self == "swordfish" } } // Usage if "swordfish".isValidPassword() { // Conditional logic } Swift
  29. @skentphd Collection Operations • Both: readable -> understandable • Both:

    flexible -> maintainable • Both: robust • Java: streams available in 8+ • Android: streams available for minSdk 24+
  30. @skentphd Collection Operations • Both: readable -> understandable • Both:

    flexible -> maintainable • Both: robust • Java: streams available in 8+ • Android: streams available for minSdk 24+
  31. @skentphd Java // Declarations List<Integer> list = Arrays.asList("1", "2", "3",

    "4"); String joinedList = ""; // Usage for (int index = 0; index > list.size(); index++) { joinedList += list.get(index); if (index < list.size() - 1) { joinedList += ", "; } } joinedList // "1, 2, 3, 4"
  32. @skentphd Kotlin // Declarations val list = listOf("1", "2", "3",

    "4") // Usage list.joinToString() // "1, 2, 3, 4" // Declarations let list = ["1", "2", "3", "4"] // Usage list.joined(separator: ", ") // "1, 2, 3, 4" Swift
  33. @skentphd Kotlin // Declarations val orders: List<Order> // Usage orders

    .map { it.dateString } .filter { it < "2017-01-01" } .sorted() .reversed() .drop(10) .take(5) // Declarations let orders: = [Order] // Usage orders .map { $0.dateString } .filter { $0 < "2017-01-01" } .sorted() .reversed() .dropFirst(10) .prefix(5) Swift
  34. @skentphd Kotlin // Declarations val orders: List<Order> // Usage orders

    .map { it.dateString } .filter { it < "2017-01-01" } .sorted() .reversed() .drop(10) .take(5) // Declarations let orders: = [Order] // Usage orders .map { $0.dateString } .filter { $0 < "2017-01-01" } .sorted() .reversed() .dropFirst(10) .prefix(5) Swift
  35. @skentphd Kotlin // Declarations val orders: List<Order> // Usage orders

    .map { it.dateString } .filter { it < "2017-01-01" } .sorted() .reversed() .drop(10) .take(5) // Declarations let orders: = [Order] // Usage orders .map { $0.dateString } .filter { $0 < "2017-01-01" } .sorted() .reversed() .dropFirst(10) .prefix(5) Swift
  36. @skentphd Kotlin // Declarations val orders: List<Order> // Usage orders

    .map { it.dateString } .filter { it < "2017-01-01" } .sorted() .reversed() .drop(10) .take(5) // Declarations let orders: = [Order] // Usage orders .map { $0.dateString } .filter { $0 < "2017-01-01" } .sorted() .reversed() .dropFirst(10) .prefix(5) Swift
  37. @skentphd Kotlin // Declarations val orders: List<Order> // Usage orders

    .map { it.dateString } .filter { it < "2017-01-01" } .sorted() .reversed() .drop(10) .take(5) // Declarations let orders: = [Order] // Usage orders .map { $0.dateString } .filter { $0 < "2017-01-01" } .sorted() .reversed() .dropFirst(10) .prefix(5) Swift
  38. @skentphd Kotlin // Declarations val orders: List<Order> // Usage orders

    .map { it.dateString } .filter { it < "2017-01-01" } .sorted() .reversed() .drop(10) .take(5) // Declarations let orders: = [Order] // Usage orders .map { $0.dateString } .filter { $0 < "2017-01-01" } .sorted() .reversed() .dropFirst(10) .prefix(5) Swift
  39. @skentphd Kotlin // Declarations val orders: List<Order> // Usage orders

    .map { it.dateString } .filter { it < "2017-01-01" } .sorted() .reversed() .drop(10) .take(5) // Declarations let orders: = [Order] // Usage orders .map { $0.dateString } .filter { $0 < "2017-01-01" } .sorted() .reversed() .dropFirst(10) .prefix(5) Swift
  40. @skentphd Kotlin // Declarations val orders: List<Order> // Usage orders

    .map { it.dateString } .filter { it < "2017-01-01" } .sorted() .reversed() .drop(10) .take(5) // Declarations let orders: = [Order] // Usage orders .map { $0.dateString } .filter { $0 < "2017-01-01" } .sorted() .reversed() .dropFirst(10) .prefix(5) Swift
  41. @skentphd Default Parameter Values • Both: improve call-site clarity •

    Both: shrink & simplify utility APIs • Both: replace builders • Both: allow cleaner manual dependency injection
  42. @skentphd Default Parameter Values • Both: improve call-site clarity •

    Both: shrink & simplify utility APIs • Both: replace builders • Both: allow cleaner manual dependency injection
  43. @skentphd Kotlin fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ",

    prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String { /* Logic */ }
  44. @skentphd Kotlin fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ",

    prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String { /* Logic */ }
  45. @skentphd Kotlin fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ",

    prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String { /* Logic */ }
  46. @skentphd Kotlin fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ",

    prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String { /* Logic */ } listOf(1, 2, 3, 4).joinToString() // "1, 2, 3, 4" listOf(1, 2, 3, 4).joinToString(separator = " & ") // "1 & 2 & 3 & 4" listOf(1, 2, 3, 4).joinToString(prefix = "(", postfix = ")") // "(1, 2, 3, 4)"
  47. @skentphd Kotlin fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ",

    prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String { /* Logic */ } listOf(1, 2, 3, 4).joinToString() // "1, 2, 3, 4" listOf(1, 2, 3, 4).joinToString(separator = " & ") // "1 & 2 & 3 & 4" listOf(1, 2, 3, 4).joinToString(prefix = "(", postfix = ")") // "(1, 2, 3, 4)" Java How many methods?
  48. @skentphd Kotlin fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ",

    prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String { /* Logic */ } listOf(1, 2, 3, 4).joinToString() // "1, 2, 3, 4" listOf(1, 2, 3, 4).joinToString(separator = " & ") // "1 & 2 & 3 & 4" listOf(1, 2, 3, 4).joinToString(prefix = "(", postfix = ")") // "(1, 2, 3, 4)" Java 26 = 64 methods... plus name collisions!
  49. @skentphd Swift extension Collection where Element: CustomStringConvertible { func joinToString(

    separator: String = ", ", prefix: String = "", postfix: String = "", limit: Int = -1, truncated: String = "...", transform: ((Element) -> String)? = nil) -> String { /* Logic */ } }
  50. @skentphd Swift extension Collection where Element: CustomStringConvertible { func joinToString(

    separator: String = ", ", prefix: String = "", postfix: String = "", limit: Int = -1, truncated: String = "...", transform: ((Element) -> String)? = nil) -> String { /* Logic */ } }
  51. @skentphd Swift extension Collection where Element: CustomStringConvertible { func joinToString(

    separator: String = ", ", prefix: String = "", postfix: String = "", limit: Int = -1, truncated: String = "...", transform: ((Element) -> String)? = nil) -> String { /* Logic */ } }
  52. @skentphd Swift extension Collection where Element: CustomStringConvertible { func joinToString(

    separator: String = ", ", prefix: String = "", postfix: String = "", limit: Int = -1, truncated: String = "...", transform: ((Element) -> String)? = nil) -> String { /* Logic */ } } [1, 2, 3, 4].joinToString() // "1, 2, 3, 4" [1, 2, 3, 4].joinToString(separator: " & ") // "1 & 2 & 3 & 4" [1, 2, 3, 4].joinToString(prefix: "(", postfix: ")") // "(1, 2, 3, 4)"
  53. @skentphd Kotlin & Swift: Safety • Both: statically typed •

    Both: force developers to confront nullability • Both: force developers to confront mutability • Kotlin: immutable views of mutable collections • Kotlin: data classes with synthetic copy methods • Swift: custom value types
  54. @skentphd Kotlin & Swift: Safety • Both: statically typed •

    Both: force developers to confront nullability • Both: force developers to confront mutability • Kotlin: immutable views of mutable collections • Kotlin: data classes with synthetic copy methods • Swift: custom value types
  55. @skentphd Java // Declarations String s1; // No nullability information

    by default // Usage s1.length(); // Could evaluate to a number; could crash the entire app
  56. @skentphd Java // Declarations @NonNull String s1; // Opt-in nullability

    information @Nullable String s2; // Opt-in nullability information
  57. @skentphd Java // Declarations @NonNull String s1; // Opt-in nullability

    information @Nullable String s2; // Opt-in nullability information // Usage s1.length(); // Presumed safe; no warning s2.length(); // Possibly unsafe; warning only if (s2 != null) { s2.length(); // Now safe(ish); no warning }
  58. "The trillion dollar mistake: assuming the billion dollar mistake of

    null is its existence, and not the absence of support in the type system."
  59. @skentphd Kotlin // Declarations var s1: String = "Hello, world!"

    var s2: String? = "Hello, world!" // Declarations var s1: String = "Hello, world!" var s2: String? = "Hello, world!" Swift
  60. @skentphd Kotlin // Declarations var s1: String = "Hello, world!"

    var s2: String? = "Hello, world!" // Declarations var s1: String = "Hello, world!" var s2: String? = "Hello, world!" Swift
  61. @skentphd Kotlin // Declarations var s1: String = "Hello, world!"

    var s2: String? = "Hello, world!" // Usage s1 = null // Does not compile! s2 = null // Compiles // Declarations var s1: String = "Hello, world!" var s2: String? = "Hello, world!" // Usage s1 = nil // Does not compile! s2 = nil // Compiles Swift
  62. @skentphd Kotlin // Declarations var s1: String = "Hello, world!"

    var s2: String? = "Hello, world!" // Usage s1.first() // Compiles s2.first() // Does not compile! // Declarations var s1: String = "Hello, world!" var s2: String? = "Hello, world!" // Usage s1.first // Compiles s2.first // Does not compile! Swift
  63. @skentphd Kotlin // Declarations var s1: String = "Hello, world!"

    var s2: String? = "Hello, world!" // Usage s1.first() // Compiles if (s2 != null) { s2!!.first() // Compiles } s2?.first() // Compiles // Declarations var s1: String = "Hello, world!" var s2: String? = "Hello, world!" // Usage s1.first // Compiles if s2 != nil { s2!.first // Compiles } s2?.first // Compiles Swift
  64. @skentphd Summary • Kotlin and Swift are fundamentally similar •

    Kotlin and Swift are functionally similar • Learning and developing with both is viable* *but not the only option!
  65. @skentphd Tactics • exercism.io language tracks • Official documentation •

    nilhcem.com/swift-is-like-kotlin/ • Spy on peer PRs • Contribute to peer projects