Irbisa · Шпаргалка
Расширения и WidgetKit
Таймлайны WidgetKit, Live Activities, App Intents, share- и notification-расширения, App Groups
Что такое app extension, какие виды встречаются чаще всего и чем расширение отличается от основного приложения по процессу, жизненному циклу и памяти?
Из каких частей состоит виджет WidgetKit и что на самом деле где выполняется?
struct StepsEntry: TimelineEntry { let date: Date let steps: Int} struct StepsProvider: TimelineProvider {…Что делают
placeholder,snapshotиtimelineв timeline provider виджета и когда WidgetKit вызывает каждый из них?struct SalesProvider: AppIntentTimelineProvider { // Synchronous and instant: the redacted skeleton. func placeholder(in context: Context) -> SalesEntry { SalesEntry(date: .now, total: 1234, store: .sample) }…Как сделать переход из виджета на конкретный экран приложения и чем
widgetURLотличается отLink?struct OrdersWidgetView: View { let entry: OrdersEntry @Environment(\.widgetFamily) private var family var body: some View { VStack(alignment: .leading) {…Что такое Live Activity и чем она отличается от виджета и от push-уведомления?
Эта view виджета должна отсчитывать время каждую секунду. Почему этот отсчёт никогда не тикает на домашнем экране и как это исправить?
struct CountdownView: View { let entry: CountdownEntry @State private var secondsLeft = 0 var body: some View { Text("\(secondsLeft) s left") .onAppear { secondsLeft = Int(entry.deadline.timeIntervalSinceNow) } .onReceive(Timer.publish(every: 1, on: .main, in: .common).autoconnect()) { _ in secondsLeft = Int(entry.deadline.timeIntervalSinceNow) } }}Как работают политики перезагрузки
.atEnd,.after(_:)и.neverи как приложение обновляет виджет сразу после изменения данных?func timeline(for configuration: SalesIntent, in context: Context) async -> Timeline<SalesEntry> { let hours = await SalesStore.forecast(for: configuration.store) let entries = hours.map { SalesEntry(date: $0.start, total: $0.total) } // Ask again only when the numbers can actually differ. return Timeline(entries: entries, policy: .after(SalesStore.nextOpeningBell))…Найдите баг: виджет шагов всегда показывает 0, хотя приложение показывает правильное число и перезагружает виджет после каждого сохранения.
// Compiled into both the app and the widget extension.enum StepsStore { private static var defaults: UserDefaults { .standard } static func save(_ steps: Int) { defaults.set(steps, forKey: "todaySteps") } static func todaySteps() -> Int { defaults.integer(forKey: "todaySteps") }} // App: called when HealthKit delivers new samples.func didUpdateSteps(_ steps: Int) { StepsStore.save(steps) WidgetCenter.shared.reloadTimelines(ofKind: "StepsWidget")} func makeEntry() -> StepsEntry { // widget extension, in getTimeline StepsEntry(date: .now, steps: StepsStore.todaySteps())}Этот provider должен показывать каждый из четырёх почасовых прогнозов, когда наступает его час. Что происходит вместо этого и как исправить?
func timeline(for configuration: CityIntent, in context: Context) async -> Timeline<WeatherEntry> { let forecast = await WeatherCache.hourly(for: configuration.city) let entries = forecast.prefix(4).map { hour in WeatherEntry(date: .now, temperature: hour.temperature) } return Timeline(entries: entries, policy: .atEnd)}Как часто виджет может обновляться и что расходует его бюджет обновлений?
Как сделать настраиваемый пользователем виджет на App Intents и откуда берётся список вариантов для выбора?
struct ProjectEntity: AppEntity { let id: String let name: String static let typeDisplayRepresentation: TypeDisplayRepresentation = "Project" var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(name)") }…Как работают интерактивные виджеты начиная с iOS 17 и почему
onTapGestureв виджете ничего не делает?struct CompleteTaskIntent: AppIntent { static let title: LocalizedStringResource = "Complete task" @Parameter(title: "Task") var taskID: String init() {}…Что такое режимы рендеринга виджета и почему цвета виджета пропадают на экране блокировки или на тонированном домашнем экране?
struct BuildStatusView: View { @Environment(\.widgetRenderingMode) private var renderingMode let build: Build var body: some View { VStack(alignment: .leading, spacing: 6) {…Что в ActivityKit относится к
ActivityAttributes, а что к егоContentState, и как запустить, обновить и завершить Live Activity?struct DeliveryAttributes: ActivityAttributes { // Fixed at request time. let orderID: String let restaurantName: String // Replaced on every update; at most 4 KB.…Какие представления есть у Live Activity в Dynamic Island и когда система использует каждое из них?
struct DeliveryLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: DeliveryAttributes.self) { context in // Lock Screen, devices without an Island, and update banners. LockScreenView(context: context) } dynamicIsland: { context in…Что такое App Intent и что нужно добавить, чтобы действие работало из Siri и Spotlight без настройки шортката пользователем?
struct LogDrinkIntent: AppIntent { static let title: LocalizedStringResource = "Log a drink" @Parameter(title: "Drink", requestValueDialog: "Which drink?") var drink: DrinkEntity…Найдите баг: текстовые push-уведомления, которые обрабатывает этот notification service extension, приходят примерно на 30 секунд позже, а push с картинкой приходит сразу.
final class NotificationService: UNNotificationServiceExtension { var handler: ((UNNotificationContent) -> Void)? var best: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { handler = contentHandler best = request.content.mutableCopy() as? UNMutableNotificationContent guard let best, let link = best.userInfo["image"] as? String, let url = URL(string: link) else { return } Task { if let attachment = try? await ImageAttachment.download(url) { best.attachments = [attachment] } contentHandler(best) } } override func serviceExtensionTimeWillExpire() { if let handler, let best { handler(best) } }}Как share-расширение получает файл, которым поделились, и как надёжно загрузить большой файл на сервер?
final class ShareViewController: UIViewController { static let groupID = "group.com.example.clips" override func viewDidLoad() { super.viewDidLoad() guard let item = extensionContext?.inputItems.first as? NSExtensionItem, let provider = item.attachments?.first(where: {…Как запускать и обновлять Live Activity с сервера через push-уведомления и какие токены нужны бэкенду?
func observeLiveActivityTokens() { // 1. One token per activity type, before anything runs. Task { for await token in Activity<DeliveryAttributes>.pushToStartTokenUpdates { await api.register(startToken: token.hex) }…На устройстве виджет пустой, хотя приложение работает нормально, а вывод
printнигде не появляется. Как отлаживать widget extension?