[iOS] WatchKit[3] - 예시를 통해 Watch Connectivity 알아보기, 주의사항

ungchun·2022년 8월 4일
0

WatchKit

목록 보기
3/5
post-thumbnail

이번 글은 제 프로젝트를 통해 Watch Connectivity를 사용한 예시를 소개하면서 몇 가지 있었던 문제들을 소개하려합니다. 제가 앞에서 설명한 부분을 안보셨다면 꼭 WatchKit[1], WatchKit[2] 를 읽어보시고 진행해주세요 !

updateApplicationContext(_:)

우선 저는 커플 디데이 프로젝트를 진행 중 입니다. iOS앱에서 day와 배경사진을 설정하면 watch앱에서 그 데이터를 가지고 watch화면에 보여지게 되는 형태입니다.

데이터를 보내는 Send 메서드 중 저는 updateApplicationContext(_:) 이 친구를 가지고 이미지와 day 데이터를 같이 넘길려고 했습니다. 그러나 문제가 발생했습니다. 저는 이미지와 day 데이터를 같이 보냈는데 하나가 사라져서 도착하는 문제가 생겼습니다.
그래서 여기저기 찾아본 결과, Why isn't iPhone transferring an array to Apple Watch? 여기서 정답을 찾을 수 있습니다. updateApplicationContext을 호출하면 watch로 전송된 이전 데이터를 덮어쓴다고 합니다..
예를 들면 데이터를 담은 dict1, dict2 가 있는데 dict1를 보낸 직후에 dict2를 보내면 dict1는 삭제되고 dict2로 바뀌는 것입니다.

transferUserInfo(_:)

반대로 updateUserInfo를 사용하는 호출은 이전 호출을 덮어쓰지 않습니다. watch 앱이 로드되면 각 호출에 대해 한 번씩 didReceiveUserInfo 코드가 트리거됩니다. 호출 순서는 유지되므로 iOS 앱에서 dict1, dict2, dict3을 전송하면 시계 앱이 해당 순서로 호출합니다.

저는 이미지 데이터는 transferUserInfo, day 데이터는 updateApplicationContext를 이용하여 데이터를 관리했습니다.
이제 코드를 보겠습니다 !


updateApplicationContext(_:)로 데이터 보내고 받기

// in iOS App
//
import WatchConnectivity
	...
	coupleTabViewModel.onPublicBeginCoupleDayUpdated = {
    		...
            // watch, days 택스트 value는 updateApplicationContext 방법으로 연동
            //
            let dayData: [String: Any] = ["dayData": String(describing: RealmManager.shared.getUserDatas().first!.beginCoupleDay)]
            try? WCSession.default.updateApplicationContext(dayData)
            
			// 이렇게도 가능함
            //
			if let validSession = self.session {
				let data: [String: Any] = ["dayData": self.coupleTabViewModel.beginCoupleDay]
				validSession.transferUserInfo(data)
			}
        }
	...

자신이 watch앱으로 데이터를 보내야 하는 시점에 사용해주시면 됩니다. 딕셔너리 데이터를 만들어서 updateApplicationContext 메서드를 이용해 데이터를 보내면

// in Watch App
//
import WatchConnectivity
	...
    override func willActivate() {
		// receive ApplicationContext
		//
		let receiveData = WCSession.default.receivedApplicationContext
		if receiveData.isEmpty == false {
			DispatchQueue.main.async {
				if let data = receiveData.values.first as? String {
					...
				} else {
					...
				}
			}
		}
	}
	...

watch앱에서 WCSession.default.receivedApplicationContext를 이용하면 iOS App에서 보낸 딕셔너리 데이터를 받을 수 있습니다.


transferUserInfo(_:)로 이미지 보내고 받기

// in iOS App
//
import WatchConnectivity
	...
	coupleTabViewModel.onMainImageDataUpdated = {
		...
		// watch, 메인 이미지는 transferUserInfo 방법으로 이미지 연동
		//
		let data = UIImage(data: self.coupleTabViewModel.mainImageData!)?.jpegData(compressionQuality: 0.1)
		let imageData: [String: Any] = ["imageData": data!]
		WCSession.default.transferUserInfo(imageData)
		
        // 이렇게도 가능함
        //
		if let validSession = self.session {
		let data: [String: Any] = ["imageData": RealmManager.shared.getImageDatas().first!.mainImageData!]
		validSession.transferUserInfo(data)
		}
	}
    ...

updateApplicationContext의 모습이랑 상당히 비슷하죠? 똑같이 딕셔너리 데이터를 만들어서 transferUserInfo 메서드를 이용해서 넘겨주면 됩니다.
추가로 혹시 저처럼 이미지를 넘겨야하는 상황이 오시는 분이면 WCSession - PayloadTooLarge 을 보면 이미지 크기 제한이 있습니다. 실제로 용량이 큰 이미지는 watch에서 응답을 하지 않더라구요.. 그래서 저는 jpegData(compressionQuality: 0.1) 이용해서 용량을 줄였습니다!

// in watch App
//
import WatchConnectivity
	...
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
		// receive transferUserInfo
		//
		if let data = userInfo["imageData"] as? Data {
			...
		} else { }
	}

Watch앱이 로드되면 didReceiveUserInfo가 실행이 되고 userInfo 딕셔너리 데이터로 받아옵니다.


결과화면

updateApplicationContext(:), transferUserInfo(:) 말고 다른 케이스들은 WCSessionActivationState 공식 문서를 참고해주세요 !

저도 완벽하게 이해하지는 못했지만 제가 느낀점은 transferUserInfo은 iOS App이 켜져야 새로 고친 데이터를 전달한다는 느낌이었습니다. 그래서 image는 바꿀려면 앱을 키고 바꿔야해서 transferUserInfo를 사용했고, updateApplicationContext은 transferUserInfo랑은 다르게 iOS App을 안켜도 데이터가 알아서 업데이트가 되더라구요..? 그래서 days 같은 경우는 하루가 지나면 앱을 안키더라도 업데이트 되어야해서 updateApplicationContext를 사용했습니다. 나름 공부해서 진행한다고 했지만.. 틀린부분이 정말 많이 있을 겁니다. 언제든지 댓글 달아주시면 수정하겠습니다 ! 그리고 다음 글은 Watch의 Complication에 대해 작성해보겠습니다 !

0개의 댓글