Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Agenda › LINE Live Commerce Assistant App › High concurrent auction chat room › New UI framework and architecture and why › SwiftUI › The Composable Architecture (TCA) › Challenge we met 2

Slide 3

Slide 3 text

LINE Live Commerce Assistant App Creating a live streaming was not that easy Hardware Camera Software Streaming Website CMS 3

Slide 4

Slide 4 text

KOREA COLOMBIA MEXICO LINE Live Commerce Assistant App Available in this year SINGAPORE THAILAND MALAYSIA THE UNITED STATES INDONESIA JAPAN TAIWAN SPAIN TURKEY 4

Slide 5

Slide 5 text

LINE LIVE Commerce Assistant App 5 An app that allows streamers to create and stream at one place

Slide 6

Slide 6 text

Why SwiftUI Is it really ready for business application? Efficiency Less Code Readability Declarative Programming Hot Reload Previews 6

Slide 7

Slide 7 text

ScrollView { LazyVStack(spacing: 0) { ForEach( viewStore.messages, id: \.id ) { message in MessageView(message: message) } ... } ... } Better Apps. Less Code 7

Slide 8

Slide 8 text

Why SwiftUI Readable code and easy to verify UI via Xcode Previews struct LiveStopwatchView_Previews: PreviewProvider { private static var startTime = Date() private static var elapsedSeconds: TimeInterval = 31_233 @ViewBuilder static func staticTimeInterval( _ elapsedSeconds: TimeInterval ) -> some View { Text("\(elapsedSeconds, format: .number) seconds") LiveStopwatchView(...) } static var previews: some View { VStack { Grid { GridRow { staticTimeInterval(10_342) } GridRow { staticTimeInterval(359_999) } GridRow { staticTimeInterval(360_000) } } } .padding(40) .previewLayout(.sizeThatFits) } } 8

Slide 9

Slide 9 text

Why The Composable Architecture (TCA) Building apps in a consistent and understandable way, with composition, testing, and ergonomics in mind Independent State SwiftUI Style Dependency Prioritized Testability 9

Slide 10

Slide 10 text

The Composable Architecture (TCA) The app is composed of state of independent features 10 Root Login Home Broadcast Settings

Slide 11

Slide 11 text

The Composable Architecture (TCA) The app is composed of state of independent features 11 Root Login Home Broadcast Settings

Slide 12

Slide 12 text

ViewStore The Composable Architecture (TCA) The flow of an independent composable feature (scope) 12 Reducer State Action StoreOf LoginView Dependencie

Slide 13

Slide 13 text

The Composable Architecture (TCA) ReducerProtocol 13 Reducer State Action StoreOf struct Login: ReducerProtocol { struct State: Equatable { var isLoading = false } enum Action { case loginButtonTapped } var body: some ReducerProtocolOf { Reduce { state, action in switch action { case .loginButtonTapped state.isLoading = true return .task { // login } } } } }

Slide 14

Slide 14 text

The Composable Architecture (TCA) Dependency Injection 14 Reducer State Action StoreOf struct Login: ReducerProtocol { @Dependency(\.authManager) var authManager enum Action { ... case loginResponse(TaskResult) } var body: some ReducerProtocolOf { Reduce { state, action in switch action { case .loginButtonTapped: state.isLoading = true return .task { .loginResponse( TaskResult(await authManager.login()) ) } case .loginResponse(let result): // handle result } } } } Dependencie

Slide 15

Slide 15 text

The Composable Architecture (TCA) Communication between View and Store 15 struct LoginView: View { let store: StoreOf @ObservedObject var viewStore: ViewStoreOf var body: some View { // ... VStack { Button("Login") { viewStore.send(.loginButtonTapped) } .disabled(viewStore.isLoading) } } } ViewStore Reducer State Action StoreOf LoginView

Slide 16

Slide 16 text

The Composable Architecture (TCA) Testing 16 func testLoginButtonTapped() async throws { let store = TestStore( initialState: Login.State(isLoading: false), reducer: Login() ) await store.send(.loginButtonTapped, assert: { $0.isLoading = true }) } struct LoginView: View { let store: StoreOf @ObservedObject var viewStore: ViewStoreOf var body: some View { // ... VStack { Button("Login") { viewStore.send(.loginButtonTapped) } .disabled(viewStore.isLoading) } } }

Slide 17

Slide 17 text

The Composable Architecture (TCA) Easy to find the non-matched changes from log 17 func testLoginButtonTapped() async throws { let store = TestStore( initialState: Login.State(isLoading: false), reducer: Login() ) await store.send(.loginButtonTapped, assert: { $0.isLoading = true }) } testLoginButtonTapped(): A state change does not match expectation: ... − Login.State(isLoading: false) + Login.State(isLoading: true) (Expected: −, Actual: +)

Slide 18

Slide 18 text

Challenge we met 18

Slide 19

Slide 19 text

Challenge We Met It’s really good enough, but we do encounter a few challenges › Content offset, if edges get reached and is scrolling › Keyboard presentation/dismissal Scrolling observation Behavior may vary according to iOS version › Extra testing effort Communication between imperative and declarative programming › UI components made for UIKit › e.g. WKWebView 19

Slide 20

Slide 20 text

Thank you 20