Выполните действие в хост-приложении из расширения Today (Widget) без открытия приложения ios

Я хочу управлять некоторыми действиями в приложении с сегодняшним расширением (Widget).

Полное описание: в моем поддерживающем приложении выполняются некоторые действия (например, воспроизведение/пауза аудио). И хотите управлять этим действием и с сегодняшнего расширения (виджет). Действие также продолжает выполняться в фоновом состоянии.

В сегодняшнем расширении будет выполнено одно и то же действие. поэтому для этого, если в основном содержащем приложении уже запускается действие и отправляется в фоновое состояние, пользователь может приостановить действие из виджета. и пользователь также может запускать/приостанавливать действие в любое время из виджета (сегодняшнее расширение).

Для достижения этой цели я использовал UserDefault с возможностью группы приложений и сохранил одно логическое значение. когда присутствует виджет, он проверяет логическое значение и устанавливает состояние воспроизведения/паузы в кнопке. он установлен правильно, но когда я нажимаю кнопку кнопки расширения, действие не выполняется в главном приложении.

код:

в главном коде приложения

override func viewDidLoad() {
    super.viewDidLoad()
    let objUserDefault = UserDefaults(suiteName:"group.test.TodayExtensionSharingDefaults")

    let objTemp = objUserDefault?.object(forKey: "value")

    self.btnValue.isSelected = objTemp

    NotificationCenter.default.addObserver(self, selector: #selector(self.userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
}




func userDefaultsDidChange(_ notification: Notification) {

        let objUserDefault = UserDefaults(suiteName: "group.test.TodayExtensionSharingDefaults")
        objUserDefault?.synchronize()   
        let objTemp = objUserDefault?.object(forKey: "value")
        self.btnValue.isSelected = objTemp
  }

В классе расширения:

@IBAction func onPlayPause(_ sender: UIButton) {
       DispatchQueue.main.async {
       let sharedDefaults = UserDefaults(suiteName: "group.test.TodayExtensionSharingDefaults")

       if let isPlaying = sharedDefaults?.bool(forKey: "isPlaing") {

       sharedDefaults?.set(!isPlaying, forKey: "isPlaying")

       }else{

        sharedDefaults?.set(false, forKey: "isPlaying")
        }

        sharedDefaults?.synchronize()
}

уведомление не было запущено, когда пользователь обновил значение по умолчанию. он обновляет значение, когда приложение перезагружается.

как решить эту проблему?

и то же самое хочет сделать в противоположном смысле от содержания приложения к виджету. (простой пользовательский объект с одним действием, но как?)

И любой другой способ выполнить быстрое действие в приложении приложения из расширения без открытия приложения?

Ответы

Ответ 1

Используйте MMWormhole (или его новую и неофициальную версию Swift, просто Wormhole). Это очень просто.

В контроллере вида приложения:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    let wormhole = MMWormhole(applicationGroupIdentifier: "group.test.TodayExtensionSharingDefaults",
                              optionalDirectory: "TodayExtensionSharingDefaults")

    wormhole.listenForMessage(withIdentifier: "togglePlayPause") { [weak self] _ in
        guard let controller = self else { return }
        controller.btnValue.isSelected = controller.btnValue.isSelected
    }
}

В расширении:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view from its nib.

    self.wormhole = MMWormhole(applicationGroupIdentifier: "group.test.TodayExtensionSharingDefaults", optionalDirectory: "TodayExtensionSharingDefaults")
}

@IBAction func onPlayPause(_ sender: UIButton) {
    guard let wormhole = self.wormhole else { extensionContext?.openURL(NSURL(string: "foo://startPlaying")!, completionHandler: nil) } // Throw error here instead of return, since somehow this function was called before viewDidLoad (or something else went horribly wrong)

    wormhole.passMessageObject(nil, identifier: "togglePlayPause")
}

Объявите foo:// (или что-то еще, что вы используете) в разделе "Типы документов Xcode" в разделе "URL-адреса", а затем выполните application(_:open:options:) в своем AppDelegate, чтобы приложение начало воспроизводить музыку, когда переданный URL был foo://startPlaying.

как добавить URL-адрес в Xcode

Ответ 2

  • Создать пользовательский URL-адрес Sceheme

  • Проверьте данные групп (вы устанавливаете правильно или нет)

  • Всякий раз, когда вы нажимаете кнопку, хост-приложение будет вызвано из Appdelegate, UIApplication делегировать

    func application(_ application: UIApplication, open urls: URL, sourceApplication: String?, annotation: Any) -> Bool {
    
            let obj = urls.absoluteString.components(separatedBy: "://")[1]
            NotificationCenter.default.post(name: widgetNotificationName, object: obj)
            print("App delegate")
            return true
        }
    
  • Огоньте свое уведомление, а затем наблюдайте за ним в любом месте вашего хостап.

    Виджет Действие кнопки code

    @IBAction func doActionMethod(_ sender: AnyObject) {
    
        let button = (sender as! UIButton)
        var dailyThanthi = ""
        switch button.tag {
        case 0:
            dailyThanthi = "DailyThanthi://h"
        case 1:
            dailyThanthi = "DailyThanthi://c"
        case 2:
            dailyThanthi = "DailyThanthi://j"
            //        case 3:
            //            dailyThanthi = "DailyThanthi://s"
            //        case 4:
            //            dailyThanthi = "DailyThanthi://s"
        default:
            break
        }
    
        let pjURL = NSURL(string: dailyThanthi)!
        self.extensionContext!.open(pjURL as URL, completionHandler: nil)
    
    }
    
  • Проверьте тип пользовательского URL: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Inter-AppCommunication/Inter-AppCommunication.html

  • Примечание:

    Прямой связи между расширением приложения и его приложение; обычно приложение, содержащее приложение, даже не работает выполняется расширенное расширение. Расширения приложений, содержащие приложение и приложение-хост вообще не связывается.

    В типичной транзакции запроса/ответа система открывает расширение приложения от имени хост-приложения, передавая данные в расширение контекст, предоставленный хостом. Расширение отображает пользовательский интерфейс, выполняет определенную работу и, при необходимости, для целей расширения, возвращает данные хосту.

    Пунктирная линия на рисунке 2-2 представляет собой ограниченное взаимодействие между расширением приложения и его содержащим приложением. Сегодня виджет (и другой тип расширения приложения) может попросить систему открыть его приложение, вызвав метод openURL:completionHandler:класс NSExtensionContext. Как указано стрелками чтения/записи в Рисунок 2-3. Любое расширение приложения и его приложение могут обращаться к общим данных в отдельном контейнере общего доступа. Полный словарь связь между расширением, его хост-приложением и его содержанием приложение показано в простой форме на рисунке 2-3.

    https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html