ios - iOS 14 小部件在本地工作,但通过 TestFlight 失败
问题描述
我有一个带有小部件的 SwiftUI 应用程序。当我通过 Xcode(直接在我的设备或模拟器上)运行应用程序时,小部件完全按预期工作。
但是,当我通过 TestFlight 运行应用程序时,小部件确实出现了,但它没有显示任何数据——它只是一个空的占位符。该小部件应该显示图像和一些文本,但它都不显示。
我在 Apple Developer 论坛上看到过一些关于类似问题的帖子。一个被接受的答案是这样说的:
- 确保在您的设备上使用 Xcode 12 beta 4 和 iOS 14 beta 4。确保您已实施占位符(in:)。确保您没有 placeholder(with:) 因为这是 Xcode 以前的 beta 建议的自动完成功能,没有它,您将无法使用占位符。我认为这整个问题是由 WidgetKit 方法重命名引起的,但这是另一回事。
- 根据发行说明,您需要在扩展目标的构建设置中将“Dead Code Stripping”设置为 NO。这仅对扩展的目标是必需的。
- 将存档上传到 App Store Connect 时,取消选中“包括 iOS 内容的位码”。
- 安装新测试版时从设备中删除旧版本。
我已经实施了这些建议,但无济于事。
这是我的小部件代码。它首先通过 CloudKit 获取游戏数据,然后创建一个时间线:
import WidgetKit
import SwiftUI
import CloudKit
struct WidgetCloudKit {
static var gameLevel: Int = 0
static var gameScore: String = ""
}
struct Provider: TimelineProvider {
private var container = CKContainer(identifier: "MyIdentifier")
static var hasFetchedGameStatus: Bool = false
func placeholder(in context: Context) -> SimpleEntry {
return SimpleEntry(date: Date(), gameLevel: 0, gameScore: "0")
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry: SimpleEntry
if context.isPreview && !Provider.hasFetchedGameStatus {
entry = SimpleEntry(date: Date(), gameLevel: 0, gameScore: "0")
} else {
entry = SimpleEntry(date: Date(), gameLevel: WidgetCloudKit.gameLevel, gameScore: WidgetCloudKit.gameScore)
}
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let pred = NSPredicate(value: true)
let sort = NSSortDescriptor(key: "creationDate", ascending: false)
let q = CKQuery(recordType: "gameData", predicate: pred)
q.sortDescriptors = [sort]
let operation = CKQueryOperation(query: q)
operation.desiredKeys = ["level", "score"]
operation.resultsLimit = 1
operation.recordFetchedBlock = { record in
DispatchQueue.main.async {
WidgetCloudKit.gameLevel = record.value(forKey: "level") as? Int ?? 0
WidgetCloudKit.gameScore = String(record.value(forKey: "score") as? Int ?? 0)
Provider.hasFetchedGameStatus = true
var entries: [SimpleEntry] = []
let date = Date()
let entry = SimpleEntry(date: date, gameLevel: WidgetCloudKit.gameLevel, gameScore: WidgetCloudKit.gameScore)
entries.append(entry)
// Create a date that's 15 minutes in the future.
let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: date)!
let timeline = Timeline(entries: entries, policy: .after(nextUpdateDate))
completion(timeline)
}
}
operation.queryCompletionBlock = { (cursor, error) in
DispatchQueue.main.async {
if let error = error {
print("queryCompletion error: \(error)")
} else {
if let cursor = cursor {
print("cursor: \(cursor)")
}
}
}
}
self.container.publicCloudDatabase.add(operation)
}
}
struct SimpleEntry: TimelineEntry {
var date: Date
var gameLevel: Int
var gameScore: String
}
struct WidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
GeometryReader { geo in
VStack {
Image("widgetImage")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: geo.size.width)
HStack {
VStack {
Text("LEVEL")
Text(entry.gameLevel == 0 ? "-" : "\(entry.gameLevel)")
}
VStack {
Text("SCORE")
Text(entry.gameScore == "0" ? "-" : entry.gameScore)
}
}
}
}
}
}
@main
struct Widget: SwiftUI.Widget {
let kind: String = "MyWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
WidgetEntryView(entry: entry)
}
.configurationDisplayName("Game Status")
.description("Shows an overview of your game status")
.supportedFamilies([.systemSmall])
}
}
问题:为什么我的小部件在通过 TestFlight 分发时不工作?我有什么选择,在这里?
谢谢!
更新:
如果我使用unredacted()
视图修饰符,小部件会显示图像以及“LEVEL”和“SCORE”文本,但仍不显示任何实际数据。所以,我的 SwiftUI 视图现在看起来像这样:
struct WidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
GeometryReader { geo in
VStack {
Image("widgetImage")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: geo.size.width)
HStack {
VStack {
Text("LEVEL")
Text(entry.gameLevel == 0 ? "-" : "\(entry.gameLevel)")
}
VStack {
Text("SCORE")
Text(entry.gameScore == "0" ? "-" : entry.gameScore)
}
}
}
.unredacted() // <-- I added this
}
}
}
更新 #2: 在文章Keeping A Widget Up To Date中,有一节讨论了后台网络请求:
当您的小部件扩展处于活动状态时,例如在提供快照或时间线时,它可以启动后台网络请求。例如,获取队友当前状态的游戏小部件,或获取带有图像缩略图的标题的新闻小部件。发出异步后台网络请求可让您快速将控制权交还给系统,从而降低因响应时间过长而被终止的风险。
我是否需要设置这个(复杂的)后台请求范例才能使 CloudKit 为我的小部件工作?我在正确的轨道上吗?
解决方案
您是否尝试将 cloudkit 容器部署到生产环境?您可以在 CloudKit 仪表板上找到它。
推荐阅读
- jquery - 按钮单击本地存储
- kotlin - 收到此错误“无法编译值文件”
- mysql - 在 JSON_KEYS 上加入
- reactjs - 反应重定向到移动网站
- api - SaaS 应用程序数据摄取到 DL/DWH - NFR 包括哪些内容?
- go - 如何使用从数组中获取的数据在 Golang 中创建对象对象?
- hyperledger-fabric - 不同的超级账本结构链码可以查看世界状态中的所有键/值对吗?
- pandas - Pandas 累积总和超过 1 个指数,但不是其他 3 个
- python - 我可以在dict中获取列表项吗?
- r - 与 geom_image 和 PNG 包结合使用时,geom_mark_hull 绘图不正确