У меня есть приложение SwiftUI с виджетом. Когда я запускаю приложение через Xcode (прямо на своем устройстве или в симуляторе), виджет работает точно так, как ожидалось.
Однако, когда я запускаю приложение через TestFlight, виджет появляется, но не отображает никаких данных — это просто пустой заполнитель. Виджет должен отображать изображение и текст, но он не отображает ни того, ни другого.
Я видел несколько сообщений на форумах разработчиков Apple о подобных проблемах. В одном из принятых ответов говорится следующее:
Убедитесь, что вы используете Xcode 12 beta 4 и iOS 14 beta 4 на своих устройствах. Убедитесь, что у вас реализован заполнитель (in:). Убедитесь, что у вас нет заполнителя (с:), потому что это то, что предлагалось в предыдущей бета-версии Xcode с автозаполнением, и без этого заполнитель не заработает. Я думаю, что вся эта проблема вызвана переименованием методов 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) -> ()) {
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? Какие у меня есть варианты?
Спасибо! Обновление:
Если я использую неотредактированные () виджет, виджет показывает изображение и текст «УРОВЕНЬ» и «ОЦЕНКА», но по-прежнему не отображает никаких фактических данных. Итак, мое представление SwiftUI теперь выглядит так:
У меня есть приложение SwiftUI с виджетом. Когда я запускаю приложение через Xcode (прямо на своем устройстве или в симуляторе), виджет работает точно так, как ожидалось. Однако, [b]когда я запускаю приложение через TestFlight, виджет появляется, но не отображает никаких данных — это просто пустой заполнитель. Виджет должен отображать изображение и текст, но он не отображает ни того, ни другого.[/b] Я видел несколько сообщений на форумах разработчиков Apple о подобных проблемах. В одном из принятых ответов говорится следующее: [list] [*]Убедитесь, что вы используете Xcode 12 beta 4 и iOS 14 beta 4 на своих устройствах. Убедитесь, что у вас реализован заполнитель (in:). Убедитесь, что у вас нет заполнителя (с:), потому что это то, что предлагалось в предыдущей бета-версии Xcode с автозаполнением, и без этого заполнитель не заработает. Я думаю, что вся эта проблема вызвана переименованием методов WidgetKit, но это уже другая история. [*]В соответствии с примечаниями к выпуску вам необходимо установить для параметра «Dead Code Stripping» значение NO в целевом расширении. настройки сборки. Это необходимо только для цели расширения. [*]При загрузке архива в App Store Connect снимите флажок «Включить биткод для контента iOS». [*] Удалите старую сборку с устройства при установке новой бета-версии. [/list] Я выполнил эти предложения, но безрезультатно. Вот мой код виджета. Сначала он извлекает игровые данные через CloudKit, а затем создает временную шкалу: [code]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
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)") } } } }
@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]) } } [/code] [b]Вопрос:[/b] Почему мой виджет не работает при распространении через TestFlight? Какие у меня есть варианты? Спасибо! [b]Обновление:[/b] Если я использую неотредактированные () виджет, виджет показывает изображение и текст «УРОВЕНЬ» и «ОЦЕНКА», но по-прежнему не отображает никаких фактических данных. Итак, мое представление SwiftUI теперь выглядит так: [code]struct WidgetEntryView : View { var entry: Provider.Entry