way to update wkinterfacecontroller during runtime in watch os 2 - objective-c

In Header file
#import <WatchKit/WatchKit.h>
#import <Foundation/Foundation.h>
#import <WatchConnectivity/WatchConnectivity.h>
#interface InterfaceController : WKInterfaceController<WCSessionDelegate>
- (IBAction)lastSongButtonClick;
- (IBAction)playSongButtonClick;
- (IBAction)nextSongButtonClick;
#property (strong, nonatomic) IBOutlet WKInterfaceLabel *songTitleLabel;
#property (strong, nonatomic) IBOutlet WKInterfaceButton *playSongButton;
#end
So I implemented the WCSessionDelegate and every time I receive about the UI, I would want it to update. So in my .m file I have:
- (void)session:(nonnull WCSession *)session didReceiveMessage:(nonnull NSDictionary<NSString *,id> *)message{
NSString* type = [message objectForKey:#"type"];
if([type isEqualToString:#"UIUpdateInfo"]){
NSLog(#"Watch receives UI update info");
[self handleUIUpdateInfo:[message objectForKey:#"content"]];
}
}
AND
- (void)handleUIUpdateInfo:(NSDictionary*)updateInfo{
[self.songTitleLabel setText:[updateInfo objectForKey:#"nowPlayingSongTitle"]];
[self.playSongButton setBackgroundImage:[updateInfo objectForKey:#"playButtonImage"]];
}
However, it doesn't seems to update. Is there any proper way to update?

You're halfway there. You've configured receiving the message on the watch side correctly, but you'll need to trigger a message to be sent when the UI is updated (therefore triggering didReceiveMessage to execute and update the appropriate content).
Where ever you are making changes to the UI, you'll need to include this:
NSDictionary *message = //dictionary of info you want to send
[[WCSession defaultSession] sendMessage:message
replyHandler:^(NSDictionary *reply) {
//handle reply didReceiveMessage here
}
errorHandler:^(NSError *error) {
//catch any errors here
}
];
Also, make sure you're activating the WCSession properly. This is usually done in viewDidLoad or willAppear depending on whether you're implementing this on the phone or the watch.
- (void)viewDidLoad {
[super viewDidLoad];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
}
You can see a full example of an end-to-end Watch to iPhone data transfer in this tutorial - http://www.kristinathai.com/watchos-2-tutorial-using-sendmessage-for-instantaneous-data-transfer-watch-connectivity-1

Related

React Native and Objective C delegates

I am quite new to react native and and the bridging mechanism with native code, especially when the framework has delegates. Assume I am trying to bridge the following framework:
#protocol BRPtouchNetworkDelegate;
#class PLNetworkModule;
#interface BRPtouchNetworkManager : NSObject <NSNetServiceBrowserDelegate,NSNetServiceDelegate>
#property(retain, nonatomic) NSMutableArray* registeredPrinterNames;
#property(assign, nonatomic) BOOL isEnableIPv6Search;
- (int)startSearch: (int)searchTime;
- (NSArray*)getPrinterNetInfo;
- (BOOL)setPrinterNames:(NSArray*)strPrinterNames;
- (BOOL)setPrinterName:(NSString*)strPrinterName;
- (id)initWithPrinterNames:(NSArray*)strPrinterNames;
- (id)initWithPrinterName:(NSString*)strPrinterName;
#property (nonatomic, assign) id <BRPtouchNetworkDelegate> delegate;
#end
#protocol BRPtouchNetworkDelegate <NSObject>
-(void) didFinishSearch:(id)sender;
#end
The following is the bridge module I implemented:
RCTBRPtouchNetworkManager.h
#import <React/RCTBridgeModule.h>
#import <BRPtouchPrinterKit/BRPtouchPrinterKit.h>
#interface RCTBRPtouchNetworkManager : NSObject <RCTBridgeModule, BRPtouchNetworkDelegate>
#end
RCTBRPtouchNetworkManager.m
#import "RCTBRPtouchNetworkManager.h"
#import <BRPtouchPrinterKit/BRPtouchPrinterKit.h>
#import <React/RCTLog.h>
#implementation RCTBRPtouchNetworkManager {
BRPtouchNetworkManager *_networkManager;
}
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(#"Pretending to create an event %# at %#", name, location); //a dummy method to test the bridge
}
RCT_EXPORT_METHOD(startSearchWithTimeout:(int)time) {
RCTLogInfo(#"Bridge started search with time %d", time);
_networkManager = [[BRPtouchNetworkManager alloc] init];
_networkManager.delegate = self; //I'm setting delegate here
_networkManager.isEnableIPv6Search = NO;
NSString * path = [[NSBundle mainBundle] pathForResource:#"PrinterList" ofType:#"plist"];
if( path )
{
NSDictionary *printerDict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *printerList = [[NSArray alloc] initWithArray:printerDict.allKeys];
[_networkManager setPrinterNames:printerList];
} else {
RCTLogInfo(#"PrinterList path not found");
}
// Start printer search
[_networkManager startSearch: 5.0];
}
- (void)didFinishSearch:(id)sender {
NSLog(#"didFinishedSearch"); //this delegate method is not called
}
#end
I can easily call the dummy method and see the results in the logs. However, the delegate method didFinishSearch() is never called. I call this from javascript as follows:
componentDidMount() {
let networkManager = NativeModules.BRPtouchNetworkManager;
networkManager.startSearchWithTimeout(5.0);
}
I there something I am missing? Am I implementing delegate properly? Is this kind of functionality even possible (can't seem to not since the delegate method was used by iOS community for a long time). Your help is much appreciated.
EDIT
I found that adding the following to my bridge manager file made the delegate to fire (thanks to this post)
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
However, even though this solves the problem, I'd like a more technical understanding on what is going on here since I can't seem to exactly grasp it. Thank you
I know this isn’t an an answer to the post but for the bit where you’ve asked for a more technical understanding - dispatch_get_main_queue(); puts the delegate method responses on to the main thread. Since JS is single threaded any process on the background thread won’t be visible to it.

How to verify that a given time WebView performs JS-code?

This is a Cocoa-component WebView. For example he called JS-function:
[webView stringByEvaluatingJavaScriptFromString:#"foo"];
I need to somehow wait until this function is executed and start doing another job. How this can be done on the Objective-C? I need something like:
[webView waitUntilJavaScriptCodeIsCompleted];
[webView stringByEvaluatingJavaScriptFromString:] is executed synchronously and will return an NSString of the result of executing the passed in script. So, if you just want to know that script has executed, call the function and when it returns, it has executed.
If, however, you're talking about a script with some sort of asynchronous execution, like an XMLHTTPRequest or setTimeout, then you're going to need to have your JavaScript call back into your Objective-C code to let it know when it's finished. Here is an example project that does just that. The most relevant parts are in AppDelegate:
#import "AppDelegate.h"
#import WebKit;
#import JavaScriptCore;
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet WebView *webView;
#end
#interface AppDelegate (WebDelegate) <WebFrameLoadDelegate>
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
JSGlobalContextRef jsGlobalContext = _webView.mainFrame.globalContext;
// Add a function named "allDone" to the window object. It can be called from JavaScript like so:
JSContext* context = [JSContext contextWithJSGlobalContextRef:jsGlobalContext];
context[#"allDone"] = ^{
NSLog(#"All Done was called");
};
context[#"jsLog"] = ^(NSString* message) {
NSLog(#"JSLOG: %#", message);
};
_webView.frameLoadDelegate = self;
NSURL* url = [NSURL URLWithString:#"https://example.com"];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:url];
[_webView.mainFrame loadRequest:req];
}
#end
#implementation AppDelegate (WebDelegate)
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
NSLog(#"Page loaded, calling JavaScript");
// Script that logs a message, and then invokes allDone after 2 seconds.
NSString* script =
#"jsLog('Script running');"
#"setTimeout(function() {\n"
#"jsLog('javascript timer fired, invoking allDone...');\n"
#"window.allDone();\n"
#"}, 2000);";
NSLog(#"Before stringByEvaluatingJavaScriptFromString");
[_webView stringByEvaluatingJavaScriptFromString:script];
NSLog(#"After stringByEvaluatingJavaScriptFromString");
}
#end
Which results in the following output:
Page loaded, calling JavaScript
Before stringByEvaluatingJavaScriptFromString
JSLOG: Script running
After stringByEvaluatingJavaScriptFromString
JSLOG: javascript timer fired, invoking allDone...
All Done was called

OSX Bluetooth scanning detects duplicate peripherals

I'm new to apple development and have been tackling the issue of Bluetooth. I trying to write a basic OSX application that will scan for nearby devices and populate a PopupList with a list of discovered devices.
Just to set the scene a little..
I have a button 'startStopScanBut' which will kickoff 'startStopScan'
I also have two logs. Conn log for connection info. stateLog for phone state info.
ViewController.h
// ViewController.h
// F4STestHarness
//
// Created by Nicholas Hayward on 26/06/2015.
// Copyright (c) 2015 Nicholas Hayward. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <CoreBluetooth/CoreBluetooth.h>
#interface ViewController : NSViewController
<CBCentralManagerDelegate,
CBPeripheralDelegate>
#end
ViewController.m
// ViewController.m
// F4STestHarness
//
// Created by Nicholas Hayward on 26/06/2015.
// Copyright (c) 2015 Nicholas Hayward. All rights reserved.
//
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic) IBOutlet NSTextView *stateLogTextView;
#property (nonatomic) IBOutlet NSTextView *bluetoothLogTextView;
#property BOOL bluetoothOn;
#property BOOL isScanning;
#property (nonatomic) IBOutlet NSButton *startStopScanBut;
#property (strong, nonatomic) CBCentralManager *centralManager;
#property (nonatomic) IBOutlet CBPeripheral *foundPeripheral;
#property (nonatomic) IBOutlet NSPopUpButton *foundPeripheralsPop;
/*#property BOOL isAdvertising;
#property BOOL isAdvertisingInitialised;
#property (nonatomic) IBOutlet NSButton *startStopAdvertisingButton;
#property (strong, nonatomic) CBPeripheralManager *peripheralManager;
#property (strong, nonatomic) CBMutableCharacteristic *myCharacteristic;
#property (strong, nonatomic) NSDictionary *advertisingData;*/
#define SERVICE_UUID # "1f9f7e9d-7a83-4053-87f9-2c328b8f315a"
#define CHARACTERISTIC_UUID # "FFF3"
#end
#implementation ViewController
//Write to log function
-(void)conLog:(NSString *)msg
{
self.bluetoothLogTextView.string = [#"\r\n" stringByAppendingString:self.bluetoothLogTextView.string];
self.bluetoothLogTextView.string = [msg stringByAppendingString:self.bluetoothLogTextView.string];
}
-(void)stateLog:(NSString *)msg
{
self.stateLogTextView.string = [#"\r\n" stringByAppendingString:self.stateLogTextView.string];
self.stateLogTextView.string = [msg stringByAppendingString:self.stateLogTextView.string];
}
//form events
//form load
- (void)viewDidLoad {
[super viewDidLoad];
//Initialise flags
self.bluetoothOn = NO;
self.isScanning = NO;
//self.isAdvertising = NO;
//self.isAdvertisingInitialised = NO;
//initialise bluetooth central manager
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:Nil];
//initialise bluetooth perihperl manager
//self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:Nil];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
//Bluetooth Central events
- (IBAction)startStopScan:(id)sender
{
//check bluetooth
if (!self.bluetoothOn){
[self conLog:#"Bluetooth is OFF, Cannot start scan"];
return;
}
//Do we need to start or stop the scan?
if (!self.isScanning)
{
//We are not scanning, but we are starting a new scan
//empty the foundPeripheralsPop
[_foundPeripheralsPop removeAllItems];
//Start the scan. (we scan for the service uuid used when advertising services)
[self.centralManager scanForPeripheralsWithServices: Nil
options: #{ CBCentralManagerScanOptionAllowDuplicatesKey : #NO }];
//update flag
self.isScanning = YES;
//change button text to say stop Scanning
[self.startStopScanBut setTitle:#"Stop Scanning"];
//update log
[self conLog:#"Starting scan for Peripherals"];
}
else
{
//We are scanning, Stop the scan
[self.centralManager stopScan];
//update flag
self.isScanning = NO;
//change button text to say start Scanning
[self.startStopScanBut setTitle:#"Get Advertising Peripherals"];
//update log
[self conLog:#"Stopped scan for peripherals"];
}
}
//the state of the adapter has changedA(su1 enabled or disabled bluetooth
-(void)centralManagerDidUpdateState:(CBCentralManager *)central
{
if (central.state != CBCentralManagerStatePoweredOn)
{
[self stateLog:#"Bluetooth OFF"];
self.bluetoothOn = NO;
}
else
{
[self stateLog:#"Bluetooth ON"];
self.bluetoothOn = YES;
}
}
//We have found a peripheral device while scanning.
-(void) centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
{
NSString *deviceName = [advertisementData objectForKey:#"kCBAdvDataLocalName"];
if (deviceName == nil || [deviceName isEqual: #""]) deviceName = #"No Name";
//log what device we discovered
[self conLog:[NSString stringWithFormat:#"Discovered %#, RSSI: %#\n", deviceName, RSSI]];
//Add device to select list
[_foundPeripheralsPop addItemWithTitle:deviceName];
self.foundPeripheral = peripheral;
//[self.centralManager connectPeripheral:peripheral options:nil];
}
The issue I am having is that this OSX app will constantly find the same peripheral over and over kicking off the didFindPeripheral Function constantly. I have created a separate IOS project based on this code above and it does not find duplicates.
The peripherals I'm trying to find to is...
1. the Lynda.com peripheral examples running on my plugged in ios device.
2. Fitbit fitness tracker. (http://www.fitbit.com)
Is there anything I'm missing in OSX to stop these duplicates coming through?? Anyone got any ideas why its finding these devices over and over??
Are you really only retaining a single discovered peripheral? Shouldn't that be storing it in an array?
If that isn't the problem, then it might be a buggy device, buggy Bluetooth firmware in the Mac, or it might simply be just at the edge of range, and the computer might be giving up on it and then re-detecting it. (I assume you've also implemented the - centralManager:didDisconnectPeripheral:error: delegate method, right?) Or you might be stopping the scan and restarting it on the Mac, but not doing that on iOS, because of differences elsewhere in the app.
Assuming that the bug is not in your code, my suggestion would be to try to recognize the redundant devices by comparing the identifier property on the CBPeripheral, and then update that item in your UI with the latest information for the device when the computer rediscovers it rather than adding a new item. (I'm assuming these devices have properly unique UUIDs, of course.)

Access to NSUInteger property

I've some problem with accessing NSUInteger property in my code, which look like that:
MyController.h
#interface MyController : UIViewController
#property (retain, nonatomic) NSArray *updatesArray;
#property (nonatomic) NSUInteger madeUpdatesCounter;
#property (nonatomic) NSUInteger allUpdatesCounter;
#end
MyController.m
#implementation MyController
#synthesize updatesArray;
#synthesize madeUpdatesCounter;
#synthesize allUpdatesCounter;
- (void)viewDidLoad
{
....
madeUpdatesCounter = 0;
allUpdatesCounter = [updatesArray count];
....
// Register for progress notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(makeProgressChange)
name:#"MadeOneUpdateNotification"
object:nil];
}
- (void)makeProgressChange
{
madeUpdatesCounter++;
NSLog(#"Update progress: %d/%d", madeUpdatesCounter, allUpdatesCounter);
}
#end
I'm prosessing my updates as NSInvocationOperation by adding to NSOperationQueue. In the end of the one update action I'm sending notification:
[[NSNotificationCenter defaultCenter] postNotificationName:#"MadeOneUpdateNotification" object:nil];
After executing above code, receiving notifocation is execute only once and in logs I see someting like this:
Update progress: 1/3
When I change line:
allUpdatesCounter = [updatesArray count];
to
allUpdatesCounter = 3;
then everything works ok, and I see in logs:
Update progress: 1/3
Update progress: 2/3
Update progress: 3/3
Variable updatedArray is initialized before view is loaded. Is done in this way:
MyController *doUpdatesVC = [self.storyboard instantiateViewControllerWithIdentifier:#"MyController"];
doUpdatesVC.updatesArray = updatesArray;
[self presentViewController:doUpdatesVC animated:YES completion:nil];
Do you have any advices or hints what I'm doing wrong in my code?
OK, I've found reason of my problems. The applications locked by accessing to the same variable by operations launched from queue. When I changed logic of my code, then everything start working correctly.

Implementing ZBar QR Code Reader in UIView

I really need help here. I'm pretty new to iOS/Objective-C so sorry if the problem resolution is obvious or if my code is terrible. Be easy on me!! :-)
I'm struggling to integrate ZBarSDK for reading QR Codes into an iPad app i'm building. If I use ZBarReaderController (of which there are plenty of tutorials and guides on implementing), it works fine. However I want to make the camera come up in a UIView as opposed to the fullscreen camera.
Now I have gotten as far as making the camera view (readerView) come up in the UIView (ZBarReaderView) as expected, but I get an error when it scans a code. The error does not come up until a code is scanned making me believe this is either delegate related or something else.
Here's the important parts of my code: (ZBarSDK.h is imported at the PCH file)
SignInViewController.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#class AVCaptureSession, AVCaptureDevice;
#interface SignInViewController : UIViewController
< ZBarReaderDelegate >
{
ZBarReaderView *readerView;
UITextView *resultText;
}
#property (nonatomic, retain) UIImagePickerController *imgPicker;
#property (strong, nonatomic) IBOutlet UITextView *resultText;
#property (strong, nonatomic) IBOutlet ZBarReaderView *readerView;
-(IBAction)StartScan:(id) sender;
SignInViewController.m
#import "SignInViewController.h"
#interface SignInViewController ()
#end
#implementation SignInViewController
#synthesize resultText, readerView;
-(IBAction)StartScan:(id) sender
{
readerView = [ZBarReaderView new];
readerView.readerDelegate = self;
readerView.tracksSymbols = NO;
readerView.frame = CGRectMake(30,70,230,230);
readerView.torchMode = 0;
readerView.device = [self frontFacingCameraIfAvailable];
ZBarImageScanner *scanner = readerView.scanner;
[scanner setSymbology: ZBAR_I25
config: ZBAR_CFG_ENABLE
to: 0];
[self relocateReaderPopover:[self interfaceOrientation]];
[readerView start];
[self.view addSubview: readerView];
resultText.hidden=NO;
}
- (void) readerControllerDidFailToRead: (ZBarReaderController*) reader
withRetry: (BOOL) retry{
NSLog(#"the image picker failing to read");
}
- (void) imagePickerController: (UIImagePickerController*) reader didFinishPickingMediaWithInfo: (NSDictionary*) info
{
NSLog(#"the image picker is calling successfully %#",info);
id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];
ZBarSymbol *symbol = nil;
NSString *hiddenData;
for(symbol in results)
hiddenData=[NSString stringWithString:symbol.data];
NSLog(#"the symbols is the following %#",symbol.data);
resultText.text=symbol.data;
NSLog(#"BARCODE= %#",symbol.data);
NSLog(#"SYMBOL : %#",hiddenData);
resultText.text=hiddenData;
}
The error I get when a code is scanned:
2012-12-16 14:28:32.797 QRTestApp[7970:907] -[SignInViewController readerView:didReadSymbols:fromImage:]: unrecognized selector sent to instance 0x1e88b1c0
2012-12-16 14:28:32.799 QRTestApp[7970:907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SignInViewController readerView:didReadSymbols:fromImage:]: unrecognized selector sent to instance 0x1e88b1c0'
I'm not too worried about what happens with the results just yet, just want to get over this error. Took me ages just to get the camera to come up in the UIView due to severe lack of tutorial or documentation on ZBarReaderView (for beginners anyway). Thanks all.
I fixed this. Had an incorrect delegate name. It should have been "ZbarReaderViewDelegate". I was missing the "view" part. Doh!!
#interface SignInViewController : UIViewController
< ZBarReaderViewDelegate >
{
ZBarReaderView *readerView;
UITextView *resultText;
}