web-dev-qa-db-ja.com

アプリのiOSを開かずに、Today拡張機能(ウィジェット)からホストアプリでアクションを実行します

今日の拡張機能(ウィジェット)からアプリを封じ込めるアクションを管理したい。

完全な説明:含まれているアプリでは、いくつかのアクション(オーディオの再生/一時停止など)が実行されます。そして、今日の拡張機能(ウィジェット)からもそのアクションを管理したい。アクションはバックグラウンド状態でも引き続き実行されます。

今日の拡張では、同じアクションが実行されます。そのため、メインのアプリを含むアプリがすでにアクションを開始してバックグラウンド状態に送信している場合、ユーザーはウィジェットからアクションを一時停止できます。また、ユーザーはウィジェットからいつでもアクションを開始/一時停止できます(現在の拡張機能)。

この目標を達成するために、アプリグループ機能を備えたUserDefaultを使用し、1つのブール値を格納しました。ウィジェットが存在すると、ブール値をチェックし、ボタンの状態の再生/一時停止を設定します。正しく設定されていますが、拡張ボタンを押すと、ホストアプリでアクションが実行されません。

コード:

主にアプリコードを含む

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()
}

ユーザーがデフォルトを更新したときに通知が発行されませんでした。アプリの再起動時に更新された値です。

では、この問題を解決する方法は?

同じことが、アプリを含めることとウィジェットを含めることとは逆の方法で行いたいと考えています。 (シングルアクションオブジェクトを使用するのは簡単ですが、どのように?)

また、アプリを開かずに拡張機能からアプリを含める際に簡単なアクションを実行する他の方法はありますか?

17

MMWormhole (またはその新しい非公式のSwiftバージョン、ただ Wormhole )を使用してください)。シンプル。

アプリのViewControllerで:

_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")
}
_

Xcodeの[ドキュメントタイプ]セクションの[URL]で_foo://_(またはその他の使用するもの)を宣言してから、AppDelegateapplication(_:open:options:) を実装してアプリを作成します渡されたURLが_foo://startPlaying_になると、音楽の再生を開始します。

how to add URL to Xcode

9
Coder-256
  1. カスタムの作成URL Sceheme

  2. グループデータを確認してください(設定が正しいかどうか)

  3. ボタンをクリックするたびに、ホストアプリはAppdelegateUIApplicationデリゲートから呼び出されます

    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
        }
    
  4. そこから通知を送信し、hostappの任意の場所でそれを観察します。

    ウィジェットボタンアクションコード

    @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)
    
    }
    
  5. カスタムURLタイプを確認してください: https://developer.Apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Inter-AppCommunication/Inter-AppCommunication.html

  6. 注意:

    アプリ拡張機能とそれを含むアプリの間には直接の通信はありません。通常、含まれている拡張機能が実行されている間、含まれているアプリは実行されていません。アプリ拡張機能を含むアプリとホストアプリはまったく通信しません。

    通常の要求/応答トランザクションでは、システムはホストアプリに代わってアプリ拡張機能を開き、ホストによって提供される拡張機能コンテキストでデータを伝達します。拡張機能はユーザーインターフェイスを表示し、いくつかの作業を実行し、拡張機能の目的に適している場合は、データをホストに返します。

    図2-2の点線は、アプリ拡張機能とそれを含むアプリの間で利用できる限られた相互作用を表しています。 Todayウィジェット(および他のアプリ拡張タイプはありません)は、NSExtensionContextクラスのopenURL:completionHandler:メソッドを呼び出すことにより、含まれているアプリを開くようにシステムに要求できます。図2-3の読み取り/書き込み矢印で示されているように、アプリ拡張機能とそれを含むアプリは、プライベートに定義された共有コンテナー内の共有データにアクセスできます。拡張機能、そのホストアプリ、およびその包含アプリ間の通信の完全な語彙は、図2-3に簡単な形式で示されています。

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

4
karthikeyan