从今日扩展(小部件)在主机应用程序中执行操作,无需打开应用程序ios

Bha*_*ani 17 ios swift today-extension swift3

我想在今天的扩展程序(Widget)中管理包含app的一些操作.

完整描述:在我的包含应用程序中,执行一些操作(如播放/暂停音频).并希望从今天的扩展(小部件)管理该操作.动作也继续在后台状态下执行.

在今天的扩展中,将执行相同的操作.因此,如果在主要包含应用程序中已经启动操作并将其发送到后台状态,则用户可以暂停窗口小部件的操作.并且用户还可以随时从小部件(今天的扩展)开始/暂停操作.

为了实现这个目标,我使用UserDefault和app Group功能并存储一个布尔值.当小部件出现时,它检查布尔值并设置按钮状态播放/暂停.它设置正确,但当我按下扩展按钮时,操作无法在主机应用程序中执行.

码:

在主要包含应用程序代码

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
  }
Run Code Online (Sandbox Code Playgroud)

在扩展类中:

@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()
}
Run Code Online (Sandbox Code Playgroud)

用户更新默认值时未触发通知.它是应用重启时的更新值.

那么如何解决这个问题呢?

同样的事情想要从包含应用程序到小部件的相反方式做.(易于用户单个动作对象,但如何?)

还有其他方法可以在不打开App的情况下从扩展程序中包含应用程序的快速操作吗?

Cod*_*256 9

使用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
    }
}
Run Code Online (Sandbox Code Playgroud)

在扩展中:

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")
}
Run Code Online (Sandbox Code Playgroud)

foo://在Xcode的文档类型部分中,在URL下声明(或者您使用的任何其他内容),然后application(_:open:options:)在您的文件中实现,AppDelegate以便应用程序在传递URL时开始播放音乐foo://startPlaying.

如何将URL添加到Xcode


kar*_*yan 5

  1. 创建自定义URL 方案

  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
        }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 从那里触发您的通知,然后在您的主机应用程序中的任何地方观察它。

    小部件按钮操作代码

    @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)
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 查看自定义 url 类型:https : //developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Inter-AppCommunication/Inter-AppCommunication.html

  6. 笔记:

    应用扩展与其包含的应用之间没有直接通信;通常,当包含的扩展正在运行时,包含的应用程序甚至不会运行。应用扩展的包含应用和宿主应用根本不通信。

    在典型的请求/响应事务中,系统代表主机应用程序打开应用程序扩展,在主机提供的扩展上下文中传送数据。扩展显示用户界面,执行一些工作,如果适合扩展的目的,将数据返回给主机。

    图 2-2 中的虚线表示应用扩展与其包含的应用之间可用的有限交互。Today 小部件(没有其他应用程序扩展类型)可以通过调用类的openURL:completionHandler:方法要求系统打开其包含的应用程序NSExtensionContext。如图 2-3 中的读/写箭头所示,任何应用扩展及其包含的应用都可以访问私有定义的共享容器中的共享数据。图 2-3 中以简单形式显示了扩展、其宿主应用程序及其包含的应用程序之间通信的完整词汇。

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