[英]Complication works on Simulator, but not on Device
I have a complication that works on Simulator, but doesn't work on an actual device when I TestFlight it to test on an actual device (and for clarity sake if there is any confusion, I'm not talking about debugging via device, but just testing if it works on a device). 我有一个适用于Simulator的复杂功能,但是当我对它进行TestFlight测试以在实际设备上进行测试时,却无法在实际设备上运行(为了清楚起见,如果有任何混淆,我不是在谈论通过设备进行调试,而是只是测试它是否可以在设备上运行)。
Specifically, on the Watch device: 具体来说,在Watch设备上:
getPlaceholderTemplateForComplication
works on Simulator too)... 我通过自定义钟面在手表上选择了Complication,这为我提供了占位符文本(到目前为止非常好,因为getPlaceholderTemplateForComplication
可以在Simulator上运行)... getCurrentTimelineEntryForComnplication
works on Simulator)... 但随后,“并发症”始终保持为占位符文本(不正确,因为getCurrentTimelineEntryForComnplication
在Simulator上有效)... getTimelineEntriesForComplication:afterDate
works on Simulator)... 即使在时间旅行中滚动时,占位符文本也不会改变,只是变暗(不正确,因为getTimelineEntriesForComplication:afterDate
在Simulator上有效)... Info
on iPhone: iPhone上的Info
:
game.duel = playoffs[“Duel”] as! String
game.tv = playoffs[“TV”] as! String
game.td = playoffs[“TD”] as! AnyObject
let dictionary = [“Duel” : game.duel, “TV” : game.tv, “TD” : game.td]
let transferComplication = WCSession.defaultSession().transferCurrentComplicationUserInfo(dictionary)
ExtensionDelegate
in WatchKit Extension: ExtensionDelegate
在WatchKit扩展:
var duelArray = [String]()
var tvArray = [String]()
var tdArray = [NSDate]()
let defaults = NSUserDefaults.standardUserDefaults()
if let duel = userInfo[“Duel”] as? String, let tv = userInfo[“TV”] as? String, let td = userInfo[“TD”] as? String {
duelArray.append(duel)
tvArray.append(tv)
tdArray.append(td as! NSDate)
defaults.setObject(duelArray, forKey: “DuelSaved”)
defaults.setObject(tvArray, forKey: "TVSaved”)
defaults.setObject(tdArray, forKey: "TDSaved”)
}
ComplicationController
in WatchKit Extension: WatchKit扩展中的ComplicationController
:
func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: ((CLKComplicationTimelineEntry?) -> Void)) {
switch complication.family {
case .ModularLarge:
let mlTemplate = CLKComplicationTemplateModularLargeStandardBody()
if let currentDuel = defaults.arrayForKey(“DuelSaved”) as? [String] {
let firstDuel = currentDuel[0]
let headerTextProvider = CLKSimpleTextProvider(text: firstDuel)
mlTemplate.headerTextProvider = headerTextProvider
} else {
// …
}
if let currentTV = defaults.arrayForKey(“TVSaved”) as? [String] {
let firstTV = currentTV[0]
let body1TextProvider = CLKSimpleTextProvider(text: firstTV)
mlTemplate.body1TextProvider = body1TextProvider
} else {
// …
}
if let currentTD = defaults.arrayForKey("TDSaved"){
let firstTD = currentTD[0]
let body2TextProvider = CLKTimeTextProvider(date: firstTD as! NSDate)
mlTemplate.body2TextProvider = body2TextProvider
} else {
// …
}
let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: mlTemplate)
handler(timelineEntry)
// …
}
func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: (([CLKComplicationTimelineEntry]?) -> Void)) {
let headerArray = defaults.arrayForKey(“DuelSaved”)
let body1Array = defaults.arrayForKey("TVSaved")
let body2Array = defaults.arrayForKey("TDSaved")
guard let headers = headerArray, texts = body1Array, dates = body2Array else { return }
var entries = [CLKComplicationTimelineEntry]()
for (index, header) in headers.enumerate() {
let text = texts[index]
let date1 = dates[index]
let headerTextProvider = CLKSimpleTextProvider(text: header as! String, shortText: headerShort as? String)
let body1TextProvider = CLKSimpleTextProvider(text: text as! String)
let timeTextProvider = CLKTimeTextProvider(date: date1 as! NSDate)
let template = CLKComplicationTemplateModularLargeStandardBody()
template.headerTextProvider = headerTextProvider
template.body1TextProvider = body1TextProvider
template.body2TextProvider = timeTextProvider
switch complication.family {
case .ModularLarge:
let timelineEntry = CLKComplicationTimelineEntry(date: date1 as! NSDate, complicationTemplate: template)
entries.append(timelineEntry)
// …
}
func requestedUpdateDidBegin() {
let server=CLKComplicationServer.sharedInstance()
for comp in (server.activeComplications) {
server.reloadTimelineForComplication(comp)
}
}
This is the flow of the data: 这是数据流:
transferCurrentComplicationUserInfo
passes data to the Watch ExtensionDelegate
wherein the data is saved in NSUserDefaults
. transferCurrentComplicationUserInfo
将数据传递到Watch ExtensionDelegate
其中数据保存在NSUserDefaults
。 ComplicationController
then pulls its initial data from NSUserDefaults
. 然后, ComplicationController
从NSUserDefaults
其初始数据。
At first glance: 乍一看:
This doesn't appear to be working code, as you'd be seeing a lot of Xcode fix-it errors about "Unicode curly quote found, ...". 这似乎不是有效的代码,因为您会看到很多有关“发现Unicode弯引号,...”的Xcode修复错误。
Please avoid force downcasting with as!
请避免用as!
强制向下转换as!
as it can fail and your code will crash. 因为它可能会失败,并且您的代码将崩溃。 You've got lots of unnecessary type casting taking place. 您发生了很多不必要的类型转换。 As I mentioned before , you should be typing your variables to allow the compiler to catch any programmer errors. 如前所述 ,您应该输入变量以允许编译器捕获任何程序员错误。
For example, if your dictionary's keys and values are both strings, then safely type it as: 例如,如果字典的键和值都是字符串,则可以安全地将其键入为:
var playoffs: [String: String]
Your extension delegate code may conditionally fail, if the as?
您的扩展委托代码可能有条件地失败,如果as?
downcast is not possible (because you passed something different than what you expected to receive). 无法进行向下转换(因为您传递的内容与预期的有所不同)。 Make sure you're passing the types of values that you expect, or that whole block won't run. 确保传递的是您期望的值类型,否则整个代码块将无法运行。 You can easily check that in the debugger by setting a breakpoint and stepping through that code. 您可以通过设置断点并逐步执行该代码来轻松地在调试器中检查该代码。
You also need to explicitly update your complication, once the info is received. 收到信息后,您还需要显式更新并发症。
func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) { if let ... { // Retrieve values from dictionary // Update complication let complicationServer = CLKComplicationServer.sharedInstance() guard let activeComplications = complicationServer.activeComplications else { // watchOS 2.2 return } for complication in activeComplications { complicationServer.reloadTimelineForComplication(complication) } } }
It's really convoluted what you're doing with the arrays and NSUserDefaults
. 确实NSUserDefaults
了您使用数组和NSUserDefaults
所做的NSUserDefaults
。 While it's perfectly appropriate to persist data between launches, NSUserDefaults
is never meant to be a way to "pass" details from one part of your code to another. 尽管在 NSUserDefaults
启动之间持久保存数据是非常合适的,但 NSUserDefaults
绝不意味着将细节从代码的一部分传递到另一部分。
Your complication data source should get its data from a model or data manager, instead of from NSUserDefaults
. 您的复杂数据源应该从模型或数据管理器而不是从NSUserDefaults
获取数据。
The getCurrentTimelineEntryForComplication
if let ... { } else {
code makes no sense. if let ... { } else {
代码,则没有意义的getCurrentTimelineEntryForComplication
。 If you didn't get an array of strings, what do you expect to do in the else
block? 如果没有得到一个字符串数组,那么您期望在else
块中做else
?
You can also prepare your data before the switch statement, to make your code more readable and compact, like so: 您还可以在switch语句之前准备数据,以使代码更具可读性和紧凑性,如下所示:
func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: ((CLKComplicationTimelineEntry?) -> Void)) { // Call the handler with the current timeline entry let recentData = DataManager.sharedManager.complicationData ?? "???" let template: CLKComplicationTemplate? let simpleTextProvider = CLKSimpleTextProvider(text: recentData) switch complication.family { case .ModularLarge: let modularLargeTemplate = CLKComplicationTemplateModularLargeStandardBody() modularLargeTemplate.headerTextProvider = CLKSimpleTextProvider(text: "Update Complication", shortText: "Update") modularLargeTemplate.body1TextProvider = simpleTextProvider template = modularLargeTemplate case .UtilitarianLarge: let utilitarianLargeTemplate = CLKComplicationTemplateUtilitarianLargeFlat() utilitarianLargeTemplate.textProvider = simpleTextProvider template = utilitarianLargeTemplate case .CircularSmall: let circularSmallTemplate = CLKComplicationTemplateCircularSmallSimpleText() circularSmallTemplate.textProvider = simpleTextProvider template = circularSmallTemplate case .ModularSmall: let modularSmallTemplate = CLKComplicationTemplateModularSmallSimpleText() modularSmallTemplate.textProvider = simpleTextProvider template = modularSmallTemplate case .UtilitarianSmall: let utilitarianSmallTemplate = CLKComplicationTemplateUtilitarianSmallFlat() utilitarianSmallTemplate.textProvider = simpleTextProvider template = utilitarianSmallTemplate } let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: template!) handler(timelineEntry) }
The likely issue is that ClockKit
asked for complication data, well before your extension even received it, so your complication data source had no data to provide, and no entries appear. 可能的问题是ClockKit
在您的扩展程序甚至没有收到并发症数据之前就要求它提供数据,因此您的并发症数据源没有要提供的数据,也没有条目出现。
Even though something happens to work on the simulator, it doesn't mean your code is robust to also work on the actual device. 即使在模拟器上发生了某些事情,这并不意味着您的代码也可以在实际设备上运行。 There are all sorts of differences that can account for why it won't work on the real hardware, which is why you absolutely need to debug interactively on the device. 有各种各样的差异可以解释为什么它不能在实际的硬件上运行,这就是为什么您绝对需要在设备上进行交互式调试的原因。 It will help you realize why your code isn't working as intended. 它将帮助您了解为什么代码无法按预期工作。
Ideally, you should be doing this type of interactive debugging and solving those other issues before coming here, so you can ask a very specific question with a minimal working block of code. 理想情况下,您应该在进行此类型的交互式调试之前,先解决其他问题,然后才能用最少的工作代码块提出一个非常具体的问题。 Questions which require someone to broadly debug your code really aren't useful to others in general. 一般而言,那些需要某人广泛调试您的代码的问题实际上并没有帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.