12월, 2023의 게시물 표시

iOS SwiftUI Asynchronous UI test

이미지
사용 언어: Swift 5 사용 버전: Xcode 15.1 iOS 스위프트유아이 Asynchronous UI test를 알아보겠습니다. UI test는 Synchronization(동기식)으로 작동합니다. 그래서, 바로바로 테스트를 진행하죠. 하지만, 비동기 작업을 UI test(테스트)에서 확인할 때에는 문제가 발생합니다. 왜냐하면 test는 현재 작업 중인지 Idle(유휴) 상태인지 알 수 없기 때문에 바로 종료시키거든요. Swift에서는 해당 문제의 해결책으로 waitForExistence를 제공해 줍니다. https://developer.apple.com/documentation/xctest/xcuielement/2879412-waitforexistence 해당 XCUIElement가 존재하면 true를 반환하고, timeout(시간 초과)이 발동된다면 false가 반환됩니다. 사용법을 알아봅시다. 108 번째 줄처럼 waitForExistence를 사용해서 callButton이 다시 나타날 때까지 기다립니다. 테스트 화면입니다. Progressbar가 완료되어 Call이 다시 나타날 때까지 기다리는 것을 볼 수 있습니다. 참고 프로젝트: https://github.com/Jaehwa-Noh/Practice-Concurrency/tree/swiftui-castle-app 끝. 카테고리: iOS, SwiftUI

iOS SwiftUI Asynchronous unit test with timeout

이미지
사용 언어: Swift 5 사용 버전: Xcode 15.1 iOS 스위프트유아이 Asynchronous unit test with timeout을 알아보겠습니다. Concurrency(동시성)을 이용하여 만든 함수를 Unit(유닛) test 하려면 어떻게 해야 할까요? 또, 일정 시간 지난 뒤에 값을 확인하고 싶다면 어떻게 해야 할까요? 위의 두 문제를 해결하기 위해 Task, async, await 등의 키워드를 사용하여 테스트하는 방법을 알아봅니다. 요런 async(어씽크) 함수가 있습니다. 이 함수를 테스트하려면 이렇게 합니다. Test Case 함수에 async를 붙여줍니다. 또, try를 사용하게 된다면 throws도 같이 적어줍니다. 그리고 테스트할 함수에는 await를 적어줍니다. 위와 같이 테스트를 작성하면 해당 함수가 완전히 완료되는 경우가 확인 가능합니다. 만약 중간에 멈추고 값을 확인하고 싶다면 어떻게 해야 할까요? 예를 들어 1초마다 5를 더하는 함수가 있다면, 5초 때에는 25인지 확인하고 싶다면 위의 방법으로는 확인할 수 없습니다. 이럴 때 사용하는 것이 withThrowingTaskGroup입니다. group.addTask를 활용해서 Task Group에 Task를 추가합니다. 42번째 줄처럼 하나의 Task(테스크)는 Task.sleep을 이용해서 멈추는 시간을 지정합니다. 44번째 줄처럼 group.next를 사용하면 child task 중에 가장 먼저 반환되는 값을 기다립니다. 둘 중 하나가 종료되면 group.cancelAll로 모든 작업을 종료합니다. 그러면, 중간에 작업이 중단되기 때문에 중간 값을 확인할 수 있습니다. 참고 프로젝트: https://github.com/Jaehwa-Noh/Practice-Concurrency/tree/swiftui-castle-app 끝. 카테고리: iOS, SwiftUI

Android Compose Asynchronous UI test

이미지
사용 언어: Kotlin 1.9.0 사용 버전: Android Studio Hedgehog 2023.1.1 안드로이드 컴포즈 Asynchronous UI test를 알아보겠습니다. UI test는 Synchronization(동기식)으로 작동합니다. 그래서, 바로바로 테스트를 진행하죠. 하지만, 비동기 작업을 UI test(테스트)에서 확인할 때에는 문제가 발생합니다. 왜냐하면 test는 현재 작업 중인지 Idle(유휴) 상태인지 알 수 없기 때문에 바로 종료시키거든요. 여러 가지 방법이 있습니다. https://developer.android.com/jetpack/compose/testing#sync-auto 이번 시간에 다뤄볼 방법은 waiting for conditions입니다. waitUntil waitUntilAtLeastOneexists waitUntilDoesNotExist waitUntilExactlyOneExists waitUntilNodeCount waitUntil은 오작동을 일으킬 수 있다는 설명도 있네요. 사용법을 알아봅시다. 69번째 줄처럼 composeTestRule에다가 waitUntil을 사용하면 됩니다. 여기서는 waitUntilDoesNotExist인데 matcher에 적힌 것이 만족할 때까지 기다린다는 뜻입니다. 무한히 기다리는 것이 아니라 뒤에 적힌 timeoutMillis에 적힌 시간만큼 기다립니다. 여기서는 Pause라는 글을 가진 것이 존재하지 않을 때까지 기다리는데, 100초 동안 기다립니다. 테스트 화면입니다. Progressbar가 완료되어 Pause가 사라질 때까지 기다리는 것을 볼 수 있습니다. 참고 프로젝트: https://github.com/Jaehwa-Noh/Practice-Concurrency/tree/compose-castle-app 끝. 카테고리: Android, Compose

Android Compose LaunchedEffect doesn't work

이미지
사용 언어: Kotlin 1.9.0 사용 버전: Android Studio Hedgehog 2023.1.1 안드로이드 컴포즈 LaunchedEffect doesn't work를 알아보겠습니다. LaunchedEffect는 Coroutine을 이용하여 UI를 업데이트할 때 사용합니다.  key parameter의 값이 변화하면 계속해서 추적하는 그러한 함수입니다. 그런데, 이게 제 예상과는 다르게 업데이트가 되지 않는 겁니다. 한 번 보시죠. 보면, 진행바가 진행되는 동안 recomposition이 일어나지 않는 것을 알 수 있습니다. 분명 해당 class는 castleUiState에서 추적되고 있는데 말이죠. 해당 progressbar는 location 값으로 작동합니다. 위의 코드를 아래처럼 변경해 줍니다. 화면을 다시 그리는 recomposition을 발동시키기 위해서는 변화하는 값을 State로 만들 필요가 있습니다. 그 부분을 놓친 것이 문제였습니다. 실행해 봅시다. LuanchedEffect가 잘 작동하는 것을 확인할 수 있습니다. 참고 프로젝트 https://github.com/Jaehwa-Noh/Practice-Concurrency/tree/compose-castle-app 끝. 카테고리: Android, Compose

Android Kotlin Coroutine

이미지
사용 언어: Kotlin 1.9.21 사용 버전: Kotlin Playground 안드로이드 코틀린 Coroutine을 알아보겠습니다. Coroutine(코루틴)은 Kotlin의 자체 기능으로 asynchronous(비동기) 또는 non-blocking(논 블로킹) 프로그래밍을 위해 만들어졌습니다. 동기는 함수가 실행되고 완료되어야 다음 코드가 실행되는 일반적인 프로그래밍을 말하고, 비동기는 함수가 실행되고 완료되기 전에 다른 코드가 실행되는 것을 말합니다. Coroutine은 suspend라는 키워드를 사용하는데요. 완료될 때까지 기다린다는 뜻입니다. Coroutine을 실행하기 위해서는 CoroutineScope 안에서 동작을 해야 합니다. 그래서 Coroutine 밖에서 코루틴을 실행하기 위해서는 runBlocking을 사용합니다. https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html 해당 문서를 보면, 새로운 coroutine을 만들고 현재 thread를 block 한다고 합니다. 코루틴 안에서 호출하면 안 됩니다. 보통은 main이나 test에서 사용됩니다. coroutine을 사용하기 위해서는 kotlinx.coroutines.* 패키지가 필요합니다. import kotlinx.coroutines.* runBlocking 안은 CoroutineScope 안이지만 순서대로 실행합니다. Coroutine은 Structured concurrency를 따르기 때문에 Coroutine 안에서만 또 다른 Coroutine을 만들 수 있습니다. 이러한 구조 덕분에 프로그램에서 관리가 가능합니다. 또 다른 Coroutine을 만드는 방법은 두 가지가 있습니다. launch와 coroutineScope입니다. https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-cor...

Android Compose Has been compiled by a more recent version of the Java Runtime

이미지
사용 언어: Kotlin 1.9.21 사용 버전: Android Studio Hedgehog 2023.1.1 안드로이드 컴포즈 Has been compiled by a more recent version of the Java Runtime를 알아보겠습니다. Unit(단위) 테스트 코드를 작성한 다음, 실행해 보니 이러한 오류가 나왔습니다. com/example/mypohangapp/CategoryViewModelTest has been compiled by a more recent version of the Java Runtime (class file version 63.0), this version of the Java Runtime only recognizes class file versions up to 61.0 Java Runtime의 버전이 63.0으로 설정되어 있는데, 현재는 61.0까지 지원한다는 설명입니다. 즉, 버전이 안 맞다가 주요 내용입니다. Java Runtime 버전을 찾아보니 이렇습니다. 63.0이니 Java 19를 사용하고 있다는 말인데, 지원하는 것은 61.0인 Java 17이라는 뜻입니다. Android Studio에 설정을 봅시다. Settings - Build, Execution, Deployment - Build Tools - Gradle - Gradle JDK를 보면 현재 Runtime version이 17.0.7이 설치된 것을 알 수 있습니다. 저한테는 17.0까지 있는데, 설정을 너무 높게 해버린 거죠. 앱 용 gradle로 옵니다. 아래의 19를 17로 변경합니다. Sync Now를 누릅니다. 잘 빌드 되네요. 참고 프로젝트 https://github.com/Jaehwa-Noh/Project-My-Pohang-App/tree/compose-my-pohang-app 끝. 카테고리: Android, Compose

Android Compose Can not extract resource error

이미지
사용 언어: Kotlin 1.9.21 사용 버전: Android Studio Hedgehog 2023.1.1 안드로이드 컴포즈 Can not extract resource error를 알아보겠습니다. 앱 빌드를 하는 중 다음과 같은 오류로 빌드가 실패하였습니다. Can not extract resource from com.android.aaptcompiler.ParsedResource@1543b8e6. 이 문제는 string에 resource를 추가하면서 발생합니다. res - values - strings.xml로 갑니다. 보시면, '갯마을차차차'라는 글자에 '가 사용되었습니다. 이것이 잘못 인식되어 발생하는 문제입니다. resource에 있는 모든 '에 역슬래시 \를 추가하여 \'로 적어줍니다. 다시 빌드 하면 잘 빌드 됩니다. 참고 프로젝트 https://github.com/Jaehwa-Noh/Project-My-Pohang-App/tree/compose-my-pohang-app 끝. 카테고리: Android, Compose