SHIPPING A MOBILE MULTIPLATFORM PROJECT ON IOS & ANDROID

5fe8f40633300a70ea088a594cb33031?s=47 Alec Strong
December 05, 2019

SHIPPING A MOBILE MULTIPLATFORM PROJECT ON IOS & ANDROID

Given by Ben Asher (@benasher44) and Alec Strong (@strongolopolis).
Video: https://youtu.be/je8aqW48JiA

Getting a multiplatform project started within an active engineering team across multiple platforms is challenging. You have to pitch the project to your colleagues, wrestle with reconciling platform-specific technical issues, and actually ship it for each supported platform.

We have spent the last year doing just that for PlanGrid and CashApp. Come learn how to get your multiplatform project started and what kinds of problems you can expect to solve along the way. We'll approach the problems from both the Android and iOS perspectives and arm you with the tools to make multiplatform not a multiproblem.

5fe8f40633300a70ea088a594cb33031?s=128

Alec Strong

December 05, 2019
Tweet

Transcript

  1. Copenhagen Denmark SHIPPING MULTIPLATFORM ON IOS & ANDROID BEN ASHER

    ALEC STRONG @benasher44 @strongolopolis
  2. Link to another sheet Markups

  3. History

  4. Live Info

  5. Championed on iOS ~15 across Android and iOS Construction Championed

    on Android ~30 across Android and iOS Financial Services
  6. Offline Sync System Search Business Logic

  7. MPP Business Logic Models

  8. MPP Business Logic Models

  9. MPP

  10. MPP Kotlin/Native

  11. None
  12. None
  13. New tools New language Uneven ecosystem !!! !!!

  14. Adopting cross-platform in your app has a cost

  15. What is the value that makes it worth the effort?

  16. Diverging Sync Systems

  17. Existing Shared Code

  18. What tools will your cross-platform solution need What is the

    common goal all stakeholders have
  19. What else? No JNI on Android Obj-C
 Interop Not New

    For Android Community Jetbrains
  20. Common Questions Kotlin/Native relies on Obj-C? What about Swift? Obj-C

    header is well-annotated for Swift This is the status quo for Apple’s own frameworks
  21. Common Questions What about performance? Same performance expectations on Android

    Can drop into C if needed on iOS
  22. Common Questions What about value types?

  23. Common Questions There’s a garbage collector? But there’s no JVM,

    right? Bacon’s Algorithm for GC
  24. Common Questions What libraries can we use for mpp? JVM

    Distributions are incomplete Special multiplatform distributions K/N ABI instability (klib)
  25. Common Questions What is “common” code?

  26. commonMain iosMain androidMain iOS - .framework Android - .aar Library

    Outputs Library Source Sets
  27. commonMain iosMain androidMain iOS - .framework Android - .aar Library

    Outputs Library Source Sets Code expect class Lock actual class Lock actual class Lock
  28. None
  29. None
  30. expect class Lock actual class Lock actual class Lock commonMain

    iosMain androidMain iOS - .framework Android - .aar Library Outputs Library Source Sets
  31. commonMain iosMain androidMain iOS - .framework Android - .aar Library

    Outputs Library Source Sets
  32. commonMain iosMain androidMain iOS - .framework Android - .aar Library

    Outputs Library Source Sets
  33. commonMain iosMain androidMain iOS - .framework Android - .aar Library

    Outputs Library Source Sets windowsMain Windows - .dll
  34. Is it platform agnostic? commonMain Yes No Will other common

    code use it? Yes No Platform
  35. None
  36. Is it platform agnostic? No Will other common code use

    it? commonMain Yes
  37. expect class DatabaseFactory { internal fun createSqliteDriver(): SqlDriver }

  38. Is it platform agnostic? No Will other common code use

    it? No Platform
  39. sourceSets {A commonMain {B}C androidLibMain {D}E iosLibMain {F}G }H

  40. sourceSets {A commonMain {B}C androidLibMain {D dependencies {I api project(‘:protos')

    }J }E iosLibMain {F}G }H
  41. fun SearchQueries.insert(entity: SyncEntity) entity: SyncEntity

  42. sourceSets {A commonMain {B}C androidLibMain {D}E iosLibMain {F}G }H

  43. sourceSets {A commonMain {B}C androidLibMain {D}E }H

  44. Is it platform agnostic? commonMain Yes

  45. fun search(terms: String): List<Search>?

  46. commonMain iosMain androidMain Library Source Sets Host
 Application

  47. Host
 Application Talks to backend API Talks to existing application

    state
  48. Host
 Application commonMain Library Initialization Library defines interface App provides

    implementation
  49. None
  50. Highly platform-specific Backend API Hosts Current user’s credential Case Study:

    Networking
  51. Make an API request Receive an async response Interface can

    be re-implemented later MPP Life Hack: Interface Network
  52. interface Network { /** * Makes an API request */

    fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable }
  53. interface Network { /** * Makes an API request */

    fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable } host: APIHost Plain enum of names of the hosts– no URL handling required
  54. interface Network { /** * Makes an API request */

    fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable } request: Request data class: - String path and body, - query params dict - enum method (GET, POST, etc.)
  55. interface Network { /** * Makes an API request */

    fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable } completion: (Request.Response) -> Unit sealed class w/ different response types:
  56. interface Network { /** * Makes an API request */

    fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable } NetworkDisposable Interface with a cancel() method
  57. suspend fun Network.makeRequest( host: APIHost, request: Request ): Request.Response

  58. Concurrency in commonMain commonMain iosMain androidMain expect fun <T> T.freeze():

    T actual fun <T> T.freeze(): T = this actual fun <T> T.freeze(): T = freeze()
  59. None
  60. None
  61. Shipping Internally commonMain iosMain androidMain Host
 Application

  62. commonMain iosMain androidMain Host
 Application Host
 Application

  63. commonMain iosMain androidMain Host
 Application Host
 Application

  64. Pros Cons Minimal change for android Seamless local development Single

    source of truth Host dependencies Two build environments for iOS CI Nightmare
  65. commonMain iosMain androidMain Host
 Application Host
 Application commonMain

  66. Pros Cons Minimal change for android Seamless local development Host

    dependencies Two build environments for iOS CI Nightmare for iOS Out of sync code
  67. commonMain iosMain androidMain Host
 Application Host
 Application

  68. Pros Cons CI is fast Encourages unit test coverage Single

    source of truth Breaking changes are painful Need good CD setup Debugging decoupled repos
  69. commonMain iosMain Host
 Application Host
 Application androidMain

  70. commonMain androidMain Host
 Application Host
 Application

  71. Shipping Externally Test Crashing!

  72. Shipping Externally fun crash() { (null as String?)!! }

  73. Shipping Externally setUnhandledExceptionHook()

  74. Shipping Externally What happens if a Worker crashes?

  75. None
  76. None
  77. None
  78. None
  79. None
  80. Define your project’s goals Be thoughtful growing your codebase Avoid

    solving general muliplatform problems Get your CI and dev setup working early Join us in Kotlin slack Review