WatchConnectivity not sending data - ios

I'm using WatchConnectivity to send a simple dictionary from an iPhone to Apple Watch.
On the Apple Watch side, to get around the fact that contexts may not be queued when the app is opened, the last received data is saved to UserDefaults and retrieved if there is nothing in the queue when setting up my WatchKit table. I have implemented this in another WatchKit app and everything worked somewhat fine, but in this one data is never received by the Watch.
I've only tried it in the simulator because my app spins for eternity on my Watch and never loads (the loading screen looks like a WatchOs 1 screen?). The WatchConnectivity framework is included in each product (Extension and iPhone app). Thanks for your help.
Here's the iPhone code (implemented in a ViewController):
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
}
- (void)viewDidAppear:(BOOL)animated {
NSDictionary *toPass = [[NSDictionary alloc] initWithObjectsAndKeys:AppDelegate.profiles,#"profiles", nil];
[[WCSession defaultSession] updateApplicationContext:toPass error:nil];
NSLog(#"sent data");
[self.tableView reloadData];
}
And the Apple Watch Code:
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
self.profiles = [[NSMutableArray alloc] init];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
[self setupTable];
}
- (void)session:(WCSession *)session didReceiveApplicationContext:(NSDictionary<NSString *,id> *)applicationContext {
NSDictionary *receieved = [[NSDictionary alloc] init];
receieved = applicationContext;
NSMutableArray *profiles = [[NSMutableArray alloc] init];
profiles = [receieved objectForKey:#"profiles"];
self.profiles = [[NSMutableArray alloc] init];
self.profiles = profiles;
NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:self.profiles];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:arrayData forKey:#"bookmarks"];
[self setupTable];
NSLog(#"new");
}
- (void)setupTable {
...
After some setup code
if (self.profiles.count == 0) {
NSLog(#"Nothing in the queue, checking defaults");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *got = [defaults objectForKey:#"bookmarks"];
NSLog(#"Got Defaults!");
self.profiles = [[NSMutableArray alloc] init];
self.profiles = [NSKeyedUnarchiver unarchiveObjectWithData:got];
}
...
More setup code later
}

Change this line:
[[WCSession defaultSession] updateApplicationContext:toPass error:nil];
To be:
NSError *error = nil;
If (![[WCSession defaultSession] updateApplicationContext:toPass error:&error]) {
NSLog(#"error: %#", error);
}
And I bet you'll see you are getting an error returned!
Also, what type of objects does AppDelegate.profiles contain?

Related

Watch App: [WCSession defaultSession].isReachable is always retrieve 'FALSE' status on background app?

I am developing apple watch application. when app on foreground it is working fine [WCSession defaultSession].isReachable and retrieve ON status. Now my watch application is goes to background mode then problem must be create.
so how to resolve this issues? and retrieve ON status on background mode.
My code is as follows.
- (void)willActivate {
[super willActivate];
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager setDelegate:self];
[self.locationManager requestLocation];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
if ([[WCSession defaultSession] isReachable]) {
NSLog(#"Session Reachable");
} else {
NSLog(#"Session Not Reachable");
}
if([WCSession defaultSession].iOSDeviceNeedsUnlockAfterRebootForReachability) {
WKAlertAction *action = [WKAlertAction actionWithTitle:#"OK"
style:WKAlertActionStyleDefault
handler:^{
}];
NSString *title = #"My App";
NSString *message = #"Reachability in the Watch app requires the paired iOS device to have been unlocked at least once after reboot";
[self presentAlertControllerWithTitle:title message:message preferredStyle:WKAlertControllerStyleAlert actions:#[action]];
}
if ([[WCSession defaultSession] isReachable]) {
NSString *strUserId = [[NSUserDefaults standardUserDefaults]
stringForKey:#"user_id"];
if ([strUserId isEqualToString:#""])
{
WKAlertAction *act = [WKAlertAction actionWithTitle:#"OK" style:WKAlertActionStyleCancel handler:^(void){
NSLog(#"ALERT YES ");
}];
NSArray *testing = #[act];
[self presentAlertControllerWithTitle:#"My App" message:#"You are not login" preferredStyle:WKAlertControllerStyleAlert actions:testing];
}else{
[self addTrackingdata];
[self loadPairList];
}
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(UpdateData) name:#"refreshData" object:nil];
ExtensionDelegate *del = (ExtensionDelegate *)[WKExtension sharedExtension].delegate;
if (del.strReminderTime == 0 && del.isTimerFlag) {
[self UpdateData];
isReminderFlag = NO;
del.isTimerFlag = NO;
NSTimer * CheckTimer = [NSTimer scheduledTimerWithTimeInterval:10.0f target:self selector:#selector(CheckConnectation) userInfo:nil repeats:YES];
[CheckTimer fire];
}
}
- (void)CheckConnectation
{
WCSession *session = [WCSession defaultSession];
if([WCSession isSupported]) {
session.delegate = self;
[session activateSession];
}
if([WCSession defaultSession].isReachable){
[_lblPairedStatus setText:#"connected"];
}
else
{
[_lblPairedStatus setText:#"disconnected"];
}
}
Appreciate if any suggestion or idea.
I just had a similar problem. The solution for me was to check isReachable in the didAppear function instead of willActivate.

How to mirror iOS screen via USB?

I'm trying to mirror iOS device screen via USB connection to OSX. QuickTime does this fine, and I read this article with a code example: https://nadavrub.wordpress.com/2015/07/06/macos-media-capture-using-coremediaio/
However, the callback of CMIOStreamCopyBufferQueue is never called and I'm wondering what am I doing wrong?
Have anyone faced this issue and can provide a working example ?
Thanks.
Well.. eventually I did what Nadav told me in his blog - discover DAL devices and capture their output using AVCaptureSession like this:
-(id) init {
// Allow iOS Devices Discovery
CMIOObjectPropertyAddress prop =
{ kCMIOHardwarePropertyAllowScreenCaptureDevices,
kCMIOObjectPropertyScopeGlobal,
kCMIOObjectPropertyElementMaster };
UInt32 allow = 1;
CMIOObjectSetPropertyData( kCMIOObjectSystemObject,
&prop, 0, NULL,
sizeof(allow), &allow );
// Get devices
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed];
BOOL deviceAttahced = false;
for (int i = 0; i < [devices count]; i++) {
AVCaptureDevice *device = devices[i];
if ([[device uniqueID] isEqualToString:/*deviceUDID*/]) {
deviceAttahced = true;
[self startSession:device];
break;
}
}
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
// Device not attached - subscribe to onConnect notifications
if (!deviceAttahced) {
id deviceWasConnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
AVCaptureDevice *device = note.object;
[self deviceConnected:device];
}];
observers = [[NSArray alloc] initWithObjects:deviceWasConnectedObserver, nil];
}
return self;
}
- (void) deviceConnected:(AVCaptureDevice *)device {
if ([[device uniqueID] isEqualToString:/*deviceUDID*/]) {
[self startSession:device];
}
}
- (void) startSession:(AVCaptureDevice *)device {
// Init capturing session
session = [[AVCaptureSession alloc] init];
// Star session configuration
[session beginConfiguration];
// Add session input
NSError *error;
newVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (newVideoDeviceInput == nil) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
NSLog(#"%#", error);
});
} else {
[session addInput:newVideoDeviceInput];
}
// Add session output
videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey: (id)kCVPixelBufferPixelFormatTypeKey];
dispatch_queue_t videoQueue = dispatch_queue_create("videoQueue", NULL);
[videoDataOutput setSampleBufferDelegate:self queue:videoQueue];
[session addOutput:videoDataOutput];
// Finish session configuration
[session commitConfiguration];
// Start the session
[session startRunning];
}
#pragma mark - AVCaptureAudioDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
NSImage *resultNSImage = [self imageFromSampleBuffer:sampleBuffer];
/*
* Here you can do whatever you need with the frame (e.g. convert to JPG)
*/
}

Receive Watch OS message on iOS device

Hi everyone and thanks for your support in advance,
I just started working with Watch OS 2 and Objective-C and I am trying to send a message to an iPhone device when a button is tapped, I don't know if the next approach is the best but from Apple docs it seems to best feet my needs, as I need to send a request to and paired iOS device and receive some user info, I also read that this only works if the app in in foreground witch is a downside :(
Update 1
- (IBAction)didTappedButton {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
if ([session isReachable] == YES) {
NSDictionary *postDictionarry = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObject:#"retrieveAPISessionKey"] forKeys:[NSArray arrayWithObject:#"request"]];
[self.button setBackgroundColor:[UIColor blueColor]];
[session sendMessage:postDictionarry
replyHandler:^(NSDictionary<NSString *,id> * _Nonnull replyMessage) {
[self postToServer];
}
errorHandler:^(NSError * _Nonnull error) {
[self.button setBackgroundColor:[UIColor redColor]];
[self showAlertViewwithTitle:#"Oops..." andMessage:#"Something went Wrong"];
}];
}else{
[self showAlertViewwithTitle:#"Oops..." andMessage:#"Please pair with a device"];
}
}
And in the AppDelegate of I implemeted the next code in .h:
#import WatchConnectivity;
#interface AppDelegate : UIResponder <UIApplicationDelegate, UIGestureRecognizerDelegate, WCSessionDelegate>
and in the .m:
- (void)session:(nonnull WCSession *)session
didReceiveMessage:(NSDictionary<NSString *,id> *)message
replyHandler:(void(^)(NSDictionary<NSString *,id> *))replyHandler {
NSString *action = message[#"request"];
NSString *actionPerformed;
// more code here...
}
note I am testing on the simulator and have an issues in getting tap gestures and also I see a lot of spinners on the watch simulator
Update 2
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
- (IBAction)didTappedButton {
if ([[WCSession defaultSession] isReachable] == YES) {
NSDictionary *postDictionarry = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObject:#"retrieveAPISessionKey"] forKeys:[NSArray arrayWithObject:#"request"]];
[self.button setBackgroundColor:[UIColor blueColor]];
[[WCSession defaultSession] sendMessage:postDictionarry
replyHandler:^(NSDictionary<NSString *,id> * _Nonnull replyMessage) {
[self postData];
}
errorHandler:^(NSError * _Nonnull error) {
[self.button setBackgroundColor:[UIColor redColor]];
[self showAlertViewwithTitle:#"Oops..." andMessage:#"Something went Wrong"];
}];
}else{
[self showAlertViewwithTitle:#"Oops..." andMessage:#"Please pair with a device"];
}
}
- (void)postData{
//post stress signal to server
}
- (void)showAlertViewwithTitle:(NSString *)title andMessage:(NSString *)message{
WKAlertAction *act = [WKAlertAction actionWithTitle:#"OK" style:WKAlertActionStyleCancel handler:^(void){}];
NSArray *actions = #[act];
[self presentAlertControllerWithTitle:title message:message preferredStyle:WKAlertControllerStyleAlert actions:actions];
}
So, just succeede in sending the message and also pair devices but now i don't receive the sent dictionary in the iOS app.
You need to activate your WCSession in both the WatchKit App Extension and in the iPhone application. Based on the code you've shown us, it does not appear you are activating it in your iPhone application.
Add the following to your iPhone application:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([WCSession isSupported]) {
WCSession* session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
...
You should then be able to receive the message via your existing session:didReceiveMessage:replyHandler: method.

Generate BarCode on Watch OS 2

How do i generate a barcode on apple watch with Watch OS2, i can do it using API's like ZXing on iOS, but i wonder is there a way to do the same in watchOS2
NSError *error = nil;
ZXMultiFormatWriter *writer = [ZXMultiFormatWriter writer];
ZXBitMatrix* result;
//generate code 128 barcode
result= [writer encode:barCodeNumber
format:kBarcodeFormatCode128
width:500
height:500
error:&error];
if (result) {
CGImageRef image = [[ZXImage imageWithMatrix:result] cgimage];
return [UIImage imageWithCGImage:image];
}
I figured it out by generating image in iOS app and passing it to watch os using background transfers, by creating NSData from image, something like this
- (void)viewDidLoad {
[super viewDidLoad];
if([WCSession isSupported]){
self.watchSession = [WCSession defaultSession];
self.watchSession.delegate = self;
[self.watchSession activateSession];
}
}
-(void)sendDatatoAppleWatch
{
NSMutableArray*barCodesArray=[[NSMutableArray alloc]init];
UIImage* barCodeImage=[self generateBarCode];
NSData *pngData = UIImagePNGRepresentation(barCodeImage);
[barCodeArray addObject:pngData]
if(self.watchSession){
NSError *error = nil;
if(![self.watchSession
updateApplicationContext:
#{#"cardData" : userCardsArray }
error:&error]){
NSLog(#"Updating the context failed: %#", error.localizedDescription);
UIAlertView* errorAlert=[[UIAlertView alloc]initWithTitle:error.localizedDescription message:error.debugDescription delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[errorAlert show];
}
}
//*** Apple Watch Code**//
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
if([WCSession isSupported]){
self.watchSession = [WCSession defaultSession];
self.watchSession.delegate = self;
[self.watchSession activateSession];
}
}
- (void) session:(WCSession *)session didReceiveApplicationContext:(NSDictionary<NSString *,id> *)applicationContext {
NSData* imageData = [[[applicationContext objectForKey:#"cardData"] objectAtIndex:0] valueForKey:#"barCodeImage"];
[self.barcodeImageView setImage:[UIImage imageWithData:imageData]];
}
Generate on iOS app, then transfer it to the watch. Good Luck!
Check this library: EFQRCode.
As from its doc, the original implementation is from swift_qrcodejs, which is Cross-appleOS SIMPLE QRCode generator for swift, without using CIFilter.

Apple Watch, WatchKit, and NSUserDefaults [duplicate]

This question already has answers here:
WatchKit SDK not retrieving data from NSUserDefaults
(2 answers)
Closed 8 years ago.
I figured since the "Watch App" is a bundle in the same iOS application, I should be able to access NSUserDefaults within my Apple Watch App. Is this not so?
I can't seem to get NSUserDefaults values from my iOS (which I made sure has data as it loads fine in the iOS app).
When I run it in the Apple Watch app, it comes in as empty. Do I not have access to the same NSUserDefaults values as the iOS parent app?
I have a custom class which contains an NSMutalArray. This mutable array is what I want to display in a Table in the Watch App. This same class which contains the NSMutableArray also has functions to save/load data to/from NSUserDefaults. It works fine in my iOS app but just doesn't load data in the Apple Watch app.
What am I doing wrong here or what am I not aware of?
Here is the class:
Keep in mind that I use it as a singleton object too. It saves it's data into NSUserDefaults by first converting itself into NSData - which is why you see extra functions in this class that you can just ignore.
#import "Teacher.h"
#import "Course.h"
#implementation Teacher
static Teacher *sharedInstance = Nil;
+ (Teacher *)sharedInstance
{
#synchronized(self)
{
if(!sharedInstance)
{
sharedInstance = [[self alloc] init];
}
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if (self)
{
_classes = [[NSMutableArray alloc] init];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:_classes forKey:#"classes"];
}
- (id)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self)
{
_classes = [coder decodeObjectForKey:#"classes"];
}
return self;
}
+ (void)saveData
{
NSMutableArray *teacherClasses = [[Teacher sharedInstance] classes];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:teacherClasses];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:data forKey:#"teacherClasses"];
}
+ (void)loadData
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *loadClasses= [NSKeyedUnarchiver unarchiveObjectWithData:[userDefaults objectForKey:#"teacherClasses"]];
if(loadClasses == Nil)
{
loadClasses = [[NSMutableArray alloc] init];
}
[[Teacher sharedInstance] setClasses:loadClasses];
}
- (Teacher *) copyWithZone:(NSZone *)zone
{
Teacher *teacher = [[Teacher alloc] init];
[teacher setClasses:[self classes]];
return teacher;
}
#end
Thanks.
You need to use a shared container if you wish to share data between your iOS app and your Apple Watch app.

Resources