I am using Core Bluetooth in my project. I have included Session Backgrounding to avail its background mode functionality. I have observed that the delegate for peripheral disconnection,
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
gets called in the background mode. However any code I write in this method is not executed except for NSLogs. Can somebody explain exactly what kind of code can be executed here?
My aim is to send this disconnection notification to my server.
Ok it seems it was some issue at my end. According the the documentation your app is woken (in the background) for around 10 seconds when it gets a bluetooth related delegate call.
You can use this time to perform any non view updating task and even request for additional time using beginBackgroundTaskWithExpirationHandler.
My code looks like this.
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
[self sendEmailInBackground:peripheral]; //Code to send a server request
return;
}
and its working in the background mode. This also works when the phone is in lock state.
Related
First some background:
My setup uses a Service, which implements BeaconConsumer and binds to the BeaconManager. I have additional handling so when my app leaves the foreground, I move my Service to run in foreground, and when my app enters the foreground, I move my Service to run in background. That way, the persistent notification should display if and only if the app isn't displaying. In accordance, I am using the pattern here to tell BeaconManager I'm running this Service in the foreground, to allow for more frequent scanning. The link above isn't quite clear about this, but I believe this pattern should work without alterations needed on both pre-Android 8 as well as Android 8+. It shouldn't be strictly necessary on pre-Android 8, since the OS is more lenient. However, using this setup across all versions has the collateral benefit of ensuring that the OS does not kill the Service. If the app is in the foreground, the Service is background but has priority by virtue of the app, and if the app is not in the foreground, the Service is, and therefore has priority.
Now the problem: On pre-Android 8 devices, my Service is not seeing didEnterRegion called when the app is not in the foreground (but the Service is). It works fine on Android 8+.
some code snippets:
In my Service, set up the BeaconManager, set scan intervals
_beaconManager = BeaconManager.getInstanceForApplication(this);
_beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout(IBEACON_PATTERN_1));
_beaconManager.setEnableScheduledScanJobs(false);
_beaconManager.setBackgroundBetweenScanPeriod(0);
_beaconManager.setBackgroundScanPeriod(1100);
Function in my Service I invoke to send the service to the foreground, and background:
private void sendServiceToForeground() {
this.startForeground(NOTIFICATION_ID, _notification);
if (_beaconManager != null) {
if (_beaconManager.isBound(this)) {
_beaconManager.unbind(this);
}
_beaconManager.enableForegroundServiceScanning(_notification, NOTIFICATION_ID);
_beaconManager.bind(this);
}
}
private void sendServiceToBackground() {
if (_beaconManager != null) {
if (_beaconManager.isBound(this)) {
_beaconManager.unbind(this);
}
_beaconManager.disableForegroundServiceScanning();
_beaconManager.bind(this);
}
this.stopForeground(true);
}
I can provide more code as requested. Not sure what all is relevant.
Calls to unbind() and bind() are asynchronous, so calling them one after another will be a problem unless you first wait for the unbind() operation to complete. This is tricky, because the library's BeaconManager does not provide a callback to tell you when unbind is complete (indeed, this is because the underlying Android service APIs also do not provide such a callback. You essentially don't know when the library's scanning service has stopped so you can safely restart it again in a different mode.)
It's a bit of a hack, but you might try adding a delay between unbind() and bind() to see if that makes a difference.
I am on XCode 9, OSX not iOS, Objective-C.
I have an XPC Service to talk to other applications.
XPC Services are completely new to me. I've read documentation and articles i found - still i'd need some help.
// NSXPC Connection stored as ivar
self.bridgeagent = [[NSXPCConnection alloc] initWithServiceName:#"com.myid.myapp.bridgeagent"];
self.bridgeagent.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:#protocol(bridgeagentProtocol)];
self.bridgeagent.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(bridgeagentProxyProtocol)];
self.bridgeagent.exportedObject = self;
[self.bridgeagent setInvalidationHandler:^{
NSLog(#"Bridgeagent invalidation handler!");
}];
[self.bridgeagent setInterruptionHandler:^{
NSLog(#"Bridgeagent interruption handler!");
}];
[self.bridgeagent resume];
The Service is called like this:
// openFile method is listed in corresponding protocol
[[self.bridgeagent remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
NSLog(#"bridgeagent.openFile errorHandler: %#",error);
}] openFile:parameters withReply:^(NSDictionary *returnParameters) { // do something with result }];
The call works and the service does its job. However - now that the service works i want to dig into making it more stable (even if i don't face any issues right now).
Can someone explain to me
the difference between interruption and invalidation (don't get it when one or the other happens)
if there's a best practice to handle both cases
how to force both cases (for debugging)
Thank you for help
Answer to question 1:
[self.xpcConnection setInterruptionHandler:^{
// Connection interrupted. Backend (service) may have crashed.
// connection used to work but suddenly terminated
}];
[self.xpcConnection setInvalidationHandler:^{
// No one is listening. Is the backend running?
// connection cannot be established
}];
Answer to question 3:
interruption: make backend exit in the middle of a transaction (just before reply is sent)
invalidation: don't start backend (service) at all
Answer to question 2:
I have heard that in case "interruption" you should try to reestablish the connection. This can be useful when your service is a launch agent which gets restarted by launchd in case it crashed.
Actually in my program I don't act upon these cases but simply issue a warning message to the command line. My frontend is a cli program.
Alternatively you could log this warning in a logfile, for example with syslog. See 'man 3 syslog'. In my app I use my own logfile with configurable verbosity AND syslog.
kind regards,
Robert
I have a SignalR hub and two clients (Windows and PCL for Android and iOS). Neither of the clients is able to call some methods on the server. This behaviour is quite odd, since the methods look very similar. Moreover, a colleague of mine is able to call methods I cannot call, and vice versa, does not invoke methods that I invoke with no problems.
Here is an example of a method, which works for me and does not work for my colleague:
public override async Task<bool> RefreshArray(User user, int waitMilis)
{
var cts = new CancellationTokenSource();
try
{
cts.CancelAfter(waitMilis);
await Proxy.Invoke("RefreshArray", user);
return true;
}
catch (Exception ex)
{
OnExceptionOccured(ex);
return false;
}
}
And a method which does not work for me, but works for my colleague:
public override async Task<bool> RequestInformation(User user, Product product, int waitMilis)
{
var cts = new CancellationTokenSource();
try
{
cts.CancelAfter(waitMilis);
await Proxy.Invoke("RequestInformation", user, product);
return true;
}
catch (Exception ex)
{
OnExceptionOccured(ex);
return false;
}
}
Yes, me and my colleague have exactly the same code. And no, there are no typos or different arguments. I have tried to get as much data from the client connection as possible, by setting _connection.TraceLevel = TraceLevels.All; However, I did not get any information on the invoked methods, just on the replies from the hub. When calling RefreshArray, I got exactly the data I requested. When calling RequestInformation, the debugger never even hit the breakpoint in the hub method and the _connection.Trace displayed only this: 11:22:45.6169660 - 7bc57897-489b-49a2-8459-3fcdb8fcf974 - SSE: OnMessage(Data: {})
Has anybody solved a similar issue? Is there a solution?
UPDATE 1
I just realized that I have encountered almost the same issue about a year ago (Possible SignalR bug in Xamarin Android). StackOverflow has also pointed me to a question with almost the same issue (SignalR on Xamarin.iOS - randomly not able to call Hub method), just related to iOS and Azure. However, I got the same proble even outside Xamarin, on Windows Phone 8.1 and and Windows 10 Universal App. Moreover, I am running the server just locally, so it is not an issue od Azure. Is it really possible, that a 2 years old bug has no solution?
UPDATE 2
I have just created a simple console application with SignalR.Client. In the console application every method worked just fine. Amazingly, also the Windows 10 Universal Application started to behave as expected - every hub method was invoked correctly. Windows Phone 8.1 also improved its behaviour (all hub methods invoked). However, every now and then the connection tried to reconnect periodically (for no apparent reason), leading to Connection started reconnecting before invocation result was received. error. The Android application still behaved as before.
So I tried to replicate my previous steps and created another console application, but this time with SignalR.Client.Portable library. To my dissapointment, there was no change in the Android application behaviour.
Next week we will start to test our application on iOS, so I really wonder what new oddities will we encounter.
I have managed to solve the problem (at least so it seems). As it turned out, there is some weird stuff going around, when an application receives an answer from SignalR hub. It seems as if the HubProxy was blocked for a certain period of time on Android, while it drops the connection and starts to reconnect periodically on Windows Phone, not waiting for an asnwer from the hub.
The implementation of RefreshArray on the hub was something like this:
public async Task RefreshArray(User user)
{
await Clients.Caller.SendArray(_globalArray);
await Clients.Caller.SendMoreInformation(_additionalInfo);
}
Because the method sent two methods as an answer, the client Proxy got stuck and each platform handled it in its own unexpected way. The reason why some methods were called on my computer and not on colleagues was, simply, because we had different position of breakpoints, which enabled the application to resolve at least some requests and responses.
The ultimate solution was to add some synchronization into the invokation of methods. Now my hub calls only await Clients.Caller.SendArray(_globalArray);. This is then handled on the client with a ArraySent(string[] array) event, which then subsequently invokes the SendMoreInformation() method on the hub.
I'm working on an OS X application after having worked almost exclusively on iOS apps. If the user quits, say by pressing Command-Q, how can I store some data locally and send some data to my server before the app truly goes out of memory?
On iOS I typically launch a background event when I detect the app going into the background. I've tried listening for "ApplicationWillTerminate" but it doesn't seem to afford me the time to send data.
Should I be intercepting the menu action and performing my work before quitting?
I realize this is a pretty basic question, but my Google-Foo has not led me to a straightforward answer.
applicationWillTerminate is indeed the method to clean up things before the app quits.
Alternatively for an asynchronous way implement applicationShouldTerminate, start you task to store and send the data asynchronously and return NSTerminateLater.
When the task is completed call
[NSApp replyToApplicationShouldTerminate:YES];
Here a snippet as example
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
[self startTaskToSendDataWithCompletion:^() {
[NSApp replyToApplicationShouldTerminate:YES];
}];
return NSTerminateLater;
}
Hmmm……
A question about UILocalNotification and the notificaton's alertLaunchImage.
My app uses UILocalNotifiaction(s) to get users' attention. As usual, an alert is presented with "Action" and "Close" buttons. When the user taps Action, the image specified by alertLaunchImage is presented. The alertLaunchImage is a screenshot of of one of the views of the app which is shown after the data is initialized when launched normally.
Here are the 3 cases when the notification is delivered:
App is running in foreground - no alert, no launchImage is shown as designed. No problems.
If my app is running in background when the notification is delivered, the launchImage works like a charm. No problems. The launchImage with no app-related data is shown and then the app fills up the data. This part works seamlessly.
However, if the app is not running when the notification is delivered, the sequence is confusing - or I missed something. The app gets launched and shows the alertLaunchImage instead of the Default image. Then is goes thru several other screens (as part of initialization and data processing) before the actual screen (live version of alertLaunchImage) is shown.
This can get very confusing to the user. My question comes in here. How can this be avoided?
R/-
Sam.!
you can try cleaning up the alert view settings in applicationWillTerminate:
According to the UIApplicationDelegate reference applicationWillTerminate::
"This method lets your application know
that it is about to be terminated and
purged from memory entirely. You
should use this method to perform any
final clean-up tasks for your
application, such as freeing shared
resources, saving user data,
invalidating timers, and storing
enough application state to
reconstitute your application’s
interface when it is relaunched"
HTH,
Oded
If your app is launched by a local notification, you will receive that notification in the options passed to -application:didFinishLaunchingWithOptions:. Based on that, you can write code that navigates to the correct screen without animations.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *localNotification = [launchOptions valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotification != nil) {
// startup by local notification
} else {
// normal startup
}
}