Share This
Связаться со мной
Крути в низ
Categories
//Как работают таймлайны и как обновлять виджеты правильно

Как работают таймлайны и как обновлять виджеты правильно

В этой статье подробно рассмотрены возможности обновления контента в Home Screen и Lock Screen виджетах для iOS 16.

kak rabotajut tajmlajny i kak obnovljat vidzhety pravilno 8d7c0da - Как работают таймлайны и как обновлять виджеты правильно

В iOS 14 Apple представила Home Screen Widgets. Можно было бы сказать, что так впервые появились виджеты, но это не правда – они и до этого существовали в iOS как Today Extensions, но совершенно не пользовались популярностью. То ли дело взорвавшие тренды Home Screen Widgets. Практически день в день с выходом в релиз новой 14-ой iOS приложения для кастомизации Home Screen завоевали все топы и не опускаются до сих пор, а это уже 2 года. Что же можно о них рассказать интересного и какие есть возможности в работе с ними? Как по мне, не так сложно их сделать, как разобраться в обновлении контента на них. Я предлагаю рассмотреть, что такое таймлайны у виджетов (актуально как для home screen, так и для lock screen) и как их менять в зависимости от вполне реальных задач.

Для обновления контента на виджете создается таймлайн-провайдер – это буквально временная шкала обновлений виджета. Таймлайн-провайдер содержит в себе массив entry-объектов, которые хранят в себе дату и время смены контента и сам контент, который необходимо отобразить. Выходит, что таймлайн-провайдер – это некоторое правило: как часто и с какой информацией будет меняться виджет. Например, мы каждый час будем менять background-картинку на виджете. Пусть у нас будет для этого 10 разных картинок. Вот так будет выглядеть код для этой задачи.

         func getTimeline(for configuration: HomeScreenWidgetConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { 		// массив entry объектов 		var entries: [SimpleEntry] = []             let currentDate = Date() 		// наш контент, который мы будем менять 		let images: [Image] = getWidgetImages()      		// мы будем менять картинку каждый час 10 раз     for hourOffset in 0 ..< 10 { 				// создается время смены картинки 				let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! 				// создается entry объект с датой обновления и обновляемым контентом 				let entry = SimpleEntry(date: entryDate, configuration: configuration, image: images[hourOffset]) 				entries.append(entry) 		} 		// создается timeline provider с массивом созданных entry oбъектов 		var timeline = Timeline(entries: entries, policy: .atEnd) 		completion(timeline) }     

Так как мы указали политику обновления policy: .atEnd, WidgetKit запросит новый таймлайн-провайдер после последней даты в entry-объектах. Таким образом, по истечении нашего созданного 10-ти часового timeline provider’а будет создан такой же новый 10 часовой таймлайн-провайдер и наш виджет будет обновлять 10 картинок бесконечно. Кроме .atEnd есть еще следующие:

  1. .never – новый таймлайн-провайдер не создастся по истечении текущего, то есть контент на виджете больше не будет меняться.
  2. .after(Date()) – новый таймлайн создастся после этой даты.

На каждый виджет система выделяет некоторую память, объем которой зависит от множества факторов, один из которых – как часто пользователь пользуется виджетом, то есть смотрит на него. Ресурсы, выделенные системой на один виджет, применяются к 24-часовому периоду, но не к календарным суткам. WidgetKit настраивает 24-часовое окно в соответствии с ежедневной моделью пользования телефоном юзером, что означает, что выделенные ресурсы на день не обязательно сбрасывается ровно в полночь. Для виджета, который пользователь часто просматривает, дневной бюджет ресурсов обычно включает от 40 до 70 обновлений таймлайн-провайдера. Это примерно соответствует перезагрузке виджета каждые 15-60 минут, но на количество обновлений таймлайн-провайдера могут повлиять и другие факторы, значительно их уменьшив. Это означает, что обновлять таймлайн-провайдеры часто мы не можем, а именно обновления чаще 5 минут не поддерживаются. В задачах, где мы точно знаем, на какой контент мы будем обновлять виджет, это ограничение нам ничего и не сделает. Мы все так же сможем обновлять часы каждую минуту, например, вот как будет выглядеть создание таймлайна:

         func getTimeline(for configuration: HomeScreenWidgetConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { 		// массив entry объектов 		var entries: [SimpleEntry] = [] 		// для того чтоб минуты обновлялись ровно по часам, дату нужно взять без секунд             let currentDate = Date().zeroSeconds      		// мы будем менять значение часов каждую минуту     for minuteOffset in 0 ..< 60 { 				// создается время смены картинки 				let entryDate = Calendar.current.date(byAdding: .minute, value: minuteOffset, to: currentDate)! 				// создается entry объект с датой обновления 				let entry = SimpleEntry(date: entryDate, configuration: configuration) 				entries.append(entry) 		} 		// создается timeline provider с массивом созданных entry oбъектов 		var timeline = Timeline(entries: entries, policy: .atEnd) 		completion(timeline) }     

Тут таймлайн мы обновляем каждый час, и внутри таймлайна добавляем 60 entry-объектов на каждую минуту, так мы сможем обновить текст с нужным значением времени. Мы можем добавить и больше 60 entry-объекта – 120, 240 и далее, но и слишком много не получится. Например, при попытке создать entry-объекты сразу на год 60*24*365, бюджет выделенной памяти заполнится и виджет вообще не будет менять информацию. А можем ли мы менять некоторую информацию чаще, чем 1 раз в минуту? Да, можем. Есть два способа это сделать. Если мы хотим виджет с часами с отображением секунд, то во вью самого виджета нам нужно использовать следующее:

         var body: some View {       VStack {          Text(entry.date, style: .timer)      } }     

И тогда мы увидим на виджете время такого формата 14:30:25, которое будет меняться каждую секунду и нам не нужно ничего особенного прописывать при создании таймлайна.

Еще мы просто можем задать таймлайн с обновлением каждую секунду, вот так:

         func getTimeline(for configuration: HomeScreenWidgetConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { 		// массив entry объектов 		var entries: [SimpleEntry] = []     let currentDate = Date()          for secondOffset in 0 ..< 60 { 				let entryDate = Calendar.current.date(byAdding: .second, value: secondOffset, to: currentDate)! 				let entry = SimpleEntry(date: entryDate, configuration: configuration) 				entries.append(entry) 		} 		// создается timeline provider с массивом созданных entry oбъектов 		var timeline = Timeline(entries: entries, policy: .atEnd) 		completion(timeline) }     

Но и тут, конечно же, мы упремся в переполнение выделенной на виджет памяти и в какой-то момент виджет просто перестанет работать, пока память не обновится. Да, это не полностью рабочий вариант, но и он может использоваться, если, например, мы хотим сделать анимационные виджеты, например, гифки. Предсказать точное время работы такого виджета будет крайне сложно, но первые 15-20 секунд точно обеспечены.

Как нам обновлять погодный виджет? Или батарейный? Или любой другой, где мы не можем знать данные наперед и создать сразу таймлайн-провайдер на день/месяц. Мы создаем таймлайн-провайдер с одним entry-объектом и указываем правило обновления – после определенной даты. Пусть это будут 20 минут.

         func getTimeline(for configuration: HomeScreenWidgetConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {     let currentDate = Date()     let currentWeather = getWeather()  		let entry = SimpleEntry(date: currentDate, configuration: configuration, weather: currentWeather)     let endDate = Calendar.current.date(byAdding: .minute, value: 20, to: currentDate)! 		 		var timeline = Timeline(entries: [entry], policy: .after(endDate)) 		completion(timeline) }     

Обновлять таймлайн-провайдер чаще 5 минут мы не можем, соответственно, и обновлять некоторую быстроменяющуюся информацию тоже.

***

Я постаралась подробно рассказать про работу таймлайн-провайдера и способы обновления виджетов. Надеюсь, что эта статья была полезной. Я буду рада фидбэку и комментариям. Спасибо!

Савицкая Надежда, iOS-разработчик.

  • 1 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close