Send Data to Apple Watch with Core Data and Telerik UI for iOS in Swift
Join the DZone community and get the full member experience.
Join For Freethe apple watch has been a long rumored device that finally appeared in september, followed by a watch sdk, called watchkit, in november. the introduction of the sdk maybe raised more questions than it answered, and like everybody else, we're looking into the future for answers from apple. one question is: how can i send data, larger than what is allowed for a push notification, from the iphone to the watch? well, there are several ways to do it:
- nsuserdefaults
- files
- core data

the most common scenario is to have an existing iphone app project, using telerik chart for ios , which you want to compliment with an apple watch app. so, this tutorial assumes that you already have such a project, and it’s called applewatchwithtelerikchart . don’t worry if you didn’t check the use core data checkbox back then when you created your iphone app. we will assume that core data is not enabled initially, and will show you how to enable it in the existing app. last but not least, this tutorial assumes that you are using swift as your primary language.
the complete project structure will consist of an iphone app, a watchkit app and a framework that directly communicates with core data. we will use xcode 6.2 beta 4 to do all this. so, let’s get started.
creating the watchkit app
-
with your iphone app project opened in xcode, go to
file >> target >> ios >> apple watch >> watchkit app
and click
next
.
-
check the
include notification scene
and
include glance scene
checkboxes as you may need them at some point during your future development. in this tutorial we will focus on the main watchkit app. the product name field will be filled in automatically for you, so just click
finish
.
- you will be asked whether to activate the applewatchwithtelerikchart watchkit app scheme. click activate .
- open the interface.storyboard from the applewatchwithtelerikchart watchkit app folder and add image and label components to the interface controller scene .
-
using ctrl+drag, create iboutlets and name them
chartimageview
and
titlelabel.
creating the data proxy library
- with your iphone app project opened in xcode, go to file >> target >> ios >> framework & library >> cocoa touch framework and click next .
- name the library watchcoredataproxy and select swift from the language drop-down. click finish.
- right-click the watchcoredataproxy folder from the project structure panel and select new file >> ios >> core data >> data model .
- name the model watchmodel and click create . this will create your model file and will open the model editor for you.
-
create a new entity called
chartdataentity
and add two fields to the entity:
- imagebinarydata of type binary data
- stringdata of type string
so, let’s add these to the watchcoredataproxy.swift the following way:
- right-click the watchcoredataproxy folder >> new file >> ios >> source >> swift file to create a new swift file and name the file watchcoredataproxy .
-
add the following code:
import coredata public class watchcoredataproxy: nsobject { public var sharedappgroup: nsstring = "" // mark: - core data stack lazy var applicationdocumentsdirectory: nsurl = { let urls = nsfilemanager.defaultmanager().urlsfordirectory(.documentdirectory, indomains: .userdomainmask) return urls[urls.count-1] as nsurl }() lazy var managedobjectmodel: nsmanagedobjectmodel = { let proxybundle = nsbundle(identifier: "com.telerik.watchcoredataproxy") let modelurl = proxybundle?.urlforresource("watchmodel", withextension: "momd")! return nsmanagedobjectmodel(contentsofurl: modelurl!)! }() lazy var persistentstorecoordinator: nspersistentstorecoordinator? = { let containerpath = nsfilemanager.defaultmanager().containerurlforsecurityapplicationgroupidentifier(self.sharedappgroup)?.path let sqlitepath = nsstring(format: "%@/%@", containerpath!, "watchmodel") let url = nsurl(fileurlwithpath: sqlitepath); let model = self.managedobjectmodel; var coordinator: nspersistentstorecoordinator? = nspersistentstorecoordinator(managedobjectmodel: model) var error: nserror? = nil var failurereason = "there was an error creating or loading the application's saved data." if coordinator!.addpersistentstorewithtype(nssqlitestoretype, configuration: nil, url: url, options: nil, error: &error) == nil { coordinator = nil // report any error we got. var dict = [string: anyobject]() dict[nslocalizeddescriptionkey] = "failed to initialize the application's saved data" dict[nslocalizedfailurereasonerrorkey] = failurereason dict[nsunderlyingerrorkey] = error error = nserror(domain: "your_error_domain", code: 9999, userinfo: dict) nslog("unresolved error \(error), \(error!.userinfo)") abort() } return coordinator }() lazy var managedobjectcontext: nsmanagedobjectcontext? = { let coordinator = self.persistentstorecoordinator if coordinator == nil { return nil } var managedobjectcontext = nsmanagedobjectcontext() managedobjectcontext.persistentstorecoordinator = coordinator return managedobjectcontext }() func savecontext () { if let moc = self.managedobjectcontext { var error: nserror? = nil if moc.haschanges && !moc.save(&error) { nslog("unresolved error \(error), \(error!.userinfo)") abort() } } } }
-
next are the methods you have to implement in the watchcoredataproxy class to send data (image and text) from the iphone app to the sqlite db through core data, and the methods to fetch the saved data and expose it to the watchkit app:
public class var sharedinstance : watchcoredataproxy { struct static { static var oncetoken : dispatch_once_t = 0 static var instance : watchcoredataproxy? = nil } dispatch_once(&static.oncetoken) { static.instance = watchcoredataproxy() } return static.instance! } public func sendimagetowatch(image:uiimage){ let imagedata: nsdata = uiimagepngrepresentation(image); let context = self.managedobjectcontext let entity = nsentitydescription.entityforname("chartdataentity", inmanagedobjectcontext: self.managedobjectcontext!) var request = nsfetchrequest() request.entity = entity var error: nserror? = nil let results = self.managedobjectcontext?.executefetchrequest(request, error: &error) if(results?.count==0){ let newmanagedobject = nsentitydescription.insertnewobjectforentityforname("chartdataentity", inmanagedobjectcontext: context!) as nsmanagedobject newmanagedobject.setvalue(imagedata, forkey: "imagebinarydata") }else{ let existingobject: nsmanagedobject = results![0] as nsmanagedobject existingobject.setvalue(imagedata, forkey: "imagebinarydata") } if !(context?.save(&error) != nil) { println("unresolved error \(error), \(error?.userinfo)") abort() } } public func receiveimage()-> uiimage? { var request = nsfetchrequest() var entity = nsentitydescription.entityforname("chartdataentity", inmanagedobjectcontext: self.managedobjectcontext!) request.entity = entity var error: nserror? = nil let results = self.managedobjectcontext?.executefetchrequest(request, error: &error) var image: uiimage? if results != nil && results?.count > 0 { let managedobject: nsmanagedobject = results![0] as nsmanagedobject let mydata: nsdata? = managedobject.valueforkey("imagebinarydata") as? nsdata image = uiimage(data: mydata!) } return image } public func sendstringtowatch(stringdata:nsstring){ let context = self.managedobjectcontext let entity = nsentitydescription.entityforname("chartdataentity", inmanagedobjectcontext: self.managedobjectcontext!) var request = nsfetchrequest() request.entity = entity var error: nserror? = nil let results = self.managedobjectcontext?.executefetchrequest(request, error: &error) if(results?.count==0){ let newmanagedobject = nsentitydescription.insertnewobjectforentityforname("chartdataentity", inmanagedobjectcontext: context!) as nsmanagedobject newmanagedobject.setvalue(stringdata, forkey: "stringdata") }else{ let existingobject: nsmanagedobject = results![0] as nsmanagedobject existingobject.setvalue(stringdata, forkey: "stringdata") } if !(context?.save(&error) != nil) { println("unresolved error \(error), \(error?.userinfo)") abort() } } public func receivestring()-> nsstring? { var request = nsfetchrequest() var entity = nsentitydescription.entityforname("chartdataentity", inmanagedobjectcontext: self.managedobjectcontext!) request.entity = entity var error: nserror? = nil let results = self.managedobjectcontext?.executefetchrequest(request, error: &error) var stringdata: nsstring? if results != nil && results?.count > 0 { let managedobject: nsmanagedobject = results![0] as nsmanagedobject stringdata = managedobject.valueforkey("stringdata") as? nsstring } return stringdata }
app groups
now that we have the iphone app, the proxy library and the watchkit app, it’s time to make sure that the iphone app and the watchkit app can actually share data. the thing that enables sharing data in whatever form between applications (files, nsuserdefaults, coredata) is called app groups; both applications should be part of the same app group. to enable the app groups feature for both apps:- select the applewatchwithtelerikchart project from the project structure panel.
- select the applewatchwithtelerikchart app target.
- go to capabilities >> app groups and turn that feature on . you will be asked to select your development team/account.
-
check the
group.watch.sample
group. of course, you can create your own group name, but for our demo purposes, we will use the default one.
- select the applewatchwithtelerikchart watchkit extension target.
- go to capabilities >> app groups and turn that feature on .
-
check the
group.watch.sample
group. it is important for the name of that group to be the same as the name of the
applewatchwithtelerikchart
’s app group.
sending and getting the data using the proxy library
-
take a screenshot of the chart. you can do it the following way, assuming that our tkchart instance is called chart:
uigraphicsbeginimagecontext(chart.bounds.size) chart.layer.renderincontext(uigraphicsgetcurrentcontext()) let image = uigraphicsgetimagefromcurrentimagecontext(); uigraphicsendimagecontext();
-
import watchcoredataproxy
import watchcoredataproxy
-
set the sharedappgroup property of the core data proxy to the name of the app group:
watchcoredataproxy.sharedinstance.sharedappgroup = "group.watch.sample"
-
send the image and the string to the sqlite db:
watchcoredataproxy.sharedinstance.sendimagetowatch(image) watchcoredataproxy.sharedinstance.sendstringtowatch(label.text!)
-
import watchcoredataproxy.
import watchcoredataproxy
-
set the sharedappgroup of the core data proxy to the app group name:
watchcoredataproxy.sharedinstance.sharedappgroup = "group.watch.sample"
-
get the image and the string from the sqlite thought the core data proxy:
let image = watchcoredataproxy.sharedinstance.receiveimage() let title = watchcoredataproxy.sharedinstance.receivestring()
-
set the image and string to the chartimageview and titlelabel:
chartimageview.setimage(image) titlelabel.settext(title)
happy coding!
Opinions expressed by DZone contributors are their own.
Comments