簡體   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