Watch Connectivity works the first time but not after that - ios

I've tested on the Simulator and Device, somehow Watch Connectivity stops working after it's used once.
I'm passing data from Watch -> iPhone, and it only works once and then stops after that.
Any ideas?
iPhone ViewController:
var session: WCSession?
override func viewDidLoad() {
super.viewDidLoad()
if WCSession.isSupported() {
session = WCSession.default()
session?.delegate = self
session?.activate()
}
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
// Received Application Context from Watch
let type = applicationContext["watchType"]
print("Type iPhone: \(type)")
DispatchQueue.main.async {
self.type.text = "Type: \(type)"
}
}
Watch InterfaceController:
let session = WCSession.default()
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if WCSession.isSupported() {
session.delegate = self
session.activate()
}
}
#IBAction func startPressed() {
saveEverything()
}
func saveEverything() {
let watchContextType = ["watchType" : "Boxing/Running"]
do {
print("Type Watch: \(watchContextType)")
try session.updateApplicationContext(watchContextType)
} catch {
print("Didn't work")
}
}

When you use the updateApplicationContext(), you need to change the parameters for each call, otherwise the msg will not be delivered. I belive this is to conserve battery.
Anyway, try sending your message using sendMessage()or sendMessageData(), then the messages get delivered each time, even when they have the same contents. And, they are higher priority than updateApplicationContext so it's win-win :)
Here is the documentation: https://developer.apple.com/library/content/documentation/General/Conceptual/WatchKitProgrammingGuide/SharingData.html#//apple_ref/doc/uid/TP40014969-CH29-SW1

If you want to Drain Battery of your apple watch app Quickly then above technique of #Jens is well and good.
Instead of shifting from updateApplicationContext() to sendMessage() better make small change in your project to get Desired result.
I will explain the question scenario and then solution along with Demo app :-
#IBAction func sliderChange(_ value: Float)
{
let str:String?
switch value {
case 0...7 :
str = "Running"
case 8...14 :
str = "Sleeping"
default:
str = "normal"
}
updateContext(value: str!)
}
func updateContext(value:String) {
let dictionary = [ "watchType" : value ]
do {
print("update application context is called do statemet")
try session?.updateApplicationContext(dictionary)
}
catch{
print("this is the catch statement")
}
}
With update in slider Values in Watch ,iPhone values get updated .As you can see there is repetition of values for iPhone i.e
When sliderValue are from 0 to 7 values remain "Running" && "Sleeping" for
8 to 14 .
App work fine if I varies values of slide and desired result is reflected in iPhone in normal scenario.
Scenrio where it fails :-
i)Change the slider values from 0 to 3 then "Running" is reflected in iPhone. Thant's fine .
ii)Now close the iPhone application then change the slider values from 3 to 5 no we can see real problem when iPhone is opened back.
iii)Values is not triggered to iPhone .
Due to internal caching mechanism of updateApplicationContext() restrict
to trigger of duplicate values to iPhone .
Store the last updated state in didReceiveApplicationContext() and display state according to stored value .
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
checkStatus()
updateSavedState()
}
func checkStatus() {
print("check status")
if WCSession.isSupported() {
session = WCSession.default()
session?.delegate = self
session?.activate()
}
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
let type = applicationContext["watchType"]!
DispatchQueue.main.async {
self.updateLabel.text = " Type: \(type)"
UserDefaults.standard.set(type, forKey: "savedState") //setObject
}
}
func updateSavedState() {
self.updateLabel.text = UserDefaults.standard.string(forKey: "savedState")
}
Now everything is work perfect.
Demo App.

Related

How to 10 second forward or backward in Spotify player

I am trying to add(move forward) 10 second song duration or minus(move backward) 10 second in Spotify player but i am really confused how to add or minus.
When i m trying to use this code the song is not changed duration
// forward button action
#IBAction func moveFrdBtnAction(_ sender: Any) {
SpotifyManager.shared.audioStreaming(SpotifyManager.shared.player, didSeekToPosition: TimeInterval(10))
}
// spotify delegate method seekToPosition
func audioStreaming(_ audioStreaming: SPTAudioStreamingController!, didSeekToPosition position: TimeInterval) {
player?.seek(to: position, callback: { (error) in
let songDuration = audioStreaming.metadata.currentTrack?.duration as Any as! Double
self.delegate?.getSongTime(timeCount: Int(songDuration)+1)
})
}
We are making a music application using the same SDK in both the platforms (Android & iOS), the seekToPosition method of the Spotify SDK is working correctly in the Android version, however, it is not working in the iOS one.The delegate method calls itself but the music stops.
Can you kindly let us know why this scenario is happening, and what should we do to run it on the iOS devices as well.
Can someone please explain to me how to solve this , i've tried to solve this but no results yet.
Any help would be greatly appreciated.
Thanks in advance.
I don't use this API so my answer will be based your code and Spotify's reference documentation.
I think there are a few things wrong with your flow:
As Robert Dresler commented, you should (approximately) never call a delegate directly, a delegate calls you.
I'm pretty sure your action currently results in jumping to exactly 10 seconds, not by 10 seconds.
(As an aside, I'd suggest changing the name of your function moveFrdBtnAction to at least add more vowels)
Anyway, here's my best guess at what you want:
// forward button action
#IBAction func moveForwardButtonAction(_ sender: Any) {
skipAudio(by: 10)
}
#IBAction func moveBackButtonAction(_ sender: Any) {
skipAudio(by: -10)
}
func skipAudio(by interval: TimeInterval) {
if let player = player {
let position = player.playbackState.position // The documentation alludes to milliseconds but examples don't.
player.seek(to: position + interval, callback: { (error) in
// Handle the error (if any)
})
}
}
// spotify delegate method seekToPosition
func audioStreaming(_ audioStreaming: SPTAudioStreamingController!, didSeekToPosition position: TimeInterval) {
// Update your UI
}
Note that I have not handled seeking before the start of the track, nor after the end which could happen with a simple position + interval. The API may handle this for you, or not.
You could take a look at the examples here: spotify/ios-sdk. In the NowPlayingView example they use the 'seekForward15Seconds', maybe you could use that? If you still need 10s I have added a function below. The position is in milliseconds.
"position: The position to seek to in milliseconds"
docs
ViewController.swift
var appRemote: SPTAppRemote {
get {
return AppDelegate.sharedInstance.appRemote
}
}
fileprivate func seekForward15Seconds() {
appRemote.playerAPI?.seekForward15Seconds(defaultCallback)
}
fileprivate seekBackward15Seconds() {
appRemote.playerAPI?.seekBackward15Seconds(defaultCallback)
}
// TODO: Or you could try this function
func seekForward(seconds: Int){
appRemote.playerAPI?.getPlayerState({ (result, error) in
// playback position in milliseconds
let current_position = self.playerState?.playbackPosition
let seconds_in_milliseconds = seconds * 1000
self.appRemote.playerAPI?.seek(toPosition: current_position + seconds_in_milliseconds, callback: { (result, error) in
guard error == nil else {
print(error)
return
}
})
})
}
var defaultCallback: SPTAppRemoteCallback {
get {
return {[weak self] _, error in
if let error = error {
self?.displayError(error as NSError)
}
}
}
}
AppDelegate.swift
lazy var appRemote: SPTAppRemote = {
let configuration = SPTConfiguration(clientID: self.clientIdentifier, redirectURL: self.redirectUri)
let appRemote = SPTAppRemote(configuration: configuration, logLevel: .debug)
appRemote.connectionParameters.accessToken = self.accessToken
appRemote.delegate = self
return appRemote
}()
class var sharedInstance: AppDelegate {
get {
return UIApplication.shared.delegate as! AppDelegate
}
}
Edit1:
For this to work you need to follow the Prepare Your Environment:
Add the SpotifyiOS.framework to your Xcode project
Hope it helps!

WatchConnectivity - transerUserInfo is sending, but not receiving data

I've been making an app that uses WatchConnectivity to transfer a simple struct from the Apple Watch to iPhone, and have been running into some troubles. Sending is perfectly fine, and both devices are reachable and activated on the same session, but the iPhone never seems to receive the struct I send it.
Here's my current code. I decided to use transferUserInfo to allow background transfer of data.
The struct:
struct myDataList {
var xAcc: [Int]
var timestamps: [Int]
}
Watch (Sending):
func sendTestData(data:myDataList) {
print("sending file to iphone")
if WCSession.default.activationState == WCSessionActivationState.activated && WCSession.isSupported() && WCSession.default.isReachable {
WCSession.default.transferUserInfo(["Data" : data])
}
else {
print("Could not send")
}
}
iPhone (Receiving):
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
print("received something")
DispatchQueue.main.async {
if let data = userInfo["Data"] as? myDataList {
for (acc,time) in zip(data.xAcc,data.timestamps){
let dataLine: String = "\(acc),\(time)\n"
self.appendToFile(file: "data", data: dataLine)
}
}
}
}
On both devices I've started a session like so:
if WCSession.isSupported() {
WCSession.default.delegate = self
WCSession.default.activate()
}
I've tested the other functions to write to file/etc and they are working individually. I'd appreciate feedback and advice on how to resolve this. Cheers!

How to transfer a UIImage using Watch Connectivity

How can I transfer an UIImage over WatchConnecitivity from the iPhone to the Apple Watch with no user interaction on the phone, and only loads because the watch calls for it programmatically. I need this because the image processing to create the UIImage uses logic unavailable in the Watchkit API, so it must be created from the phone. I have seem some examples of Watch Connectivity using:
func startSession() {
session?.delegate = self
session?.activateSession()
}
However, I am new to watch kit and am confused on how to use this session manager, particularly to go from the watch to the device instead of the other way around like I see in apple documentation. Can someone please provide an example of how to do this on both the watch and the phone to call for a UIImage on the phone from the watch?
To transfer files between your iPhone and your Apple Watch you should use Watch Connectivity, using WCSession to handle the communication properly. You can send an UIImage as NSData using the didReceiveMessageData delegate method of the WCSessionDelegate.
The first thing you should know is convert your UIImage to NSData and viceversa. You can use for this the following code:
If PNG images
let image = UIImage(named: "nameOfYourImage.jpg")
let data = UIImagePNGRepresentation(image)
If JPG images
let image = UIImage(named: "nameOfYourImage.jpg")
let data = UIImageJPEGRepresentation(image, 1.0)
Then you can use the WCSession to send the message like in the following way:
ViewController.swift
class ViewController: UIViewController, WCSessionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if WCSession.isSupported() {
WCSession.defaultSession().delegate = self
WCSession.defaultSession().activateSession()
}
let image = UIImage(named: "index.jpg")!
let data = UIImageJPEGRepresentation(image, 1.0)
WCSession.defaultSession().sendMessageData(data!, replyHandler: { (data) -> Void in
// handle the response from the device
}) { (error) -> Void in
print("error: \(error.localizedDescription)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
InterfaceController.swift
import WatchKit
import Foundation
import WatchConnectivity
class InterfaceController: WKInterfaceController, WCSessionDelegate {
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
if WCSession.isSupported() {
WCSession.defaultSession().delegate = self
WCSession.defaultSession().activateSession()
}
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
func session(session: WCSession, didReceiveMessageData messageData: NSData, replyHandler: (NSData) -> Void) {
guard let image = UIImage(data: messageData) else {
return
}
// throw to the main queue to upate properly
dispatch_async(dispatch_get_main_queue()) { [weak self] in
// update your UI here
}
replyHandler(messageData)
}
}
In the above code when you open the ViewController it sends the UIImage, the above example is only for learning purposes, you have to handle it in a more proper way regarding the complexity of your project.
I hope this help you.
you have to transfer your image as NSData and then decode it on watch's side. You can have a look at my blog post where I covered similar case.
http://eluss.github.io/AppleWatch_iPhone_data_transfer/
This is not the way of doing it with session as I was not aware of it back then, but maybe it will help you get the whole logic.
In InterfaceController, if you use WCSessionDelegate, remember to extend method activationDidCompleteWith:
func session(_ session: WCSession, activationDidCompleteWith
activationState: WCSessionActivationState, error: Error?) {
}

WCSession.sendMessage works 50/50

Lately, I am working on a project is related to Watch/iPhone communication again. But my code works sometimes and doesn’t work sometimes which is kind of weird to me because I think the code should either work or not. It cannot be 50/50. Therefore, I have no idea what goes wrong.
setup WCSession on iPhone:
class WatchCommunicationController: NSObject, WCSessionDelegate {
var session : WCSession?
override init(){
// super class init
super.init()
// if WCSession is supported
if WCSession.isSupported() { // it is supported
// get default session
session = WCSession.defaultSession()
// set delegate
session!.delegate = self
// activate session
session!.activateSession()
} else {
print("iPhone does not support WCSession")
}
}
... ...
}
similar WCSession setup on Watch:
class PhoneCommunicationController: NSObject, WCSessionDelegate {
var session : WCSession?
override init(){
// super class init
super.init()
// if WCSession is supported
if WCSession.isSupported() { // it is supported
// get default session
session = WCSession.defaultSession()
// set delegate
session!.delegate = self
// activate session
session!.activateSession()
} else {
print("Watch does not support WCSession")
}
}
... ...
}
send out message on Watch:
func sendGesture(gesture : GKGesture){
// if WCSession is reachable
if session!.reachable { // it is reachable
// create the interactive message with gesture
let message : [String : AnyObject]
message = [
"Type":"Gesture",
"Content":gesture.rawValue
]
// send message
session!.sendMessage(message, replyHandler: nil, errorHandler: nil)
print("Watch send gesture \(gesture)")
} else{ // it is not reachable
print("WCSession is not reachable")
}
}
related enum:
enum GKGesture: Int {
case Push = 0, Left, Right, Up, Down
}
receive message on iPhone:
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
//retrieve info
let type = message["Type"] as! String
let content = message["Content"]
switch type {
case "Gesture":
handleGesture(GKGesture(rawValue: content as! Int)!)
default:
print("Received message \(message) is invalid with type of \(type)")
}
}
func handleGesture(gesture : GKGesture){
print("iPhone receives gesture \(gesture)")
var notificationName = ""
switch gesture {
case .Up:
notificationName = "GestureUp"
case .Down:
notificationName = "GestureDown"
case .Left:
notificationName = "GestureLeft"
case .Right:
notificationName = "GestureRight"
case .Push:
notificationName = "GesturePush"
}
NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil)
}
somehow I can’t debug my Watch app on Xcode, the debug session just won’t attach. I don’t know why. Therefore, I debug one-sided with just the iPhone.
sometimes I got "receives gesture” print out, and sometimes not. And the same for getting the notification.
I don't know if Int would be wrapped around to NSNumber while being transfer within WCSession. If it would be, then that must be why when I use Int as the base class of the enum it won't work and works when String is the base class.
Connectivity Known Issue Your app may crash when using NSNumber and
NSDate objects with the WCSession API.
Workaround: Convert an NSNumber or NSDate object to a string before
calling WCSession APIs. Do the opposite conversion on the receiving
side.
Watch OS 2 Beta 4 release note
My guess is your call to sendMessage is returning an error in the cases where it fails, but you haven't implemented the error handler!! For now while you are getting up and running you can get away with just printing the error, but if this is shipping code you really ought to handle the appropriate errors:
// send message
session.sendMessage(message, replyHandler: nil, errorHandler: { (error) -> Void in
print("Watch send gesture \(gesture) failed with error \(error)")
})
print("Watch send gesture \(gesture)")
Your flow is correct but the difficulty is to understand how to debug:
Debug Watch:
Run the iPhone target and when it is done hit the Stop button.
Open the iOS app inside the simulator (run it manually from the simulator and not from Xcode) and let it hang there.
Switch to the Watch target (yourAppName WatchKit App), put the relevant breakpoint and run it.
The iOS app will be put automatically in the background and then you will be able to use sendMessage method (at the Watch target) to send whatever you need and if you have a replayHandler in your iOS app you will even receive the relevant messages inside the sendMessage at your Watch target (i.e InterfaceController)
Small Swift example:
Sending a Dictionary from Watch to iOS app:
if WCSession.defaultSession().reachable == true {
let requestValues = ["Send" : "From iWatch to iPhone"]
let session = WCSession.defaultSession()
session.sendMessage(requestValues,
replyHandler: { (replayDic: [String : AnyObject]) -> Void in
print(replayDic["Send"])
}, errorHandler: { (error: NSError) -> Void in
print(error.description)
})
}
else
{
print("WCSession isn't reachable from iWatch to iPhone")
}
Receiving the message from the Watch and sending a replay from the iOS app:
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
print(message.values)
var replyValues = Dictionary<String, AnyObject>()
replyValues["Send"] = "Received from iphone"
// Using the block to send back a message to the Watch
replyHandler(replyValues)
}
Debug iPhone:
The exact opposite of debug watch
Also, the answer by #sharpBaga has an important consideration.

Passing data to Apple Watch app

I am trying to pass data from my app into my Apple Watch app. Basically, I am using the same method as I used for creating the today widget and so I am passing data through NSUserDefaults.
The problem is, that when I run my app, the data does not update the labels in the Watch app as I would expect it to.
Here is what I have...
override init(context: AnyObject?) {
// Initialize variables here.
super.init(context: context)
// Configure interface objects here.
NSLog("%# init", self)
var defaults = NSUserDefaults(suiteName: "group.AffordIt")
var totalBudgetCalculation = ""
if (defaults!.stringForKey("totalBudgetWidget") != nil) {
println("Worked")
totalBudgetCalculation = defaults!.stringForKey("totalBudgetWidget")!
initialBudgetLabel.setText("Initial: \(totalBudgetCalculation)")
}
var currentBudgetCalculation = ""
if (defaults!.stringForKey("currentBudgetWidget") != nil) {
currentBudgetCalculation = defaults!.stringForKey("currentBudgetWidget")!
currentBudgetLabel.setText("Current: \(currentBudgetCalculation)")
}
}
I tried putting this code in willActivate(), however that doesn't seem to make a difference.
Anyone know where I am going wrong?
This applies to OS 1 only. See below for better answers.
I got it working using your method. I guess there's a couple of things you can check:
1) Are you synchronising the defaults after you set the value:
defaults?.synchronize();
NSLog("%# ", defaults?.dictionaryRepresentation())
2) Have you enabled the App Group in both your app and your extension?
3) Are you using the correctly named app group when constructing the NSDefaults? For example, I use:
NSUserDefaults(suiteName: "group.com.brindysoft.MyWatch");
Once all that's set up I run the app, set the value in the defaults, then run the glance target which reads the value from the default and that seems to work!
Still stuck? check your app groups in your apple account
The accepted answer applies to apple watch os 1. See NSUserDefaults not working on Xcode beta with Watch OS2
For OS2 - you will need to use the WatchConnectivity frameworks and implement the WCSessionDelegate.
import WatchConnectivity
import WatchKit
#available(iOS 9.0, *)
var alertDelegate:HomeIC? = nil
public class WatchData: NSObject,WCSessionDelegate {
var session = WCSession.defaultSession()
//
class var shared: WatchData {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: WatchData? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = WatchData()
}
return Static.instance!
}
public func session(session: WCSession, didReceiveFile file: WCSessionFile){
print(__FUNCTION__)
print(session)
}
public func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print(__FUNCTION__)
print(session)
alertDelegate?.showMessage("didReceiveApplicationContext")
}
public func sessionReachabilityDidChange(session: WCSession){
print(__FUNCTION__)
print(session)
print("reachability changed:\(session.reachable)")
let text = session.reachable ? "reachable" : "unreachable"
alertDelegate?.showMessage(text)
}
public func sessionWatchStateDidChange(session: WCSession) {
print(__FUNCTION__)
print(session)
print("reachable:\(session.reachable)")
// alertDelegate?.showMessage("sessionWatchStateDidChange")
if !session.receivedApplicationContext.keys.isEmpty {
alertDelegate?.showMessage(session.receivedApplicationContext.description)
}
}
public func session(session: WCSession, didReceiveMessageData messageData: NSData){
if !session.receivedApplicationContext.keys.isEmpty {
alertDelegate?.showMessage(session.receivedApplicationContext.description)
}
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject]){
print(__FUNCTION__)
if let data = message["data"] {
alertDelegate?.showMessage(data as! String)
return
}
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
print(__FUNCTION__)
if let data = message["data"] {
alertDelegate?.showMessage(data as! String)
return
}
guard message["request"] as? String == "showAlert" else {return}
}
public func activate(){
if WCSession.isSupported() { // it is supported
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
print("watch activating WCSession")
} else {
print("watch does not support WCSession")
}
if(!session.reachable){
print("not reachable")
return
}else{
print("watch is reachable")
}
}
}
Sample Usage
class HomeIC: WKInterfaceController {
// MARK: Properties
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Initialize the `WCSession`.
WatchData.shared.activate()
alertDelegate = self
}
internal func showMessage(msg:String){
let defaultAction = WKAlertAction(title: msg, style: WKAlertActionStyle.Default) { () -> Void in }
let actions = [defaultAction]
self.presentAlertControllerWithTitle( "Info", message: "", preferredStyle: WKAlertControllerStyle.Alert, actions: actions)
}
}
in my iphone code / I can invoke sharing data here
if #available(iOS 9.0, *) {
WatchData.shared.sendInbox()
} else {
// Fallback on earlier versions
}
And somewhere else I have another discrete singleton for watch data session.
#available(iOS 9.0, *)
public class WatchData: NSObject,WCSessionDelegate {
var session = WCSession.defaultSession()
var payload:String = ""
class var shared: WatchData {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: WatchData? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = WatchData()
}
return Static.instance!
}
public func sessionReachabilityDidChange(session: WCSession){
print(__FUNCTION__)
print(session)
print("reachability changed:\(session.reachable)")
if (session.reachable){
}
}
public func sessionWatchStateDidChange(session: WCSession) {
print(__FUNCTION__)
print(session)
print("reachable:\(session.reachable)")
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
print(__FUNCTION__)
guard message["request"] as? String == "showAlert" else {return}
guard let m = message["m"] as? String else { return }
print("msg:",m)
}
public func sendInbox(){
if (!session.reachable){
if WCSession.isSupported() { // it is supported
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
print("iphone activating WCSession")
} else {
print("iphone does not support WCSession")
}
session.activateSession()
}
if(session.paired){
if(session.watchAppInstalled){
print("paired | watchAppInstalled")
}
}else{
print("not paired | or no watchAppInstalled")
}
if(!session.reachable){
print("not reachable")
return
}else{
/*let transfer:WCSessionUserInfoTransfer = (session.transferUserInfo(["data" : "Test2"]) as WCSessionUserInfoTransfer?)!
if(transfer.transferring){
print("-> iphone")
}else{
print("!-> iphone")
}*/
session.sendMessage(["data" :"test"],
replyHandler: { reply in
},
errorHandler: { error in
print(error)
})
}
}
}
Refer to sample watch os2 app
https://github.com/shu223/watchOS-2-Sampler/tree/20eeebeed66764d0814603e97d3aca5933236299
As #johndpope said, shared NSUserDefaults no longer work on WatchOS2.
I'm posting a simplified solution that's not as full featured as john's but will get the job done in most cases.
In your iPhone App, follow these steps:
Pick find the view controller that you want to push data to the Apple Watch from and add the framework at the top.
import WatchConnectivity
Now, establish a WatchConnectivity session with the watch and send some data.
if WCSession.isSupported() { //makes sure it's not an iPad or iPod
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
if watchSession.paired && watchSession.watchAppInstalled {
do {
try watchSession.updateApplicationContext(["foo": "bar"])
} catch let error as NSError {
print(error.description)
}
}
}
Please note, this will NOT work if you skip setting the delegate, so even if you never use it you must set it and add this extension:
extension MyViewController: WCSessionDelegate {
}
Now, in your watch app (this exact code works for Glances and other watch kit app types as well) you add the framework:
import WatchConnectivity
Then you set up the connectivity session:
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
}
and you simply listen and handle the messages from the iOS app:
extension InterfaceController: WCSessionDelegate {
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print("\(applicationContext)")
dispatch_async(dispatch_get_main_queue(), {
//update UI here
})
}
}
That's all there is to it.
Items of note:
You can send a new applicationContext as often as you like and it
doesn't matter if the watch is nearby and connected or if the watch
app is running. This delivers the data in the background in an
intelligent way and that data is sitting there waiting when the
watch app is launched.
If your watch app is actually active and running, it should receive
the message immediately in most cases.
You can reverse this code to have the watch send messages to the
iPhone app the same way.
applicationContext that your watch app receives when it is viewed will ONLY be the last message you sent. If you sent 20 messages before the watch app is viewed, it will ignore the first 19 and handle the 20th one.
For doing a direct/hard connection between the 2 apps or for background file transfers or queued messaging, check out the WWDC video.
Another way to communicate between the app and the watch is via wormhole:
https://github.com/mutualmobile/MMWormhole
Send:
[self.wormhole passMessageObject:#{#"titleString" : title}
identifier:#"messageIdentifier"];
id messageObject = [self.wormhole messageWithIdentifier:#"messageIdentifier"];
Recieve:
[self.wormhole listenForMessageWithIdentifier:#"messageIdentifier"
listener:^(id messageObject) {
// Do Something
}];
Just use watch connectivity for communicate between these two platform you can read more about this in apple document
https://developer.apple.com/documentation/watchconnectivity

Resources