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.)
Related
I have looked all over the place for anyone who has experienced this issue but have yet to find anything relevant, so I thought I'd ask it myself...
I have a custom object (HitterData) which I will use to populate cells in a UITableView, then two ViewControllers (one is hitterTableViewController, the other is a "detail" view controller labeled "AddPlayerViewController").
The problem is that I can add HitterData objects to the NSMutableArray in my Table VC, but only one, and then when I add another one using the detail view controller, the Mutable array is "reinitialized" and I can again only have one object at a time.
HitterObject:
#implementation HitterData.m
#synthesize hitterName = _hitterName;
#synthesize position = _position;
-(id)initWIthNameAndPosition:(NSString *)hitterName position:(NSString *)position {
if ((self = [super init])) {
self.hitterName = _hitterName;
self.position = _position;
}
return self;
}
HitterTableViewController.h
#import "HitterData.h"
#import "HitterDoc.h"
#import "AddPlayerViewController.h"
#interface HitterTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *hitters;
- (IBAction)backButton:(id)sender;
- (IBAction)addPlayerView:(id)sender;
-(void)addHitterObject:(HitterData *)hitter;
HitterTableViewController.m (only relevant to make this more readable)
#implementation HitterTableViewController
#synthesize hitters = _hitters;
- (void)viewDidLoad {
[super viewDidLoad];
self.hitters = [NSMutableArray array];
}
-(void)addHitterObject:(HitterData *)hitter {
if(_hitters != nil) {
[_hitters addObject:hitter];
} else {
_hitters = [NSMutableArray array];
[_hitters addObject:hitter];
NSLog(#"MutableArray is not valid");
}
}
AddPlayerViewController.h
#interface AddPlayerViewController : UIViewController <UITextFieldDelegate, UINavigationControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *positionTextField;
#property (nonatomic) HitterTableViewController *hitterTable;
#property (nonatomic) NSString *hitterName;
#property (nonatomic) NSString *position;
//-(void)addNewHitterToHittersArray:(HitterData *)hitter;
- (IBAction)addPlayerToRoster:(id)sender;
AddPlayerViewController.m
#implementation AddPlayerViewController
#synthesize hitterTable;
- (void)viewDidLoad {
[super viewDidLoad];
hitterTable = [[HitterTableViewController alloc] init];
}
- (IBAction)addPlayerToRoster:(id)sender {
self.hitterName = [self.nameTextField text];
self.position = [self.positionTextField text];
HitterData *hitter = [[HitterData alloc] init];
hitter.hitterName = self.hitterName;
hitter.position = self.position;
[hitterTable addHitterObject:hitter];
ArraySingleton *arrayS = [[ArraySingleton alloc] init];
[arrayS initializeArray];
[arrayS addToHittersArray:hitter];
if (arrayS) {
NSLog(#"arrayS exists in AddPlayerVC");
} else {
NSLog(#"arrayS does not exist");
}
[self performSegueWithIdentifier:#"backToTeamTableViewController" sender:self];
}
Am I missing something here?
Guess based just on the code shown:
Every time you wish to add a player it looks like you create a new AddPlayerView/AddPlayerViewController. In turn that controller creates, in its viewDidLoad, a new HitterTableViewController - which of course has its own empty array. The code should instead be referencing the existing HitterTableViewController.
BTW: The common design pattern is MVC - model, view, controller - consider whether you are in your current situation because you've stored part of your model, the array, in your controller, and maybe both controllers should be referencing a common shared model object containing that array.
BTW: All those #synthesise statements are unneeded. In modern Obj-C synthesis is automatic and you rarely need these statements. Also it is generally recommended to not use the property backing variable directly, and certainly not when storing into the property as this breaks KVO. (There also appears to be a related typo in HitterData.m but as you don't report that as not working it is probably just a typo in your question and not code.)
HTH
AddPlayerViewController should know nothing about HitterTableViewController, return the new HitterData object with a delegate method.
- (IBAction)addPlayerToRoster:(id)sender
{
Hitter *hitter = [[Hitter alloc] init];
hitter.name = [self.nameTextField text];
hitter.position = [self.positionTextField text];
[self.delegate didAddHitter:hitter];
}
Then back in HitterTableViewController
- (void)didAddHitter:(Hitter *)hitter
{
[self.hitters addHitter:hitter];
[self dismissViewControllerAnimated:YES completion:nil];
}
I’m just a newbie in Mac OS X programming so please, be patient with me. I’m trying to make a cocoa app which the goal is to read some infos from Info.plist kext’s file, which the full path is /System/Library/Extensions/NVDAResman.kext/Contents/Info.plist
h.
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (weak) IBOutlet NSTextField *nvidiaNameTextField;
#end
m.
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self setnvidiaNameTextField];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
// This will allow the application to quit instead of just closing the window
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
return YES;
}
// Find NVIDIA Kernel Extension Name
-(void)setnvidiaNameTextField
{
NSString *nvidiaNameTextField = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleName"];
self.nvidiaNameTextField.stringValue = [NSString stringWithFormat:#"%#", nvidiaNameTextField];
}
#end
it works but with my project Info.plist file, and it’s not what I want.
So my question is how can I read Info.plist from NVDAResman.kext?
Thank you in advance
PS: I’m using Xcode 7.1 beta (7B60)
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:#"/System/Library/Extensions/NVDAResman.kext/Contents/Info.plist"];
Please mind that any search using "[NSBundle mainBundle]" will results within your application package (under .app folder structure). Also if you plan to sandbox your application the above code will not work and there is no solution to make it work with Sandboxing (security of OS X platform).
here’s how I did it (thanks to xhruso00)
m.
// Find NVIDIA Kernel Extension Name
-(void)setnvidiaVersionTextField
{
NSDictionary *infoDict = [[NSDictionary alloc] initWithContentsOfFile:#"/System/Library/Extensions/NVDAResman.kext/Contents/Info.plist"];
NSString* nvidiaVersionTextField = [infoDict objectForKey:#"CFBundleName"];
self.nvidiaVersionTextField.stringValue = [NSString stringWithFormat:#"%#", nvidiaVersionTextField];
}
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
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;
}
I am trying to integrate facebook iOS SDK in my app, in my app delegate header I do the following :
#import <UIKit/UIKit.h>
#import "Facebook.h"
#import "FBConnect.h"
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
Facebook *facebook;
}
#property (nonatomic,strong) Facebook *facebook;
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) ViewController *viewController;
#end
and in implementation file's method didFinishLaunchingWithOptions method:
MyFacebooDelegate *controllerDelegate = [[MyFacebooDelegate alloc] init];
facebook = [[Facebook alloc] initWithAppId:appID andDelegate:controllerDelegate];
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
if([userDefault objectForKey:#"FBAccessTokenKey"] && [userDefault objectForKey:#"FBExpirationDateKey"])
{
facebook.accessToken = [userDefault objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [userDefault objectForKey:#"FBExpirationDateKey"];
}
if(![facebook isSessionValid])
{
NSArray *permision = [[NSArray alloc]initWithObjects:#"read_stream",nil] ;
[facebook authorize:permision];
}
where the MyFacebooDelegate class is where I implement the Facebook delegates like the FBSessionDelegate and others.
Also I handled the handleOpenURL and the OpenURL too, when I run the app i get the facebook authenticate screen in safari and then press "Okay" the screen dismissed and back to my app, but some times the app crash and exit and here is where the compiler tells me the error :
- (void)fbDialogLogin:(NSString *)token expirationDate:(NSDate *)expirationDate {
self.accessToken = token;
self.expirationDate = expirationDate;
[_lastAccessTokenUpdate release];
_lastAccessTokenUpdate = [[NSDate date] retain];
[self reloadFrictionlessRecipientCache];
if ([self.sessionDelegate respondsToSelector:#selector(fbDidLogin)]) {
[self.sessionDelegate fbDidLogin];
}
Specifically the compiler indicate this line :
if ([self.sessionDelegate respondsToSelector:#selector(fbDidLogin)]) {
any help will be appreciated
Mohammed
The following line is wrong:
if ([self.sessionDelegate respondsToSelector:#selector(fbDidLogin)]) {
It should look like this:
if ([self.sessionDelegate respondsToSelector:#selector(fbDidLogin:)]) {
When you instantiate your session delegate:
MyFacebooDelegate *controllerDelegate = [[MyFacebooDelegate alloc] init];
facebook = [[Facebook alloc] initWithAppId:appID andDelegate:controllerDelegate];
You do not retain it in any other way. If you look at the Facebook SDK file Facebook.h, you see that the sessionDelegate property is of type assign. Which means you must be responsible for making sure the delegate object exists when it's time to send messages to it.
To fix this, add your AppDelegate.h file:
#property (strong, nonatomic) MyFacebooDelegate *controllerDelegate;
And in didFinishLaunchingWithOptions:, instead of the code at the top of my post, do:
self.controllerDelegate = [[MyFacebooDelegate alloc] init];
facebook = [[Facebook alloc] initWithAppId:appID andDelegate:self.controllerDelegate];
This way, a strong reference will be maintained to your delegate object and it will not be prematurely deallocated.
Hope this helps! Let me know if you have any questions.