본문 바로가기
🔨 Xcode/⌚ Apple Watch

Apple Watch • 아이폰과 애플워치에 데이터 교환을 해보자!

by 제로데이 2021. 2. 21.

안녕하세요 제로데이입니다!

 

오늘은 아이폰과 애플워치를 연동하여 데이터 통신을 해볼 건데요.

 

아직까지는 애플워치가 그렇게 유명하지는 않아도 언젠간 애플워치도 아이폰처럼 엄청난 효과를 가져올 것입니다.

 

그때를 위하여 저희는 열심히 공부해 봅시다!

 


프로젝트 생성

 

프로젝트를 생성을 누르고 watchOS - iOS App with Watch App을 누르고 Next를 눌러줍니다.

 

iOS App with Watch App은 iOS와 Watch 앱을 같이 생성하는 것이고,

Watch App은 Watch 단독으로 생성하는 것입니다.

 

 

 

이름만 작성하신 뒤 기본 설정으로 놔두신 뒤 Next를 눌러줍니다.

 

 

 

 

 

 

아이폰 앱을 만들 때와 파일들이 많이 다릅니다.

그 이유는 저희가 애플워치 앱을 만들었기 때문인데요.

 

 

 

빨간색 네모 박스를 친 게 워치 앱에 관련된 파일들입니다.

이렇게 생성은 끝났군요!

 


아이폰 부분 UI생성 & 코드 작성

 

코드를 작성하기 전에 스토리보드에 버튼과 라벨을 생성해 줍시다.

 

 

라벨 하나(Label)버튼 두 개(Reset, Send to Watch)를 생성해 주세요!

 

이제 코드를 작성해 봅시다.

 

ViewController.swift에 들어가서 

import WatchConnectivity

 

WatchConnectivity를 import 해주시고요.

 

소스를 넣어줍니다.

import UIKit
import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate {
    var session: WCSession?
    
    @IBOutlet weak var WatchMessage: UILabel!
    
    @IBAction func onClickSendButton(_ sender: Any) {
        if let validSession = self.session {
            let data: [String: Any] = ["iPhone": "아이폰에서 데이터 전송!" as Any]
            validSession.transferUserInfo(data)
        }
    }
    
    @IBAction func onClickReset(_ sender: Any) {
        self.WatchMessage.text = "Label"
    }
    
    func configureWatchKitSesstion() {
        if WCSession.isSupported() {
            session = WCSession.default
            session?.delegate = self
            session?.activate()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.configureWatchKitSesstion()
    }
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        
    }
    
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        print("수신받은 데이터 : \(userInfo)")
        DispatchQueue.main.async {
            if let value = userInfo["watch"] as? String {
                self.WatchMessage.text = value
            }
        }
    }
    
    func sessionDidBecomeInactive(_ session: WCSession) {
        
    }
    
    func sessionDidDeactivate(_ session: WCSession) {
        
    }

}

 

추가된 코드를 살펴봅시다. 

 

 

    5. var session: WCSession?

 

애플워치와 아이폰 통신에 필수인 변수를 선언합니다.

애플 공식 홈페이지에는 WCSession을 아래처럼 설명합니다.

    7. @IBOutlet weak var WatchMessage: UILabel!

애플워치에서 보낸 데이터가 아이폰에 각인될 라벨입니다.

스토리보드에 있는 라벨에 연결해 주세요.

 

    9. @IBAction func onClickSendButton(_ sender: Any) {

애플워치에 데이터를 보내기 위한 버튼입니다. Send to Watch 버튼에 연결해 주세요.

 

    10. if let validSession = self.session {

옵셔널 바인딩을 사용하여 self.session이 true라면 validSession에 넣고 다음 행으로 넘어갑니다.

 

    11. let data: [String: Any] = ["iPhone": "아이폰에서 데이터 전송!" as Any]

딕셔너리를 생성하여 ["iPhone": "아이폰에서 데이터 전송!" as Any]를 data에 넣어줍니다.

 

    12. validSession.transferUserInfo(data)

transferUserInfo 메서드는 딕셔너리 데이터를 통신하는 기기에 보낼 수 있게 해줍니다.

 

    16. @IBAction func onClickReset(_ sender: Any) {

라벨의 문자 리셋을 위해 만든 버튼입니다. Reset 버튼에 연결해 주세요.

 

    17. self.WatchMessage.text = "Label"

라벨을 초기화하기 위해 현재 라벨 Text에 "Label"을 넣습니다.

 

    21. if WCSession.isSupported() {

isSupported() 함수는 아이폰이 워치와 연결이 가능한지 보고 가능하다면 True 불가능하다면 False를 반환합니다.

 

    22. session = WCSession.default

WCSession을 싱글톤 패턴으로 지정하고 session에 넣습니다.

* 싱글톤 패턴이란?

여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다. 

 

    23. session?.delegate = self

session.delegate가 viewcontroller로 자기 역할을 위임해 줍니다.viewcontroller는 session의 일을 대신 처리해 줍니다.

 

    24. session?.activate()

session을 활성화시켜 통신할 준비를 마칩니다. 이 메서드를 활성화시키기 전까진 메시지를 주고받을 수 없습니다.

 

 

    30. self.configureWatchKitSesstion()

메서드를 실행시켜 통신할 준비를 마칩니다.

 

    33. func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {

세션 활성화가 되면즉 activate() 메서드로 인해 통신할 준비를 마치면 메서드가 호출됩니다.

필수 메서드입니다.

 

    37. func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {

애플워치의 데이터를 성공적으로 수신하면 메서드가 호출됩니다.

 

    39. DispatchQueue.main.async

37번의 메서드는 백그라운드 스레드를 사용하기 때문에 41번 코드가 작동하려면 메인 스레드를 적용시켜 줘야 합니다.

 

    40. if let value = userInfo["watch"] as? String {

userInfo에 watch라는 문자열 이을 value에 넣고 없으면 그냥 넘어가는 옵셔널 바인딩을 합니다.

 

    41. self.WatchMessage.text = value

Label의 text에 수신 받은 값을 넣습니다.

 

    46. func sessionDidBecomeInactive(_ session: WCSession) {

세션이 비활성화될 때 호출되는 필수 메서드입니다.

 

    50. func sessionDidDeactivate(_ session: WCSession) {

세션의 모든 데이터가 전달되었고 애플워치와 통신이 종료되었을 때 호출되는 필수 메서드입니다.

 

 

 


애플워치 부분 UI생성 & 코드 작성

 

Interface.storyboard가 애플워치의 스토리보드입니다.

 

이것을 열어 봅시다.

 

???????????

 

해괴한 레이아웃이 나옵니다.

지금 저희에게 필요한 건 제일 왼쪽 위에 레이아웃이니 UI를 넣어봅시다.

 

이 레이아웃과 똑같이 버튼과 라벨을 넣어주세요!

 

이제 코드질을 해봅시다.

 

InterfaceController.swift를 열어주세요.

 

import WatchKit
import Foundation
import WatchConnectivity

class InterfaceController: WKInterfaceController, WCSessionDelegate {
    @IBOutlet weak var label: WKInterfaceLabel!
    let session = WCSession.default
    
    @IBAction func onClickReset() {
        label.setText("Label")
    }
    
    @IBAction func SendMessage() {
        let data: [String: Any] = ["watch": "data from watch" as Any]
        session.transferUserInfo(data)
    }

    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        
        session.delegate = self
        session.activate()
    }
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    }

    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        print("received data: \(userInfo)")
        DispatchQueue.main.async {
            if let value = userInfo["iPhone"] as? String {
                self.label.setText(value)
            }
        }
    }
    
    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()
    }
    
    override func didDeactivate() {
        // This method is called when watch view controller is no longer visible
        super.didDeactivate()
    }
    
}

 

    5. class InterfaceController: WKInterfaceController, WCSessionDelegate {
WCSessionDelegate를 추가해 주세요.

 

    6. @IBOutlet weak var label: WKInterfaceLabel!

워치의 스토리보드에 Label과 연결해 주세요.

 

    7. let session = WCSession.default

싱글톤 패턴의 session이라는 변수를 생성합니다.

 

    9. @IBAction func onClickReset() {
라벨의 초기화를 위한 액션을 생성합니다. 스토리보드에 Reset과 연결해 주세요.

 

    13. @IBAction func SendMessage() {
메시지를 보낼 액션을 추가합니다. 스토리보드에 Send와 연결해 주세요.

 

    14. let data: [String: Any] = ["watch": "data from watch" as Any]

딕셔너리 형태의 문자열을 만들고 data라는 이름으로 정의합니다.

 

    15. session.transferUserInfo(data)
transferUserInfo 메서드는 딕셔너리 데이터를 통신하는 기기에 보낼 수 있게 해줍니다.

 

    18. override func awake(withContext context: Any?) {
애플워치에서 생명주기 역할을 합니다.

iOS의 ViewDidLoad와 비슷합니다.

 

    21. session.delegate = self
session.delegate가 InterfaceController로 자기 역할을 위임해 줍니다.InterfaceController는 session의 일을 대신 처리해 줍니다.

 

    22. session.activate()
session을 활성화시켜 통신할 준비를 마칩니다. 이 메서드를 활성화시키기 전까진 메시지를 주고받을 수 없습니다.

 

    25. func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
세션 활성화가 되면 즉 activate() 메서드로 인해 통신할 준비를 마치면 메서드가 호출됩니다.

필수 메서드입니다.

 

    28. func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
아이폰의 데이터를 성공적으로 수신하면 메서드가 호출됩니다.

 

    30. DispatchQueue.main.async {
28번의 메서드는 백그라운드 스레드를 사용하기 때문에 32번 코드가 작동하려면 메인 스레드를 적용시켜 줘야 합니다.

 

    32. self.label.setText(value)
label의 text를 value 값으로 설정합니다.

 

    37. override func willActivate() {
인터페이스 컨트롤러가 활성화되면 호출됩니다.

생명주기는 awake보다 늦습니다.

 

    42. override func didDeactivate() {
인터페이스 컨트롤러가 비활성화된다면 호출합니다.

 


코드를 보시게 되면 iOS의 코드와 WatchOS의 코드가 다르지 별반 다르지 않다는 걸 보실 수 있습니다.

Send와 Reset으로 번갈아 가면서 테스트 해보시면 통신의 신기함을 느끼실거에요.😜

애플 공식 홈페이지를 보니 시뮬레이터로 하면 잘 안될수도 있다고 하니 참고하시면 좋겠습니다!

 

신념은 자신이 생각하고 있는 단순한 뭔가가 아니다. 신념은 행동하는 것이다.  - 오바마

댓글