Skip to content

๐Ÿ”ง [Chore] WatchConnectivity ํ…Œ์ŠคํŠธ ํƒ€๊ฒŸ ยท CI watchOS ๋นŒ๋“œ ๊ฒ€์ฆ (#1216) - #1283

Merged
JEONG-J merged 5 commits into
developfrom
chore/1216
Aug 30, 2026
Merged

๐Ÿ”ง [Chore] WatchConnectivity ํ…Œ์ŠคํŠธ ํƒ€๊ฒŸ ยท CI watchOS ๋นŒ๋“œ ๊ฒ€์ฆ (#1216)#1283
JEONG-J merged 5 commits into
developfrom
chore/1216

Conversation

@JEONG-J

@JEONG-J JEONG-J commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

๐Ÿ”— ๊ด€๋ จ ์ด์Šˆ

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 โ€” WatchSessionProviding

WatchSessionCoordinator ๊ฐ€ WCSession.default ์‹ฑ๊ธ€ํ„ด์„ ์ง์ ‘ ์žก๊ณ  ์žˆ์–ด์„œ ์ƒํƒœ ์ „์ด๋ฅผ ๊ฒฐ์ •์ ์œผ๋กœ
์žฌํ˜„ํ•  ๋ฐฉ๋ฒ•์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.
(WCSession ์€ ์ง์ ‘ ์ƒ์„ฑํ•  ์ˆ˜ ์—†๊ณ , ํŽ˜์–ด๋ง๋œ ์›Œ์น˜๊ฐ€ ์—†์œผ๋ฉด isReachable
์ด ํ•ญ์ƒ false ๋ผ ์ „์†ก ์„ฑ๊ณต ๊ฒฝ๋กœ๋ฅผ ์•„์˜ˆ ๋ฐŸ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.)

์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ ์‹ค์ œ๋กœ ์“ฐ๋Š” ํ‘œ๋ฉด๋งŒ ์ถ”๋ฆฐ ์–‡์€ ํ”„๋กœํ† ์ฝœ์„ ๋‘๊ณ  WCSession ์ด ์ค€์ˆ˜ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ”„๋กœ๋•์…˜์€ ๊ธฐ๋ณธ ์ธ์ž WCSession.default ๋ฅผ ๊ทธ๋Œ€๋กœ ์“ฐ๋ฏ€๋กœ ํ˜ธ์ถœ๋ถ€ ๋ณ€๊ฒฝ์€ ์—†์Šต๋‹ˆ๋‹ค
(ํ˜„์žฌ ์ด ๋ชจ๋“ˆ์˜ ์™ธ๋ถ€ ํ˜ธ์ถœ๋ถ€๋Š” 0๊ณณ).

ํ”„๋กœํ† ์ฝœ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ WCSession ์›๋ณธ๊ณผ ๋‹ค๋ฅด๊ฒŒ(startActivationยทsendยทapply) ๋‘” ์ด์œ ๋Š” ์ฝ”๋“œ
์ฃผ์„์— ์ ์–ด๋’€์Šต๋‹ˆ๋‹ค โ€” ๋™์ผ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋กœ ์„ ์–ธํ•˜๋ฉด ์ค€์ˆ˜ ์ต์Šคํ…์…˜์ด ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๊ณ ,
SDK ๋ธ”๋ก ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ค€์ˆ˜๊ฐ€ ์กฐ์šฉํžˆ ๊นจ์ง‘๋‹ˆ๋‹ค. ์ „๋‹ฌ(forwarding)๋งŒ ํ•˜๋ฉด ๊ทธ ์œ„ํ—˜์ด ์—†์Šต๋‹ˆ๋‹ค.

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๊ฐœ ์Šคํ…์„ ์ด์–ด ๋ถ™์˜€์Šต๋‹ˆ๋‹ค.

  1. Check watchOS simulator runtime โ€” ๋Ÿฐํƒ€์ž„ ์กด์žฌ ํ™•์ธ, ์—†์œผ๋ฉด xcodebuild -downloadPlatform watchOS
    ์‹œ๋„. ์‹คํŒจ์‹œํ‚ค์ง€ ์•Š๊ณ  ๊ฒฝ๊ณ ๋งŒ ๋‚จ๊น๋‹ˆ๋‹ค (generic ๋ชฉ์ ์ง€ ๋นŒ๋“œ๋Š” ๋Ÿฐํƒ€์ž„ ์—†์ด๋„ ํ†ต๊ณผํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ณ ,
    ์ง„์งœ๋กœ ๋น ์กŒ๋‹ค๋ฉด ๋‹ค์Œ ์Šคํ…์ด ๊นจ์ง€๋ฉด์„œ ์ด ๋กœ๊ทธ๊ฐ€ ์›์ธ์„ ๋ฐ”๋กœ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค).
  2. Build watchOS app โ€” make build-watch
  3. Test CoreWatchConnectivity โ€” make test SCHEME=CoreWatchConnectivity

๋ณ„๋„ ์žก์œผ๋กœ ๋นผ์ง€ ์•Š์€ ์ด์œ : ์‹œํฌ๋ฆฟ ๋ณต์› ยท mise/Tuist ์„ค์น˜ ยท SPM ์บ์‹œ ยท tuist generate ๋ฅผ
ํ†ต์งธ๋กœ ํ•œ ๋ฒˆ ๋” ๋Œ๋ ค์•ผ ํ•ด์„œ ๋Ÿฌ๋„ˆ ์‹œ๊ฐ„์ด ๋‘ ๋ฐฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ์žก์— ์ด์–ด ๋ถ™์—ฌ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์Šคํ‚ด์„ ๋ช…์‹œํ•ด ๋”ฐ๋กœ ํ…Œ์ŠคํŠธํ•˜๋Š” ์ด์œ : UMCApp ์Šคํ‚ด์˜ test action ์—๋Š” UMCAppTests ๋งŒ ๋“ค์–ด ์žˆ์–ด
๊ธฐ์กด make test ๋กœ๋Š” Core ๋ชจ๋“ˆ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•œ ๊ฐœ๋„ ๋Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

5. UMCApp/Makefile โ€” make build-watch

WATCH_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 ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

โœ… ๊ฒ€์ฆ ๊ฒฐ๊ณผ

make test SCHEME=CoreWatchConnectivity
  โ†’ โœ” Test run with 17 tests in 3 suites passed. ** TEST SUCCEEDED **

make build-watch
  โ†’ ** BUILD SUCCEEDED **

์ด์Šˆ์˜ "์˜๋„์  ์ปดํŒŒ์ผ ์—๋Ÿฌ ์‹œ CI ๊ฐ€ ์‹คํŒจํ•˜๋Š”์ง€" ๊ฒ€์ฆ๋„ ์‹ค์ œ๋กœ ๋Œ๋ ธ์Šต๋‹ˆ๋‹ค.
UMCWatchApp/Sources/ContentView.swift ์— ํƒ€์ž… ์˜ค๋ฅ˜ ํ•œ ์ค„์„ ๋„ฃ๊ณ  make build-watch ๋ฅผ ๋Œ๋ ค
error: cannot convert value of type 'String' to specified type 'Int' โ†’ ** BUILD FAILED ** ๋ฅผ
ํ™•์ธํ•œ ๋’ค ์›๋ณตํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ „์—๋Š” ์ด ์—๋Ÿฌ๊ฐ€ CI ๋ฅผ ์ „ํ˜€ ๊ฑด๋“œ๋ฆฌ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“‹ ์ถ”ํ›„ ์ง„ํ–‰ ์ƒํ™ฉ

  • ํŽ˜์ด๋กœ๋“œ Codable ์ธ์ฝ”๋”ฉ/๋””์ฝ”๋”ฉ ํ…Œ์ŠคํŠธ๋Š” ์ด๋ฒˆ PR ๋ฒ”์œ„์— ์—†์Šต๋‹ˆ๋‹ค. ์ด์Šˆ ์™„๋ฃŒ ์กฐ๊ฑด์— ์žˆ์ง€๋งŒ,
    ์„ ํ–‰ ์ด์Šˆ(โœจ Feature: Watch ๋””์ž์ธ ํ† ํฐยท๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ๊ตฌ์ถ• (Glass ์ ˆ์ œ ๊ทœ์น™)ย #1205~โœจ Feature: Watch Complication 3์ข… (WidgetKit accessory family)ย #1215)์˜ ํŽ˜์ด๋กœ๋“œ ๊ณ„์•ฝ ๊ฒฐ๊ณผ๋ฌผ์ด ์•„์ง develop ์— ์—†์Šต๋‹ˆ๋‹ค โ€” ํ˜„์žฌ
    Core/WatchConnectivity/Sources ์—๋Š” WatchSessionCoordinator / WatchMessenger ๋‘ ํŒŒ์ผ๋ฟ์ด๊ณ 
    Codable ๋„๋ฉ”์ธ ํƒ€์ž…์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๊ณ„์•ฝ์„ ์ƒˆ๋กœ ๋ฐœ๋ช…ํ•˜๋ฉด ๊ทธ ์ด์Šˆ๋“ค๊ณผ ์ถฉ๋Œํ•˜๋ฏ€๋กœ,
    ์ง€๊ธˆ ์กด์žฌํ•˜๋Š” ๊ณ„์•ฝ([String: Any] ๋”•์…”๋„ˆ๋ฆฌ ๋ฌด๋ณ€ํ˜• ์ „๋‹ฌ)๋งŒ ๊ณ ์ •ํ•ด๋’€์Šต๋‹ˆ๋‹ค.
    ํŽ˜์ด๋กœ๋“œ ํƒ€์ž…์ด ๋จธ์ง€๋˜๋ฉด CoreWatchConnectivityTests ์— ๋ผ์šด๋“œํŠธ๋ฆฝยทํ•˜์œ„ํ˜ธํ™˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ด์–ด ๋ถ™์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • WatchMessenger ์˜ Swift 6 Sendable ๊ฒฝ๊ณ (stored property 'coordinator' of 'Sendable'-conforming class ... has non-Sendable type)๋Š” ์ด PR ์ด์ „๋ถ€ํ„ฐ ์žˆ๋˜ ๊ฒƒ์ด๋ผ ์†๋Œ€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ฝ”๋””๋„ค์ดํ„ฐ์˜ ๊ฒฉ๋ฆฌ
    ๋ชจ๋ธ(@MainActor ๋กœ ๋ฌถ์„์ง€, ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ํ๋ฅผ ๋ช…์‹œํ• ์ง€)์„ ์ •ํ•˜๋Š” ๊ฑด ํŽ˜์ด๋กœ๋“œ ๊ณ„์•ฝ ์ด์Šˆ์™€ ๊ฐ™์ด ๋ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • CI ๋Ÿฌ๋„ˆ์˜ watchOS ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๋Ÿฐํƒ€์ž„ ์œ ๋ฌด๋Š” ์‹ค์ œ ๋Ÿฌ๋„ˆ์—์„œ๋งŒ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ์€ watchOS 26.5 ๊ฐ€
    ์„ค์น˜๋ผ ์žˆ์–ด ํ†ต๊ณผํ–ˆ๊ณ , ๋Ÿฌ๋„ˆ์—์„œ ๋น ์ ธ ์žˆ์œผ๋ฉด ์œ„ 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 ์˜ˆ์™ธ๋กœ ์ฃฝ์—ˆ์œผ๋ฏ€๋กœ ๋™์ž‘ ๋ณ€ํ™”์ง€๋งŒ ๊ฐœ์„  ๋ฐฉํ–ฅ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.
  • CI ์Šคํ…์„ ๋ณ„๋„ ์žก์ด ์•„๋‹ˆ๋ผ ๊ธฐ์กด ์žก์— ์ด์–ด ๋ถ™์ธ ์„ ํƒ (๋Ÿฌ๋„ˆ ์‹œ๊ฐ„ vs ์žก ๋ถ„๋ฆฌ ๊ฐ€๋…์„ฑ).
  • AppProduct/ ๋Š” ํ•œ ์ค„๋„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค (์ ˆ๋Œ€ ๊ทœ์น™ โœจ Feature: LoadingView ์ œ์ž‘ย #9).

โœ… Checklist

PR์ด ๋‹ค์Œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”!!!

  • ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒค์…˜์— ๋งž๊ฒŒ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค
  • ์œ ์ง€-๋ณด์ˆ˜๋ฅผ ์œ„ํ•ด ์ฃผ์„์ฒ˜๋ฆฌ๋ฅผ ์ž˜ ์ž‘์„ฑํ•˜์˜€๋Š”๊ฐ€?

- 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 ์— ์›Œ์น˜ ๋นŒ๋“œยทํ…Œ์ŠคํŠธ ์ ˆ๊ณผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋ฌธ์„œํ™”
@JEONG-J JEONG-J added the ๐Ÿ”ง chore ํ”„๋กœ์ ํŠธ ์„ธํŒ…, ๋นŒ๋“œ ์„ค์ •, ์˜์กด์„ฑ ๊ด€๋ฆฌ ๋“ฑ ๊ธฐํƒ€ ์ž‘์—…์ž…๋‹ˆ๋‹ค. label Aug 30, 2026
@JEONG-J JEONG-J self-assigned this Aug 30, 2026
# Conflicts:
#	UMCApp/Core/WatchConnectivity/Sources/WatchSessionCoordinator.swift
@JEONG-J

JEONG-J commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

develop ๋ณ‘ํ•ฉ ์‹œ ์žฌ์ž‘์„ฑํ•œ ๋ฒ”์œ„

#1212 ๋ฆฌํŒฉํ„ฐ๋กœ ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ WatchMessage/WatchEnvelope ๋ด‰ํˆฌ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์‹œ ์“ฐ์˜€๊ณ 
WatchMessenger ๋Š” ์‚ญ์ œ๋์Šต๋‹ˆ๋‹ค. ์ด ๋ธŒ๋žœ์น˜๊ฐ€ ๋“ค๊ณ  ์˜จ seamยทํ…Œ์ŠคํŠธ๋Š” ์˜› API ๋ฅผ ๊ฒจ๋ƒฅํ•˜๊ณ 
์žˆ์–ด ๊ทธ๋Œ€๋กœ๋Š” ์ปดํŒŒ์ผ๋˜์ง€ ์•Š์•„์„œ
, develop ๊ตฌํ˜„์„ ์ •๋‹ต์œผ๋กœ ๋‘๊ณ  ์•„๋ž˜์ฒ˜๋Ÿผ ์žฌ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

1. WatchSessionProviding ์žฌ์„ค๊ณ„

develop ์ฝ”๋””๋„ค์ดํ„ฐ๊ฐ€ WCSession ์—์„œ ์‹ค์ œ๋กœ ์“ฐ๋Š” ํ‘œ๋ฉด์ด ๋Š˜์–ด๋‚˜์„œ(ํยท์ปจํ…์ŠคํŠธ ์‹œ๋”ฉยทํ™œ์„ฑํ™”
์ƒํƒœ) seam ๋„ ๊ทธ๋งŒํผ ๋„“ํ˜”์Šต๋‹ˆ๋‹ค.

  • ์ถ”๊ฐ€: isActivated ยท receivedContext ยท outstandingTransfers ยท transfer(userInfo:)
  • ์ถ”๊ฐ€: WatchUserInfoTransfer ํ”„๋กœํ† ์ฝœ (WCSessionUserInfoTransfer ์ค€์ˆ˜) โ€” ํ ๋งŒ๋ฃŒ ์ทจ์†Œ๋ฅผ
    ๊ฒ€์ฆํ•˜๋ ค๋ฉด ์ „์†ก ํ•ธ๋“ค๋„ ๋Œ€์—ญ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์œ ์ง€: ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ WCSession ์›๋ณธ๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋‘๋Š” ๊ทœ์น™(์ „๋‹ฌ ์ต์Šคํ…์…˜์˜ ์ž๊ธฐ ์žฌ๊ท€ ๋ฐฉ์ง€)

์ฝ”๋””๋„ค์ดํ„ฐ๋Š” WCSession.default ์ง์ ‘ ์ ‘๊ทผ์„ ๊ฑท์–ด๋‚ด๊ณ  init(session:) ์œผ๋กœ ์ฃผ์ž…๋ฐ›์Šต๋‹ˆ๋‹ค.
๊ธฐ๋ณธ๊ฐ’์ด WCSession.default ๋ผ ๊ธฐ์กด ํ˜ธ์ถœ๋ถ€(WatchSessionCoordinator())๋Š” ๊ทธ๋Œ€๋กœ ์”๋‹ˆ๋‹ค.

2. ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ๋กœ์ง์„ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์ง€์ ์œผ๋กœ ๋ถ„๋ฆฌ

WCSession ์ธ์Šคํ„ด์Šค๋ฅผ ํ…Œ์ŠคํŠธ์—์„œ ๋งŒ๋“ค ์ˆ˜ ์—†์–ด ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์ฝœ๋ฐฑ์„ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
์ฝœ๋ฐฑ์€ ๊ฐ’๋งŒ ๋ฝ‘์•„ ์•„๋ž˜ ๋‚ด๋ถ€ ํ•จ์ˆ˜๋กœ hop ํ•˜๊ณ , ํ…Œ์ŠคํŠธ๋Š” ๊ทธ ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ๊ตฌ๋™ํ•ฉ๋‹ˆ๋‹ค.

  • applyActivation(_:reachable:seeded:) ยท applyReachability(_:) ยท applyReceivedContext(_:)
  • ingest(userInfo:) ยท handle(request:reply:)

์ด ๊ณผ์ •์—์„œ ์ด ๋ธŒ๋žœ์น˜๊ฐ€ ๋‹ด๊ณ  ์žˆ๋˜ ๊ฐœ์„  ๋‘ ๊ฐ€์ง€๋ฅผ ์ƒˆ ๊ตฌํ˜„์— ์ด์‹ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • .activated ๋ผ๋„ error ๊ฐ€ ์žˆ์œผ๋ฉด ํ™œ์„ฑํ™”๋กœ ๋ณด์ง€ ์•Š๋Š”๋‹ค โ€” ๋ฌด์‹œํ•˜๋ฉด ํ™”๋ฉด์€ ใ€Œ์—ฐ๊ฒฐ๋จใ€์ธ๋ฐ
    ๋ชจ๋“  ์ „์†ก์ด ์กฐ์šฉํžˆ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.
  • ํ™œ์„ฑํ™” ์ „ ๋„๋‹ฌ์„ฑ ๋ณ€ํ™”๋Š” ๋ฌด์‹œํ•œ๋‹ค โ€” ใ€Œ๋„๋‹ฌ ๊ฐ€๋Šฅใ€์ธ๋ฐ ์ „์†ก์ด .sessionNotActivated ๋กœ
    ์ฃฝ๋Š” ์ƒํƒœ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

3. ํ…Œ์ŠคํŠธ ์žฌ์ž‘์„ฑ

FakeWatchSession ์€ ๋„“์–ด์ง„ seam ์— ๋งž์ถฐ ๋‹ค์‹œ ์ผ๊ณ , WatchSessionCoordinatorTests ๋Š” ์ƒˆ API
(requestSync() ยท requestAttendance(_:) ยท notifyAttendanceChanged(_:) ยท
publishSessionState(_:) ยท enqueue(_:) ยท purgeExpiredQueue(now:) ยท setRequestHandler(_:) ยท
receivedUserInfo())๋ฅผ ๊ฒจ๋ƒฅํ•ด 26๊ฐœ๋กœ ์žฌ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ํ™œ์„ฑํ™”/๋„๋‹ฌ์„ฑ ์ „์ด, ์ฝœ๋“œ๋Ÿฐ์น˜ ์ปจํ…์ŠคํŠธ ์‹œ๋”ฉ๊ณผ ๋ฎ์–ด์“ฐ๊ธฐ ๊ธˆ์ง€
  • requestSync ์„ฑ๊ณต ยท .remote ยท .unexpectedReply ยท .notReachable ยท .sessionNotActivated
  • ์ „์†ก ์‹คํŒจ ๋ถ„๋ฅ˜ (WCError ์ฝ”๋“œ โ†’ ๋„๋ฉ”์ธ ์—๋Ÿฌ / ๊ทธ ์™ธ โ†’ transportFailure ์›๋ณธ ๋ณด์กด)
  • ํ ์ฑ„๋„ ๊ทœ์น™(.noticeRead ํ—ˆ์šฉ, ์™•๋ณตํ˜• 3์ข…์€ .unsupportedChannel)
  • purgeExpiredQueue(now:) 180๋ถ„ ๊ฒฝ๊ณ„ โ€” ์ •ํ™•ํžˆ 180๋ถ„์€ ์œ ํšจ
  • handle(request:) ์˜ ใ€Œ์‘๋‹ต์€ ์ •ํ™•ํžˆ ํ•œ ๋ฒˆใ€ ๊ณ„์•ฝ: ๋ฏธ๋“ฑ๋ก ยท ์†์ƒ ยท ์ƒ์œ„ ์Šคํ‚ค๋งˆ ยท ์ •์ƒ ํšŒ์‹ 

์‚ญ์ œ: WatchMessengerTests.swift โ€” ๊ฒ€์ฆ ๋Œ€์ƒ์ธ WatchMessenger ์ž์ฒด๊ฐ€ #1212 ์—์„œ
์‚ญ์ œ๋์Šต๋‹ˆ๋‹ค. ๊ทธ ํŒŒ์ผ์ด ์ง€ํ‚ค๋˜ ๊ณ„์•ฝ(๋ด‰ํˆฌ ์™•๋ณต ยท ์Šคํ‚ค๋งˆ ๋ฒ„์ „ ์ƒํ•œ)์€ develop ์˜
WatchEnvelopeTests ยท WatchMessageTests ๊ฐ€ ์ด๋ฏธ ๋ฎ๊ณ  ์žˆ์–ด ๋ณ„๋„๋กœ ์˜ฎ๊ธฐ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

4. ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜ํ•œ ๊ฒƒ

.github/workflows/tuist-ci.yml watchOS ์Šคํ… 3์ข…, UMCApp/Makefile ์˜ build-watch ยท
WATCH_SCHEME ยท WATCH_DESTINATION, MAKEFILE_GUIDE.md ๋ฌธ์„œ๋Š” ์›์•ˆ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค.
Core/WatchConnectivity/Project.swift ์˜ includesTests: true ๋Š” develop ์— ์ด๋ฏธ ์žˆ์–ด
์ค‘๋ณต ์—†์ด ๋ณ‘ํ•ฉ๋์Šต๋‹ˆ๋‹ค.

๊ฒ€์ฆ

ํ•ญ๋ชฉ ๊ฒฐ๊ณผ
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 ๋ฒ”์œ„ ๋ฐ–์ด๋ผ ๊ทธ๋Œ€๋กœ ๋’€์Šต๋‹ˆ๋‹ค.

@JEONG-J
JEONG-J merged commit ac66a74 into develop Aug 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

๐Ÿ”ง chore ํ”„๋กœ์ ํŠธ ์„ธํŒ…, ๋นŒ๋“œ ์„ค์ •, ์˜์กด์„ฑ ๊ด€๋ฆฌ ๋“ฑ ๊ธฐํƒ€ ์ž‘์—…์ž…๋‹ˆ๋‹ค.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

๐Ÿ€ ETC: WatchConnectivity ํ…Œ์ŠคํŠธ ํƒ€๊ฒŸ + CI watchOS ๋นŒ๋“œ ๊ฒ€์ฆ ์ถ”๊ฐ€

1 participant