iOS 13 Killing app because it never posted an incoming call to the system after receiving a PushKit VoIP callback - apple-push-notifications

After upgrading to iOS beta 13 I've noticed an unpleasant thing: my app crashes sometimes on incoming VoIP pushes.
In the crash report I see the following:
iOS 13 Killing app because it never posted an incoming call to the system after receiving a PushKit VoIP callback
Fatal Exception: NSInternalInconsistencyException
0 CoreFoundation 0x1af21b9f0 __exceptionPreprocess
1 libobjc.A.dylib 0x1af7284fc objc_exception_throw
2 CoreFoundation 0x1af11efec +
[_CFXNotificationTokenRegistration keyCallbacks]
3 Foundation 0x1aeda1330 -[NSAssertionHandler
handleFailureInMethod:object:file:lineNumber:description:]
4 PushKit 0x19caa6b54 -[PKPushRegistry
_terminateAppIfThereAreUnhandledVoIPPushes]
5 libdispatch.dylib 0x1afa441ec _dispatch_client_callout
6 libdispatch.dylib 0x1af9f6c6c
_dispatch_lane_barrier_sync_invoke_and_complete
7 PushKit 0x19caa5b74 __73-[PKPushRegistry
voipPayloadReceived:mustPostCall:withCompletionHandler:]_block_invoke
8 libdispatch.dylib 0x1afa43678
_dispatch_call_block_and_release
9 libdispatch.dylib 0x1afa441ec
_dispatch_client_callout
10 libdispatch.dylib 0x1af9f61f8
_dispatch_main_queue_callback_4CF$VARIANT$mp
11 CoreFoundation 0x1af1992a0
CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
12 CoreFoundation 0x1af1942a8 __CFRunLoopRun
13 CoreFoundation 0x1af1937ac CFRunLoopRunSpecific
14 GraphicsServices 0x1ae395180 GSEventRunModal
15 UIKitCore 0x1b6e60244 UIApplicationMain
16 VOIPProject 0x1009822d8 main + 25
(AppDelegate.swift:25)
17 libdyld.dylib 0x1af6e9e7c start
I can not understand how to fix the problem. Am I obliged to post CallKit incoming call screen whenever I receive a VoIP push? It sounds crazy because I check if the notification is valid before showing the screen with incoming call. Can anybody explain what should I do?

On this thread from apple forums, someone from apple staff explained this:
On iOS 13.0 and later, incoming Voice over IP calls must be reported
when they are received and before the didReceiceIncomingPush() method
finishes execution, using the CallKit framework, or the system will
terminate your app.
Repeatedly failing to report calls may prevent
your app from receiving any more incoming call notifications.
Basically, you can no longer use VoIP pushes for non VoIP messaging,
and will need to use regular push notifications for those.
This was
announced during the WWDC session "Advances in App Background
Execution" https://developer.apple.com/videos/play/wwdc2019/707/
I've been searching for answers on how to adapt an app for this change, and what I could gather is the following:
Voip Pushes
When your app receive this kind of push, it will need to report a new incoming call using CallKit. Therefore, this kind of push will be exclusive for calls that use CallKit.
It's recommended that you set the notification's apns-expiration to 0, so you won't receive a push and be forced to present a call screen for a call that already expired.
Push Notifications
Regular push notifications are another option. If your server has all the information you need to write the notification text, you can send notifications that won't even run your app in the background. If you need to modify the content of the notification before presenting it to the user, you can use a Notification Service app extension, and if you need your app to be woken up and execute something in background, you can send silent push notifications.
Notification Service App Extension
To use this, you must set your notification's mutable-content to 1. This way, your extension will receive the notification before it is presented to the user, allowing you to change its content, with a 30 seconds time limit.
The cons are that your app will stay in the background, only your extension will be allowed to run. This might mean that you will need to share information and code between your app and the extension, either by using user defaults, keychain, or by sharing your entire database (which might not be a simple task if your app is not prepared for that).
Silent Push Notifications
To send silent push notifications, you must set your notification's content-available to 1 and remove it's alert, badge and sound. This notification will wake up your app in the background, and call your app delegate's didReceiveRemoteNotification.
The downsides are quite annoying for this option:
You will only get 30 seconds to run.
These notifications must have a apns-priority of 5, which might cause them to be grouped and delivered in bursts, and even throttled or not delivered.
If the user force closes the app, it will completely ignore all silent notifications until the user opens the app again.

I've faced same crash when tried using async version of didReceiveIncomingPushWith. The code I've used:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) async {
guard type == .voIP else { return }
let callUpdate = CXCallUpdate()
let phoneNumber = CXHandle(type: .phoneNumber, value: "handle")
callUpdate.remoteHandle = phoneNumber
try? await callProvider.reportNewIncomingCall(with: UUID(), update: callUpdate)
}
It works fine when the app is in foreground, but is crashing in background. Logs show that for some reason it's not getting called at all.
At the same time completion version works fine:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void) {
guard type == .voIP else { return }
let callUpdate = CXCallUpdate()
let phoneNumber = CXHandle(type: .phoneNumber, value: "handle")
callUpdate.remoteHandle = phoneNumber
callProvider.reportNewIncomingCall(with: UUID(), update: callUpdate) { error in
completion()
}
}

Related

Need to store handled exceptions globally without calling the save log method inside Every catch in xamarin.forms

try
{
int c = 0;
int a = 5 / c;
}
catch (Exception ex)
{
}
Need to write Handled Exceptions globally in one place and without calling the errorlog method in each and every catch block in xamarin.forms and expect to get details in mainactivity for android and appdelegate for ios or any comman place in xamarin.forms
In your case you could use App Center . App Center Crashes will automatically generate a crash log every time your app crashes. The log is first written to the device's storage and when the user starts the app again, the crash report will be sent to App Center. Collecting crashes works for both beta and live apps, i.e. those submitted to the App Store. Crash logs contain valuable information for you to help fix the crash.
App Center Crashes provides you with an API to generate a test crash for easy testing of the SDK. This API checks for debug vs release configurations. So you can only use it when debugging as it won't work for release apps.
Crashes.GenerateTestCrash();
If your app crashed previously, you can get details about the last crash.
ErrorReport crashReport = await Crashes.GetLastSessionCrashReportAsync();
Set this callback if you want to decide if a particular crash needs to be processed or not. For example, there could be a system level crash that you'd want to ignore and that you don't want to send to App Center.
Crashes.ShouldProcessErrorReport = (ErrorReport report) =>
{
// Check the report in here and return true or false depending on the ErrorReport.
return true;
};
For more details about App Center you could check Get Started with Xamarin .

APP launch error: Service could not initialize: 18E226: xpcproxy + 11291

I've built an App in Xcode. I've signed it with a valid Developer ID certificate. Notarized and released the app via Archive function in XCode. And the app launches fine on some MacBooks.
But on another launch fails with error
The application %name% can't be opened.
I've checked the system.log and it says
Jul 15 09:59:40 com.apple.xpc.launchd1 (com.apple.xpc.launchd.oneshot.0x10000ad8.Gaetano Lunches[59755]): Service could not initialize: 18E226: xpcproxy + 11291 [1534][1571C9B2-C489-3268-9D6B-73137AC77303]: 0x1
Jul 15 09:59:40 com.apple.xpc.launchd1 (com.apple.xpc.launchd.oneshot.0x10000ad8. Gaetano Lunches[59755]): Service exited with abnormal code: 78
The app is very simple. It is just a Notification center widget (APP Type XPC!). It receives data from remote URL and displays it via NCWidgetListViewController.
The source code could be found on github
As I mentioned the problem exists only on some machines. The rate is about 50/50. On all machines app installation from Anywhere is allowed. spctl-master is disabled. Permissions for files are correct.

Couchbase Lite iOS Replication stops replicating after restart

I'm still a bit new to Couchbase and iOS, but I'm running into a problem restarting my replications that I'm having trouble with. Here are a few notes about the flow.
The backend is using custom authentication.
When the user logs in, a new session is created in the sync gateway and those session details are returned to the iOS device. The app then uses those credentials to set up a push and pull replication (I've dropped the push replication for now while trying to debug this). The options on the replications aren't much and are as follows:
let pull = self.database.createPullReplication(self.remoteDBURL);
pull.continuous = true;
pull.headers["uuid"] = "device-1";
pull.setCookieNamed(sessionName, withValue: sessionId, path: "/", expirationDate: cookieExpirationDate, secure: false);
pull.start();
self._pull = pull;
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DataService.replicationChanged(_:)), name: kCBLReplicationChangeNotification, object: self._pull);
This works great and all the proper documents are synced to the device. Currently I have the backend created cookies that only last for about 5 minutes so I can test the refreshing of cookies. So, during the first few minutes, any docs I add to the channel that the app gets, the app receives the doc and all is good.
About halfway into the life of the token, the backend is set up to return with a 401 error telling the app to use it's token to get a new token. So, I have this in the replication change listener:
#objc public func replicationChanged(n: NSNotification) {
let replication = n.object as! CBLReplication;
let error = replication.lastError;
if (error != nil) {
print("last error is NOT nil");
print("last error = \(error)");
switch error!.code {
case 401:
self.updateReplicationSession();
default:
break;
}
}
}
Then, the updateReplication function looks like this:
... make http call to getNewToken url using the 'old' or 'soon to be expired' token. *The server is successfully returning this new session.
self._pull.setCookieNamed(newSessionName, withValue: newSessionId, path: "/", expirationDate: newExpirationDate, secure: false);
self._pull.restart();
...
It is at this time that the syncing stops working. No errors are thrown that I can see of other than once I received a CFNetwork Internal error. I can see on the server logs that the replication sends in the new session once... then everything just seems to hang for the replication. Any new docs to a channel that it gets and it doesn't get them. I don't see anything in the Sync Gateway logs indicating what the problem is. However, I'm still pretty new to this... so there may be something. Additionally, I set up a function to run every few seconds and print the status of the replication and it is stuck on ACTIVE.
I'm using sync gateway v 1.3 and CBL ios 1.3. I was using version 1.2.1 and was having this problem... hoping ugrading to 1.3 would magically fix it. It didn't, but I'm not sure I should go back to 1.2.1.
I'm completely stumped on this. I've searched high and low and often seem to find an answer that fits the bill... but it still doesn't work.
I've tried delaying the updating of the session in order for all calls from replication to 'fail' first. I've tried just calling start() on the replication thinking that maybe the 401 killed the replication and restart() isn't going to do anything. I've called stop()... then waited a bit and called start(). Not sure what to do next.
Any help is appreciated guys! Is it possible the local DB on the phone and the sync gateway have a unresolvable rev problem?
EDIT 1
The only way I can currently get it to work is to completely delete the local db in the replication changed function and restart it... then start a new replication... this works... unfortunately though, I then have to broadcast a notification so that any table view that may be up reloads the query. This causes a refresh animation in tableviews and isn't sustainable... but at least I can keep moving for now.
EDIT 2
I found how to enable better logging of the CBL on iOS and here's an error I keep seeing after a token refresh/ replication restart:
CBLWebSocketChangeTracker[0x7ff9bb53b5e0 iphone]: Connection error #8, retrying in 256.0 sec: PSWebSocketError[3, "Output stream end encountered"]
Thoughts?
EDIT 3
I changed things around a bit to get rid of the refreshing of tokens. I attempted to make it so when a user logs in from the iOS app, the backend creates a session with the sync gateway for that user and returns it. The app then starts a replication with that session. Then, after the 5 minutes (the ttl used when creating the session), the next time the app tries to sync, it will get a 401 and stop the replication and present the login screen. Then, when the user logs in again, a new session is created, etc...
I found 2 things:
-Anytime I added a doc to a channel that would sync with the app, when the app synced, the session expiration date would increase by about 20ish seconds. Is this normal behavior? The only way it would log the user out on the expired token is if I didn't add any docs for long enough.
-The restarted replication still gets stuck. Here are the logs for the sync gateway and xcode:
This is the last part of the sync gateway when the session is expired... which will send the 404 to the app.
2016-08-23T21:11:51.089-05:00 Changes+: Sending seq:163 from channel jmoore2
2016-08-23T21:11:51.089-05:00 Changes+: MultiChangesFeed sending &{Seq:163 ID:un:jmoore2_116 Deleted:false Removed:{} Doc:map[] Changes:[map[rev:1-e775ef6713dc39f6d52d35cefb396fe3]] Err:<nil> allRemoved:false branched:false} (to jmoore2)
2016-08-23T21:11:51.089-05:00 Changes: MultiChangesFeed done (to jmoore2)
2016-08-23T21:11:51.089-05:00 HTTP+: #212: --> 200 OK (0.0 ms)
2016-08-23T21:11:51.459-05:00 HTTP: #216: GET /my_gateway/_session/3fa29222db286e8ec67a51e88b613ba4cd5cbf31 (ADMIN)
2016-08-23T21:11:51.459-05:00 HTTP: #216: --> 404 missing (0.2 ms)
2016-08-23T21:11:51.461-05:00 HTTP: #217: GET /my_gateway/_session/3fa29222db286e8ec67a51e88b613ba4cd5cbf31 (ADMIN)
2016-08-23T21:11:51.461-05:00 HTTP: #217: --> 404 missing (0.2 ms)
Then, when the user logs back in, here are SG log for that:
2016-08-23T21:15:41.200-05:00 HTTP: #223: GET /my_gateway/_session/1a6105eaf2c91de320a47422041c655dd3d5c279 (ADMIN)
2016-08-23T21:15:41.201-05:00 HTTP+: #223: --> 200 (0.5 ms)
2016-08-23T21:15:41.203-05:00 HTTP: #224: GET /my_gateway/_local/78c229762074a95c864f7fecc03ce88f0ef6c499 (as jmoore2)
2016-08-23T21:15:41.203-05:00 HTTP+: #224: --> 200 (0.5 ms)
2016-08-23T21:15:41.371-05:00 Cache: Received #164 after 455ms ("user-login-info:jmoore2" / "48-ea0b2d9771fa2be1d76838f9e4d55081")
2016-08-23T21:15:41.371-05:00 Cache: #164 ==> channel "*"
2016-08-23T21:15:41.371-05:00 Changes+: Notifying that “mdatabase” changed (keys="{*}") count=31
And the xcode log when restarting the replication immediately goes to this:
21:15:41.195‖ Sync: CBLRestPuller[http://mycomp.local:8080/iphone/]: Reachability state = <mycomp.local>:reachable (30002), suspended=0
21:15:41.205‖ Sync: CBLRestPuller[http://mycomp.local:8080/iphone/]: Server is Couchbase Sync Gateway/1.3.0
21:15:41.205‖ Sync: CBLRestPuller[http://mycomp.local:8080/iphone/]: Replicating from lastSequence=162
21:15:41.205‖ Sync: CBLRestPuller[http://mycomp.local:8080/iphone/] starting ChangeTracker: mode=3, since=162
21:15:41.207‖ ChangeTracker: CBLWebSocketChangeTracker[0x7f97d8698190 iphone]: Starting...
21:15:41.207‖ Sync: CBLWebSocketChangeTracker[0x7f97d8698190 iphone]: GET //mycomp.local:8080/iphone/_changes?feed=websocket
21:15:41.208‖ ChangeTracker: CBLWebSocketChangeTracker[0x7f97d8698190 iphone]: Started... <http://mycomp.local:8080/iphone/_changes?feed=websocket>
21:15:41.211‖ CBLWebSocketChangeTracker[0x7f97d8698190 iphone]: Connection error #1, retrying in 2.0 sec: PSWebSocketError[3, "Output stream end encountered"]
and also sometimes this error shows up:
2016-08-23 21:01:15.232 MyApp[52094:36001215] 52094: CFNetwork internal error (0xc01a:/BuildRoot/Library/Caches/com.apple.xbs/Sources/CFNetwork_Sim/CFNetwork-758.3.15/Loading/URLConnectionLoader.cpp:289)

Azure Notification Hub iOS template not accepting badge field anymore?

We are registering for template push notifications using Xamarin's Azure Messaging component.
So far we've used the following template to register to notifications:
#"{""aps"": {""alert"": ""$(message)"",""badge"": ""$(badge)"",""sound"": ""$(sound)""}}"
We are passing this string to the Hub.RegisterTemplateAsync in the iOS app.
Since some time ago (less than a month), the call to RegisterTemplateAsync generates the following response:
Apr 3 13:35:03 iPhone MyApp[352] <Warning>: Error Response:<Error><Code>400</Code><Detail>The supplied notification payload is invalid.TrackingId:5dbf4199-cf52-4245-b8e4-acfa4e00cfee_G7,TimeStamp:4/3/2016 10:35:04 AM</Detail></Error>
Apr 3 13:35:03 iPhone MyApp[352] <Warning>: -------------> Notifications: RegisterTemplateAsync error: Fail to perform registration operation. Response:<Error><Code>400</Code><Detail>The supplied notification payload is invalid.TrackingId:5dbf4199-cf52-4245-b8e4-acfa4e00cfee_G7,TimeStamp:4/3/2016 10:35:04 AM</Detail></Error>
It is exactly the same binary which used to work. Now it doesn't.
When troubleshooting the issue, we found out the problem is the badge field. When we remove it, the registration to the notification hub succeeds:
#"{""aps"": {""alert"": ""$(message)"",""sound"": ""$(sound)""}}";
So now we have a broken production app in AppStore that can no longer get notifications.
Is this something that changed on the hub side or is there a chance that we did something wrong?
Have you tried using ""#(badge)"" instead of ""$(badge)""?

Google Analytics on iOS: events not tracked

I added Google Analytics SDK to my project, following the provided instructions on the guide by Google. However, when I try to track some events, this is the debug output:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[…]
// Google Analytics
[GAI sharedInstance].trackUncaughtExceptions = YES;
[GAI sharedInstance].dispatchInterval = 20;
[[[GAI sharedInstance] logger] setLogLevel:(PADEV ? kGAILogLevelVerbose : kGAILogLevelError)];
[[GAI sharedInstance] trackerWithTrackingId:#"UA-XXXXXXXX-Y"]; // This is obviously replaced with my tracking ID
return YES;
}
and
id<GAITracker> tracker = [[GAI sharedInstance] defaultTracker];
[tracker send:[[GAIDictionaryBuilder createEventWithCategory:#"article"
action:#"read"
label:#""
value:self.post[#"id"]] build]];
Log:
2014-11-12 01:02:25.144 PonzApp[5236:112706] INFO: GoogleAnalytics 3.10 -[GAIBatchingDispatcher didSendHits:response:data:error:] (GAIBatchingDispatcher.m:208): Hit(s) dispatched: HTTP status -1
2014-11-12 01:02:25.146 PonzApp[5236:112781] INFO: GoogleAnalytics 3.10 -[GAIBatchingDispatcher deleteHits:] (GAIBatchingDispatcher.m:509): hit(s) Successfully deleted
2014-11-12 01:02:25.147 PonzApp[5236:112781] INFO: GoogleAnalytics 3.10 -[GAIBatchingDispatcher didSendHits:] (GAIBatchingDispatcher.m:219): 1 hit(s) sent
It says that the hits are sent but the HTTP status is weirdly -1 and if I check on the dashboard, section “real-time”, nothing appears.
What could be wrong?
From Google Analytics tracking setup
Once you've correctly installed the tracking code snippet, you should
allow up to 24 hours for data to appear in your account. If it has
been more than 24 hours and you're still not seeing any data, take a
look at the list of possible reasons below:
Once the system has detected your tracker you will start seeing data in the real-time reports. Some of the data may also appear in the standard reports but it is not reliable as it takes 24 hours for data to finish processing.
All and all your code looks fine you just need to wait. The above is the same even though it is a application tracking. I suspect it takes time for Google to set up the new account on there side.
For me the problem was that I was testing it in simulator... :|
If you are using simulator to test it, make sure to press command + shift + H on the simulator to back to home since the data will be sent when it's on background.

Resources