iOS Tutorial Silent Push Notifications

This tutorial explains how you can use push notifications that don't show any visual cue to the user and start your app in the background for a limited time (up to 30 seconds).
You can use it for example to download new content.

Make sure to start with standard push notifications: https://www.b4x.com/android/forum/t...ions-push-messages-server-not-required.68645/

1. Add the remote notification background mode:
B4X:
#PlistExtra: <key>UIBackgroundModes</key><array><string>remote-notification</string></array>

2. When a remote notification arrives the Application_RemoteNotification event will be raised. You have up to 30 seconds to do whatever your app needs to and then call CompletionHandler.Complete.

3. Sending code: remove the notification element and add the content_available field:
B4X:
If Topic.StartsWith("ios_") Then
   Dim iosalert As Map =  CreateMap("title": Title, "body": Body, "sound": "default")
   'm.Put("notification", iosalert) 'comment this line
   m.Put("priority", 10)
   m.Put("content_available", True) '<--- add
End If
Note that you can add other fields to 'data' map.

Testing

iOS will not start the app automatically if the user has killed it. This makes it a bit difficult to test that it really works.
In order to test it you need to compile it in Release mode and run it once.
Now install the app again and do not start it. Send a silent push message and the process should start.
The app will not be visible. You can show a local notification to see that it is working.
 
Last edited:

fbritop

Active Member
Licensed User
Longtime User
Anyone know why notifications in background do not work when we use:

B4X:
#PlistExtra:<key>UIBackgroundModes</key><array><string>remote-notification</string></array>
#PlistExtra:<key>UIBackgroundModes</key><array><string>bluetooth-central</string></array>
#PlistExtra:<key>UIBackgroundModes</key><array><string>location</string></array>

If I comment the second and third PlistExtra it works.
 

fbritop

Active Member
Licensed User
Longtime User
To whom it may help. same "keys" shoud go on the array.

So:
B4X:
#PlistExtra:<key>UIBackgroundModes</key><array><string>remote-notification</string></array>
#PlistExtra:<key>UIBackgroundModes</key><array><string>bluetooth-central</string></array>
#PlistExtra:<key>UIBackgroundModes</key><array><string>location</string></array>

Should be:

B4X:
#PlistExtra:<key>UIBackgroundModes</key><array><string>remote-notification</string><string>bluetooth-central</string><string>location</string></array>
 

moster67

Expert
Licensed User
Longtime User
I have been experimenting with silent push notifications and sometimes they work, other times my test-app is not launched (can not see my logs with B4iLogger). I noted that if I previously had "killed" my app, then it would not work. I found this from Apple's docs which seems to confirm my finding:

In addition, if you enabled the remote notifications background mode, the system launches your app (or wakes it from the suspended state) and puts it in the background state when a remote notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.

I don't know about you, but during the day when I am using my iPhone, quite often do I kill various apps. In addition, I rarely switch off my device so it can be restarted. So for me, this behavior would be a problem...

Testing 'normal push notifications', everything seems to work just fine without this problem.

Let's say that I need to launch my app on my users' devices every 30 minutes so they can download some data, what other method/work-around could be implemented to overcome the shortcoming mentioned above and ensure regular communication between my server and the app? Could the 'Background fetch' feature somehow 're-trigger' the app (after a user-kill) and make silent push notifications start working again?
 
Last edited:

moster67

Expert
Licensed User
Longtime User
Thanks Erel. It seems like in this case I must indeed implement something on the server-side as suggested in the SO-thread and ask the user to open the App again with a non-silent push notification.

In this regard, in your above tutorial for silent push notifications, you said to remove 'CheckForPushMessage' in Application_Start. If I now in my code must also enable normal push notifications, should I call it again? What I am asking is actually if my app can handle both normal push notifications and silent push notifications and if I must handle/write my code in a particular way to make sure that both will work at the same time. Any suggestions?
 
Last edited:

moster67

Expert
Licensed User
Longtime User
I am still having some problems. I am still only testing silent push notifications and I would have expected to see a log B4iLogger when sending a silent push notification AFTER the device has been restarted.

Can someone else please check if silent push notifications are being received after your have restarted your iOS device? According to documentation it should work. Thanks.
 

moster67

Expert
Licensed User
Longtime User
Started testing Silent Push Notifications again, after a pause, but this time it is not working at all. Really weird.

I had a look at the EDIT (the link you added) and followed your updated instructions. Deleted the main.m file in the Special folder and downloaded the main.m file in the first post and put it in my b4i project folder (c:\Anywhere Software\B4i\Project). Modified my server as you wrote in the first post and so forth...

I am testing this on my device, and not on the simulator. I have built the app in release mode using both my local MAC and the hosted compiler but no luck.

The Application_RemoteNotification sub/event is not triggering - I checked this using a local notification (as in your example) but also by monitoring the logs using iReleaseLogger.

I am wondering why you commented the following line in your example-code?
B4X:
'#ProvisionFile: Push.mobileprovision
If I comment this line in my code, I get an error.

Of course, maybe the Apple-servers could be down but I have been trying this at different times during the day so I don't think so since Apple would surely ensure their servers would be up and running in a short time. Just to be sure I made a telnet connection from my server:
B4X:
telnet gateway.sandbox.push.apple.com 2195
and it connected fine....

I am really getting lost here. Maybe the changes Apple did to Xcode 7 when it builds the project have broken something? Is it working for others?
 
Last edited:

moster67

Expert
Licensed User
Longtime User
I got it sorted. I had updated my provision file but I was still using the older keystore on my server:mad:
With iOS, there are many things to consider - much more than Android. Oh well, I guess by trial and error one will eventually learn!
 

marcick

Well-Known Member
Licensed User
Longtime User
I'm trying to play a sound after receiving a silent push notification, but it doesn't work

B4X:
Main.MyPlayer1.Initialize(File.DirAssets, fn & ".wav","MyPlayEnd")
    Main.MyPlayer1.Looping=false
    Main.MyPlayer1.Play

I can see the log, I can show a local notification, but no sound. The sound i splyed only if receiving the notification while the app is active

Is there any limitation in the kind of code that can be executed after triggering the notification event ?
 

marcick

Well-Known Member
Licensed User
Longtime User
This is the code, tested in RELEASE on a Iphone6S

B4X:
Private Sub Application_RemoteNotification (Message As Map)
       Log("Remote notification")
       Dim m As Map = Message.Get("aps")
       Log(m.Get("alert"))
    Dim ln As Notification
       ln.Initialize(DateTime.Now + 1 * DateTime.TicksPerSecond)
       ln.AlertBody = "This is a local notification"
       ln.Register
    MyPlayer1.Initialize(File.DirAssets, "bubo3.wav","MyPlayEnd")
    MyPlayer1.Looping=False
    MyPlayer1.Play       
End Sub
Sub MyPlayEnd_Complete
    Dim no As NativeObject = App
    no = no.GetField("delegate")
    no.RunMethod("complete:", Array(0))
End Sub

When I send a SILENT PUSH NOTIFICATION I have two behaviour:

1) App Foreground: Event fired, I have the log, I have the local notification, I hear the sound "bubo3"
2) App background: Event fired, I have the log, I have the local notification, but no sound "bubo3"

In the second case, I see this also in the log with grey color:

didReceiveRemoteNotification fetchCompletionHandler
Warning: Application delegate received call to -application:didReceiveRemoteNotification:fetchCompletionHandler: but the completion handler was never called.
Application_Remotenotification

Besides this Mplayer problem, I have tried to add other code to the event above (a RDC connection that uses a background task for example) that seems not to work properly.
 

netkomm

Active Member
Licensed User
Longtime User
Hi Erel,

this example works - however there is one condition where it does not.
I am building an app to log all the notifications received:

- when the app is open then a local notification is triggered and the notification logged (and it's ok);
- when the app is in background mode the notification is triggered and IF I click the notification then the notification data is passed to the app. (and it's ok)

However IF I receive a notification when the app is in background mode and I open directly the app instead, there is no notification data sent to the app UNTIL I go and click on the notification in the notification centre.

I do understand that I should be able to "execute" a background job (up to 30 sec. max) but it does not seem to work like that.

I used the source provided and placed the main.m file in the "Special" folder.

B4X:
Private Sub Application_RemoteNotification (Message As Map)
   Log("Remote notification: " & Message)
   Dim m As Map = Message.Get("aps")
   Log(m)
   Log(m.Get("alert"))
   Msgbox("remote: " & Message, "")
   Dim ln As Notification
   ln.Initialize(DateTime.Now + 2 * DateTime.TicksPerSecond)
   ln.AlertBody = "This is a local notification"
   ln.Register
   Log ("message received")
   Dim no As NativeObject = App
   no = no.GetField("delegate")
   no.RunMethod("complete:", Array(0))
End Sub
 
Top