CTCallCenter crash - ios

I am developing an iPhone application that has to synchronize with phone call statuses. I am currently playing audio and/or communicating with an external accessory so my application can run in background. So when there is a phone call i show a view telling the user that a phone call is taking place. I am using the CTCallCenter (using an instance variable in my appDelegate) to respond to phone call statuses and i am allocating that instance every time i test if there is a phone call or not. Bur when allocating that instance each time , sometimes i get a strange crash concerning "CUTWeakReference" class. i want to know if there is a best practice for using CTCallCenter when application is in background or is suspended to prevent that crash from happening ? Thanks

Points to consider to fix the issue.
There is an iOS bug that causes instances of the CTCallCenter class to sometimes get notifications after they have been deallocated. Instead of instantiating, using, and releasing instances you must instead retain and never release them to work around the bug.
The [[CTCallCenter alloc] init] must be run in the main queue. Is it thread safe ??? Better call it on main thread only.
static CTCallCenter *netInfo; static dispatch_once_t dispatchToken;
if (!netInfo) {
dispatch_once(&dispatchToken, ^{
netInfo = [[CTCallCenter alloc] init];
});
}

Related

iOS CoreBluetooth dispatch queue for app background processing

First of all the question what is the best way of using core bluetooth in the central role to send data to a bluetooth LE devices. The data need to be processed and that takes enough time to cause problems on the UI thread if it runs on it. The user will initiate the process with the phone app open and then either keep using the app or close the app and expect the data to continue sending to the device.
I have found 2 really bad ways of doing it which seem to work
Put the bluetooth CBCentralManager objet on the main queue and risk blocking the UI
Ignore the indications from the iOS bluetooth stack that its not ready to transmit and risk loosing data.
This seems to have its roots in both iOS threading/dispatch queues as well as iOS Bluetooth internals.
The bluetooth LE iOS application connects to a bluetooth LE device as central role. The CBCentralManager is initialized according to apples documentation. The queue defined as:
The dispatch queue to use to dispatch the central role events. If the
value is nil, the central manager dispatches central role events using
the main queue.
As suggested by vladiulianbogdan answer to Swift: Choose queue for Bluetooth Central manager we should be creating a serial queue for the CBCentralManager. This seems to make sense and for a while I was following this advice. Also allprog comment to Swift CoreBluetooth: Should CentralManager run in a separate thread
suggests that the main queue will be suspended but other queues will not, which is the opposite of what I am seeing.
While using a serial queue for bluetooth, and using a different one than the main thread would be preferable. There is a problem: The callback:
-(void)peripheralIsReadyToSendWriteWithoutResponse:(CBPeripheral *)peripheral
{
[self sendNextBluetoothLePacket];
}
stop getting called. There is another way to check if the peripheral is ready to send more data, CBPeripheral has a member variable canSendWriteWithoutResponse which returns true if its ok to send. This variable also begins retuning false and never goes back to true.
I found this comment from Sandeep Bhandari which says that all of the queue threads are stopped when the app goes in the background unless they are one of the background modes provided by apple. Biniou found that he was able to solve his core bluetooth background issue by initializing in a view controller instead of the app delegate. This does not make sense to me.
My app does have the core-bluetooth background mode selected in its info.plist so it should quality as one of those background modes. What I find is that when my app goes in the background the app does continue process data. I see log messages from polling loops that run every 100 milliseconds.
If I trigger bluetooth LE writes from those polling loops I am able to keep sending data. The problem is I am unable to determine a safe rate to send the data and either its very slow or data is sometimes lost.
Im not sure how to best deal with this. Any suggestions would be appreciated. It seems like no matter what I do when I go in to the background I loose my ability to determine if its safe to send data.
I see this comment that indicates that the only solution would be to change how the bluetooth device connects to the phone, is this the case? Im not sure changing the hardware is an option for me at this point.
The ideal solution would be to find a way to put CBCentralManager on its own serial queue but create that queue in such a way that the queue was not stopped when the app goes in to the background. If someone knows how to do that I believe it would solve my problem.
The way my current code is goes like this. When the bluetooth service is created in the applicationDidFinishLaunchingWithOptions callback to my AppDelegate
self.cm = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:#{CBCentralManagerOptionShowPowerAlertKey:#(0)}];
Or with the serial queue which should work but is not
dispatch_queue_t bt_queue = dispatch_queue_create("BT_queue", 0);
self.cm = [[CBCentralManager alloc] initWithDelegate:self
queue:bt_queue
options:#{CBCentralManagerOptionShowPowerAlertKey:#(0)}];
When its time to send some data I use one of these
[self.cbPeripheral writeValue:data
forCharacteristic:self.rxCharacteristic
type:CBCharacteristicWriteWithoutResponse];
If I just keep calling this writeValue with no delay in between without trying to check if its safe to send data. It will eventually fail.
Extra background execution time is requested once the connection is established with this code
- (void) centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral
{
UIApplication *app = [UIApplication sharedApplication];
if (self.globalBackgroundTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:self.globalBackgroundTask];
self.globalBackgroundTask = UIBackgroundTaskInvalid;
}
self.globalBackgroundTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:_globalBackgroundTask];
_globalBackgroundTask = UIBackgroundTaskInvalid;
}];
Any thoughts on this or suggestions as to how I could give CBCentralManager a queue that was not on the UI thread and would not get shut down when the app goes in to the background would be greatly appreciated. If thats not possible I need to try and pick one of the workarounds.

How to keep CoreBluetooth connection alive between views

I have implemented CoreBluetooth for an app based on Apples CoreBluetooth TemperatureSensor example (https://developer.apple.com/library/ios/samplecode/TemperatureSensor/Introduction/Intro.html). It works great searching for devices, populating the results in a tableview, selecting one and connecting. My issue is that I want to keep the connection alive between views across the app.
In my current setup I have a view with a button that takes you to the Bluetooth setup view. The Bluetooth view is presented modally and here I search for BT devices and connects to one of them. As soon as I dismiss the view, the connection is lost, probably due to it not being retained?
Therefore I use a singleton implementation to keep the object (also as in Apples example) hoping to keep the connection alive, but with no luck. I can however retrieve the object from the singleton and call connectPeripheral and re-connect, but from a user perspective, it is not that great that the user has to input the password again after just connecting earlier.
So, how can I keep the bluetooth connection alive between views, e.g. when having a settings view where the BT device is connected, and then use the same connection in the remaining app?
Update: included code
Also worth mentioning is that my class (incl. shared instance) is not only based on the Apple example but also the SerialGATT implementation from HMSoft (which I guess is based on Apples implementation). Here is a link to one place I found on git https://github.com/ezefranca/kit-iot-wearable-ios/blob/master/kit-iot-wearable-ios/SerialGATT.h
So for my singleton I have added the following to SerialGATT.h (also tried with id, not that it made a difference)
+ (SerialGATT *)sharedManager;
and in SerialGATT.m I have
+ (SerialGATT*)sharedManager {
static SerialGATT *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
When using SerialGATT and the singleton in my Bluetooth viewcontroller, I implement the delegate methods of SerialGATT, and then I have the following in viewDidLoad
[[SerialGATT sharedManager] setup];
[[SerialGATT sharedManager] setDiscoveryDelegate:self];
NB! I have renamed discoveryDelegate from the original SerialGATT example, before it was simply named "delegate". discoveryDelegate is the name used in Apples example, not that it matters.
From here on everything works fine in my modal Bluetooth settings view. As stated above, I can search and connect devices. The delegates are being called, all is fine and dandy. However, when I close the modal, the device is disconnected, but I can still find the object in other views when accessing the singleton, and e.g. reconnect. But then again, I would rather keep the connection alive instead of reconnecting and having to input the password again.

IOS Vars on AppDelegate are released when app enter in background?

This is a question about iOS memory management when app enters the background.
If I have a var in the AppDelegate with a value:
#interface MyAppDelegate : NSObject {
NSString *userName;
}
// In some place
((MyAppDelegate*)[UIApplication sharedApplication] delegate]).userName = #"StackOverflow"
My question is: when the app enters the background, because the user receives a call or opens another app, is it possible that iOS releases the memory for any reason (so destroying my AppDelegate instance)? So I lose the value of userName?
Programming in Android we have to use persistence mechanisms like Parcelable, using methods of lifecycle like onSaveInstance, etc. But in iOS we don't have to manage values of var for the "iOS garbage collection"?
Maybe the answer can be trivial talking about a String var, but what happens when I stored in the AppDelegate a big Array of UIImages? It will never be release when app enter on background?
Sorry for my bad english and I can't find information about lifecycle iOS, appDelegate and memory management.
You never really know. iOS might need to remove the app from memory to use it for something else. If the user closes the app for just a minute then you might be ok, but the app could be closed for days, and the user may do any number of other things...
So, you should really either:
Save data each time it changes, or
Save data when the app is told that it is going to the background
Option 1. is more safe, because it also saves data against crashes, but it isn't always practical. Generally, save as soon as you can and, if testing shows you need to, later work on making things run better (like by processing and saving to disk on a background thread).
When an app is launched, the UIApplicationMain function is called, this function creates a singleton UIApplication object.
When you declare an iVar like this : NSString *userName; you are creating a strong reference to it ( by default it's strong).
To answer your question : the userName will be released when the UIApplication will be released and because the UIApplication is a singleton, it will be never release until the application is not killed. The application will be killed in many situations :
The user kill the app
The system kill the app (memory,...)
Documentation :
Memory management
iOS App life cycle

Continue a NSURLConnection after the App has been terminated

I would like to know how it is possible to continue a async NSURLConnection, which has been started in the foreground, when the app will be terminated.
Currently I am starting a NSURLConnection when the app goes in the background. This works fine as long as the user is slower than the connection, when he wants to terminate the app. But when the user is quicker than it, the connection can't be established.
Here is my code:
// AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application
{
AnObject *newObject = [[AnObject alloc] init];
[newObject InactiveApp];
}
// AnObject.m
- (void)InactiveApp
{
self.backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
// setting up the NSURLRequest
// [...]
dispatch_async(dispatch_get_main_queue(), ^
{
[NSURLConnection connectionWithRequest:request delegate:self];
});
}
// delegate functions, endBackgroundTask-closing, etc. is following
Unfortunately this is not working and I would like to know whether someone knows another way to fix it. There has to be a way which is similar like Snapchat or WhatsApp is doing it, because when you write an message and terminate the app right after pressing send, the message will be delivered.
The only way I could imagine is to do it with a background fetch but I think that is not the best solution, due to the fact that I just want to make one single connection when the App is send to the background.
I agree with Andy, that you should pursue NSURLSession and a background NSURLSessionConfiguration. See downloading content in the background section of the App Programming Guide for iOS: Background Execution.
By the way, the idea in your question will work fine (especially if you need support for iOS versions prior to 7.0, where NSURLSession and its background sessions are not available). Two observations regarding your code snippet:
The way you've written it, would appear that your AnObject would be prematurely deallocated when it falls out of scope and your app would therefore fail when it tried to call the delegate methods. Make sure to maintain a strong reference to AnObject.
Don't forget to call endBackgroundTask when the download is done. Likewise (and more subtly), the timeout handler should end the background task, too. See the Executing Finite Length Task section of the aforementioned App Programming Guide.
By the way, you mention requests continuing after the app is terminated. If a user manually terminates an app, that kills both background tasks contemplated in your question as well as background NSURLSession tasks. These are intended to gracefully handle continuing tasks if the app leaves foreground, not if the user manually terminates the app. The NSURLSession approach gracefully handles terminations due to memory pressure, but not manual termination.

Long-term background task execution on iOS 6

I'm creating an online-shop-style app where users can browse different products on their iPad and order these products. The ordering process consists of creating an xml-file with the user's data and the relevant products he would like to order. But sometimes there might be the case, that users don't have an internet connection right now and I would like to create some mechanism, which checks every x minutes for an active internet connection and then tries to deliver the order-xml. It should repeat this step until it gets connected to the web and then just stop it, when all offline carts have been sent.
I have already been searching the web but only found ways to do this on iOS 7 (with UIBackgroundModes - fetch). But I don't want to use iOS 7 because the app is already done and I'm not planning to redesign it for iOS 7 (it's an Enterprise App). As far as I know, the current Background Execution time on iOS 6 is limited to something like 15 minutes, is that correct?
Any ideas on how to solve that?
Thanks.
EDIT:
I have tried the following in - (void)applicationDidEnterBackground:(UIApplication *)application
self.queue = [[NSOperationQueue alloc] init];
[self.queue addOperationWithBlock:^{
[[InstanceHolder getInstance] startNetworkTimer];
}];
and here is what should happen next:
- (void) startNetworkTimer{
if ([CommonCode getAllOfflineCartsForClient:nil].count > 0){
NSTimer *pauseTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(offlineCartLoop:) userInfo:nil repeats:YES];
}
}
- (void) offlineCartLoop:(id)sender{
if([CommonCode isInternetConnectionAvailable]){
[self sendOfflineCarts];
[sender invalidate];
}
}
startNetworkTimer gets called as it should, but then it doesn't call the offlineCartLoop function :-(
EDIT 2:
I think the timer-thing was the problem. I'm now calling the offlineCartLoop function like this:
self.queue = [[NSOperationQueue alloc] init];
[self.queue addOperationWithBlock:^{
[[InstanceHolder getInstance] offlineCartLoop:nil];
}];
and changed the offlineCartLoop function to this:
- (void) offlineCartLoop:(id)sender{
if([CommonCode isInternetConnectionAvailable]){
[self sendOfflineCarts];
}else{
[NSThread sleepForTimeInterval:10.0];
[self offlineCartLoop:nil];
}
}
Seems to work, but will this run forever? Is there anything else I need to take care of?
There is no solution to what you want - there is no such thing as being able to periodically check every N minutes in the background unless it is within the time window granted by beginBackgroundTaskWithExpirationHandler.
However that only permits 10 minutes of execution time for iOS6 and earlier, or approximately 3 minutes for iOS7.
You cannot cheat and try and use a background mode if your app does not need it, and even the background modes do not permit you to freely run whenever you want.
Even the new background modes in iOS 7 do not permit you to run on a scheduled basis.
Your best best actually is iOS7 even though you don't want to migrate to iOS7 - the background fetch being the relevant mode (even though you are pushing not fetching). With that background mode you will be able to have the opportunity to execute but not when you decide, only when the OS decides - and the frequency of that depends upon how the user uses your app.
With iOS6 your options are even less restricted.
See iOS: Keep an app running like a service
Basically there just is no such thing as continuous background execution, nor periodic background execution, nor the app deciding when it wants to run when in the background.
If the user does not have an internet connection at the time they use your app to place the order then you should be notifying them of that anyway (if you don't then your app risks rejection from the app store) and maybe tell them to try again later.
If they are in flight mode the user will know they are in flight mode, if there is a temporary interruption (such as the phone is in an elevator or tunnel) then your app could keep on trying for as long as it is able - keep trying every minute while in the foreground, then when you switch to the background you know you have 10 minutes left, keep trying until the 10 minutes has nearly expired then post a local notification to the user notifying them that the app was unable to place the order due to lack of connectivity. If the user clicks on the notification and your app launches then the app will have the chance to retry again at that point.
If you still cannot make a connection then so be it, but you will have the chance to start the retry algorithm again. But at least you have notified the user their order has not gone through.
If what you need to know is if and when a data connection is available, I recommend inverting the process: rather then querying for a data connection, let your app be notified when a data connection is available. It's more efficient.
On this subject, I suggest using Reachability: you can make a call to know if a specific URL is accessible, and execute a block of code as soon as a connection is available.
Reachability *reach = [Reachability reacabilityWithHostName:#"www.myservice.com"];
...
reach.reachableBlock = ^(Reachability *reach) {
// Process the requests queue
// You should implement the method below
[self processQueue];
}
...
if ([reach isReachable]) {
// Upload the XML file to the server
// You should implement the method below
[self uploadToServer:myRequest];
} else {
// Enqueue your request somewhere, for example into an NSArray
// You should implement the method below
[self addToQueue:myRequest];
}
The above code is meant to be a showcase (it doesn't work as is), use it as reference. I can just say that the reach variable should be a class property or data member, and that it should be initialized once.
Also, if you enqueue your requests into an NSArray, be sure to do it in thread safe mode
Alternatively, Reachability can also notify via NSNotification when a connection is available - a different way to achieve the same result. Up to you to decide which one better fits with your needs.

Resources