I’m trying to update the watchOS2 clockkit complication with the data transmitted by WatchConnectivity from IOS/iPhone.
Despite quite a lot of research, but It has not been successful so far. I found that other posts are describing similar challenges (no solution yet)
Facing 3 problems:
1) The sendMessage from ComplicationController does not seem to wake up IOS Parent App (the same sendMessage sent from InterfaceController at the same time will wake up IOS Parent App)
2) Even if the value is passed to ComplicationController (via sendUserInfoToComplication and when the IOS application is in the foreground), the value displayed in the complication Sometimes it also updates (the pattern is not found, why sometimes it will/sometimes not)
3) I set “getNextRequestUpdate ..” to 2 minutes (for testing purposes). But it doesn’t seem to have any Difference. (It can be triggered at any time even in the simulator, but the “budget” is not used / I set a plug to verify)
Please note that I am relatively new to IOS/Swift programming. But I think, based on other questions/posts, I don’t seem to be the only one struggling with this.
Here is the sample code:
ComplicationController:
//
// ComplicationController.swift
// IOSDataToComplication WatchKit Extension
//
// Created by Thomas Peter on 11.10.2015 .
// Copyright © 2015 Thomas Peter. All rights reserved.
//
import ClockKit
import WatchConnectivity
class ComplicationController: NSObject, CLKCompl icationDataSource, WCSessionDelegate {
var session:WCSession!
var text:String = "watchdefault"
var textOld:String = ""
var header:String = " TestHeader"
override init(){
super.init()
startSession()
}
// MARK:-Timeline Configuration
func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
handler([.None])
}
func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
handler(nil)
}
func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
handler(nil)
}
func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComp licationPrivacyBehavior) -> Void) {
handler(.ShowOnLockScreen)
}
// MARK:-Timeline Population
func getCurrentTimelineEntryForComplication(complication: CLKComplication , withHandler handler: ((CLKComplicationTimelineEntry?) -> Void)) {
// Call the handler with the current timeline entry
if complication.family == .ModularLarge {
//createData()
let entry = self.createTimeLineEntry(text, date: NSDate())
handler(entry)
} else {
handler(nil)
}
}
func getTimelineEntriesForComplication( complication: CLKComplication, beforeDate date: NSDate, limit: Int, withHandler handler: (([CLKComplicationTimelineEntry]?) -> Void)) {
// Call the handler with the timeline entries prior to the given date
handler(nil)
}
func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: (([CLKComplicationTimelineEntry]?) -> Void)) {
// Call the handler with the timeline entries after to the given date
handler(nil)
}
// MARK:-Update Scheduling
func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void ) {
// Call the handler with the date when you would next like to be given the opportunity to update your complication content
handler(NSDate(timeIntervalSinceNow: (60 * 2)))
}
// MARK:-Placeholder Templates
func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {
// This method will be called once per supported complication, and the results will be cached
let template = CLKComplicationTemplateModula rLargeStandardBody()
template.headerTextProvider = CLKSimpleTextProvider(text: "header")
template.body1TextProvider = CLKSimpleTextProvider(text:"defaul text")
handler( nil)
}
func requestedUpdateDidBegin() {
print("Complication update is starting")
createData()
let server=CLKComplicationServer.sharedInstance()
for comp in (server.activeComplications) {
server.reloadTimelineForComplication(comp)
print("Timeline has been reloaded!")
}
}
func requestedUpdateBudgetExhausted() {
print( "Budget exhausted")
}
func createTimeLineEntry(bodyText: String, date:NSDate) -> CLKComplicationTimelineEntry {
let template = CLKComplicationTemplateModularLargeStandardBody( )
template.headerTe xtProvider = CLKSimpleTextProvider(text: header)
template.body1TextProvider = CLKSimpleTextProvider(text: text)
let entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
return(entry )
}
func createData(){
let applicationData = ["wake":"fromComplication"]
session.sendMessage(applicationData, replyHandler: {(replyMessage: [String: AnyObject]) -> Void in
// handle reply from iPhone app here
//let answer:[String:AnyObject] = replyMessage
self.text = replyMessage["text"] as! String
print("complication received messagereply \(self.text)")
}, errorHandler: {(error) -> Void in
// catch any errors here
print("no reply message")
})
print("complication sent \(applicationD ata) to iOS")
}
func session(session: WCSession, didReceiveUserInfo userInfo: [String: AnyObject]) {
textOld = text
text = userInfo["text"] as! String
print("complication received userinfo \(text)")
dispatch_async(dispatch_get_main_queue()) {
//if text != textOld {
self.requestedUpdateDidBegin()
//}
}
}
private func startSession(){
if (WCSession.isSupported()) {
session = WCSession.defaultSession()
session.delegate = self;
session.activateSession()
}
}
}
View Controller:
//
// ViewController.swift
// IOSDataToComplication
//
// Created by Thomas Peter on 11.10.2015.< br />// Copyright © 2015 Thomas Peter. All rights reserved.
//
import UIKit
import WatchConnectivity
class ViewController: UIViewController, WCSessionDelegate {
var session:WCSession!
var time:String = "default"
var text:String = "default"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
startSession()
getData()
sendUserInfoToComplication()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getData(){
let timeValue = NSDate()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "hh: mm"
time = dateFormatter.stringFromDate(timeValue)
text = "iPhone \(time)"
print("constructed \(text)")
}
private func startSession(){
if (WCSession.isSupported()) {
session = WCSession.defaultSession()
session.delegate = self;
session.activateSession()
}
print("iPhone session started" )
}
func session(session: WCSession, didReceiveMessage message: [String: AnyObject], replyHandler: ([String: AnyObject]) -> Void) {
let receivedMessage = message["wake"] as! String
print("iphone received message \(receivedMessage)")
let applicationDict = ["text":text]
replyHandler(applicationDict as [String: AnyObject])
getData()
}
func sendUserInfoToComplication(){
let applicationDict = ["text":text]
session.transferCurrentComplicationUserInfo(ap plicationDict)
print("iphone sent userinfo \(applicationDict) to complication")
}
}
In addition , When running the simulator, I received a lot of similar messages, as shown below:
objc[7501]: Class SPAssetCacheAssets is implemented in both /Applications/Xcode.app /Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk/System/Library/Frameworks/WatchKit.framework/WatchKit and /Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/ SDKs/WatchSimulator.sdk/System/Library/PrivateFrameworks/SockPuppetGizmo.framework/SockPuppetGizmo. One of the two will be used. Which one is undefined.
objc[7501]: Class SPAssetCacheSyncData is implemented in both /Applications/Xcode .app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk/System/Library/Frameworks/WatchKit.framework/WatchKit and /Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/ Developer/SDKs/WatchSimulator.s dk/System/Library/PrivateFrameworks/SockPuppetGizmo.framework/SockPuppetGizmo. One of the two will be used. Which one is undefined.
< div class="answer"> Use CLKComplicationServer like this:
CLKComplicationServer* server = [CLKComplicationServer sharedInstance];
[server.activeComplications enumerateObjectsUsingBlock:^( CLKComplication * _Nonnull each, NSUInteger idx, BOOL * _Nonnull stop) {
[server reloadTimelineForComplication: each];
}];
It will call your ComplicationController.
I’m trying to update the watchOS2 clockkit complication with the data transmitted by WatchConnectivity from IOS/iPhone.
Despite quite a lot of research, but to It has not been successful so far. I found that other posts are describing similar challenges (no solution yet)
Facing 3 problems:
1) The sendMessage from ComplicationController does not seem to wake up IOS Parent App (the same sendMessage sent from InterfaceController at the same time will wake up IOS Parent App)
2) Even if the value is passed to ComplicationController (via sendUserInfoToComplication and when the IOS application is in the foreground), the value displayed in the complication Sometimes it will also update (the pattern is not found, why sometimes it will/sometimes not)
3) I will “getNextRequestUpdat e..” is set to 2 minutes (for testing purposes). But it does not seem to make any difference. (Even in the simulator, it can be triggered at any time, but the “budget” is not used / I set a stopper to verify)
Please note that I am relatively new to IOS/Swift programming. But I think, based on other questions/posts, I don’t seem to be the only one struggling with this.
< p>Here is the sample code:
ComplicationController:
//
// ComplicationController.swift
// IOSDataToComplication WatchKit Extension
//
// Created by Thomas Peter on 11.10.2015.
// Copyright © 2015 Thomas Peter. All rights reserved.
//
import ClockKit
import WatchConnectivity
class ComplicationController: NSObject, CLKComplicationDataSource, WCSessionDelegate {
var session:WCSession!
var text: String = "watchdefault"
var textOld:String = ""
var header:String = "TestHeader"
override init(){
super.init()
startSession()
}
// MARK:-Timeline Configuration
func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler h andler: (CLKComplicationTimeTravelDirections) -> Void) {
handler([.None])
}
func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
handler(nil)
}
func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
handler(nil )
}
func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
handler(.ShowOnLockScreen)
}
// MARK:-Timeline Population
func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: ((CLKComplicationTimelineEntry?) -> Void)) {
// Call the handler with the current timeline entry
if complication.family == .ModularLarge {
//createData()
let entry = self.createTimeLineEntry(text, date: NSDate())
handler(entry)
} else {
handler(nil)
}
}
func getTimelineEntriesForComplication(complication: CLKComplication, beforeDate date: NSDate, limit: Int, withHandler handler: (([CLKComplicationTimelineEntry]?) -> Void)) {
// Call the handler with the timeline entries prior to the given date
handler(nil)
}
func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: (([CLKComplicationTimelineEntry]?) -> Void)) {
// Call the handler with the timeline entries after to the given date
handler(nil)
}
// MARK:-Update Scheduling
func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
// Call the h andler with the date when you would next like to be given the opportunity to update your complication content
handler(NSDate(timeIntervalSinceNow: (60 * 2)))
}
/ / MARK:-Placeholder Templates
func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {
// This method will be called once per supported complication, and the results will be cached
let template = CLKComplicationTemplateModularLargeStandardBody()
template.headerTextProvider = CLKSimpleTextProvider(text: "header")
template.body1TextProvider = CLKSimpleTextProvider(text:"defaul text")
handler(nil)
}
func requestedUpdateDidBegin() {
print("Complication update is starting")
< br />
createData()
let server=CLKComplicationServer.sharedInstance()
for comp in (server.activeComplications) {
server.reloadTimelineForComplication(comp)
print("Timeline has been reloaded!")
}
}
func requestedUpdateBudgetExhausted() {
print("Budget exhausted")
}
func createTimeLineEntry(bodyText: String, date:NSDate) -> CLKComplicationTimelineEntry {
let template = CLKComplicationTemplateModularLargeStandardBody()
template.headerTextProvider = CLKSimpleTextProvider(text: header)
template.body1TextProvider = CLKSimpleTextProvider(text: text)
let entry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
return(entry)
}
func createData(){
let applicationData = ["wake":"fromComplication"]
session.sendMessage(applicationData, replyHandler: {( repl yMessage: [String: AnyObject]) -> Void in
// handle reply from iPhone app here
//let answer:[String:AnyObject] = replyMessage
self. text = replyMessage["text"] as! String
print("complication received messagereply \(self.text)")
}, errorHandler: {(error) -> Void in< br /> // catch any errors here
print("no reply message")
})
print("complication sent \( applicationData) to iOS")
}
func session(session: WCSession, didReceiveUserInfo userInfo: [String: AnyObject]) {
textOld = text
text = userInfo["text"] as! String
print("complication received userinfo \(text)")
dispatch_async(dispatch_get_main_queue()) {
//if text != textOld {
self.requestedUpdateDidBegin()
//}
}
}
private func startSession(){
if (WCSession.isSupported()) {< br /> session = WCSession.defaultSession()
session.delegate = self;
session.activateSession()
}
}
} View Controller:
//
// ViewController.swift
// IOSDataToComplication
//
// Created by Thomas Peter on 11.10.2015.
// Copyright © 2015 Thomas Peter. All rights reserved.
//
import UIKit
import WatchConnectivity
class ViewController: UIViewController, WCSessionDelegate {
var session:WCSession!
var time:String = "default"
var text:String = "default"
override func viewDidLoad() {
super.viewDidLoad()< br /> // Do any additional setup after loading the view, typically from a nib.
startSession( )
getData()
sendUserInfoToComplication()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getData(){
let timeValue = NSDate()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "hh:mm"
time = dateFormatter.stringFromDate(timeValue)
text = "iPhone \(time)"
print("constructed \(text)")
}
private func startSession(){
if (WCSession.isSupported()) {
session = WCSession.defaultSession()
session.delegate = self;
session.activateSession()
}
print("iPhone session started")
}
func session(session: WCSession, didReceiveMessage message: [String: AnyObject] , replyHandler: ([String: AnyObject]) -> Void) {
let receivedMessage = message["wake"] as! String
print("iphone received message \ (receivedMessage)")
let applicationDict = ["text":text]
replyHandler(applicationDict as [String: AnyObject])
< br /> getData()
}
func sendUserInfoToComplication(){
let applicationDict = ["text":text]
session.transferCurrentComplicationUserInfo(applicationDict)
print("iphone sent userinfo \(applicationDict) to complication")
}
}
In addition, when running the simulator, I received a lot of similar messages, as shown below:
objc[7501]: Class SPAssetCacheAssets is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk/System/Library/Frameworks/WatchKit.framework/WatchKit and /Applications/Xcode.app/Contents/Developer/ Platf orms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk/System/Library/PrivateFrameworks/SockPuppetGizmo.framework/SockPuppetGizmo. One of the two will be used. Which one is undefined.
objc[7501]: Class SPAssetCacheSyncData is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk/System/Library/Frameworks/WatchKit.framework/WatchKit and /Applications/Xcode.app/Contents/ Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk/System/Library/PrivateFrameworks/SockPuppetGizmo.framework/SockPuppetGizmo. One of the two will be used. Which one is undefined.
< p>
Use CLKComplicationServer like this:
CLKComplicationServer* server = [CLKComplicationServer sharedInstance];
[server.activeComplications enumerateObjectsUsingBlock: ^(CLKComplication * _Nonnull each, NSUInteger idx, BOOL * _Nonnull stop) {
[server reloadTimelineForComplication: each];
}];
It will call your ComplicationController.