I'm writing an application which is based on Apple's Temperature Sensor Application for iOS devices. I'm trying to implement a Disconnect button which will disconnect the currently connected device from the iPhone, however when the disconnect button is pressed there is a BAD_ACCESS error, I know this is memory based but I'm completely at a loss on how to fix it. Code follows.
- (IBAction)clickbutton:(id)sender
{
[[LEConnect sharedInstance] startScan:AccelerometerServiceUUID];
}
- (IBAction)disconnectButton:(id)sender
{
CBPeripheral *peripheral;
if(CBPeripheralStateDisconnected)
{
[[LEConnect sharedInstance] disconnectPeripheral:peripheral];
}
}
The startScan button works correctly but the disconnect button does not. The code in the button is based on the code for finding devices shown below:
if (CBPeripheralStateConnected)
{
[[LEConnect sharedInstance] connectPeripheral:peripheral];
[currentlyConnectedDevice setText:[peripheral name]];
}
earlier in this function the same CBPeripheral *peripheral; pointer is made.
Sorry if this is a dumb question or has been asked before, I'm just really struggling and in desperate need of help! Thanks
The disconnectButton method has two errors. First the peripheral variable is used without being initialized (are you ignoring compiler warnings?). Second, the if statement checks if the peripheral is disconnected and then disconnects it again (you should be checking that the peripheral is connected).
Related
For people wanting to reply quickly without reading the post: I am not hitting any memory limits. Read the whole post for details.
My WatchKit extension cannot properly function without the user first being "onboarded" through the phone app. Onboarding is where the user must accept the permissions that we require, so it's very crucial.
On my WatchKit extension, I wanted to display a simple warning for users who had not finished onboarding within our phone app yet.
As such, I thought I'd get the status of onboarding from the phone in two ways:
When the user opens the app/the app is activated (I use the willActivate method to detect this)
When the app finishes onboarding it sends a message to the watch of its completion (if the extension is reachable, of course)
Both of these combined would ensure that the status of onboarding is always kept in sync with the watch.
I wrote the first possibility in, utilizing reply handlers to exchange the information. It worked just fine, without any troubles. The warning telling the user to complete disappears, the extension does not crash, and all is well.
I then wrote in the second possibility, of the extension being reachable when the user finishes onboarding (with the phone then directly sending the companion the new status of onboarding). My extension crashes when it receives this message, and I am stuck with this odd error.
Program ended with exit code: 0
My extension does not even get a chance to handle the new onboarding status, the extension just quits and the above error is given to me.
I am not hitting any sort of memory limit. I have read the technical Q&A which describes what a memory usage limit error looks like, and I don't receive any sort of output like that whatsoever. As well, before the extension should receive the message, this is what my memory consumption looks like.
I have monitored the memory consumption of the extension right after finishing onboarding, and I see not a single spike indicating that I've gone over any kind of threshold.
I have tried going line by line over the code which manages the onboarding error, and I cannot find a single reason that it would crash with this error. Especially since the reply handler method of fetching the onboarding status works so reliably.
Here is the code of how I'm sending the message to the watch.
- (void)sendOnboardingStatusToWatch {
if(self.connected){
[self.session sendMessage:#{
LMAppleWatchCommunicationKey: LMAppleWatchCommunicationKeyOnboardingComplete,
LMAppleWatchCommunicationKeyOnboardingComplete: #(LMMusicPlayer.onboardingComplete)
}
replyHandler:nil
errorHandler:^(NSError * _Nonnull error) {
NSLog(#"Error sending onboarding status: %#", error);
}];
}
}
(All LMAppleWatchCommunicationKeys are simply #define'd keys with exactly their key as the string value. ie. #define LMAppleWatchCommunicationKey #"LMAppleWatchCommunicationKey")
Even though it's never called by the extension, here is the exact receiving code of the extension which handles the incoming data, if it helps.
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message {
NSString *key = [message objectForKey:LMAppleWatchCommunicationKey];
if([key isEqualToString:LMAppleWatchCommunicationKeyOnboardingComplete]){
BOOL newOnboardingStatus = [message objectForKey:LMAppleWatchCommunicationKeyOnboardingComplete];
[[NSUserDefaults standardUserDefaults] setBool:newOnboardingStatus
forKey:LMAppleWatchCommunicationKeyOnboardingComplete];
dispatch_async(dispatch_get_main_queue(), ^{
for(id<LMWCompanionBridgeDelegate> delegate in self.delegates){
if([delegate respondsToSelector:#selector(onboardingCompleteStatusChanged:)]){
[delegate onboardingCompleteStatusChanged:newOnboardingStatus];
}
}
});
}
}
Before including this onboarding-related code, my WatchKit extension was tested by over 100 people, without any troubles. I am using the exact same custom error dialogue that I was using before, just with a different string. I cannot for the life of me figure out what is causing this crash, and the ambiguity of it has given me very little to work with.
Any help would be greatly appreciated. Thank you very much for taking your time to read my post.
Edit: I just tried creating a symbolic breakpoint for exit(), which is never hit. If I call exit() myself, it calls the breakpoint, so I know the breakpoint itself is working.
I have an iOS app which advertises itself successfully using a CBPeripheralManager. In a Chrome App, I have successfully discovered the device (hence obtaining its address).
I have added a service with a custom UUID to the peripheral manager. didAddService is called (after the peripheral manager is powered on), and so I assume that part is successful.
I call chrome.bluetoothLowEnergy.connect(device address) in the Chrome app. The callback function is called without issue, and so I believe the connection is successful. There is no function called within the app when the connection has occurred - should there be?
I then call chrome.bluetooth.getServices(device address) in the Chrome app. An array of services is returned, however the array is empty. I believe it should return an array of length 1, not 0.
Am I overlooking something? Why are no services being returned? Should any of the peripheralManager functions be called during these operations?
Connect code:
function connect(address) {
/* Connect persistently to the device. */
chrome.bluetoothLowEnergy.connect(address, {persistent: true}, function() {
if(chrome.runtime.lastError) {
/* If we click a green device, we get this. We should try and handle it
earlier. e.g. by removing the listener, or instead using the click
to disconnect the device. */
if(chrome.runtime.lastError.message == 'Already connected')
setDeviceConnected(address);
/* Print error and exit. */
console.log(chrome.runtime.lastError.message);
return;
}
console.log('Successfully connected to ' + address);
setDeviceConnected(address);
// getDeviceService();
getDeviceServices(address);
});
return true;
}
Get services code:
function getDeviceServices(address) {
chrome.bluetoothLowEnergy.getServices(address, function(services) {
console.log('services.length: ' + services.length);
});
}
Peripheral setup code:
- (void)setupPeripheral {
_serviceName = SERVICE_NAME;
_serviceUUID = [CBUUID UUIDWithString:SERVICE_UUID_STRING];
_characteristicUUID = [CBUUID UUIDWithString:CHARACTERISTIC_UUID_STRING];
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
_characteristic = [[CBMutableCharacteristic alloc] initWithType:_characteristicUUID
properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable];
_service = [[CBMutableService alloc] initWithType:_serviceUUID primary:YES];
_service.characteristics = #[_characteristic];
NSLog(#"Peripheral set up");
}
Start advertising (add the service just before we start advertising):
/* Start advertising */
- (void)startAdvertising {
NSLog(#"Starting advertising...");
NSDictionary *advertisment = #{
CBAdvertisementDataServiceUUIDsKey : #[self.serviceUUID],
CBAdvertisementDataLocalNameKey: self.serviceName
};
[self addServices];
[self.peripheralManager startAdvertising:advertisment];
}
Call to start advertising within didUpdateState:
/* Did update state */
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
switch (peripheral.state) {
case CBPeripheralManagerStatePoweredOn:
NSLog(#"peripheralStateChange: Powered On");
// When the bluetooth has turned on, start advertising.
[self startAdvertising];
break;
Update
On connecting to the peripheral app with OSX app LightBlue, an alert shows up on the app to request a pairing. Then LightBlue can read characteristics from the app. When connecting with the Chrome app, the pairing alert does not show up.
Having chatted to web-bluetooth developers at W3C, I have now been able to discover services from the device, by updating chrome from v51 to v52 (apparantly also v53 works).
However currently (though I am yet to try it out), reading characteristics is currently unsupported for OSX, as seen here.
(I have not yet looked into it but this may provide some options on how to overcome the issues described here.)
I've ran into this problem myself. I've developed a simple app that scans for devices and their services, but all devices found always report back an empty array of services. I have this problem on both OsX and ChromeOS.
There's an exception to this: when the device has previously been paired, I was able to get the services array on ChromeOS. This is confusing to me, since nothing I've read anywhere says a device has to be manually paired first, and there's no programatic way to pair a device one discovered.
EDIT: Upgrading to Chrome 53 fixed the problem. The site linked in louisdeb's answer is related to web-bluetooth, not chrome.bluetooth, so characteristics may actually work.
Following is my code which i have written.I found many similar question but could not get proper answer helpful to me.
I am using swift and ios 8 for working and using core bluetooth Did discover peripheral method should be called by itself but is not getting called. what should I do to resolve?
code
Your problem is that you are invoking scanForPeripheralsWithServices immediately after creating the CBCentralManager - You need to wait until the central manager is in the powered on state.
ie -
func centralManagerDidUpdateState(central: CBCentralManager) {
...
else if central.state == CBCentralManagerState.PoweredOn {
central.scanForPeripheralsWithServices(nil,options:nil)
}
...
}
Also, in future you should post your code into your question and use the {} icon to mark it as code rather than inserting images, as images makes it impossible to copy your code into an answer
UPDATE
Your other problem is that you are creating your CBcentralManager as a local variable inside your btnScanClicked so as soon as this method ends your CBCentralManager will be released. You need to create it as a property on your class and then refer to self.central
I am using iOS 7 multi peer technology for connecting my iPad and iPod touch. But whenever iPod touch or iPad goes to sleep it gets disconnected which is fine because multi peer dont work in background mode, but when i discover again it shows iPods name twice in the MCBrowserViewController list. Tried this with every sample code and every code has same issue any one know how to fix this bug.
Also there is one weird issue with MCBrowserViewController if i connect a device and other device accepts it, even though it gets connected MCBrowserViewController will still show as connecting and "Done" button is disabled. I am using MCBrowserViewController and no custom code for this so i guess this is issue from apple.
Also any one knows how to directly connect to the device when app comes back to active state from sleep mode?
Discovering your same name twice is because you do "init" the peerID (withDisplayName) each time you init your session.
From apple's documentation, it's a known bug and you should not do so. Rather, save your peerID somewhere (such as NSUserDefaults), and when you init your session, verify if peerID exists, load it, else create/save it.
The simplest code will look like this:
In the init of your session, replace:
_peerID = [[MCPeerID alloc] initWithDisplayName:XXX];
by:
//If there is no PeerID save, create one and save it
if ([[NSUserDefaults standardUserDefaults] dataForKey:#"PeerID"] == nil)
{
_peerID = [[MCPeerID alloc] initWithDisplayName:XXX];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_peerID] forKey:#"PeerID"];
}
//Else, load it
else
{
_peerID = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] dataForKey:#"PeerID"]];
}
Of course, you can make a more sophisticated code, such deallocating it and create it from a dynamic variable in case you wanna change name, etc.
I had the same issue and this is how I solved it,,
In my case I used a UIViewController to handle the connections and every time I open the view I alloc and init the view -viewDidLoad will be called each time- , then in viewDidLoad I initial the MCPeerID & MCSession and thats the problem and this is why we see multi peer connectivity showing twice, so I solved it by doing the initialisation of MCPeerID & MCSession only once in the AppDelegate or a global Class.
I am trying to make my iPhone app more robust, by ensuring that it doesn't crash when there is no network connection. Right now the app attempts to make a connection on startup immediately, through the app delegate. It has no issues if wifi or cellular is available, but it will crash if there is no network connection visible. I have looked around on this site and haven't found anything that quite fits my problem. I have the feeling it should be just a simple line of code, like an objective-c equivalent of a pseudo- 'isConnection', or something similar:
if (isConnection) {
- sendSynchronousRequest for json data I'm using
- manipulate the data, etc., and continue with normal operations
} else {
- send an output message to a view controller,
letting the user know what's wrong.
}
I can't seem to isolate the (admittedly abstract) "isConnection" condition that I'm looking for, specifically. Does anyone have experience or advice with this topic?
The reachability class is very easy to use. Download the class files here https://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html
You also need to add the SystemConfiguration.framework
Here's the code you need:
-(BOOL)isConnection {
Reachability *reach = [Reachability reachabilityWithHostName:#"www.google.com"];
//replace www.google.com with your own host you're checking for
NetworkStatus hostStatus = [reach currentReachabilityStatus];
if (hostStatus != NotReachable) {
//There are also other status enums like
//ReachableViaWiFi
//ReachableViaWWAN (3G/LTE)
//if you need to detect if user is on cellular, act accordingly
return YES;
}
return NO;
}
Then you can call your method:
if ([self isConnection]) {
//do something
} else {
//no connection, inform user
}
You can use the Reachability class that Apple provides in the Reachability sample application. Not only does it tell you if you're connected, but how you're connected (WiFi, cellular or no connection at all). You can even register for a notification when the connection status changes.
Even though you can use that to check the connection status before initiating the code you describe above, I still think you should investigate why the app crashes when you have no connection, and attempt to resolve it. You could always lose connectivity in the middle of a request attempt, and you don't want that to crash the app either.