티스토리 뷰

🍏/iOS

[WatchOS] Watch Connectivity

사용자 eungding 2021. 6. 14. 18:03
728x90
반응형

[1] iOS App과 WatchKit Extension 사이의 관계

 

WatchApp도 위젯처럼 Shared Container를 이용하여 간단히 앱과 워치 사이의 데이터를 공유할 수 있을 줄 알았으나,,,

(참고: App과 Extension간 UserDefaults 공유하기 )

 

Watch는 이 방법말고 Watch Connectivity 라는 framework를 사용해서

iOS App과 paired watchOS app 사이의 two-way communication을 구현해줘야한다고 합니다. 

 

그 이유는 다음과 같습니다. 

 

WatchOS 1에서는 

iOS App과 Watchkit Extension이 둘다 아이폰 안에 있었으며 data store를 공유했습니다. 

 

 

하지만 WatchOS 2부터는 WatchKit Extension을 Watch로 옮겼고 

각각의 data store를 가지게 되었습니다. 

 

 

이제 이 그림처럼 App과 AppExtension의 개념이 아니므로

Shared Container를 사용할 수 없는 것이죠,,, 

 

 

 

 

[2] Watch Connectivity Framework

 

Watch Connectivity Framework에 대해서 알아보겠습니다. 

이 Framework를 사용하여서 iOS app과 paired WatchOS app의 WatchKit extension 는 data transfer를 할 수 있습니다.

그리고 이 Framework를 이용해서 WatchOS app의 complication의 업데이트를 trigger 할 수도 있습니다. 

 

앱에서 transter를 초기화 한 후부터는, 시스템이 data 전송에 대한 모든 책임을 집니다. 

대부분의 transfer는 receiving app이 inactive인 상태일 때 background에서 일어납니다. 

app이 깨어나면, inactive 상태였을 때 도착한 데이터들을 노티받게 됩니다. 

 

두 app이 모두 active 상태일 때, 실시간 통신(Live communication)도 가능합니다. 

 

 

[2.1] Initiate transfers between your iOS app and watchOS app

 

communication 하기 전에 먼저 초기화 과정이 필요합니다. 

iOS app과 watchOS app 둘다  WCSession object를 create & configure 해야합니다. 

session object를 configure하고 active 시킨 후,  메세지를 보내거나 연결 상태에 대한 정보를 얻으려고 시도할 수 있습니다. 

 

또한 session을 활성화하기 전에 isSupported() 메소드를 통해

현재 디바이스가 Watch Connectivity framework를 사용할 수 있는 지 알 수 있습니다. 

 

위에서 말한 과정을 아래의 코드로 해줄 수 있습니다.

session을 만들고 delegate 를 assign 해줍니다. 

그리고 activate() 메소드를 불러 session을 활성화시켜줍니다. 

 

 

WatchKit extension과 iOS app 에 모두 각각의 session을 configure 해줘야합니다.

예제를 보면 iOS app은  AppDelegate > didFinishLaunching에서 해줬고

 

 

WatchKit Extension은

ExtensionDelegate의 init에서 해줬네요 

 

 

그리고 info.plist에 WKExtensionDelegateClassName을 추가해준 것을 볼 수 있습니다. 

 

 

두 session object가 active되면, 두 프로세스는 즉시 커뮤니케이션을 할 수 있습니다. 

하나의 session만 active 되었을 때는 active session이 계속 update를 보내고 file을 전송할 수 있지만

이 전송은 background에서 기회적으로(opportunistically) 수행됩니다. 

(아래의 설명을 보면 이해가 갑니다)

 

[2.2] Communicating with the Counterpart App

이제 초기화과정이 끝났으니 커뮤니케이션을 어떻게 하는 지 알아봅시다.

그 전에 먼저 문서에서 계속 언급되는 sessionActivationState를 살펴봅시다.

WCSessionActivationState 는 session의 현재 activation state를 말하며 notActivated / inactive / activate 세가지가 있습니다.

 

제가 이해하기로는

- notActivated : 세션이 비활성화인 상태. 아무런 커뮤니케이션도 할 수 없음

- inactive: 세션 활성화되었지만, 비활성화상태로 전환 중.. session delegate는 이 상태에서 데이터를 받기는 가능하지만, 상대방 앱으로 data를 보낼 수는 없음 (받기 가능 / 보내기 불가능)

- activate: 받기 / 보내기 모두 가능! 

 

 

 

 

# Send

 

나의 activationState가 activated여야지만 상대방에게 데이터를 전송할 수 있습니다. 

iOS App은 watch app으로 데이터를 전송하기 전에 isPaired 랑 isWatchAppInstalled 프로퍼티를 체크하세요

 

 

+

iOS 9.3 이후부터는 하나 이상의 애플워치(watchOS 2.2 이후)와 페어링가능합니다. 

multiple Apple Watch를 지원하는 경우 주의사항도 문서에 있습니다

 

 

# 1. 

상대방에게 최근 상태 정보를 전달하려면 

updateApplicationContext(_:) 메소드를 사용하세요

 

상대방이 깨어나면 이 정보를 사용하여 자신의 상태를 업데이트 할 수도 있습니다. 

 

 

# 2.

 iOS app과 WatchKit extension 사이의 즉각적인 커뮤니케이션을 가능하게 해주는 메소드는 아래 두개가 있습니다.

- sendMessage(_:replyHandler:errorHandler:) 

- sendMessageData(_:replyHandler:errorHandler:) 

 

(참고로 isReachable 프로퍼티가 true여야지 메소드들이 성공합니다)

 

 

# 3.

 

background에서 dictionary를 전달하고 싶으면 

transferUserInfo(_:)  를 쓰면 되고

 

background에서 file을 전달하고 싶으면

transferFile(_:metadata:) 를 쓰면 됩니다. 

 

 

 

# 4.

iOS 앱이 Wath app의 complicaton에게 데이터를 보내고 싶으면

 

transferCurrentComplicationUserInfo(_:) 를 쓰면 됩니다. 

 

 

# 5. 

상대방에게 메세지를 보낼 때, background message는 queue에 배치되고 순서대로 전달됩니다.

한편 아래 세개 메소드를 통해 보내진 데이터는 더 높은 우선순위를 가지며 즉시 전송됩니다. 

sendMessage(_:replyHandler:errorHandler:)
sendMessageData(_:replyHandler:errorHandler:),
transferCurrentComplicationUserInfo(_:) 

 

앱에서 받은 모든 메세지는 background thread에서 session delegate에게 연속적으로 전달됩니다.

 

 

 

# Receive 

 

WCSessionDelegate 의 메소드들을 통해 message를 Receive 할 수 있습니다. 

 

 

 

[ 주의사항 ]

- Send: func transferCurrentComplicationUserInfo(_ userInfo: [String : Any] = [:]) -> WCSessionUserInfoTransfer

- Receive: func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:])

 

이 두개가 한 세트인데, 시뮬레이터말고 실기기에서만 테스트해볼 수 있다고 합니다,,🥲

 

 

 

그리고 reloadComplicationDescriptors 에 있는 내용도 주의해주세요!

 

 

이미 watch face에 present 되고 있는 complication을 data source에서 제거한 경우,

ClockKit은 해당 compication을 계속 보여주고 계속 새로운 timeline entries를 요청합니다.

하지만 사용자가 new watch face에서 compication을 편집할 때 해당 complication을 추가할 수는 없습니다. 

 

=> datasource에서 지우고 reload해도 기존 워치 페이스에 있는 complication은 지워지지 않는다!! 대신 새로운 Watch Face에서는 지운 complication이 안나온다!! 

 

 

 

[ Reference ]

 

- 개발문서: https://developer.apple.com/documentation/watchconnectivity

 

- 애플 예제 프로젝트: Using Watch Connectivity to Communicate Between Your Apple Watch App and iPhone App

 

- WWDC: 
   WWDC 2015 - Introducing Watch Connectivity 

   WWDC 2020 - Create complications for Apple Watch

 

- 추천 아티클: https://betterprogramming.pub/get-started-with-watch-connectivity-with-swiftui-51722324b6f6

 

728x90
반응형
댓글
댓글쓰기 폼