Реализация Handoff между iPhone и iPad

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1735 услуг
Реализация Handoff между iPhone и iPad
Средний
~3-5 дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    495

Реализация Handoff между iPhone и iPad

Handoff позволяет пользователю продолжить работу в приложении с одного Apple-устройства на другом. Открыл статью на iPhone — иконка приложения появляется на iPad в Dock, и при нажатии iPad открывает тот же экран в том же месте прокрутки. Реализуется через NSUserActivity и требует правильной настройки на нескольких уровнях.

Предварительные требования

Оба устройства должны быть залогинены под одним Apple ID, Bluetooth и Wi-Fi включены. На уровне проекта — включить Handoff в Capabilities (автоматически добавляет com.apple.developer.associated-domains и нужные entitlements).

В Info.plist указываем NSUserActivityTypes — массив строк-идентификаторов активностей. Соглашение по именованию: com.bundleid.activityname. Активность, не перечисленная в этом массиве, не будет принята системой.

Создание и обновление активности

class ArticleViewController: UIViewController {
  var article: Article

  override func viewDidLoad() {
    super.viewDidLoad()
    setupUserActivity()
  }

  private func setupUserActivity() {
    let activity = NSUserActivity(activityType: "com.myapp.reading-article")
    activity.title = article.title
    activity.userInfo = [
      "articleId": article.id,
      "scrollPosition": 0.0
    ]
    activity.isEligibleForHandoff = true
    // isEligibleForSearch и isEligibleForPrediction — для Spotlight и Siri Suggestions
    self.userActivity = activity
    activity.becomeCurrent()
  }

  // Обновляем состояние при прокрутке
  func scrollViewDidScroll(_ scrollView: UIScrollView) {
    userActivity?.userInfo?["scrollPosition"] = scrollView.contentOffset.y
    userActivity?.needsSave = true  // триггерит updateUserActivityState перед передачей
  }

  override func updateUserActivityState(_ activity: NSUserActivity) {
    activity.addUserInfoEntries(from: [
      "scrollPosition": scrollView.contentOffset.y
    ])
  }
}

needsSave = true — ключевой момент. Система не вызывает updateUserActivityState постоянно — только когда needsSave выставлен. Это значит, что если забыть его выставить при изменении состояния, принимающее устройство получит устаревшие данные.

Обработка на принимающем устройстве

В AppDelegate или SceneDelegate:

func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
  guard userActivity.activityType == "com.myapp.reading-article",
        let articleId = userActivity.userInfo?["articleId"] as? String else {
    return false
  }

  let scrollPosition = userActivity.userInfo?["scrollPosition"] as? CGFloat ?? 0

  // Навигируем к нужному экрану и восстанавливаем позицию
  navigator.openArticle(id: articleId, scrollPosition: scrollPosition)
  return true
}

Для SwiftUI через .onContinueUserActivity:

WindowGroup {
  ContentView()
    .onContinueUserActivity("com.myapp.reading-article") { activity in
      guard let articleId = activity.userInfo?["articleId"] as? String else { return }
      appState.openArticle(id: articleId)
    }
}

Типичные ошибки

userInfo в NSUserActivity должен содержать только property list–совместимые типы: String, Int, Double, Bool, Data, Date, Array, Dictionary. Попытка положить туда кастомный объект — silent failure, активность не передаётся без каких-либо ошибок в лог.

Вызов resignCurrent() при уходе с экрана обязателен — иначе старая активность продолжает рекламировать себя на других устройствах, пока не истечёт таймаут системы.

Сроки

3–5 дней с учётом тестирования на двух физических устройствах. Симулятор Handoff не поддерживает. Стоимость рассчитывается индивидуально.