@dpad85 recently discovered a bug that affect's Phoenix's ability to receive payments via the background process (on iOS).
Steps to reproduce:
Using iOS 15 or 16 (not 17+)
Open the Phoenix app, create an invoice, and then force-quit the app
Pay the invoice with another wallet
Note that the payment succeeds
Open the Phoenix app again, create another invoice, and then force-quit the app
Attempt to pay the invoice with another wallet
Note that this time the payment fails immediately, and a Phoenix notification appears ("Missed incoming payment")
Analysis
On iOS we are forced to use a notification-service-extension to handle incoming payments when the Phoenix app is in the background, or not running. (See #280 for details.) Since this is a completely separate process, there are various edge cases we have to protect against.
One of the edge cases is that the server only allows a single client to be connected at-a-time (for the same wallet). Which means:
if the background process is launched when the foreground process is active, then the bg process should yield to the fg process.
if the foreground process is launched while the background process is in the middle of receiving a payment, then the fg process should wait to connect until the bg process completes
As part of the architecture to implement these safety measures, we have a simple IPC channel where each process can send flags to the other (such as ping, pong, or offline).
The simple fix
On iOS 17, when you force-quit the app, the SceneDelegate.sceneDidEnterBackground() method is called. However, this does NOT occur on iOS 15 or 16. And because of this, the offline flag was not sent via the IPC channel. So what was happening:
Phoenix app is launched, and a ping_from_mainApp message is sent on the IPC channel
Phoenix app is force-closed (and no message is sent on IPC channel)
Background app is launched to handle incoming payment
Background app ignores existing IPC channel message, and sends ping_from_bgApp
Background app finishes receiving payment, but iOS does NOT kill the process. It keeps the process running for a period of time in case another notification arrives. (Unless there is memory pressure, or a timeout occurs.)
Phoenix app is launched again, and sends a ping_from_mainApp message on the IPC channel
Phoenix app is force-killed
Background app (which is still running) is invoked again to process the new incoming notification
The background app sees the new ping_from_mainApp and interprets this to mean that the main app is running, and thus it immediately yields (stops).
But since the interpretation was wrong, the payment is missed, and the corresponding notification is displayed.
So the simple fix is that the main Phoenix app should send the offline_from_mainApp when force-quit. I've implemented this change in the PR.
The harder fix
Even with the simple fix, there's still the edge-case where the main Phoenix app crashes. In which case it won't send the offline_from_mainApp message. So the background app still needs to be smarter about how it handles IPC messages. Especially since iOS may keep the background process open.
So now, when the background process isn't actively processing a notification, it closes the IPC channel. And everytime it opens a new IPC channel, it always ignores any existing message on the channel.
(ios) Backgroud receive bug
@dpad85 recently discovered a bug that affect's Phoenix's ability to receive payments via the background process (on iOS).
Steps to reproduce:
Analysis
On iOS we are forced to use a notification-service-extension to handle incoming payments when the Phoenix app is in the background, or not running. (See #280 for details.) Since this is a completely separate process, there are various edge cases we have to protect against.
One of the edge cases is that the server only allows a single client to be connected at-a-time (for the same wallet). Which means:
As part of the architecture to implement these safety measures, we have a simple IPC channel where each process can send flags to the other (such as
ping
,pong
, oroffline
).The simple fix
On iOS 17, when you force-quit the app, the
SceneDelegate.sceneDidEnterBackground()
method is called. However, this does NOT occur on iOS 15 or 16. And because of this, theoffline
flag was not sent via the IPC channel. So what was happening:ping_from_mainApp
message is sent on the IPC channelping_from_bgApp
ping_from_mainApp
message on the IPC channelping_from_mainApp
and interprets this to mean that the main app is running, and thus it immediately yields (stops).So the simple fix is that the main Phoenix app should send the
offline_from_mainApp
when force-quit. I've implemented this change in the PR.The harder fix
Even with the simple fix, there's still the edge-case where the main Phoenix app crashes. In which case it won't send the
offline_from_mainApp
message. So the background app still needs to be smarter about how it handles IPC messages. Especially since iOS may keep the background process open.So now, when the background process isn't actively processing a notification, it closes the IPC channel. And everytime it opens a new IPC channel, it always ignores any existing message on the channel.
This change has also been implemented in the PR.