NEULiee / iOS-App-Dev-Tutorials

Apple 개발자 홈페이지의 UIKit 튜토리얼을 보고 정리하는 repository 입니다.
0 stars 0 forks source link

6. System Frameworks #10

Closed NEULiee closed 2 years ago

NEULiee commented 2 years ago

Loading Reminders

NEULiee commented 2 years ago

Loading Reminders

(마지막 부분이라 마무리 느낌일 줄 알았는데 하이라이트였다..)

Learned

  1. Fetch Reminders Asynchronously

    • LocalizedError

      • 이 프로토콜을 상속 받아서 필요한 Error 정의를 enum 으로 할 수 있다.
    • EKEventStore

      • EKEventStore object 는 유저의 캘린더 이벤트와 미리알림에 접근할 수 있다.
      • Calendar, Reminder 데이터는 Calendar database에 저장되고 EventKit을 통해 접근할 수 있다.(CRUD)
      • EventKit이 Calendar, Reminder 앱의 모든 기능을 제공하는 것은 아니다. 단, 알람 및 일정 반복 설정은 가능하다.
      • 외부 앱이나 클라우드로 공유되어 있는 다른 기기(맥북, 아이패드 등)에서 데이터를 변경하게 되면 EventKit을 사용하는 모든 앱에 알람을 보내 변경사항에 대한 업데이트를 할 수 있다.
    • async, await, continuation

      • 어렵다! 100 중 10만 이해한 느낌
      • withCheckedThrowingContinuation : Swift 5.5 동시성
      • Checked 가 붙으면 동시 실행 시 단일 실행을 보장하는 함수라는 특징이 있다. (Thread-Safety)
      • continuation : CheckedContinuation 구조체
      • resume(returing:) - 태스크 반환 값을 조작할 수 있다.
  2. Convert Between Model Types

    • EKReminder data -> Reminder
      • 연동할 미리알림 앱은 due date 가 필요하지 않지만 Today 앱은 사용자가 일의 due date 순서대로 알려줘야하기 때문에 due date 가 없는 알림이면 거른다. -> error
  3. Create a Reminder Store

    • 지금까지 간단한 배열에 reminder를 저장한 것을 데이터의 지속성을 위해 reminder store abstraction (reminder 저장소) 을 만든다.
    • static let shared = ReminderStore() -> singleton pattern
    • reminder 권한 부여 상태가 .authorized 이면 true를 반환한다. -> 사용자가 reminder 데이터에 대한 접근권한을 부여했는지 확인
    var isAvailabel: Bool {
        EKEventStore.authorizationStatus(for: .reminder) == .authorized
    }
  4. Read All Reminders

    • 1 에서 만든 비동기 함수를 사용하여 EveneKit 에서 모든 reminder 를 반환한다. -> 앱의 내부 Reminder 타입으로 변환
    • 검색 조건을 remider로 한정하여 데이터를 가져온다.
     // reminder 항목으로만 좁힐 수 있다.
    let predicate = ekStore.predicateForReminders(in: nil)
    let ekReminders = try await ekStore.fetchReminders(matching: predicate)
    • compactMap 을 사용하여 dueDate가 없는 reminder를 거르고 앱 내부의 Reminder 타입으로 변환한다.
      // [EKReminder] -> [Reminder]
      let reminders: [Reminder] = try ekReminders.compactMap { ekReminder in
      do {
          return try Reminder(with: ekReminder)
      } catch TodayError.reminderHasNoDueDate {
          // nil을 반환하면 대상 컬렉션에서 이 reminder를 삭제
          return nil
      }
      }
  5. Request Access to Reminder Data

    • 아이폰의 미리알림 데이터에 대한 권한 요청
    • 권한 상태를 가져올 수 있다. let status = EKEventStore.authorizationStatus(for: .reminder)
    • 권한 상태에 따라서 권한을 요청을 하거나 예외를 던질 수 있다. try await ekStore.requestAccess(to: .reminder)
    • switch 문의 @unknown default
      • @unknown을 추가하면 enum에 새 속성을 추가할 때 컴파일러에게 경고를 제공한다.
  6. Display Errors to the User

    • 오류 alert
      func showError(_ error: Error) {
      let alertTitle = NSLocalizedString("Error", comment: "Error alert title")
      let alert = UIAlertController(title: alertTitle, message: error.localizedDescription, preferredStyle: .alert)
      let actionTitle = NSLocalizedString("OK", comment: "Alert OK button title")
      alert.addAction(UIAlertAction(title: actionTitle, style: .default, handler: { [weak self] _ in
          self?.dismiss(animated: true)
      }))
      present(alert, animated: true, completion: nil)
      }
  7. Display Reminders

    • reminder store 적용하여 snapshot 을 업데이트 한다.

    • 비동기적으로 실행되는 새로운 작업 단위 : Task

      func prepareReminderStore() {
      Task {
          do {
              try await reminderStore.requestAccess()
              reminders = try await reminderStore.readAll()
          } catch TodayError.accessDenied, TodayError.accessRestricted {
              #if DEBUG
              reminders = Reminder.sampleData
              #endif
          }
      }
      }
    • if DEBUG, #endif

    • 오류가 발생하면 디버그 모드에서 샘플데이터를 할당한다. 이 플래그는 realse 용 앱을 빌드할 때 코드가 컴파일되지 않도록 하는 컴파일 지시문이다. 디버그 빌드에서 코드를 테스트하거나 샘플 테스트 데이터 제공을 위해 이 플래그를 사용할 수 있다.

    • Info.plist 말고 [프로젝트 - Info]

  8. Test EventKit Integration



Quiz

Q. Which class provides access to the user's calendar and reminders data? A. EKEventStroe The EKEventStore class is an app's point of contact for accessing calendar and reminder data


Q. What is the event store's authorization status for reminders for if the user taps "Don't Allow" when the app asks for access to Reminders? A. denied denied indicates that the user explicitly denied app access to reminders data.


Q. What is the type of fetched in this code sample?

let eventStore = EKEventStore()

func loadReminders() {
   let predicate = eventStore.predicateForReminders(in: nil)
   eventStore.fetchReminders(matching: predicate) { (fetched) in

   }
}

A. [EKReminder]? fetchReminders(matching:completion:) fetches reminders that match a given predicate. It passes an optional array of EKReminder to its completion handler.

NEULiee commented 2 years ago

Saving Reminders

Today 앱을 닫으면 사용자가 변경한 내용이 사라지고, 사용자가 앱 외부에서 변경한 내용이 자동으로 반영되지 않는다.

Learned

  1. Respond to Change Notifications

    • 사용자가 다른 앱에서 remider 를 변경하는 경우 해당 변경 사항이 Today 앱에서 자동으로 표시되도록 구현
    • Task : 비동기 작업의 단위
    • 변경알림을 수신하면 뷰컨트롤러에서 eventStorChanged() 함수를 호출한다. NotificationCenter.default.addObserver(self, selector: #selector(eventStoreChanged(_:)), name: .EKEventStoreChanged, object: nil)
    • 시뮬레이터에서 앱 전환 화면 : Control-Shift-Command-H
  2. Read Reminders Individually

  3. Convert Between Model Types

    • forEach
  4. Save Reminders Individually

  5. Save Added Reminders

    • 매개변수와 같은 이름으로 변수를 선언하면 더이상 매개변수의 데이터에 접근할 수 없다.
  6. Save Updated Reminders

  7. Remove Reminders