본문 바로가기

iOS 앱 개발자 프로젝트

[iOS] Widget - IntentConfiguration (4)

  • 정적/동적 선택 목록을 만들기 위해서 Custom Intent Definition 을 생성  완료
  • 동적 선택 목록을 설정하고, 기본값을 설정하기 위해서 Intents Extension 추가  ← 여기서 진행 예정
  •  Intents Extension 활용한 Intent  핸들링  ← 여기서 진행 예정
  • configurable properties 를 지원하기 위해 IntentTimelineProvider 사용  ← 진행 예정

 

동적 데이터 로드는 사용자 경험을 향상시키기 위해 사용자의 의도에 따라 필요한 데이터를 실시간으로 로드하고 표시하는 것을 뜻하며, 이를 통해 사용자는 필요한 정보를 즉시 확인할 수 있고, 보다 인터랙티브하고 반응성이 좋은 애플리케이션을 사용할 수 있다.


 

 

현재 위젯 코드 현황

 

앞서 진행한 TodoWidget.swift 에서는 Provider 구조체가 IntentTimelineProvider 프로토콜을 구현하여 위젯의 데이터를 제공하고 있다. 특히 getTimeline(for:in:completion:) 메서드에서 메인 앱의 정보를 랜덤하게 가져오는 로직이 포함되어 있다. 

 

 

SimpleEntry 구조체

  • SimpleEntry는 타임라인 항목을 정의하는 구조체. 위젯에 표시될 데이터를 나타낸다.
struct SimpleEntry: TimelineEntry {
    let date: Date
    let title: String
    let isCompleted: Bool
}

 

 

Provider 구조체

  • Provider 구조체는 IntentTimelineProvider 프로토콜을 구현하여 위젯의 데이터를 제공하고 타임라인을 관리한다.
struct Provider: IntentTimelineProvider {
    typealias Entry = SimpleEntry
    typealias Intent = TodoListIntent

    // 플레이스홀더 항목을 반환하는 함수
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), title: "Sample Task", isCompleted: false)
    }

    // 스냅샷을 반환하는 함수
    func getSnapshot(for configuration: TodoListIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), title: "Sample Task", isCompleted: false)
        completion(entry)
    }

    // 타임라인을 반환하는 함수
    func getTimeline(for configuration: TodoListIntent, in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> ()) {
        var entries: [SimpleEntry] = []

        // Core Data에서 할 일 목록을 가져옴
        let todos = CoreDataManager.shared.fetchTodos()

        if let randomTodo = todos.randomElement() {
            let entry = SimpleEntry(date: Date(), title: randomTodo.title, isCompleted: randomTodo.iscompleted)
            entries.append(entry)
        } else {
            // 할 일 목록이 없으면 플레이스홀더 항목 추가
            let entry = SimpleEntry(date: Date(), title: "No Tasks", isCompleted: false)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

 

 

placeholder(in:)

 

  • 위젯이 처음 로드되거나 데이터가 아직 준비되지 않았을 때 표시할 플레이스홀더 항목을 반환.
  • 여기서는 임시 데이터("Sample Task")를 사용하여 플레이스홀더 항목을 생성.
func placeholder(in context: Context) -> SimpleEntry {
    SimpleEntry(date: Date(), title: "Sample Task", isCompleted: false)
}

 

 

 

getSnapshot(for:in:completion:)

  • 위젯의 스냅샷을 반환하는 함수
  • 이 함수는 사용자가 위젯을 미리보기 할 때 호출
  • 임시 데이터("Sample Task")를 사용하여 스냅샷 항목을 생성
func getSnapshot(for configuration: TodoListIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
    let entry = SimpleEntry(date: Date(), title: "Sample Task", isCompleted: false)
    completion(entry)
}

 

 

 

 

getTimeline(for:in:completion:)

 

  • getTimeline 메서드는 위젯에 표시할 데이터의 타임라인을 반환
  • CoreDataManager.shared.fetchTodos()를 호출하여 Core Data에서 할 일 목록을 가져온다.
  • 가져온 할 일 목록에서 무작위로 하나의 할 일을 선택 (todos.randomElement())
    • 선택된 할 일이 있다면, SimpleEntry를 생성하여 entries 배열에 추가
    • 선택된 할 일이 없다면, "No Tasks"라는 제목을 가진 플레이스홀더 항목을 생성하여 entries 배열에 추가
  • 최종적으로 entries 배열을 사용하여 Timeline 객체를 생성하고, 완료 핸들러에 전달
func getTimeline(for configuration: TodoListIntent, in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> ()) {
    var entries: [SimpleEntry] = []

    // Core Data에서 할 일 목록을 가져옴
    let todos = CoreDataManager.shared.fetchTodos()

    if let randomTodo = todos.randomElement() {
        let entry = SimpleEntry(date: Date(), title: randomTodo.title, isCompleted: randomTodo.iscompleted)
        entries.append(entry)
    } else {
        // 할 일 목록이 없으면 플레이스홀더 항목 추가
        let entry = SimpleEntry(date: Date(), title: "No Tasks", isCompleted: false)
        entries.append(entry)
    }

    let timeline = Timeline(entries: entries, policy: .atEnd)
    completion(timeline)
}

 

[정리]

 

  • Provider 구조체: IntentTimelineProvider 프로토콜을 구현하여 위젯에 필요한 데이터를 제공
  • placeholder(in:) 메서드: 위젯의 플레이스홀더 데이터 제공
  • getSnapshot(for:in:completion:) 메서드: 위젯의 스냅샷 데이터를 제공
  • getTimeline(for:in:completion:) 메서드: Core Data에서 할 일 목록을 가져와 무작위로 하나의 할 일을 선택하여 타임라인 데이터 생성 → 할 일 목록이 없는 경우에는 "No Tasks"라는 플레이스홀더 항목을 생성

 

 

 


 

 

이제, Intent가 무엇인지 짚고 넘어가자.

 

  • Intent:
    • 사용자가 수행하려는 작업 또는 요청 (앱과의 상호작용 정의) 
    • 예: 할 일 목록을 추가하거나, 특정 정보를 검색하는 것 등
  • Intent 정의:
    • Intents.intentdefinition 파일을 사용하여 Intents 정의
    • Intents의 매개변수, 유형, 사용자 인터페이스 등을 설정
  • Intent Handler:
    • Intent를 처리하는 클래스 또는 구조체.
    • 사용자가 요청한 작업을 수행하고, 그 결과를 반환

 

 

Intents Extension은 "Intent Extension"과 "Intent UI Extension"이 있는데
둘 다 사용자와의 상호작용을 개선하는 데 사용되지만,
그 목적과 기능에는 차이가 있다.

 

 

 

Intent Extension

 

Intent Extension은 주로 음성 비서 또는 챗봇 시스템에서 사용된다. 이는 사용자가 특정 의도를 표현할 때 이를 인식하고 해당 의도에 맞는 응답이나 동작을 수행하도록 돕는다. 기본적으로 다음과 같은 역할을 한다.

  • 의도 인식: 사용자의 발화나 입력에서 의도를 추출
  • 대응 로직: 인식된 의도에 따라 특정 작업을 수행하거나 응답을 제공
  • 데이터 처리: 사용자 입력을 처리하고 필요한 데이터를 검색하거나 연산

 

Intent UI Extension

 

Intent UI Extension은 Intent Extension의 기능에 사용자 인터페이스(UI)를 추가한 것으로, 사용자의 의도에 따라 UI 요소를 생성하고 제어하여 보다 직관적이고 풍부한 사용자 경험을 제공한다. 기본적으로 다음과 같은 역할을 한다.

  • UI 요소 생성: 사용자의 의도에 따라 버튼, 리스트, 폼 등 다양한 UI 요소를 동적으로 생성
  • 상호작용 관리: 사용자가 UI 요소와 상호작용하는 동안 이를 관리하고 필요에 따라 업데이트
  • 시각적 피드백 제공: 사용자의 의도에 대한 시각적 피드백을 제공하여 상호작용을 더욱 명확하게 만든다.

 


 

 

 Intents Extension을 추가해보자.

 

 

 

Next를 누르면, 다음과 같은 팝업이 뜨는데, 각 필드에 대한 설명은 대략 아래와 같다.

intents extension 추가

 

 

  • Product Name:
    • 새 Extension의 이름. TodoIntents라고 입력했다.
  • Team:
    • 애플 개발자 계정을 선택. 만약 설정되어 있지 않다면, 팀을 선택하지 않거나 나중에 설정할 수 있다.
  • Organization Identifier:
    • 이 필드는 기본적으로 프로젝트의 설정을 따른다. 보통 com.yourcompany.yourappname 형태.
  • Bundle Identifier:
    • 번들 식별자가 자동으로 생성. 우리는 com.seungwon.Challendar.TodoIntents 로 생성되었다.
    • 원하는 경우 수동으로 변경할 수 있다.
  • Starting Point:
    • 시작 지점. 기본값은 Messaging이지만, 필요에 따라 다른 값을 선택할 수 있다.
  • Include UI Extension:
    • UI Extension을 포함할지 선택. 위젯의 UI가 필요 없다면 체크를 해제할 수 있다.
  • Project:
    • 새 Extension이 추가될 프로젝트를 선택. 기본적으로 현재 작업 중인 프로젝트로 설정.
  • Embed in Application:
    • 새 Extension이 포함될 애플리케이션을 선택. 기본적으로 현재 작업 중인 애플리케이션으로 설정된다.

 

 

 

자동 생성된 IntentHandler에 기본적인 코드만 넣었더니 기존 코드에서 다음과 같은 에러를 비롯한..

추가된 파일에서 앱과 intent 타게팅 설정 오류들이 나타나기 시작했다.. 

 

 

        // Intents 핸들러를 통해 할 일 목록을 가져옴
        let handler = IntentHandler()
        handler.handle(intent: configuration) { response in
            switch response.code {
            case .success:
                if let title = response.title, let isCompleted = response.isCompleted {
                    let entry = SimpleEntry(date: Date(), title: title, isCompleted: isCompleted)
                    entries.append(entry)
                }
            default:
                let entry = SimpleEntry(date: Date(), title: "No Tasks", isCompleted: false)
                entries.append(entry)
            }
            let timeline = Timeline(entries: entries, policy: .atEnd)
            completion(timeline)
        }
    }
}