Getting CMAccelerometer data in background - ios

Currently I'm able to get updates from the accelerometer using CMAccelerometer
_motionManager.accelerometerUpdateInterval = kUpdateInterval;
[_motionManager startAccelerometerUpdatesToQueue:self.queue withHandler:
^(CMAccelerometerData *accelerometerData, NSError *error) {
[(id) self setAcceleration:accelerometerData.acceleration];
[self performSelectorOnMainThread:#selector(update) withObject:nil waitUntilDone:NO];
}];
- (void)update {
... code to handle data ...
}
And this is working fine, however when I close my app (i.e. put it into the background not kill the instance), I no longer receive updates from my _motionManager. How do I handle this?

You can register your app to listen to location, and use CoreMotion. Then you can get accelerometer data in background.
Basically your app needs to be allowed to run in the background for other reasons.
Example:

Related

iOS app stuck after push notification is received in UIApplicationStateInactive mode

I have this really simple flow, where a notification is received when the app is in killed mode and the user taps on that notification.
Logic will append two controllers to the array of current viewControllers in the rootNavigationController. So, if first view was the usual screen for my app, in this case it will be first>second>third.
If you open the app with the app-icon, you'll only see the first view.
Issue is, when you tap on the notification, third view has an async API call, which is invoked from viewDidLoad. The SVProgressHUD is used for ux purpose, which is hidden in the completion block of the API. Sometimes, the progressHUD keeps on rotating and nothing happens. It gets stuck, but If I set the breakpoint in the API call method itself, everything works fine every time.
Not able to figure out, how to debug this issue. Tried pausing the execution when it gets stuck, but no relevant thread seems to be in the list.
CODE in third VC:
- (void) fetchDataPoints
{
//[SVProgressHUD show];
[[ServerHandler sharedInstance]fetchDataFromServerWithApiUrlString:GET_ALL_DATAPOINTS methodType:#"GET" httpBodyData:nil contentType:nil otherHeaderFields:nil queryStringParams:nil withCompletionBlock:^(BOOL success, NSData *responseData, NSError *error, NSHTTPURLResponse *response,NSDictionary *responseDict)
{
[SVProgressHUD dismiss];
if (success)
{
}
}];
}
CODE: push notification
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateInactive)
{
ChildViewController *childList = ViewControllerWithSBID (#"DATASB",#"ChildScreenID");
DetailViewController *detailVC = ViewControllerWithSBID(#"DATASB", #"DetailScreenID");
NSMutableArray *viewControllers = [self.rootNavigationController.viewControllers mutableCopy];
[viewControllers addObjectsFromArray:#[childList,detailVC]];
self.rootNavigationController.viewControllers=[[NSArray alloc]initWithArray:viewControllers];
}

Device motion updates in background

I am trying to get device motion updates on an iPhone or iPad in the background using CMMotionManager. I have reviewed all previous posts on this topic and thought that I had code that would work. My app also uses background audio, and this works properly in the foreground and background. In Info.plist, I have background audio and background location updates enabled.
For testing purposes, I have declared "var motionManager = CMMotionManager()" in AppDelegate, and included the following code in didFinishLaunchingWithOptions:
motionManager.deviceMotionUpdateInterval = 0.10
let queue = NSOperationQueue()
motionManager.startDeviceMotionUpdatesToQueue(queue, withHandler: {
data, error in
let accelerationVector = sqrt(pow(data!.userAcceleration.x, 2) + pow(data!.userAcceleration.y, 2) + pow(data!.userAcceleration.z, 2))
print("\(accelerationVector)")
})
When I run the app on my device, the code executes in the foreground as expected, but when I press the home button, I get about 10 more readings before it stops. When I tap on the app icon, the readings start again. I have also placed breakpoints on the code in the handler, and get similar results.
What am I missing?
Do NOT trust the NSLog or other similar log output
I used to meet this problem. I want collect the motion data in background and I made a demo about this. I found I can get all the data and log when my app in active, but the xCode log console output nothing when the application in background.
I used to think the problem is the CoreMotion data can only be collected in foreground, but I am wrong. All the callbacks still working when application enter background, Just the NSLog stop telling me the data.
If you don't believe, just collect all the data into a NSMutableArray or other collections, then check the data collected when app in background.
e.g.
#property (nonatomic, strong) NSMutableArray *arrAltimeters;
...
[self.altimeter startRelativeAltitudeUpdatesToQueue:self.operationQueue withHandler:^(CMAltitudeData * _Nullable altitudeData, NSError * _Nullable error) {
[self.arrAltimeters addObject:altitudeData];
NSLog(#"Altimate data count = %ld", self.arrAltimeters.count);
}];
I have declared the motion manager in ViewDidlLoad
CMMotionManager *motionManager;
/*--Initialising Motion Manager--*/
motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 1.0;
As whenever the app goes in background the motionManager stop providing callbacks in this case , You need to restart the motionmanager when the app goes in background or in foreground.To do this we need to follow the process below:
1) we first need to Register for the app transition notification :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
2)Notification callback functions, in which you will restart the motion manager
-(void)appDidEnterBackground{
[self restartMotionUpdates];
}
-(void)appDidBecomeActive{
[self restartMotionUpdates];
}
3)This function which will restart the motion manager
-(void)restartMotionUpdates{
[motionManager stopDeviceMotionUpdates];
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
NSLog(#"x=%f y=%f z=%f",fabs(motion.userAcceleration.x),fabs(motion.userAcceleration.y),fabs(motion.userAcceleration.z));
}];
}
After doing more research, I figured out the problem. I was using mpmusicplayercontroller to play background audio. This will play the audio in the background, but this does not keep my app running in the background. When I switched to using avplayer to play audio, the app runs in the background including the device motion updates.

Delay in receiving device motion updates from CMMotionManager

I have an app with a view that, when loaded, gets regular device motion updates (20 per second) and uses them to display things on the screen. This was working fine using the old (deprecated) UIAccelerometer code.
I have since ported it to use Core Motion and the CMMotionManager but am having the problem that every time (except the first time) one of these views is loaded, only one or two updates are received in the first second or so, and then they start being received as expected (20 per second).
Relevant code in the view:
- (void)viewDidLoad {
[super viewDidLoad];
// Register to receive acceleration from motion manager.
motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 0.05;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error) {
// Handle motion and update UI here.
}
];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// Stop the motion updates.
[motionManager stopDeviceMotionUpdates];
}
- (IBAction)exitView:(id)sender {
// Stop the motion updates.
[motionManager stopDeviceMotionUpdates];
// Pop the view from the navigation controller.
}
I've added some NSLog statements and can confirm that motion updates are being requested as soon as the view loads, and that the handler is only receiving updates as I described above.
Is there anything I'm missing that I can do to get the CoreMotion version to behave the same as the old version, i.e. without the delay?

When retrieving data from the server in dispatch queue, how to handle application going to background?

I am creating an application where I am retrieving data from the server like below:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self retrievedatafromserver];
dispatch_async(dispatch_get_main_queue(), ^{
//UIUpdation, fetch the image/data from DB and update into your UI
});
});
How do I retrieve data from the server even if application goes to background?
Thanks & Regards
sumana
If Your scope of project is in only iOS 7 then you can use A new background mode which comes in the iOS 7 and onwards. You can fetch the data in background mode without any extra efforts of coding.
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
Now that your app already knows to initiate background fetch, let’s tell it what to do. The method -(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler will assist in doing so. This method is called every time that a background fetch is performed, and should be included in the AppDelegate.m file. The complete version is provided below:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
UINavigationController *navigationController = (UINavigationController*)self.window.rootViewController;
id topViewController = navigationController.topViewController;
if ([topViewController isKindOfClass:[ViewController class]]) {
[(ViewController*)topViewController insertNewObjectForFetchWithCompletionHandler:completionHandler];
} else {
NSLog(#"Not the right class %#.", [topViewController class]);
completionHandler(UIBackgroundFetchResultFailed);
}
}
Now in your controller. Do like that
- (void)insertNewObjectForFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"Update the tableview.");
self.numberOfnewPosts = [self getRandomNumberBetween:0 to:4];
NSLog(#"%d new fetched objects",self.numberOfnewPosts);
for(int i = 0; i < self.numberOfnewPosts; i++){
int addPost = [self getRandomNumberBetween:0 to:(int)([self.possibleTableData count]-1)];
[self insertObject:[self.possibleTableData objectAtIndex:addPost]];
}
/*
At the end of the fetch, invoke the completion handler.
*/
completionHandler(UIBackgroundFetchResultNewData);
}
Note :- If you have to give supportability on iOS 6 and below then avoid this approach. Because it's not available.
When your app enters background mode. you can access code for couple of seconds. Suppose the background queue is still performing and you entered background. then you might need to recall the method when app entered foreground. (take a bool variable and check whether the process is completed or not, if process is completed no issues. if not call the method again.).
If you want to make app run in background mode also then you need to request for background run mode in plist. See this link for reference only for these features we can active background run mode and you can active any of them according to you usage http://blogs.innovationm.com/support-for-applications-running-in-background-ios/

Dealing with two screens and one activity indicator in iOS

I have 3 screens on my app.First is login. Second is search and third is process the task.
On login i retrieve data from a web service. It returns data in XML format. So the data is considerably large. So i am doing that task on a background thread like this to stop Mainthread freezing up on me:
-(BOOL)loginEmp
{
.....some computation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self getAllCustomerValues];
});
}
-(void)getAllCustomerValues
{
....more computation.Bring the data,parse it and save it to CoreData DB.
//notification - EDIT
NSNotification *notification =[NSNotification notificationWithName:#"reloadRequest"
object:self];
[[NSNotificationCenter defaultCenter] postNotification : notification];
}
//EDIT
//SearchScreenVC.m
- (void)viewDidLoad
{
....some computation
[self.customerActIndicator startAnimating];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(stopActivityIndicator)
name:#"reloadRequest"
object:nil];
}
- (void)stopActivityIndicator
{
[self.customerActIndicator stopAnimating];
self.customerActIndicator.hidesWhenStopped = YES;
self.customerActIndicator.hidden =YES;
NSLog(#"HIt this at 127");
}
So on condition that login was successful, i move to screen 2. But the background thread is still in process( i know because i have logs logging values) . I want an activity indicator showing up here (2nd screen)telling user to wait before he starts searching. So how do i do it?How can i make my activity indicator listen/wait for background thread. Please let me know if you need more info.Thanks
EDIT: so I edited accordingly but the notification never gets called. I put a notification at the end of getAllCustomerValues and in viewDidLoad of SearchScreen i used it. That notification on 2nd screen to stop animating never gets called. What is the mistake i am doing.?Thanks
EDIT 2: So it finally hits the method. I dont know what made it to hit that method. I put a break point. I wrote to stop animating but it wouldn't. I wrote hidesWhenStoppped and hidden both to YES. But it still keeps animating.How do i get it to stop?
Ok, if it is not the main thread, put the following in and that should fix it.
- (void)stopActivityIndicator
{
if(![NSThread isMainThread]){
[self performSelectorOnMainThread:#selector(stopActivityIndicator) withObject:nil waitUntilDone:NO];
return;
}
[self.customerActIndicator stopAnimating];
self.customerActIndicator.hidesWhenStopped = YES;
self.customerActIndicator.hidden =YES;
NSLog(#"HIt this at 127");
}
Could you put your background operation into a separate class and then set a delegate on it so you can alert the delegate once the operation has completed?
I havent tried this, its just an idea :)
You could use a delegate pointing to your view controller & a method in your view controller like:
- (void) updateProgress:(NSNumber*)percentageComplete {
}
And then in the background thread:
float percentComplete = 0.5; // for example
NSNumber *percentComplete = [NSNumber numberWithFloat:percentComplete];
[delegate performSelectorOnMainThread:#selector(updateProgress:) withObject:percentageComplete waitUntilDone:NO];

Resources