CapacitorJS:如何在WebView和iOS小部件之间共享本地数据?

问题描述 投票:0回答:1

我在我的电容器应用程序中使用一个小部件(目前这个问题仅关注 iOS)。

我想将 webview 代码中的整数保存到首选项中,然后能够从 iOS 小部件中读取它。

也许我也可以使用 SQLite 来实现此目的,如下所示: https://forum.ionicframework.com/t/will-app-clips-be-available-in-ionic/194768/10?u=folsze

但我认为我仍然需要使用应用程序组,即使对于 sqlite,这也会使电容器的写入过程变得复杂?还是我错了?

这有可能吗?还是我正在尝试完成一些不可能的事情? (iOS 和 Android 都不能实现吗?)。我特别怀疑我的 webview 代码的写入是否能够写入特定组,因为我不确定 Preferences.ConfigureOptions.group 是否引用与 iOS 应用程序扩展组相同的概念

我在 Xcode 中为我的本机应用程序和本机小部件创建了一个组,具有相同的名称: enter image description here

enter image description here

我尝试使用configureoptions组来做到这一点,但整数永远不会到达小部件,它不存在:

https://capacitorjs.com/docs/apis/preferences#configureoptions

这是我的网络视图代码:

const key = 'widgetStreak';
const value = String(this.streak);
console.log("AAA", key, value);
await Preferences.configure({group: "group.com.company.name"});
await Preferences.set({ key, value });

这是我的小部件代码:

import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), streak: 0)
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), streak: fetchStreak())
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []

        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate, streak: fetchStreak())
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }

    private func fetchStreak() -> Int {
        guard let userDefaults = UserDefaults(suiteName: "group.com.company.name") else {
            print("Failed to initialize UserDefaults.")
            return 0
        }
        
        // Ensure that UserDefaults are synchronized
        userDefaults.synchronize()
        
        // Check if the key exists
        if userDefaults.object(forKey: "widgetStreak") == nil {
            print("Key 'widgetStreak' does not exist.")
            return 0
        }
        
        let ret = userDefaults.integer(forKey: "widgetStreak")
        
        if ret == 0 {
            print("The value for 'widgetStreak' is zero.")
        } else {
            print("The value for 'widgetStreak' is: \(ret)")
        }
        
        return ret
    }

}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let streak: Int
}

struct TestWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        VStack {
            Text("Daily Streak")
            Text("\(entry.streak) days")
                .font(.largeTitle)
        }
    }
}

struct TestWidget: Widget {
    let kind: String = "TestWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            if #available(iOS 17.0, *) {
                TestWidgetEntryView(entry: entry)
                    .containerBackground(.fill.tertiary, for: .widget)
            } else {
                TestWidgetEntryView(entry: entry)
                    .padding()
                    .background()
            }
        }
        .configurationDisplayName("Daily Streak Widget")
        .description("Displays your daily streak.")
    }
}

#Preview(as: .systemSmall) {
    TestWidget()
} timeline: {
    SimpleEntry(date: .now, streak: 0)
    SimpleEntry(date: .now, streak: 5)
}

这是我从本机小部件代码获得的输出:

Couldn't read values in CFPrefsPlistSource<0x101c854a0> (Domain: group.com.felixolszewski.geochamp1, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.
Key 'widgetStreak' does not exist.

显然这个警告可以忽略: https://stackoverflow.com/a/39923879/20009330

我还尝试了这篇文章中的所有建议: 无法读取 CFPrefsPlistSource iOS 10 中的值

但是没有任何帮助,仍然得到

Key 'widgetStreak' does not exist.
,这让我认为这对于电容器来说是不可能的。也许有人可以建议一些替代的解决方法?也许可以从小部件访问 webview localStorage?我在这里找到了一些线索,无论出于何种原因,localStorage 或 indexedDB 都不是一个好的选择:

https://forum.ionicframework.com/t/will-app-clips-be-available-in-ionic/194768/10?u=folsze

ios swift cordova ionic-framework capacitor
1个回答
0
投票

不要让 JS 写入数字,而是让 JS 将数字传递给 iOS 端写入数字的插件。我最近已经做到了这一点,并从 Capacitor 的文档中学到了我需要的一切

https://capacitorjs.com/docs/plugins/ios

基本轮廓是

  1. 创建一个 Swift 类来完成这项工作(在他们的示例中是 Echo 类)
  2. 创建一个 Swift 插件,用于侦听来自 Capacitor 的调用并将该数据传递给类 #1
  3. 创建一个JS接口,定义与类1相同的功能
  4. 通过调用Capcitor的“registerPlugin”来制作插件的JS实例

然后您需要使用 Xcode 的应用程序组来确保 App 和 Widget 都可以读取相同的文件

© www.soinside.com 2019 - 2024. All rights reserved.