Checkk if Apple Watch is connected (Swift 5) - ios

I have made an application for iOS device to get the heart rate data continuously from the watch. It is working as intended and I wanted to add a test case checking if the watch is connected (paired) with the device. My code for testing it is:
if WCSession.isSupported() {
let wcsession = WCSession.default
wcsession.delegate = self as? WCSessionDelegate
wcsession.activate()
if(wcsession.isPaired){
print("paired")
}else{
print("not paired")
}
}
However, I get an error:
2020-02-29 15:25:55.044843+0900 HeartRateApp[68975:11196372] [WC] denying activation due to missing delegate
2020-02-29 15:25:55.044969+0900 HeartRateApp[68975:11196372] [WC] WCSession has not been activated
even though everything is working and I receive the data from the watch successfully...
I have looked at other questions here but they all have similar solutions:
Solution 1
Solution 2

Problably self as? WCSessionDelegate returns nil because the class doesn't implement the WCSessionDelegate protocol. The cast as? WCSessionDelegate should not be necessary. You need to make your class conform to the protocol (as seen in Solution 2):
class SomeClass: WCSessionDelegate {
// ...
}

Related

Using CocoaAsyncSocket to receive UDP in Swift iOS

I am trying to receive broadcast UDP packets on iOS. I've verified that the packets are being sent (I can see them in another iOS app on the same device).
CocoaAsyncSocket seemed like the most straightforward library to do this with. I added the framework to my project using Carthage. Following the example in Objective-C, I have an iOS app with not much else in it, and this view being loaded:
import UIKit
import CocoaAsyncSocket
class FirstViewController: UIViewController
var sock:AsyncUdpSocket?
override func viewDidLoad() {
super.viewDidLoad()
if (sock == nil){
sock = AsyncUdpSocket(delegate: self)
}
do{
try sock!.bindToPort(54545)
try sock!.enableBroadcast(true) // Also tried without this line
sock!.receiveWithTimeout(10,tag: 0)
} catch {
print("error")
}
print("View Loaded...")
}
func onUdpSocket(cbsock:AsyncUdpSocket!,
didReceiveData data: NSData!){
print("Recv...")
print(data)
cbsock.receiveWithTimeout(10, tag: 0)
}
}
In either the Simulator, or on a device, in the debug console, I see the "View loaded..." message, but nothing from the callback, neither the "Recv..." or the data. It seems the callback isn't being called, but I am not getting any errors either. I've tried other timeouts, and various tweaks like making functions and classes public, but no luck.
I've written a receiver in Python, but this has me stumped.
EDIT: sock now used as a property at paulw's suggestion

watchOS 2 Reachability in Swift

I have looked around at some of the similar answers but none of these seem to be helping me.
I seem to be having a problem with my application. I have created the single view application and also added in the WCSessionDelegate into my extension.
import WatchKit
class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate { }
I have also checked the session if the session is not there and told it to print out if it cant find it
guard WCSession.isSupported() else {
print("Session is not supported")
return
}
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
However I am still getting a No WatchConnectivity error. I'm confused as If the session is working then I think I have it coded right?
Answer
Whilst waiting for your code, I think I can see the issue. Where you have declared the session delegate as a delegate here:
import WatchKit
class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate { }
You need to also import WatchConnectivity
like so:
import WatchKit
import WatchConnectivity
class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate { }
Let me know if his helps!
Tutorial
There is a tutorial which explains exactly this issue: http://ios-blog.co.uk/tutorials/swift/watchos-2-checking-reachability/
Plugin
Also in the same search is a nift plugin you can use: https://github.com/ashleymills/Reachability.swift - This would be my option if you're not confident enough
Tip
(Google is your friend)

How to share data using Watch Connectivity when working with Core Data

In my iOS application I use Core Data to store data and a fetch request to create an array of NSManagedObjects to display in a UITableView.
On the Watch OS I check if WCSession is supported and active a session, then send the iOS application a message from the watchOS extension.
When the iOS application receives the message from the watchOS it should send the array of Objects to the watchOS extension to display the data in the WKInterfaceTable, but I am unsure how to do this. Ultimately what I am trying to achieve is;
How to share the array of Objects with the watchOS extension?
If the user adds/edits/deletes objects in the array on the Watch, how can we update the data on the iPhone ?
Also, the iOS application is embedded within a UITabBarController so does it matter which view controller I communicate with?
Watch OS FavouritesInterfaceController
var session : WCSession!
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
//Check if session is supported and Activate
if (WCSession.isSupported()) {
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
}
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Interface Objects
//Send Message
sendmessagetoiphone()
}
func sendMessageToIphone() {
if(WCSession.isSupported()){
session.sendMessage(["b":"goodBye"], replyHandler: nil, errorHandler: nil)
}
}
IOS Application : FavouritesViewController
var objects = [Objects]()
func loadData() {
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let request = NSFetchRequest(entityName: "Objects")
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
do {
try
self.objects = moc.executeFetchRequest(request) as! [Objects]
// success ...
} catch {
// failure
print("Fetch failed")
}
}
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
//handle received message
let value = message["Value"] as? String
dispatch_async(dispatch_get_main_queue()) {
self.messageLabel.text = value
}
//send a reply
replyHandler(["Value":"Hello Watch"])
}
How to share the array of Objects with the Watch OS Extension ?
Since you are using WatchConnectivity framework, send the array of objects from iPhone using sendMessage method and in your FavoritesInterfaceController implement the func session(session: WCSession, didReceiveMessage method in order to get the response or you can get the array in replyhandler to.
If the user adds/edits/deletes objects in the array on the Watch OS
how can we update the data on the iPhone ?
Send the objectId along the with new changes in your sendMessage method from watch to phone, on receiving on phone made the changes in your database save it and send the updated value in your replyHandler so that your watch content will be updated accordingly.
Also the iOS application is embedded within a UITabBarController so
does it matter which view controller I communicate with ?
You desired viewController to which you are communicating OR the one that is responsible for doing changes should be alive. If multiple ViewControllers are listening to WCSessionDelegates then when you send any message from watch all of the live controllers will receive that message. You should include some kind of identifier in your sendMessage dictionary so you can know which operation to perform. Like if you want to delete an object then when watch sends a message the identifier will contain delete so that on receiving you can check the identifier value and perform the delete operation.
You can use the replyHandler in the sendMessage to do this. Make sure you implement the reply handler on both Watch and iOS App to get this.
Basically, if you get it right, your reply handler can ensure what your iOS app does in response for a watch app's message.
Also, speaking of your response (of sending an array of objects) - you should send it as a dictionary and fetch it on the watch.
First of, this is a really good question. For starters I'd recommend that you watch this session from the WWDC 2015: Session 713 - Introducing Watch Connectivity. This can be found here.
Now to your actual question. There is a great tutorial and Github repo that show you how to communicate Core Data between your Apple Watch app and the container app using App Groups, as this enables you to access all shared content, such as Core Data and even NSUSerdefaults.
You can then find the complete tutorial on how to do this under the following link.
Hope that helps, Julian.

Sending notification to Apple Watch extension

I have got an iPhone 8.2 app that communicates with a bluetooth accessory during background mode (I enabled it on the capabilities tab). Whenever I receive a message from the accessory (handled in the iPhone app bundle) I'd like to send a notification to the Apple Watch extension (so that the user can visualise the updated state of the accessory).
How can I do this?
Additional sub-questions:
Ideally I'd like the user to see the notification also if the Apple
Watch app extension is in background mode (question 2: can apple
watch extension go in background mode?).
I am also unsure if I can send a notification without turning that
Apple watch app on. question 3: Is this possible?
You can send that notification using MMWormhole.
You send it using:
[self.wormhole passMessageObject:#{#"titleString" : title}
identifier:#"messageIdentifier"];
and you receive it using:
[self.wormhole listenForMessageWithIdentifier:#"messageIdentifier"
listener:^(id messageObject) {
// Do Something
}];
Note that wormhole uses app groups to communicate, so you need to enable it.
What MMWormhole uses, under the hood, is CFNotificationCenterGetDarwinNotifyCenter and you have more info about that in this medium post.
As for the sub-questions I am afraid I don't have 100% certain, but I believe that yes, you the apple watch extension also works in background mode. As for the third question, I didn't understand it.
An update for iOS 9.0 and above.
While MMWormhole works on watchOS 2 as well, it would be preferable to use Apple's WatchConnectivity framework on watchOS 2.0 and above. MMWormhole requires the use of App Groups, while WatchConnectivity does not. WatchConnectivity does indeed require iOS 9.0 and above.
Below is a quick example of how to send a simple String from an iOS app to a WatchKit Extension. First let's set up a helper class.
class WatchConnectivityPhoneHelper : NSObject, WCSessionDelegate
{
static let sharedInstance:WatchConnectivityPhoneHelper = WatchConnectivityPhoneHelper()
let theSession:WCSession = WCSession.defaultSession()
override init()
{
super.init()
// Set the delegate of the WCSession
theSession.delegate = self
// Activate the session so that it will start receiving delegate callbacks
theSession.activateSession()
}
// Used solely to initialize the singleton for the class, which calls the constructor and activates the event listeners
func start()
{
}
// Both apps must be active to send a message
func sendStringToWatch(message:String, callback:[String : AnyObject] -> ())
{
// Send the message to the Watch
theSession.sendMessage(["testString" : message], replyHandler:
{ (reply: [String : AnyObject]) -> Void in
// Handle the reply here
// Send the reply in the callback
callback(reply)
},
errorHandler:
{ (error:NSError) -> Void in
// Handle the error here
})
}
// A message was sent by the Watch and received by the iOS app. This does NOT handle replies to messages sent from the iOS app
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void)
{
// Handle received message logic here
if (message["testString"] != nil)
{
replyHandler(["testString" : "Received Message!"])
}
}
}
This helper class would be almost identical for the WatchKit Extension. I've named the WatchKit Extension version of this class WatchConnectivityExtensionHelper. I won't paste it because, again, it is just about identical to the helper class above.
Usage
We need to start the iOS and WatchKit Extension message listeners by instantiating the singleton helper class. All we need to do is call some function or refer to some variable within the singleton to initialize it. Otherwise, iOS or the WatchKit Extension will be sending out messages but the other might not receive them.
iOS - AppDelegate.swift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
// Start the event listener for communications between the iOS app and the Apple Watch
WatchConnectivityPhoneHelper.sharedInstance().start()
...
}
...
}
WatchKit Extension - ExtensionDelegate.swift
class ExtensionDelegate: NSObject, WKExtensionDelegate
{
func applicationDidFinishLaunching()
{
// Start the event listener for communications between the iOS app and the Apple Watch
WatchConnectivityExtensionHelper.sharedInstance.start()
...
}
...
}
Then anywhere in your iOS app, call sendStringToWatch to send a String to the WatchKit Extension:
WatchConnectivityPhoneHelper.sharedInstance.sendStringToWatch("Test message!")
{ (reply:[String : AnyObject]) -> Void in
if (reply["testString"] != nil)
{
let receivedString:String = reply["testString"] as! String
print(receivedString)
}
}

Check if iPhone is paired with apple watch?

In my app,
I need to find if my phone is paired with a apple watch and get some information about the paired watch like its name. I tried reading the documentation but I couldn't seem to find any thing specific to my use case.
Any help is appreciated.
So since WatchOS 2 that is possible !
You have to do on iPhone side :
First :
import WatchConnectivity
Then :
if WCSession.isSupported() { // check if the device support to handle an Apple Watch
let session = WCSession.default()
session.delegate = self
session.activate() // activate the session
if session.isPaired { // Check if the iPhone is paired with the Apple Watch
// Do stuff
}
}
I hope It would help you :)
The best you can do is write to a shared NSUserDefaults value the first time the user opens your WK app, then check for that value in your iOS app. Beyond that there's no info you can get.
The idea is taken from #BilalReffas answer, but in WatchOS versions greater than 2.1 activate() method is asynchronous, so the offered solution won't work (it always returns false, even if watch is connected)
Firstly import SDK
import WatchConnectivity
Then implement session activation request
if WCSession.isSupported() { // check if the device support to handle an Apple Watch
let session = WCSession.default
session.delegate = self
session.activate() // activate the session
}
Then implement methods from WCSessionDelegate
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if activationState == .activated && session.isPaired { // Check if the iPhone is paired with the Apple Watch
// Do stuff
}
}

Resources