cmdevicemotion stops providing update - ios

I have some weird issue in my code that I can't figure out and need some help. I have defined a CMMotionManager in my AppDelegate. In my view controller, I define a property:
#property (nonatomic, retain) CMMotionManager *motionManager;
and then use this code to start the updates:
- (void)startMyMotionDetect
{
motionManager = [(AppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
motionManager.deviceMotionUpdateInterval = 1.0/60.0;
[motionManager startDeviceMotionUpdates];
timer = [NSTimer scheduledTimerWithTimeInterval:20/60 target:self selector:#selector(handleDeviceMotion) userInfo:nil repeats:YES];
}
However, the handleDeviceMotion get called only a few time and then it stops updating the deviceMotion output. I have changed the code to the push style (block code) but in that case the block is not getting executed! A test NSLog comment in the block never gets executed. Here is how that code looks like:
motionManager = [(AppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
motionManager.deviceMotionUpdateInterval = 1.0/60.0;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *dMotion, NSError *error)
{
NSLog(#"test 1");
[self performSelectorOnMainThread:#selector(handleDeviceMotion) withObject:nil waitUntilDone:YES];
}];
Here is how the handleDeviceMotion code looks like:
- (void)handleDeviceMotion//:(CMDeviceMotion *)dMotion
{
if ([motionManager isDeviceMotionActive])
{
NSLog(#"test");
}
....}
With the first method, I see "test" printed a few times and then it stops.
I thought by retaining motionManager as a property, I don't have to worry about it being released but it appears that it is getting released somewhere (or do you think something else is going on?).
Many thanks for your help.

Related

Hidden button action won't change issue

I'm trying to disable the navigation bar button when I start the app and after I finish the process(fetching data), I enable it back but unfortunately it won't enable.
Please where would be my issue? While I'm putting enable to YES and when I debug it I can see that it enabling it to YES.
- (void)viewDidLoad {
UIImage *searchBtn = [UIImage imageNamed:#"search_icon.png"];
barButtonSearch = [[UIBarButtonItem alloc] initWithImage:[searchBtn imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] style:UIBarButtonItemStylePlain target:self action:#selector(searchButton)];
UIImage *menuBtn = [UIImage imageNamed:#"menu_icon.png"];
barButtonMenu = [[UIBarButtonItem alloc] initWithImage:[menuBtn imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] style:UIBarButtonItemStylePlain target:self action:#selector(menuButton)];
self.navigationItem.rightBarButtonItem = barButtonMenu;
self.navigationItem.leftBarButtonItem = barButtonSearch;
barButtonMenu.enabled = NO;
barButtonSearch.enabled = NO;
}
- (void)unhide{
if (!(barButtonSearch.enabled && barButtonMenu.enabled)) {
barButtonMenu.enabled = YES;
barButtonSearch.enabled = YES;
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
ViewController *theInstance = [[ViewController alloc] init];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
_dic = (NSDictionary *)responseObject;
[theInstance unhide];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Err");
}];
[operation start];
return YES;
}
}
Well there it is!
You are initializing your ViewController but that doesn't call your viewDidLoad: method. The viewDidLoad: method gets called when your ViewController is either Pushed or Presented! That is the time when the view gets loaded into the memory.
Therefore, the barButtons are never created and you are unable to see them.
So either make your network call inside the viewDidLoad: method of your ViewController
OR
Push the instance of your ViewController and then call the method unhide.
Edit
Since you are using Storyboards and not pushing any ViewController from AppDelegate, you need to use reference of your ViewController.
replace this in your - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method
ViewController *theInstance = (ViewController *)[(UINavigationController*)self.window.rootViewController topViewController];
You are calling [theInstance unhide] from the completion block of the AFHTTPOperation - this will almost certainly be executed on a background queue.
All UI operations must be performed on the main queue.
You should use -
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
_dic = (NSDictionary *)responseObject;
dispatch_async(dispatch_get_main_queue(),^{
[theInstance unhide];
});
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Err");
}];
Update
Your main problem is that theInstance points to an instance of your view controller that isn't on the screen - It is just an instance you have allocated but not actually presented.
Assuming this view controller is the initial view controller loaded by your application you can get a reference to the correct instance using [UIApplication sharedApplication].keyWindow.rootViewController
Remove this method from the completion block of app delegate.
[theInstance unhide];
And add some delegate function which will be activated after the asynchronous call completes its task. And add that unhide method there (In your view controller may be).

Is there a way, in background, to get my app notified when the CMMotionActivity changes?

I'd like to know if the system can wake an application in the background if the CMMotionActivity changes, for example, if the user starts walking/running after sitting, I'd like to be able to execute some code and schedule a local notification.
Is there a way to ask the system to wake my app in the background for that ?
EDIT : By looking at the reference, it doesn't seem to be possible ("[...] and updates are not delivered while your app is suspended."), but maybe is there an other way ?
I solved this creating a background timer and checking for the activity type in the selector called method. Just have a look to the code in case it can be useful for you. I accept suggestions, corrections and advices over this.
#define k_timer_time 10.0f
#property NSTimer *timer;
- (void) createBackGroundTimerToCheckMotion{
// create new uiBackgroundTask
__block UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
__weak __typeof(self) weakSelf = self;
// and create new timer with async call:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
weakSelf.timer = [NSTimer scheduledTimerWithTimeInterval:k_timer_time target:self selector:#selector(onTick:) userInfo:nil repeats:YES];
weakSelf.timer.tolerance = 5;
[[NSRunLoop currentRunLoop] addTimer:weakSelf.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
- (void) onTick:(NStimer*)timer{
if([CMMotionActivityManager isActivityAvailable])
{
__weak __typeof(self) weakSelf = self;
CMMotionActivityManager *cm = [[CMMotionActivityManager alloc] init];
CMPedometer *sc = [[CMPedometer alloc] init];
NSDate *now = [NSDate date];
NSDate *last30Sec = [now dateByAddingTimeInterval:-30];
[cm queryActivityStartingFromDate:last30Sec toDate:now toQueue:[NSOperationQueue mainQueue] withHandler:^(NSArray *activities, NSError *error)
{
[activities enumerateObjectsUsingBlock:^(CMMotionActivity *a, NSUInteger idx, BOOL * _Nonnull stop) {
//Your stuff here
}];
}];
}
else
{
NSLog(#"Error accessing Motion data");
}
}

Database locked using FMDB for iOS

First of all, I'd like to say that I'm really having a bad time trying to configure and use my SQLite DB in a background thread so that the main thread is not blocked.
After I found a little guide somewhere on the Internet, I've decided to go for the FMDB wrapper.
All the methods related to the DB operations are in the same class and this is where I'm getting errors:
I've set the static variables like this:
static FMDatabaseQueue *_queue;
static NSOperationQueue *_writeQueue;
static NSRecursiveLock *_writeQueueLock;
Then in my init method I have:
- (id)init {
self = [super init];
if (self) {
_queue = [FMDatabaseQueue databaseQueueWithPath:[self GetDocumentPath]];
_writeQueue = [NSOperationQueue new];
[_writeQueue setMaxConcurrentOperationCount:1];
_writeQueueLock = [NSRecursiveLock new];
}
return self;
}
And this is the method that gives me the error:
- (void)UpdateTime:(NSString *)idT :(int)userId {
[_writeQueue addOperationWithBlock:^{
[_writeQueueLock lock];
[_queue inDatabase:^(FMDatabase *dbase) {
AppDelegate *deleg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (![dbase executeUpdate:#"update orari set orario=datetime(orario, '? minutes') where nome=? and dataid>=? and idutente=?"
withArgumentsInArray:#[[NSNumber numberWithFloat:deleg.diff], deleg.nome, [NSNumber numberWithInt:deleg.idMed], [NSNumber numberWithInt: userId]]]) {
NSLog(#"error");
}
}];
[_writeQueueLock unlock];
}];
[_writeQueue addOperationWithBlock:^{
[_writeQueueLock lock];
[_queue inDatabase:^(FMDatabase *dbase) {
AppDelegate *deleg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (![dbase executeUpdate:#"UPDATE orari SET presa=1 where dataid=? and idutente=?"
withArgumentsInArray:#[[NSNumber numberWithInt:deleg.identific], [NSNumber numberWithInt: userId]]]) {
NSLog(#"error");
}
}];
[_writeQueueLock unlock];
}];
[self AddNotification];
}
These are the errors I'm getting:
*** -[NSRecursiveLock dealloc]: lock (<NSRecursiveLock: 0xc38b350> '(null)') deallocated while still in use
DB Error: 5 "database is locked"
*** -[NSRecursiveLock unlock]: lock (<NSRecursiveLock: 0x13378d20> '(null)') unlocked when not locked
From the guide I've read, I supposed that the access to my DB would have been "serialized", and each update would have been added to a queue and executed one at a time.
As you can see, I have a lot to learn about this topic, so any help would really be appreciated.
As I can See you have not created shared instance or singleton instance of this init call
- (id)init {
self = [super init];
if (self) {
_queue = [FMDatabaseQueue databaseQueueWithPath:[self GetDocumentPath]];
_writeQueue = [NSOperationQueue new];
[_writeQueue setMaxConcurrentOperationCount:1];
_writeQueueLock = [NSRecursiveLock new];
}
return self;
}
This should be a singleton call as you will create multiple instance of NSOperationQueue which will make DB vulnurable in a multi-threaded environment, try making it singleton call for your database either using GCD or
static DBManager *sharedInstance = nil;
+(DBManager*)getSharedInstance{
if (!sharedInstance) {
sharedInstance = [[super allocWithZone:NULL]init];
_queue = [FMDatabaseQueue databaseQueueWithPath:[self GetDocumentPath]];
_writeQueue = [NSOperationQueue new];
[_writeQueue setMaxConcurrentOperationCount:1];
_writeQueueLock = [NSRecursiveLock new];
}
return sharedInstance;
}
It might solve your problem and this is first time I am answering here and I am new to the environment so please be little bit forgiving :) Thanks

Getting accelerometer data in background using CoreMotion

I'm unable to receive accelerometer data in the background, despite the seemingly correct solution per this question How Nike+ GPS on iPhone receives accelerometer updates in the background?
[_motionManager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init]
withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"perform");
[(id) self setAcceleration:accelerometerData.acceleration];
[self performSelectorOnMainThread:#selector(update) withObject:nil waitUntilDone:NO];
});}];
perform is logged whenever the app is in the foreground, but whenever I exit out to background it stops running. Does anyone have any idea why this might be? I've checked "Location Updates" in Background Modes...
Put this code right in front of that line (after making a member variable called bgTaskID of type UIBackgroundTaskIdentifier):
UIApplication *application = [UIApplication sharedApplication];
__weak __typeof(&*self)weakSelf = self;
self.bgTaskID = [application beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof(&*weakSelf)strongSelf = weakSelf;
NSLog(#"BG TASK EXPIRED!");
if (strongSelf) {
[application endBackgroundTask:strongSelf.bgTaskID];
strongSelf.bgTaskID = UIBackgroundTaskInvalid;
}
}];
It can be done by using CoreMotion framework. You have to import CoreMotion framework, then #import <CoreMotion/CoreMotion.h> in your appdelegate.
Here motionManager is object of CMMotionManager.
xData, yData, zData are double values to store accelerometer data.
if (motionManager ==nil) {
motionManager= [[CMMotionManager alloc]init];
}
[motionManager startAccelerometerUpdates];
[motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
xData = accelerometerData.acceleration.x;
yData = accelerometerData.acceleration.y;
zData = accelerometerData.acceleration.z;
}];
You have to do it in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.
You can then use these value of xData, yData, zData where ever you want by appdelegate object, even in background.

Stop gyro updates

I cant work out to stop the Gyro updates in my Cocos2D game,
I have got the following code in my init: ` //gyro
motionManager = [[CMMotionManager alloc] init];
referenceAttitude = nil;
[motionManager startGyroUpdates];
timer = [NSTimer scheduledTimerWithTimeInterval:0.2
target:self
selector:#selector(doGyroUpdate)
userInfo:nil
repeats:YES];
Then in the gyro update itself I check when the progress is more than 100% if (progress > 100) {
[self pauseSchedulerAndActions];
[self stopGyroUpdates];
Then:
- (void)stopGyroUpdates{
NSLog(#"Stop gyro update");
}
but it keeps checking... So the code within the if statement keeps getting called.
I found the solution with the following code: `
-(void) callgyro:(int)gyroprocess {
NSLog(#"%i", gyroprocess);
if (gyroprocess < 100 ) {
motionManager = [[CMMotionManager alloc] init];
referenceAttitude = nil;
[motionManager startGyroUpdates];
timertwee = [NSTimer scheduledTimerWithTimeInterval:0.2
target:self
selector:#selector(doGyroUpdate)
userInfo:nil
repeats:YES];
}
else {
[motionManager stopGyroUpdates];
[timertwee invalidate];
NSLog(#"Gyro moet stoppen!");
}`
and within the gyro update itself:
if (progress > 100) {
[self callgyro:progress];
[self.timer invalidate];
self.timer = nil;
[Blikje setImage:[UIImage imageNamed:#"pop3.png"]];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setFloat:numhundreds forKey:#"score"];
progress = 0;
[self stopGyroUpdates];
}
Like rickster said, you need to call stopGyroUpdates on the CMMotionManager instance.so you need to create an instance variable or property in the interface implementation.
In your .m file where your declare the interface:
#interface ViewController ()
#property (strong, nonatomic) CMMotionManager *motionManager;
#end
Then initialize it as you require
motionManager = [[CMMotionManager alloc] init];
Then when you need to stop updates you call
[self.motionManager stopGyroUpdates]
You need to call stopGyroUpdates on your instance of CMMotionManager, not (or in addition to) on self. (This means that instance needs to persist beyond the scope if the method in which you call startGyroUpdates -- say, in a property or ivar -- if it doesn't already.)

Resources