Site icon AppTractor

Создание интерактивного виджета с помощью App Intents

В этом примере мы создадим интерактивный виджет, который увеличивает глобальный счетчик в целевом приложении, используя нажатия кнопки и App Intents.

Этот пример требует двух разных файлов из-за расширения Widget.

Начнем с основного целевого приложения.

Здесь мы сначала создаем общую структуру, которую будут использовать как целевое приложение, так и расширение виджета. Она просто хранит счетчик в пользовательских настройках по умолчанию с включенными App Groups (это позволяет и приложению, и его расширению виджета получить к нему доступ).

Далее, во второй части, используем ExampleIntent, — это то, что приводит в действие нажатие кнопки в виджете. Тут просто получаем доступ к общему счетчику, увеличиваем его и возвращаемся. Если вам нужно знать, как настроить базовый App Intent, посмотрите этот фрагмент.

import SwiftUI
import AppIntents
import WidgetKit

// 1
class Counter {
    private static let sharedDefaults: UserDefaults = UserDefaults(suiteName: "group.examples.sjc")!
    
    static func incrementCount() {
        var count = sharedDefaults.integer(forKey: "count")
        count += 1
        sharedDefaults.set(count, forKey: "count")
    }
    
    static func currentCount() -> Int {
        sharedDefaults.integer(forKey: "count")
    }
}

// 2
struct ExampleIntent: AppIntent {
    static var title: LocalizedStringResource = "Increment Count"
    static var description = IntentDescription("Increments a shared count with the main app.")
    
    func perform() async throws -> some IntentResult {
        Counter.incrementCount()
        return .result()
    }
}

struct ContentView: View {
    @Environment(\.scenePhase) private var phase
    @State private var count: Int = 0
    
    var body: some View {
        VStack {
            Text("Count: \(count)")
        }
        .padding()
        .onChange(of: phase) {
            count = Counter.currentCount()
        }
    }
}

А в расширении Widget используем такой код:

import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), count: "\(Counter.currentCount())")
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        completion(SimpleEntry(date: Date(), count: "\(Counter.currentCount())"))
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let timeline = Timeline(entries: [SimpleEntry(date: Date(), count: "\(Counter.currentCount())")], policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let count: String
}

struct WidgetsEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        VStack {
            Text("Count:")
            Text(entry.count)
            // 3
            Button(intent: ExampleIntent()) {
                Text("Increment Count")
            }
        }
        .containerBackground(.fill.tertiary, for: .widget)
    }
}

struct Widgets: Widget {
    let kind: String = "Widgets"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            WidgetsEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}

Тут мы используем инициализатор Button, который принимает App Intent. Мы передаем в него наш ExampleIntent для выполнения. Когда он будет выполнен, WidgetKit запросит свежую временную шкалу и, таким образом, покажет наш обновленный счетчик в виджете (и в самом приложении).

В результате получаем общий счетчик для виджета и целевого приложения:

Вот и все, до следующего раза ✌️

Источник

Exit mobile version