繁体   English   中英

如何从 iOS 应用程序向其 Safari 分机发送消息(令牌)?

[英]How to send a message (tokens) from an iOS App to its Safari Extension?

我尝试在诸如此类的帖子中寻找解决方案, 其中人们遇到了非常相似的问题: How to send a message from iOS App to a Safari Extension?

我什至读过这篇文章,其中作者解释了如何使用SafariExtensionHandler在选择上下文菜单后将消息从浏览器发送到应用程序并返回到浏览器,但这并不是我想要的。

将令牌从 iOS 应用程序发送到 Safari 分机

在应用程序中,用户必须输入 email 和密码才能登录他们的帐户。 他们登录后,我将他们的信息保存在UserDefaults中,如下所示:

class AuthDataService {
{...}
        URLSession.shared.dataTaskPublisher(for: urlRequest)
            .tryMap { data, response -> Data in
                guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200,
                      let accessToken = httpResponse.value(forHTTPHeaderField: "Access-Token"),
                      let clientId = httpResponse.value(forHTTPHeaderField: "Client"),
                      let uid = httpResponse.value(forHTTPHeaderField: "Uid")
                else {
                    throw CustomError.cannotExecuteRequest
                }
                
                let sharedDefaults = UserDefaults(suiteName: "group.com.MyCompany.MyProject")
                sharedDefaults?.set(accessToken, forKey: "Access-Token")
                sharedDefaults?.set(clientId, forKey: "Client")
                sharedDefaults?.set(uid, forKey: "Uid")

                return data
            }
{...}
}

应用组

根据我对这篇文章的理解,我需要创建一个App Group,以便在iOS App和Safari Extension之间共享数据。 我将该组命名为: "group.com.MyCompany.MyProject" (就像UserDefaults中的 suiteName 一样)。

主视图

用户登录时看到的屏幕是一个 SwiftUI 视图,它有一个链接,将用户带到 Safari,这样他们就可以自己打开扩展程序:

struct HomeView: View {
    @EnvironmentObject var viewModel: AuthViewModel
    var body: some View {
        Link(destination: URL(string: "https://www.apple.com/")!) {
            Text("Take me to Safari")
        }
    }
}

SafariWebExtensionHandler

现在,我阅读的所有文章都在谈论如何通过 SafariWebExtensionHandler 的beginRequest(with:)将数据从 Safari 扩展发送到 iOS 应用程序。 但是,每当用户登录应用程序或打开 Safari 扩展程序时,我都会尝试在 UserDefaults 中发送令牌。

我尝试从UserDefaults中检索数据,看看我是否至少可以在终端中读取它,但调试器永远无法print语句:

import SafariServices
import os.log

let SFExtensionMessageKey = "message"

class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {

    func readData() {
        let sharedDefaults = UserDefaults(suiteName: "group.com.lever.clientTokens")
        print(sharedDefaults?.object(forKey: "Access-Token")) //<-- This line never gets executed
    }
    
    func beginRequest(with context: NSExtensionContext) {
        let item = context.inputItems[0] as! NSExtensionItem
        let message = item.userInfo?[SFExtensionMessageKey]
        os_log(.default, "Received message from browser.runtime.sendNativeMessage: %@", message as! CVarArg)

        let response = NSExtensionItem()
        response.userInfo = [ SFExtensionMessageKey: [ "Response to": message ] ]

        readData()
        
        context.completeRequest(returningItems: [response], completionHandler: nil)
    }

}

问题

macOS 与 iOS

Apple 的这份文档有一个名为“ Send messages from the app to JavaScript部分,这几乎是我想要做的。 该文档甚至提到了SFSafariApplication.dispatchMessage(withName:toExtensionWithIdentifier:userInfo:completionHandler:)理论上它会向 JavaScript 脚本发送一条消息,但它说它只适用于 macOS:

您不能将消息从包含 iOS 的应用程序发送到您的 web 扩展程序的 JavaScript 脚本。

这篇出色的 Medium 文章讨论了使用来自 openai.com 的 API 从应用程序向 Safari 扩展发送 APIKey。 似乎它还使用SFSafariApplicationSafariWebExtensionHandler通信,但它又看起来只适用于 macOS。

Safari 网页扩展

我还阅读了其他 Apple 文档,认为它会有所帮助,但它只讨论了将消息从 Safari 扩展的弹出窗口传递到网页。

结论

所以我的问题是:

SafariWebExtensionHandler中编写代码是否是将数据从 iOS 应用程序发送到我的 Safari 扩展程序的正确方法? 这个可以在iOS做吗? 还是仅适用于 macOS?

我阅读了一些其他文章,这些文章讨论了如何使用 Resources 文件夹中的 JavaScript 文件来“收听”更改。 但我对如何从我的应用程序发送这些更改以使 Safari 扩展程序能够收听它们感到有点困惑。

我想要实现的是,用户在从 iOS 应用程序中的 HomeView 重定向后,已经登录到 Safari 扩展,而不必再次登录。

感谢您的宝贵时间和帮助!

虽然应用程序无法发起与扩展的连接,但支持另一个方向。 当扩展程序向应用程序发送数据时,您可以附加响应并将其发送回扩展程序。

我不确定为什么您使用SafariWebExtensionHandler的示例不起作用。 看起来不错。 也许问题出在 JS 方面。

应用程序

import SafariServices
import os.log

extension String: Error {}

let SFExtensionMessageKey = "message"

class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
    func beginRequest(with context: NSExtensionContext) {
        let item = context.inputItems[0] as! NSExtensionItem
        let data = item.userInfo?[SFExtensionMessageKey] as AnyObject?
        guard let key = data?["key"] as? String else {
            return
        }
        let response: [String: Any]
        do {
            switch(key) {
            case "getToken":
                os_log(.default, "handling getToken")
                guard let sharedDefault = UserDefaults(suiteName: "group.com.lever.clientTokens") else {
                    throw "UserDefaults not found"
                }
                guard let tokenData = sharedDefault.data(forKey: "Access-Token") else {
                    throw "Access-Token not found"
                }
                guard let tokenString = String(data: tokenData, encoding: .utf8) else {
                    throw "tokenData could not be converted into string";
                }
                response = ["success": true, "resp": tokenString]
            default:
                throw "Invalid key"
            }
        } catch {
            os_log(.error, "Error: %s", String(reflecting: error))
            response = ["success": false, "resp": ["message": error.localizedDescription]]
        }
        let resp = NSExtensionItem()
        resp.userInfo = [SFExtensionMessageKey: response]
        context.completeRequest(returningItems: [resp], completionHandler: nil)
    }
}

扩大

首先将"nativeMessaging"添加到“manifest.json”中的"permissions"

async function getToken() {
  const resp = await browser.runtime.sendNativeMessage("_", { 
    key: "getToken"
  });
  if (resp.success) {
    return resp.resp
  } else {
    throw new Error(resp.message)
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM