๐ง [Chore] WatchConnectivity ํ ์คํธ ํ๊ฒ ยท CI watchOS ๋น๋ ๊ฒ์ฆ (#1216) - #1283
Conversation
- sendMessage ๊ฐ replyHandler: nil ๋ก ๋ณด๋ด ์ฑ๊ณต ์ continuation ์ด ์ฌ๊ฐ๋์ง ์์ ํธ์ถ์๊ฐ ์๊ตฌ ์ ์งํ๋ ๋ฌธ์ ์์ โ ack ๋ฅผ ๋ฐ์ ์ฌ๊ฐํ๋ค - activate() ์๋ฃ ์ ์ธ์ ์ reachability ๋ฅผ ํ ๋ฒ ๋๊ธฐํ โ ์ด์ ์๋ ๋ณ๊ฒฝ ์ด๋ฒคํธ๊ฐ ์ค๊ธฐ ์ ๊น์ง ์ค์ ๋ก ์ฐ๊ฒฐ๋ผ ์์ด๋ isReachable ์ด false ์๋ค - ์ ์ธ๋ง ๋๊ณ ํ ๋ฒ๋ ๋์ ธ์ง์ง ์๋ sessionNotActivated ๋ฅผ ํ์ฑํ ๊ฐ๋๋ก ์ฌ์ฉ. updateApplicationContext ๋ ํ์ฑํ ์ ํธ์ถ ์ ObjC ์์ธ๋ก ์ฑ์ด ์ฃฝ๋๋ค - ํ ์คํธ์์ WCSession ์ฑ๊ธํด์ ๋์ฒดํ ์ ์๋๋ก WatchSessionProviding seam ๋์
- coreProject(includesTests:) ๋ก CoreWatchConnectivityTests ํ๊ฒ ์์ฑ - ํ์ฑํ ์ฑ๊ณต/์คํจยท๋ฏธ์ง์ ๊ธฐ๊ธฐ no-opยทreachability ๋ณ๊ฒฝ ์ํ ์ ์ด ๊ฒ์ฆ - sessionNotActivated / notReachable ์ฐจ๋จ๊ณผ ์ ์ก ๊ณ์ธต ์๋ณธ ์๋ฌ ์ ํ ๊ฒ์ฆ - ํ์ฌ ์กด์ฌํ๋ ํ์ด๋ก๋ ๊ณ์ฝ([String: Any] ๋ฌด๋ณํ ์ ๋ฌ)๋ง ๊ณ ์ . Codable ๋๋ฉ์ธ ํ์ด๋ก๋๋ ์ ํ ์ด์ ๊ฒฐ๊ณผ๋ฌผ์ด develop ์ ๋ค์ด์จ ๋ค ์ถ๊ฐํ๋ค
- make build-watch ํ๊ฒ ์ถ๊ฐ. ์์น ์๋ฎฌ๋ ์ดํฐ ๊ธฐ์ข ์ ๋จธ์ ๋ง๋ค ๋ค๋ฅด๋ฏ๋ก generic/platform=watchOS Simulator ๋ก ์ปดํ์ผ๋ง ๊ฒ์ฆํ๋ค - tuist-ci.yml ์ ๋ฐํ์ ํ์ธยท์์น ๋น๋ยทCoreWatchConnectivity ํ ์คํธ ์คํ ์ถ๊ฐ. UMCApp ์คํด test action ์๋ UMCAppTests ๋ง ์์ด ์คํด์ ๋ช ์ํด์ผ ํ๋ค - MAKEFILE_GUIDE.md ์ ์์น ๋น๋ยทํ ์คํธ ์ ๊ณผ ํ๊ฒฝ ๋ณ์ ๋ฌธ์ํ
# Conflicts: # UMCApp/Core/WatchConnectivity/Sources/WatchSessionCoordinator.swift
develop ๋ณํฉ ์ ์ฌ์์ฑํ ๋ฒ์
1.
|
| ํญ๋ชฉ | ๊ฒฐ๊ณผ |
|---|---|
make test SCHEME=CoreWatchConnectivity |
69 tests / 5 suites ํต๊ณผ |
make build-watch |
BUILD SUCCEEDED |
UMCWatchApp (watchOS Simulator) ํ
์คํธ |
91 tests / 11 suites ํต๊ณผ |
make test (iOS) |
43 tests / 8 suites ํต๊ณผ |
ํ์ ํ์ธ ๋ถํ๋๋ฆฝ๋๋ค
CI ์ make build-watch ๋ build ์ก์
์ด๋ผ UMCWatchAppTests ๋ ์ปดํ์ผ๋์ง ์์ต๋๋ค
(์ 91๊ฐ๋ ๋ก์ปฌ ์คํ ๊ฒฐ๊ณผ). ์์น ํ
์คํธ๊น์ง CI ์์ ์ก์ผ๋ ค๋ฉด build-for-testing ์ผ๋ก ๋ฐ๊พธ๊ฑฐ๋
๋ฐํ์์ด ์ค์น๋ ๋ฌ๋์์ ๊ธฐ์ข
์ ์ง์ ํด์ผ ํ๋๋ฐ, ์ด PR ๋ฒ์ ๋ฐ์ด๋ผ ๊ทธ๋๋ก ๋์ต๋๋ค.
๐ ๊ด๋ จ ์ด์
Closes #1216
โจ PR ์ ํ
choreโ ํ ์คํธ ํ๊ฒ ์ ์ค + CI ๊ฒ์ฆ ๊ฒ์ดํธ ์ถ๊ฐ (๋ถ์์ ์ผ๋ก ๋๋ฌ๋ ์์น ์ธ์ ๋ฒ๊ทธ 2๊ฑด ์์ )์์น ๊ด๋ จ ์ฝ๋๊ฐ ํ ์คํธ๋ CI ๊ฒ์ฆ๋ ์ ํ ๋ฐ์ง ์๋ ์ํ์์ต๋๋ค. ํ๊ท๋ฅผ ์ก์ ๊ทธ๋ฌผ์ ๋จผ์ ์นฉ๋๋ค.
๐ท ์คํฌ๋ฆฐ์ท or ์์(UI ๋ณ๊ฒฝ ์)
ํด๋น ์์ โ UI ๋ณ๊ฒฝ์ด ์์ต๋๋ค.
๐ ๏ธ ์์ ๋ด์ฉ
1.
CoreWatchConnectivityํ ์คํธ ํ๊ฒ ์ถ๊ฐCore/WatchConnectivity/Project.swift๋ฅผcoreProject(..., includesTests: true)๋ก ์ ํํ์ต๋๋ค.3๊ฐ ์ค์ํธ / 17๊ฐ ํ ์คํธ๊ฐ ์ถ๊ฐ๋๊ณ ์ ๋ถ ํต๊ณผํฉ๋๋ค.
WatchSessionCoordinator โ ํ์ฑํยทreachability ์ํ ์ ์ดactivate()์ ๋ธ๋ฆฌ๊ฒ์ดํธ ์ฐ๊ฒฐ ์์, ๋ฏธ์ง์ ๊ธฐ๊ธฐ no-op, ํ์ฑํ ์ฑ๊ณต/์คํจ(.notActivatedยท.inactiveยทerror ๋๋ฐ), reachability ๋ณ๊ฒฝ ๋ฐ์WatchSessionCoordinator โ ์ ์ก ๊ณ์ฝ๊ณผ ์๋ฌ ๊ฒฝ๋กsessionNotActivatedยทnotReachable์ฐจ๋จ, ์ ์ก ์ฑ๊ณต ์ ์ ์ ๋ฐํ, ์ ์ก ๊ณ์ธต ์๋ณธ ์๋ฌ ์ ํ, ์ปจํ ์คํธ ๊ฐฑ์ ์ ๋ฌ/์๋ฌ ์ ํWatchMessenger โ ๋์ ๋๋ฆฌ ํ์ด๋ก๋ ์ ๋ฌ ๊ณ์ฝ2. ํ ์คํธ๋ฅผ ์ํ ์ต์ seam โ
WatchSessionProvidingWatchSessionCoordinator๊ฐWCSession.default์ฑ๊ธํด์ ์ง์ ์ก๊ณ ์์ด์ ์ํ ์ ์ด๋ฅผ ๊ฒฐ์ ์ ์ผ๋ก์ฌํํ ๋ฐฉ๋ฒ์ด ์์์ต๋๋ค. (
WCSession์ ์ง์ ์์ฑํ ์ ์๊ณ , ํ์ด๋ง๋ ์์น๊ฐ ์์ผ๋ฉดisReachable์ด ํญ์
false๋ผ ์ ์ก ์ฑ๊ณต ๊ฒฝ๋ก๋ฅผ ์์ ๋ฐ์ ์ ์์ต๋๋ค.)์ฝ๋๋ค์ดํฐ๊ฐ ์ค์ ๋ก ์ฐ๋ ํ๋ฉด๋ง ์ถ๋ฆฐ ์์ ํ๋กํ ์ฝ์ ๋๊ณ
WCSession์ด ์ค์ํ๋๋ก ํ์ต๋๋ค.ํ๋ก๋์ ์ ๊ธฐ๋ณธ ์ธ์
WCSession.default๋ฅผ ๊ทธ๋๋ก ์ฐ๋ฏ๋ก ํธ์ถ๋ถ ๋ณ๊ฒฝ์ ์์ต๋๋ค(ํ์ฌ ์ด ๋ชจ๋์ ์ธ๋ถ ํธ์ถ๋ถ๋ 0๊ณณ).
3. ํ ์คํธ๋ฅผ ๋ถ์ด๋ฉด์ ๋๋ฌ๋ ์ค์ ๋ฒ๊ทธ 3๊ฑด ์์
sendMessage(_:)๊ฐ ์ฑ๊ณต ์ ์์ํ ๋ฐํํ์ง ์์์ต๋๋ค.replyHandler: nil๋ก ๋ณด๋ด๋ฉดWatchConnectivity ๋ ์คํจํ ๋๋ง
errorHandler๋ฅผ ํธ์ถํฉ๋๋ค. ์ฆ ์ ์ก์ด ์ฑ๊ณตํ๋ฉดwithCheckedThrowingContinuation์ด ์์ ์ฌ๊ฐ๋์ง ์์ ํธ์ถ์๊ฐ ์๊ตฌ ์ ์งํฉ๋๋ค.replyHandler๋ฅผ ๋๊ฒจ ack ๋ฅผ ๋ฐ๋๋ก ๊ณ ์ณค์ต๋๋ค. ํ๊ท ๋ฐฉ์ง ํ ์คํธ๊ฐ ๋ถ์ด ์์ต๋๋ค.activate()์ดํisReachable์ด ์ธ์ ๊ฐ์ผ๋ก ์ด๊ธฐํ๋์ง ์์์ต๋๋ค. ํ์ฑํ ์๋ฃ ์ฝ๋ฐฑ์์isActivated๋ง ์ธํ ํด, reachability ๋ณ๊ฒฝ ์ด๋ฒคํธ๊ฐ ํ ๋ฒ ์ค๊ธฐ ์ ๊น์ง ์ค์ ๋ก ์ฐ๊ฒฐ๋ผ ์์ด๋false๋ก ๋ณด์์ต๋๋ค. ํ์ฑํ ์ฑ๊ณต ์์ ์ ์ธ์ ๊ฐ์ ๋๊ธฐํํฉ๋๋ค.WatchConnectivityError.sessionNotActivated๊ฐ ์ ์ธ๋ง ๋๊ณ ํ ๋ฒ๋ ๋์ ธ์ง์ง ์๋ ์ฃฝ์ ์ผ์ด์ค์์ต๋๋ค. ํนํ
updateApplicationContext๋ ํ์ฑํ ์ ์ ํธ์ถํ๋ฉด Swift ์๋ฌ๊ฐ ์๋๋ผ ObjC ์์ธ๋ก์ฑ์ด ์ฃฝ์ต๋๋ค. ๋ ๋ฉ์๋ ์์ ํ์ฑํ ๊ฐ๋๋ฅผ ๋ฃ์ด ์ด ์ผ์ด์ค๋ฅผ ์ด๋ ธ์ต๋๋ค.
4. CI watchOS ๊ฒ์ฆ (
.github/workflows/tuist-ci.yml)๊ธฐ์กด
build-test์ก ๋ค์ 3๊ฐ ์คํ ์ ์ด์ด ๋ถ์์ต๋๋ค.Check watchOS simulator runtimeโ ๋ฐํ์ ์กด์ฌ ํ์ธ, ์์ผ๋ฉดxcodebuild -downloadPlatform watchOS์๋. ์คํจ์ํค์ง ์๊ณ ๊ฒฝ๊ณ ๋ง ๋จ๊น๋๋ค (generic ๋ชฉ์ ์ง ๋น๋๋ ๋ฐํ์ ์์ด๋ ํต๊ณผํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ณ ,
์ง์ง๋ก ๋น ์ก๋ค๋ฉด ๋ค์ ์คํ ์ด ๊นจ์ง๋ฉด์ ์ด ๋ก๊ทธ๊ฐ ์์ธ์ ๋ฐ๋ก ๊ฐ๋ฆฌํต๋๋ค).
Build watchOS appโmake build-watchTest CoreWatchConnectivityโmake test SCHEME=CoreWatchConnectivity5.
UMCApp/Makefileโmake build-watchWATCH_SCHEME ?= UMCWatchApp/WATCH_DESTINATION ?= generic/platform=watchOS Simulator์ถ๊ฐ.์์น ์๋ฎฌ๋ ์ดํฐ ๊ธฐ์ข ์ ํ๋์ฝ๋ฉํ์ง ์์์ต๋๋ค. ์ด์ ๋ณธ๋ฌธ ์์(
Apple Watch Series 10 (46mm))๋ํ์ฌ ๋ก์ปฌ ๋จธ์ ์ ์๊ณ (์ค์น๋ ๊ฑด Series 11 / Ultra 3 / SE 3), ๋ฌ๋ ์ด๋ฏธ์ง์๋ ๋ค๋ฆ ๋๋ค. ๊ธฐ์ข ์ ๋ฐ์ผ๋ฉด
ํ์ชฝ์ด ๋ฐ๋์ ๊นจ์ง๋ฏ๋ก generic ๋ชฉ์ ์ง๋ก ์ปดํ์ผ๋ง ๊ฒ์ฆํฉ๋๋ค. ๊ธฐ์ข ์ง์ ์ด ํ์ํ๋ฉด
WATCH_DESTINATION์ผ๋ก ๋ฎ์ด์ธ ์ ์๊ณ ๊ฐ์ด๋์ ์์๋ฅผ ์ ์ด๋์ต๋๋ค.6.
UMCApp/MAKEFILE_GUIDE.mdยง2 ์
make build-watchํ๊ณผ "watchOS ํ๊ฒ ๋น๋ยทํ ์คํธ" ์ , ยง4 ํ๊ฒฝ ๋ณ์ ํ์WATCH_SCHEME/WATCH_DESTINATION์ ์ถ๊ฐํ์ต๋๋ค.โ ๊ฒ์ฆ ๊ฒฐ๊ณผ
์ด์์ "์๋์ ์ปดํ์ผ ์๋ฌ ์ CI ๊ฐ ์คํจํ๋์ง" ๊ฒ์ฆ๋ ์ค์ ๋ก ๋๋ ธ์ต๋๋ค.
UMCWatchApp/Sources/ContentView.swift์ ํ์ ์ค๋ฅ ํ ์ค์ ๋ฃ๊ณmake build-watch๋ฅผ ๋๋ คerror: cannot convert value of type 'String' to specified type 'Int'โ** BUILD FAILED **๋ฅผํ์ธํ ๋ค ์๋ณตํ์ต๋๋ค. ์ด์ ์๋ ์ด ์๋ฌ๊ฐ CI ๋ฅผ ์ ํ ๊ฑด๋๋ฆฌ์ง ๋ชปํ์ต๋๋ค.
๐ ์ถํ ์งํ ์ํฉ
์ ํ ์ด์(โจ Feature: Watch ๋์์ธ ํ ํฐยท๊ณตํต ์ปดํฌ๋ํธ ๊ตฌ์ถ (Glass ์ ์ ๊ท์น)ย #1205~โจ Feature: Watch Complication 3์ข (WidgetKit accessory family)ย #1215)์ ํ์ด๋ก๋ ๊ณ์ฝ ๊ฒฐ๊ณผ๋ฌผ์ด ์์ง
develop์ ์์ต๋๋ค โ ํ์ฌCore/WatchConnectivity/Sources์๋WatchSessionCoordinator/WatchMessenger๋ ํ์ผ๋ฟ์ด๊ณCodable ๋๋ฉ์ธ ํ์ ์ด ์กด์ฌํ์ง ์์ต๋๋ค. ์ฌ๊ธฐ์ ๊ณ์ฝ์ ์๋ก ๋ฐ๋ช ํ๋ฉด ๊ทธ ์ด์๋ค๊ณผ ์ถฉ๋ํ๋ฏ๋ก,
์ง๊ธ ์กด์ฌํ๋ ๊ณ์ฝ(
[String: Any]๋์ ๋๋ฆฌ ๋ฌด๋ณํ ์ ๋ฌ)๋ง ๊ณ ์ ํด๋์ต๋๋ค.ํ์ด๋ก๋ ํ์ ์ด ๋จธ์ง๋๋ฉด
CoreWatchConnectivityTests์ ๋ผ์ด๋ํธ๋ฆฝยทํ์ํธํ ํ ์คํธ๋ฅผ ์ด์ด ๋ถ์ด๋ฉด ๋ฉ๋๋ค.WatchMessenger์ Swift 6Sendable๊ฒฝ๊ณ (stored property 'coordinator' of 'Sendable'-conforming class ... has non-Sendable type)๋ ์ด PR ์ด์ ๋ถํฐ ์๋ ๊ฒ์ด๋ผ ์๋์ง ์์์ต๋๋ค. ์ฝ๋๋ค์ดํฐ์ ๊ฒฉ๋ฆฌ๋ชจ๋ธ(
@MainActor๋ก ๋ฌถ์์ง, ๋ธ๋ฆฌ๊ฒ์ดํธ ํ๋ฅผ ๋ช ์ํ ์ง)์ ์ ํ๋ ๊ฑด ํ์ด๋ก๋ ๊ณ์ฝ ์ด์์ ๊ฐ์ด ๋ด์ผ ํฉ๋๋ค.์ค์น๋ผ ์์ด ํต๊ณผํ๊ณ , ๋ฌ๋์์ ๋น ์ ธ ์์ผ๋ฉด ์ 1๋ฒ ์คํ ๊ฒฝ๊ณ ๋ก๊ทธ๋ก ๋ฐ๋ก ๋๋ฌ๋ฉ๋๋ค. ์ด PR ์ CI ์คํ
๊ฒฐ๊ณผ์์ ํ์ธํด ์ฃผ์ธ์.
sendMessage๊ฐ ack(replyHandler)๋ฅผ ์๊ตฌํ๊ฒ ๋ ๋งํผ, ์๋ ์ฑ ๊ตฌํ ์session(_:didReceiveMessage:replyHandler:)๋ก ์๋ตํด์ผ ํฉ๋๋ค. ์๋ตํ์ง ์์ผ๋ฉด ์ ๋ฌ ์คํจ๋ก ์๋ฌ๊ฐ์ฌ๋ผ์ต๋๋ค(์ ์งํ์ง๋ ์์ต๋๋ค).
๐ ๋ฆฌ๋ทฐ ํฌ์ธํธ
sendMessage์replyHandler๋์ ์ด ๊ฐ์ฅ ์ค์ํ ํ๋จ ์ง์ ์ ๋๋ค.async throws์๊ทธ๋์ฒ๋ฅผ์ ์งํ๋ฉด์ ์๊ตฌ ์ ์ง๋ฅผ ์์ ๋ ค๋ฉด ack ๋ฅผ ๋ฐ๋ ์๋ฐ์ ์๋ค๊ณ ๋ดค์ต๋๋ค. ์์น ํ์ด๋ก๋๋ฅผ ๋จ๋ฐฉํฅ fire-and-forget
์ผ๋ก ๊ฐ ๊ณํ์ด๋ผ๋ฉด ์ฌ๊ธฐ์ ์๊ทธ๋์ฒ ์์ฒด๋ฅผ ๋ฐ๊พธ๋ ๊ฒ ๋ง์ต๋๋ค โ ์๊ฒฌ ์ฃผ์ธ์.
WatchSessionProviding์ด ๊ณผํ ์ถ์ํ์ธ์ง. ๊ตฌํ์ฒด๊ฐ ์ค์ WCSession+ ํ ์คํธ ๋์ญ 2๊ฐ๋ผ seam ์์ฒด๋์ ๋นํ๋ค๊ณ ๋ดค์ง๋ง, ๋ ธ์ถ ๋ฒ์(๋ฉ์๋ 5๊ฐ)๊ฐ ๋ ์ค์ด๋ ๋๋์ง ๋ด์ฃผ์ธ์.
sessionNotActivated)๋ฅผupdateApplicationContext์๋ ๋ฃ์ ๊ฒ โ ๊ธฐ์กด์๋ ํ์ฑํ ์ ํธ์ถ์ดObjC ์์ธ๋ก ์ฃฝ์์ผ๋ฏ๋ก ๋์ ๋ณํ์ง๋ง ๊ฐ์ ๋ฐฉํฅ์ด๋ผ๊ณ ํ๋จํ์ต๋๋ค.
AppProduct/๋ ํ ์ค๋ ๊ฑด๋๋ฆฌ์ง ์์์ต๋๋ค (์ ๋ ๊ท์น โจ Feature: LoadingView ์ ์ย #9).โ Checklist
PR์ด ๋ค์ ์๊ตฌ ์ฌํญ์ ์ถฉ์กฑํ๋์ง ํ์ธํด์ฃผ์ธ์!!!