I have found that when I want to deliver Notifications to particular threads, there is a sample from Apple.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Notifications/Articles/Threading.html#//apple_ref/doc/uid/20001289-CEGJFDFG
In this article, Apple suggests that using a machport to monitor the thread which we want to process the notification.
- (void)processNotification:(NSNotification *)notification {
if ([NSThread currentThread] != notificationThread) {
// Forward the notification to the correct thread.
[self.notificationLock lock];
[self.notifications addObject:notification];
[self.notificationLock unlock];
[self.notificationPort sendBeforeDate:[NSDate date]
components:nil
from:nil
reserved:0];
}
else {
// Process the notification here;
}
}
My question is: if I get the notification and I use dispatch_async to process the notification, does it has a different appearance?
dispatch_async(dispatch_get_main_queue(), ^{
// Process the notification here;
});
the simple answer is : no difference
I also noticed the apple suggested link is Updated: 2009-08-18. it seems to already outdated. GCD is a more powerful and convenient way to achieve multi-thread work.
Related
I have registered for Calendar Change Notifications using the following:
- (void) registerForLocalCalendarChanges
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(localCalendarStoreChanged) name:EKEventStoreChangedNotification object:self.store ];
}
This should call the following when a change is made to the local calendar:
- (void) localCalendarStoreChanged
{
// This gets called when an event in store changes
// you have to go through the calendar to look for changes
// launch this in a thread of its own!
ashsysCalendarEventReporter *eventReport = [ashsysCalendarEventReporter new];
NSLog(#"Local Calendar Store Changed");
[NSThread detachNewThreadSelector:#selector(getCalendarEvents) toTarget:eventReport withObject:nil];
}
BUT...when I start the app, then send it to the background so I can change a calendar entry, nothing happens when I change the calendar entry. It DOES fire when I return to the app. But, of course that is not the objective.
store is defined in the header file with:
#property (strong,nonatomic) EKEventStore *store;
Update...forgot to show the stuff I have in the background fetch.
This is in didFinishLaunchingWithOptions
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
This is in the app delegate:
- (void) application:(UIApplication*) application performFetchWithCompletionHandler:(void (^) (UIBackgroundFetchResult))completionHandler
{
// UIBackgroundTaskIdentifier uploadCalInfo = [application beginBackgroundTaskWithExpirationHandler:nil];
NSLog(#"A fetch got called");
// ashsysCalendarEventReporter *eventReport = [ashsysCalendarEventReporter new];
// [eventReport getCalendarEvents];
// // [NSThread detachNewThreadSelector:#selector(getCalendarEvents) toTarget:eventReport withObject:nil];
// [application endBackgroundTask:uploadCalInfo];
completionHandler(UIBackgroundFetchResultNewData);
The performFetch gets called at what seem like random times some not at all related to the calendar. Is there a way to find out what is firing the background fetch? Is it always the calendar? The actual execution is commented out -- is it correct?
What am I missing?
I've been trying to find this answer out myself and I don't think there's an actual way to accomplish this. As per Apple's documentation, we're not allowed access to system resources while in a background state:
Stop using shared system resources before being suspended. Apps that interact with shared system resources such as the Address Book or calendar databases should stop using those resources before being suspended. Priority for such resources always goes to the foreground app. When your app is suspended, if it is found to be using a shared resource, the app is killed.
I'm still looking for a better answer/work around but would love to hear this from anyone else.
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/
When I enter the app it is working fine but when I re-enter the app it will not shown the particular page or starting page of the app but it is again displaying the Launcher image and crashes and after crash again it is working fine.
[NSThread sleepForTimeInterval:1.0];
UILocalNotification *locationNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (locationNotification) {
// Set icon badge number to zero
NSLog(#"Recieved Notification %#",locationNotification);
}
return YES;
please give the solution I am new bee to iOS.
Anupma
From seeing the problem which is described here I suspect some methods or notifications are calling in applicationDidEnterBackground. Please check it and if it is you can find the solution to solve it.
Hope this helps.
-(void)applicationDidEnterBackground:(UIApplication *)application
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self Backgroundmethods];
dispatch_async(dispatch_get_main_queue(), ^{
//your code to update UI if any goes here
});
});
}
I worte the all my background methods in seperate method and calling that method with in applicationdidEnterbackground in seperate thread.
The documentation for CTCallCenter:setCallEventHandler: states that:
However, call events can also take place while your application is
suspended. While it is suspended, your application does not receive
call events. When your application resumes the active state, it
receives a single call event for each call that changed state
The part relevant to this question is
When your application resumes the active state, it receives a single
call event for each call that changed state
Implying the app will receive a call event for a call that took place in the past while the app was suspended. And this is possible according to the answer to this question: How does the Navita TEM app get call log information?
My question is: if my app is suspended and a call takes place, then when my app resumes the active state how can it retrieve the call event for the call that took place?
I have tried many, many code experiments but have been unable to retrieve any call information when my app resumes the active state.
This is the most simplest thing I have tried:
1) Create a new project using the Xcode single view application template.
2) Add the code shown below to didFinishLaunchingWithOptions
3) Launch the app
4) Task away from the app
5) Make a call from another device, answer the call, hang up the call from either device
6) Bring the app back to the foreground thus resuming the active state.
The code to register for call events is:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.callCenter = [[CTCallCenter alloc] init];
[self.callCenter setCallEventHandler:^(CTCall *call)
{
NSLog(#"Event handler called");
if ([call.callState isEqualToString: CTCallStateConnected])
{
NSLog(#"Connected");
}
else if ([call.callState isEqualToString: CTCallStateDialing])
{
NSLog(#"Dialing");
}
else if ([call.callState isEqualToString: CTCallStateDisconnected])
{
NSLog(#"Disconnected");
} else if ([call.callState isEqualToString: CTCallStateIncoming])
{
NSLog(#"Incomming");
}
}];
return YES;
}
With this code I am able to get call events if the app is in the foreground when the call occurs. But if I task away from the app before making the call then I am unable to get a call event when my app next resumes the active state - as it states it should in the Apple documentation.
Other things I have tried:
1) The documentation states that the block object is dispatched on the default priority global dispatch queue, so I have tried placing the registration of setCallEventHandler within dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{})
2) Calling setCallEventHandler: in appBecameActive instead of didFinishLaunchingWithOptions
3) Adding background abilities to the app - via beginBackgroundTaskWithExpirationHandler and/or location updates using startUpdatingLocation or startMonitoringForSignificantLocationChanges.
4) Various combinations of the above.
The bounty will be awarded once I get code running on my device which is able to get call events that took place while the app was suspended.
This is on iOS 7.
I've found a solution but I have no idea why it's working. Only thing I can think of is a bug in GCD and/or CoreTelephony.
Basically, I allocate two instances of CTCallCenter like this
void (^block)(CTCall*) = ^(CTCall* call) { NSLog(#"%#", call.callState); };
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
callCenter1 = [[CTCallCenter alloc] init];
callCenter1.callEventHandler = block;
callCenter2 = [[CTCallCenter alloc] init];
callCenter2.callEventHandler = block;
return YES;
}
Similar Code in Swift:
func block (call:CTCall!) {
println(call.callState)
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//Declare callcenter in the class like 'var callcenter = CTCallCenter()'
callcenter.callEventHandler = block
return true
}
To test this I made a call, answered it and then hanged up it while app was in background. When I launched it I received 3 call events: incoming, connected, disconnected.
In my case, I was working on an enterprise app which doesn't need to be approved by Apple's app market - so if you develop an enterprise app this solution is for you.
Also, the chosen answer didn't work while the app is the background.
The solution is simple, basically you just need to add 2 capabilities (VOIP & Background fetch) in the Capabilities tab:
Your project target -> Capabilities -> Background Modes -> mark Voice over IP & Background fetch
Now, your app is registered to the iOS framework calls "delegate" so the OP code snip solution:
[self.callCenter setCallEventHandler:^(CTCall *call)
{
NSLog(#"Event handler called");
if ([call.callState isEqualToString: CTCallStateConnected])
{
NSLog(#"Connected");
}
else if ([call.callState isEqualToString: CTCallStateDialing])
{
NSLog(#"Dialing");
}
else if ([call.callState isEqualToString: CTCallStateDisconnected])
{
NSLog(#"Disconnected");
} else if ([call.callState isEqualToString: CTCallStateIncoming])
{
NSLog(#"Incomming");
}
}];
Would defiantly work and you will get notifications even if your app is in the background.
In relation to this question I was wondering if there is any generally accepted logic regarding when to use NSNotification, with an observer in your main thread, vs using GCD to dispatch work from a background thread to the main thread?
It seems that with a notification-observer setup you have to remember to tear down the observer when your view unloads but then you reliably ignore the notification, where as dispatching a job to the main thread may result in a block being executed when the view has been unloaded.
As such, it seems to me that notifications should provide improved app stability. I'm assuming that the dispatch option provides better performance from what I've read of GCD?
UPDATE:
I'm aware that notifications and dispatch can work happily together and in some cases, should be used together. I'm trying to find out if there are specific cases where one should/shouldn't be used.
An example case: Why would I select the main thread to fire a notification from a dispatched block rather than just dispatching the receiving function on the main queue? (Obviously there would be some changes to the receiving function in the two cases, but the end result would seem to be the same).
The NSNotificationCenter and gcd & dispatch_get_main_queue() serve very different purposes. I don't thing "vs" is truly applicable.
NSNotificationCenter provides a way of de-coupling disparate parts of your application. For example the kReachabilityChangedNotification in Apple's Reachability sample code is posted to the notification center when the system network status changes. And in turn you can ask the notification center to call your selector/invocation so you can respond to such an event.(Think Air Raid Siren)
gcd on the other hand provides a quick way of assigning work to be done on a queue specified by you. It lets you tell the system the points at which your code can be dissected and processed separately to take advantage of multiple-threads and of multi-core CPUs.
Generally (almost always) the notifications are observed on the thread on which they are posted. With the notable exception of one piece of API...
The one piece of API where these to concepts intersect is NSNotificationCenter's:
addObserverForName:object:queue:usingBlock:
This is essentially a convenient method for ensuring that a given notification is observed on a given thread. Although the "usingBlock" parameter gives away that behind the scenes it's using gcd.
Here is an example of its usage. Suppose somewhere in my code there is an NSTimer calling this method every second:
-(void)timerTimedOut:(NSTimer *)timer{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// Ha! Gotcha this is on a background thread.
[[NSNotificationCenter defaultCenter] postNotificationName:backgroundColorIsGettingBoringNotification object:nil];
});
}
I want to use the backgroundColorIsGettingBoringNotification as a signal to me to change my view controller's view's background color. But it's posted on a background thread. Well I can use the afore mentioned API to observe that only on the main thread. Note viewDidLoad in the following code:
#implementation NoWayWillMyBackgroundBeBoringViewController {
id _observer;
}
-(void)observeHeyNotification:(NSNotification *)note{
static NSArray *rainbow = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
rainbow = #[[UIColor redColor], [UIColor orangeColor], [UIColor yellowColor], [UIColor greenColor], [UIColor blueColor], [UIColor purpleColor]];
});
NSInteger colorIndex = [rainbow indexOfObject:self.view.backgroundColor];
colorIndex++;
if (colorIndex == rainbow.count) colorIndex = 0;
self.view.backgroundColor = [rainbow objectAtIndex:colorIndex];
}
- (void)viewDidLoad{
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
__weak PNE_ViewController *weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:backgroundColorIsGettingBoringNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note){
[weakSelf observeHeyNotification:note];
}];
}
-(void)viewDidUnload{
[super viewDidUnload];
[[NSNotificationCenter defaultCenter] removeObserver:_observer];
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:_observer];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
The primary advantage of this API seems to be that your observing block will be called during the postNotification... call. If you used the standard API and implemented observeHeyNotification: like the following there would be no guarantee how long it would be before your dispatch block was executed:
-(void)observeHeyNotification:(NSNotification *)note{
dispatch_async(dispatch_get_main_queue(), ^{
// Same stuff here...
});
}
Of course in this example you could simply not post the notification on a background thread, but this might come in handy if you are using a framework which makes no guarantees about on which thread it will post notifications.