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

MyShop Mobile App and Next Tech Stack

MyShop Mobile App and Next Tech Stack

LINE Developers Thailand

October 16, 2021
Tweet

More Decks by LINE Developers Thailand

Other Decks in Technology

Transcript

  1. Songpol Rungsawang Software Engineer, LINE Thailand MyShop MOBILE APP &

    NEXT TECH STACK Komsit Chusangthong Software Engineer, LINE Thailand
  2. What is Jetpack Compose? “Jetpack Compose is Android’s modern toolkit

    for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.” https://developer.android.com/jetpack/compose
  3. Built-in Support for Material Design, Dark Theme, Animation and more

    https://material.io/blog/jetpack-compose-beta
  4. Support Material You is In Focus Jetpack Compose Roadmap: https://developer.android.com/jetpack/androidx/compose-roadmap

    ‘In Focus’ items are being worked on soon and are likely to land in an upcoming stable release.
  5. Recomposition @Composable fun ChatItem( … lastMessage: String ) { …

    Column( … ) { … CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text(text = lastMessage, style = MaterialTheme.typography.body2) } } } Imag Tex Tex
  6. Stateful With Remember @Composable fun WelcomeContent() { Column(modifier = Modifier.padding(16.dp))

    { val (name, setName) = remember { mutableStateOf("") } … } } • Hold and modify its state internally • Help persist state by remember : from re-compositions rememberSaveable : from con fi guration changes • Caller can use without managing 
 the state themselves
  7. Stateless With State Hoisting class WelcomeViewModel : ViewModel() { private

    val _name = MutableLiveData("") val name: LiveData<String> = _nam e fun onNameChange(newName: String) { _name.value = newName } } Use ViewModel to hold state and handle event
  8. Stateless With State Hoisting @Composabl e fun WelcomeScreen(welcomeViewModel: WelcomeViewModel =

    viewModel()) { val name: String by welcomeViewModel.name.observeAsState("") WelcomeContent(name, onNameChange = { welcomeViewModel.onNameChange(it) }) } Observe value as state in composable function
  9. Stateless With State Hoisting State Hoisting : Pattern where we

    move the state to the caller by replace the internal state with two parameters value: T value to display onValueChange: (T) -> Unit event when value change @Composabl e fun WelcomeContent ( name: String, onNameChange: (String) -> Unit ) { Column(modifier = Modifier.padding(16.dp)) { Text ( text = "Welcome, $name! to Compose" , … ) OutlinedTextField( onValueChange = { onNameChange(it) } , … ) } }
  10. State Flow in class AnnouncementViewModel ( … ) : AndroidViewModel(application)

    { private val _announcement = MutableStateFlow<AnnouncementUiModel?>(null) val announcement: StateFlow<AnnouncementUiModel?> = _announcement private val _isShow = MutableStateFlow(false) val isShow: StateFlow<Boolean> = _isShow private val _isDoNotShowAgain = MutableStateFlow(false) val isDoNotShowAgain: StateFlow<Boolean> = _isDoNotShowAgain … }
  11. State Flow in @Composabl e fun SingleAnnouncementModal ( announcementViewModel: AnnouncementViewMode

    l ) { val announcement = announcementViewModel.announcement.collectAsState() val isShow: Boolean by announcementViewModel.isShow.collectAsState() val isDoNotShowAgain: Boolean by announcementViewModel.isDoNotShowAgain.collectAsState( ) SingleAnnouncementModal( isShow = isShow, announcement = announcement.value, isDoNotShowAgain = isDoNotShowAgain, onImageClicked = { announcementViewModel.onImageClicked() }, onCloseClicked = { announcementViewModel.onCloseClicked() }, onBackPressed = { announcementViewModel.onBackPressed() }, onDoNotShowAgainClicked = { announcementViewModel.onDoNotShowAgainClicked() } ) }
  12. State Flow in @Composabl e fun SingleAnnouncementModal ( isShow: Boolean,

    announcement: AnnouncementUiModel?, isDoNotShowAgain: Boolean, onImageClicked: () -> Unit, onCloseClicked: () -> Unit, onBackPressed: () -> Unit, onDoNotShowAgainClicked: () -> Unit ) { if (!isShow || announcement == null || announcement.imageUrl.isBlank()) return }
  13. State Flow in @Composabl e fun SingleAnnouncementModal ( .. .

    ) { … Dialog ( … ) { … IconButton( onClick = { … onCloseClicked() } ) { Icon( … painterResource(id = R.drawable.icon_close_24px), ) } … } }
  14. State Flow in @Composabl e fun SingleAnnouncementModal ( .. .

    ) { … Dialog ( … ) { … val painter = rememberImagePainter( … ) Image( … painter = painter, modifier = Modifier … .clickable( … ) { … onImageClicked() }, ) .. . } }
  15. State Flow in @Composabl e fun SingleAnnouncementModal ( .. .

    ) { … Dialog ( … ) { … Checkbox( … checked = isDoNotShowAgain, onCheckedChange = { onDoNotShowAgainClicked() }, ){ Text( … text = stringResource(id = R.string.ecnotification_action_dontshowagain_text), ) } … } }
  16. Event Flow out @Composabl e fun SingleAnnouncementModal ( onCloseClicked: ()

    -> Unit, ) { … Dialog ( … ) { … IconButton( onClick = { … onCloseClicked() } ) { … } … } }
  17. State Flow in @Composabl e fun SingleAnnouncementModal ( announcementViewModel: AnnouncementViewMode

    l ) { … SingleAnnouncementModal ( … onCloseClicked = { announcementViewModel.onCloseClicked() }, ) }
  18. Event Flow out class AnnouncementViewModel ( … ) : AndroidViewModel(application)

    { private val _isShow = MutableStateFlow(false) val isShow: StateFlow<Boolean> = _isShow … fun onCloseClicked() { _isShow.value = false … } … }
  19. Example ViewController protocol WelcomePageDisplayLogic: class { func displayShowError() } fi

    nal class WelcomePageViewController: BaseViewController, WelcomePageDisplayLogic { var interactor: WelcomePageBusinessLogic? var router: (NSObjectProtocol & WelcomePageRoutingLogic & WelcomePageDataPassing)? // MARK: Object lifecycle // ... init method // MARK: View lifecycle override func viewDidLoad() { super.viewDidLoad() interact?.fetch() } func displayShowError() { router?.routeToErrorPage() } }
  20. Example Interactor protocol WelcomePageBusinessLogic { func fetch() } protocol WelcomePageDataStore

    { } fi nal class WelcomePageInteractor: WelcomePageBusinessLogic, WelcomePageDataStore { var presenter: WelcomePagePresentationLogic? lazy var worker: WelcomePageWorkerable? = { return WelcomePageWorker() }() func fetch() { if worker?.fetchAPI() { presenter?.presentShowError() } } }
  21. Example Worker protocol WelcomePageWorkable { func fetchAPI() } fi nal

    class WelcomePageWorker: WelcomePageWorkable { func fetchAPI() -> Bool { return true } }
  22. Example Presenter protocol WelcomePagePresentationLogic { func presentShowError() } fi nal

    class WelcomePagePresenter: WelcomePagePresentationLogic { weak var viewController: WelcomePageDisplayLogic? func presentShowError() { viewController?.displayShowError() } }
  23. Example Router @objc protocol WelcomePageRoutingLogic { func routeToErrorPage() } protocol

    WelcomePageDataPassing { var dataStore: WelcomePageDataStore? { get } } fi nal class WelcomePageRouter: NSObject, WelcomePageRoutingLogic, WelcomePageDataPassing { weak var viewController: WelcomePageViewController? var dataStore: WelcomePageDataStore? // MARK: Routing func routeToErrorPage() { //TODO handle navigate vc } }
  24. Example fi nal class AppCoordinator: Coordinator { var childCoordinators =

    [Coordinator]() var navigationController: UINavigationController // ... init method func goToWelcomePage() { let welcomePageVC = WelcomePageViewController() welcomePageVC.coordinator = self guard var destinationDS = welcomePageVC.router?.dataStore else { return } guard let usecaseProvider = usecaseProvider else { return } passDataToWelcomePage(usecaseProvider: usecaseProvider, sourceVC: sourceVC, destination: &destinationDS) navigationController.pushViewController(welcomePageVC, animated: true) } private func passDataToWelcomePage(usecaseProvider: OAPUseCaseProvider, sourceVC: UIViewController?, destination: inout WelcomeDataStore) { destination.usecaseProvider = usecaseProvider if let sourceVC = sourceVC, let nav = sourceVC.navigationController { destination.isNavigationBarHidden = nav.isNavigationBarHidden } } }
  25. Example fi nal class AcceptConsentRouter: NSObject, AcceptConsentRoutingLogic, AcceptConsentDataPassing { weak

    var viewController: AcceptConsentViewController? var dataStore: AcceptConsentDataStore? // MARK: Routing func routeToWelcomePage() { if let coordinator = viewController?.coordinator { coordinator.goToWelcomePage() } } func routeBack(completion: (() -> Void)? = nil) { if let coordinator = viewController?.coordinator { coordinator.didFinishConsent(sourceVC: viewController, completion: completion) } } } INTERACTOR COORDINATOR VIEW CONTROLLER ROUTER WORKER DATA SOURCE VIEW PRESENTER