I am trying to use iCloud in my app. In the simulator, I notice that it takes a short amount of time after launch for new files in iCloud to be added to the ubiquity container. So to refresh my UI to reflect these changes, I set up an NSMetadataQuery as shown:
_query = [[NSMetadataQuery alloc] init];
_query.searchScopes = #[ubiquityContainer];
_query.predicate = [NSPredicate predicateWithFormat:#"%K == '*'", NSMetadataItemFSNameKey];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshFiles) name:NSMetadataQueryDidUpdateNotification object:nil];
[_query startQuery];
refreshFiles is never called...
My app saves its files directly in ubiquityContainer (an NSURL) - it doesn't create any subdirectories...
What am I doing wrong?
I found out what the issue was! I needed to use NSMetadataQueryUbiquitousDataScope as the search scope. That fixed the problem!
Related
I'm trying to make an API call after getting "didEnterRegion" call. When the app is on front, it works great. But if the app is deactivated or even in background, the call is never made. I tried to debug it but once "reportLocationEventToServerWithType" is called the debugger "dies" and the call is never made. I don't think it matters, but i'm using Estimote sdk for the beacon monitoring.
Here are the code snippets:
- (void)beaconManager:(id)manager didEnterRegion:(CLBeaconRegion *)region{
UIApplication *app = [UIApplication sharedApplication];
NSNumber * mallID = [self.dictBeaconsPerMall objectForKey:region.identifier];
Mall * mall = [Mall getMallWithID:mallID];
[ApplicationManager sharedInstance].locationManager.beaconRecognizedMall=mall;
[[NSNotificationCenter defaultCenter]postNotificationName:nLocationMallUpdated object:nil];
UILocalNotification * not = [[UILocalNotification alloc]init];
not.alertBody = [NSString stringWithFormat:#"DID Enter beacon region at: %#",mall.strTitle];
not.alertAction = #"alertAction";
not.soundName = UILocalNotificationDefaultSoundName;
[app presentLocalNotificationNow:not];
[[ApplicationManager sharedInstance].crashAndReportsManager reportLocationEventToServerWithType:#"beacon" andSubParam:region.identifier extraInfo:#{#"paramKey":#"key"}];
}
-(void)reportLocationEventToServerWithType: (NSString*)type andSubParam:(NSString*) strID extraInfo: (NSDictionary*)extraInfo{
NSString* udid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
NSMutableDictionary * params = [[NSMutableDictionary alloc]init];
[params setObject:type forKey:#"type"];
[params setObject:strID forKey:#"subParam"];
[params setObject:udid forKey:#"udid"];
if(extraInfo)
[params addEntriesFromDictionary:extraInfo];
UserLogRequest * req = [[UserLogRequest alloc]initWithCallerObject:self andParams:params];
req.showHud=NO;
req.showMessage=NO;
[[ApplicationManager sharedInstance].requestManager sendRequestForRequest:req];
}
The request initialization and the request manager "sendRequestForRequest" are working fine in million other situations so their implementation is irrelevant.
Thanks!
Background execution is little bit different in ios. you can't execute in background without some settings from capabilities under project target. you should enable background execution from it for specific requirement like location, audio etc. and then you should implement code accordingly.
You have limited options to execute in background like audioplaying, location updating, finite length task, voice over ip etc.
refer this apple documentation for more detail on background execution and you can google background execution you will got some good results.
So, reason of not working your task in background is this that you have not properly configured it.
hope this will help :)
I have app that stores system events to Core Data Database. To perform saving I use MagicalRecord.
So, in my logger class in init I have:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleDidFinishLaunchingNotification) name:UIApplicationDidFinishLaunchingNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleWillTerminateNotification) name:UIApplicationWillTerminateNotification object:nil];
In handle functions I store simple entity with text and timestamp property:
- (void)handleDidFinishLaunchingNotification
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
DBMessage *dbMessage = [DBMessage createEntityInContext:localContext];
dbMessage.text = #"Did finish launching";
dbMessage.timestamp = [NSDate date];
}];
}
- (void)handleWillTerminateNotification
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
DBMessage *dbMessage = [DBMessage createEntityInContext:localContext];
dbMessage.text = #"Will terminate";
dbMessage.timestamp = [NSDate date];
}];
}
When I open and close (without crash) app few times, in my DB I can see "Did finish launching" entries (more that one, so I'm sure app was closed, not only moved to BG), but none of "Will terminate".
I would be less surprised if the launching event were missed, because I could expect that init method will be called after notification is posted.
What I can do to store terminate events?
I have an answer:
It looks like when application gets terminated, wont't let me to save in new background thread. But saving in current thread works fine. So all I had to do was change saving method to save in current thread, by calling saveWithBlockAndWait: instead of saveWithBlock:
- (void)handleWillTerminateNotification
{
[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) {
DBMessage *dbMessage = [DBMessage createEntityInContext:localContext];
dbMessage.text = #"Will terminate";
dbMessage.timestamp = [NSDate date];
}];
}
Now events are succesfully saved on DB.
Prior iOS 8 all works fine. The problem is:
I have two observers in different classes:
class1:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishParseUser:)
name:USERS_LOADED_NOTIFICATION_ID object:nil];
class2:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishParseUser:)
name:USERS_LOADED_NOTIFICATION_ID object:nil];
and notification is posted in some other place:
[FBRequestConnection startWithGraphPath:#"me/friends?fields=id,first_name,last_name,picture.type(small)" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
[[NSNotificationCenter defaultCenter] postNotificationName:USERS_LOADED_NOTIFICATION_ID object:nil userInfo:[NSDictionary dictionaryWithObjectsAndKeys: currentUser, #"user", friends, #"friends", nil]];
} else {
// An error occurred, we need to handle the error
// See: https://developers.facebook.com/docs/ios/errors
}
}];
addObserver method is called for both of mentioned classes, however notification is being delivered just to one observer. If I delete this observer(which receives the notification), then another one receives the notification.
Prior to iOS 8 both observers receive the notification.
Can you, please, help me with this issue?
Found answer.
There is another way in iOS 8 to register for receiving remote notifications. I get nil for device token and app break on line:
NSDictionary *item = #{UID_ID : sCurrentUserId, #"deviceToken": appDelegate.deviceToken, #"handle": #"", #"friends": friends};
and second observer never receives notification.
You need to register your NSNotifications to work on iOS 8 and later but don't need to register if your iOS version is less than iOS 8.
Use Following Code
NSArray *vComp = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:#"."];
if ([[vComp objectAtIndex:0] intValue] >= 8) {
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationActivationModeBackground categories:nil]];
}
happy Coding
I have a code like the following:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemPlayEnded:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:AVPlayerItemDidPlayToEndTimeNotification object:nil userInfo:userInfo];
In the selector method:
- (void)itemPlayEnded:(NSNotification *)notification
{
NSLog(#"Entered itemPlayEnded");
AVPlayerItem *p = [notification object];
NSLog(#"userinfo description %#",[[notification userInfo] description]);
}
The first time the itemPlayEnded is accessed the userInfo is non-null. After the first time the value is null.
Can anybody tell me why this is happening? Why null value for userInfo after the first time?
EDIT:
I need to clarify what is happening. I also updated my Notification code to use the lastItem which is a AVPlayerItem.
queuePlayer = [[AVQueuePlayer alloc] init];
NSDictionary *userInfo = #{#"tones": copyoftones};
for (NSString *playThis in listOfTonesToBePlayed) {
NSString *soundPath =[[NSBundle mainBundle] pathForResource:playThis ofType:#"mp3"];
NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
AVPlayerItem *thePlayerItemA = [[AVPlayerItem alloc] initWithURL:soundURL];
lastItem = thePlayerItemA;
[queuePlayer insertItem:thePlayerItemA afterItem:nil];
}
queuePlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemPlayEnded:) name:AVPlayerItemDidPlayToEndTimeNotification object:lastItem];
[[NSNotificationCenter defaultCenter] postNotificationName:AVPlayerItemDidPlayToEndTimeNotification object:lastItem userInfo:userInfo];
[queuePlayer play];
What happens.
before the queuePlayer is able to play a tone the itemPlayEnded is entered along with the Dictionary non nil
Next the list of tones are played
The itemPlayEnded is re-entered with the Dictionary nil.
I wanted to use code to reset the Notification inside of the itemPlayEnded method with something like the following (p is a AVPlayerItem which is the lastItem from the code with the Notification):
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:p];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemPlayEnded:) name:AVPlayerItemDidPlayToEndTimeNotification object:p];
[[NSNotificationCenter defaultCenter] postNotificationName:AVPlayerItemDidPlayToEndTimeNotification object:p userInfo:userInfo];
[queuePlayer play];
But then the itemPlayEnded method is re-entered in a never-ending loop without playing the player. A race condition.
Any suggestions?
EDIT 2:
I have determined that the offending code is:
[[NSNotificationCenter defaultCenter] postNotificationName:AVPlayerItemDidPlayToEndTimeNotification object:lastItem userInfo:userInfo];
some how I need to set the object to use the correct sender. Not sure yet on what to put there.
EDIT 3:
What I really wanted to do was be able to loop thru the complete sequence of sounds or mp3s. I finally figured that what I was trying to do here was not going to work like I wanted it. So, I ended up using the class from https://github.com/dgiovann/AVQueuePlayerPrevious and this worked out great for me!
You are subscribing to the AVPlayerItemDidPlayToEndTimeNotification which is being posted first by yourself (with some user info) and then posted again, this time NOT by you and therefore may or may not contain any user info. Refer to the documentation to see when and why Apple will post this notification and what information you can expect with it.
https://developer.apple.com/library/mac/documentation/AVFoundation/Reference/AVPlayerItem_Class/Reference/Reference.html
I'm still new to blocks in objective-c and wondering if I have this psuedo code correct. I'm not sure if it's enough to just remove the observer or if i have to call removeObserver:name:object:
-(void) scan {
Scanner *scanner = [[Scanner alloc] init];
id scanComplete = [[NSNotificationCenter defaultCenter] addObserverForName:#"ScanComplete"
object:scanner
queue:nil
usingBlock:^(NSNotification *notification){
/*
do something
*/
[[NSNotificationCenter defaultCenter] removeObserver:scanComplete];
[scanner release];
}];
[scanner startScan];
}
Update: I'm receiving intermittent EXC_BAD_ACCESS from this block, so this can't be right.
Declare the scanComplete variable before defining the block itself.
The reason why you need to do this is because you're trying to access a variable that doesn't exist within the block at the time of definition since the variable itself has not been assigned yet.
What is EXC_BAD_ACCESS? Well, it's an exception that is thrown when you try to access a reference that doesn't exist. So that is exactly the case in your example.
So if you declare the variable before the block itself, then it should work:
-(void) scan {
Scanner *scanner = [[Scanner alloc] init];
__block id scanComplete;
scanComplete = [[NSNotificationCenter defaultCenter] addObserverForName:#"ScanComplete"
object:scanner
queue:nil
usingBlock:^(NSNotification *notification){
/*
do something
*/
[[NSNotificationCenter defaultCenter] removeObserver:scanComplete];
[scanner release];
}];
[scanner startScan];
}
You should not unregister in the register block. Instead, store the token returned from addObserverForName (in this case, your scanComplete) as an instance variable or in a collection that is an instance variable, and unregister later when you're about to go out of existence (e.g. in dealloc). What I do is keep an NSMutableSet called observers. So:
id ob = [[NSNotificationCenter defaultCenter]
addObserverForName:#"whatever" object:nil queue:nil
usingBlock:^(NSNotification *note) {
// ... whatever ...
}];
[self->observers addObject:ob];
And then later:
for (id ob in self->observers)
[[NSNotificationCenter defaultCenter] removeObserver:ob];
self->observers = nil;
Apple Document about this method:
The following example shows how you can register to receive locale change notifications.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
self.localeChangeObserver = [center addObserverForName:NSCurrentLocaleDidChangeNotification object:nil
queue:mainQueue usingBlock:^(NSNotification *note) {
NSLog(#"The user's locale changed to: %#", [[NSLocale currentLocale] localeIdentifier]);
}];
To unregister observations, you pass the object returned by this method to removeObserver:. You must invoke removeObserver: or removeObserver:name:object: before any object specified by addObserverForName:object:queue:usingBlock: is deallocated.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self.localeChangeObserver];
The scope of the block doesn't have permission to release the scanner object. If you're not using garbage collection, removing the release and making the scanner autorelease ([[[Scanner alloc] init] autorelease]) should do the trick.
You should also be able to safely move the call to removeObserver outside of the block.
For the case of EXC_BAD_ACCESS: Entering bt in the console window after the application crashes will give you a backtrace, and should inform you where the error occurred.