[SwiftUI] CloudKit: Setup & User Info

Junyoung Park·2022년 8월 24일
0

SwiftUI

목록 보기
49/136
post-thumbnail

Setup CloudKit in SwiftUI project and get user info | Advanced Learning #21

CloudKit: Setup & User Info

구현 목표

  • CloudKit → 애플 기기 연동 가능한 공용 데이터베이스
  • iCloud 연동 허용 및 유저 데이터 패치

구현 태스크

  • Xcode을 통해 iCloud 추가
  • iCloud 콘솔을 통해 레코드 생성
  1. CloudKit 실행 → 데이터베이스 설치
  2. iCloud 상태 읽기
  3. 유저 정보 접근 허용
  4. 유저 레코드 ID 얻기
  5. 유저 레코드 ID를 통한 유저 정보 데이터 패치

핵심 코드

    func fetchiCloudUserRecordID() {
        CKContainer.default().fetchUserRecordID { [weak self] returnedId, returnedError in
            guard let self = self else { return }
            if let id = returnedId {
                self.discoveriCloudUser(id: id)
            }
        }
    }
    
    func discoveriCloudUser(id: CKRecord.ID) {
        CKContainer.default().discoverUserIdentity(withUserRecordID: id) { [weak self] returnedIdentity, returnedError in
            guard let self = self else { return }
            DispatchQueue.main.async {
                if let name = returnedIdentity?.nameComponents?.givenName {
                    self.userName = name
                }
            }
        }
    }
  • 유저 정보를 가져올 수 있는 방법은 상당히 많다. 유저 레코드 아이디, 휴대폰 번호, 이메일 주소 등을 통해 가져올 수 있다. (유니크한 프라이머리 키이기 때문이다)
  • 이중 어떤 키를 통해 유저 정보에 접근을 하든, 특정 키를 얻어야 하는 데에는 동일 → 유저 레코드 아이디 접근을 위한 허락이 필요
  • 데이터 패치 과정은 비동기 데이터 접근이므로 DispatchQueue.main을 통해 스레드 관리 → 강한 참조 사이클 방지를 위한 weak self 선언 주의

소스 코드

struct CloudKitUserBootCamp: View {
    @StateObject private var viewModel = CloudKitUserBootCampViewModel()
    var body: some View {
        VStack {
            Text("IS SIGNED IN: \(viewModel.isSignedIntoiCloud.description.uppercased())")
            Text(viewModel.error)
            Text("Permission Status: \(viewModel.permissionStatus.description)")
            Text("User Name: \(viewModel.userName)")
        }
    }
}
import SwiftUI
import CloudKit

class CloudKitUserBootCampViewModel: ObservableObject {
    @Published var permissionStatus: Bool = false
    @Published var isSignedIntoiCloud: Bool = false
    @Published var error: String = ""
    @Published var userName: String = ""
    
    init() {
        getiCloudStatus()
        requestPermission()
        fetchiCloudUserRecordID()
    }
    
    private func getiCloudStatus() {
        CKContainer.default().accountStatus { [weak self] status, error in
            guard let self = self else { return }
            DispatchQueue.main.async {
                switch status {
                case .couldNotDetermine:
                    self.error = CloudKitError.iCloudAccountNotDetermined.rawValue
                    break
                case .available:
                    self.isSignedIntoiCloud = true
                    break
                case .restricted:
                    self.error = CloudKitError.iCloudAccountRestricted.rawValue
                    break
                case .noAccount:
                    self.error = CloudKitError.iCloudAccountNotFound.rawValue
                    break
                case .temporarilyUnavailable:
                    self.error = CloudKitError.iCloudAccountUnavailable.rawValue
                    break
                @unknown default:
                    self.error = CloudKitError.iCloudAccountUnknown.rawValue
                    break
                }
            }
        }
    }
    
    enum CloudKitError: String, LocalizedError {
        case iCloudAccountUnavailable
        case iCloudAccountNotFound
        case iCloudAccountNotDetermined
        case iCloudAccountRestricted
        case iCloudAccountUnknown
    }
    
    func requestPermission() {
        CKContainer.default().requestApplicationPermission([.userDiscoverability]) { [weak self] returnedStatus, returnedError in
            guard let self = self else { return }
            DispatchQueue.main.async {
                if returnedStatus == .granted {
                    self.permissionStatus = true
                }
            }
        }
    }
    
    func fetchiCloudUserRecordID() {
        CKContainer.default().fetchUserRecordID { [weak self] returnedId, returnedError in
            guard let self = self else { return }
            if let id = returnedId {
                self.discoveriCloudUser(id: id)
            }
        }
    }
    
    func discoveriCloudUser(id: CKRecord.ID) {
        CKContainer.default().discoverUserIdentity(withUserRecordID: id) { [weak self] returnedIdentity, returnedError in
            guard let self = self else { return }
            DispatchQueue.main.async {
                if let name = returnedIdentity?.nameComponents?.givenName {
                    self.userName = name
                }
            }
        }
    }
}
  • 현재 iCloud 상태에 접근 가능한지 확인
  • iCloud 유저 정보에 접근 가능한지 유저에게 직접 허락을 받아야 함
  • 허락을 받았다면 유저 레코드 정보를 통해 유저 정보를 자동으로 패치 가능

    직접 허락을 받은 뒤 불리언 변수 permissionStatus를 참으로 변경하는 데, 이 값이 바뀌었을 때 자동으로 fetchCloudUserRecordID 함수를 호출하는 게 앱 사용 흐름 상 적절할 것 같다.

profile
JUST DO IT

0개의 댓글