Notifications! How can I send data with my notifications and receive it in Lua within my Corona App?

Hi Rob, you wrote: “On iOS, when the app starts or comes back into the foreground, any notifications you have received will be cleared.”

  1. Am I to assume that my App will receive the notification events (via the listener code) once the App is loaded (if it wasn’t previously loaded)?
  2. Will this clearing affect all REMOTE and LOCAL notifications or just REMOTE?

You also wrote: “Any pending local notifications as long as you have not exited your app can still be canceled with the cancel function. The only thing where it becomes a problem is if you completely exit your app you loose the handles to the notifications and you can’t do much about them.”
3) I have noticed that even if I save the ID for a local notification, the next time the app is loaded Corona is unable to connect the saved local notification ID to the currently still-standing local notifications - so cancelling them later is impossible. Will a future Corona build resolve this?
4) For clarification, if I set a local notification and for any reason a user leaves the app, those local notifications will remain in place and there is no way for my app to turn them off? (This will undoubtedly frustrate any user - making local notifications essentially unusable in Corona). Do I understand this correctly?

I am about to test the new PushWoosh custom and report back my findings.

I have some GOOD and some BAD news - I’ll start with the good news. When the App is running, it gets the notification as you suggested. The one line of Lua code “print( json.prettify( event ) )” in the notification listener worked and shows the results:
{ “alert”:“This is a test message.”,  “applicationState”:“active”,  “custom”:{ “key1”:“test1”, “pw”:{ “p”:“4” },“key2”:“test2” }, “name”:“notification”, “sound”:“default”, “type”:“remote” }

The bad news is that when the App is NOT running, and I receive the notificiation, whether I click on the notification to bring up my App or load the App later, it doesen’t matter - in both cases, once the App loads up, the notification listener never receives the notification data. In other words, the ONLY way that I can “receive data” from a notification in a Corona App is if the App is actually running at the time the Push is sent.

If this cannot be resolved, then there is no way to send virtual goods to a gamer (i.e. holiday event, birthday, etc…) like King (makers of Candy Crush) - the need for a reliable notification is a vitally important function of any competitive App or platform (i.e. Corona). Are there any other things we can try to make it work? The only solution presently around this would be for me to use notification for the sole purpose of displaying a message to the user.

It’s also worth mentioning that because Apple (iOS) doesn’t allow us to keep the UDID anymore, every fresh install of our Apps shows up as another Device in the Push Woosh control panel. This is likely true for any notification service for iOS Apps.

But there is also another problem. Even after loading up my App, which no longer pushes any LOCAL notifications, the original notification shows up as (2) on my App icon from my iOS home page. So, is there no way to clear all outstanding notifications for our Apps, even if we can’t keep/store and use a notification ID to cancel it at a later date? What’s odd about this is that I completely deleted my App before reinstalling it. And even though PushWoosh recognizes my device as a new device because of the fresh install, it seems that iOS associates any previously outstanding local notifications with my freshly installed App - even though they are not received by my listener code. In other words, my App is stuck with a (2) on my App icon forever in such cases.

So, is there no way to clear all outstanding notifications for our Apps, even if we can’t keep/store and use a notification ID to cancel it at a later date?

Troy, you have a lot of questions and this thread is being difficult to follow and answer. Let me try to respond to all of these.

If your app is active, that is it’s in the foreground. You will get a notification event and your notification event listener will trigger.

If your app is backgrounded or completely stopped, the notification will come into the OS’s Notification system. You have to interact with that notification and your app will either start up from scratch or it will be foregrounded from the background. Depending on which state your app was in, you will get the notification in a couple of different ways. If you were backgrounded, you should get a notification event just like if you were active when the notification came in. If you’re code starting, you will get the push via launchArgs. There won’t be an event in this case.

UDID? There is a plugin OpenUDID that should take care of this for you.

The red 2 is called a badge. It’s actually not tied to any pending notifications. The sending push can request the badge number to be increased. There is an API Call native.setProperty() https://docs.coronalabs.com/api/library/native/setProperty.html that you can use to control the value of the badge.

Rob

I’m going to have to go to the pushNotifications sample app to get it for you or Google “corona launchArgs” to find tutorials for this. Give me a bit, I’m trying to finish up other stuff.

local launchArgs = ... print("### --- Launch Arguments ---") print( json.prettify( launchArgs ) )

The launchArgs table will contain the same values the event table does in a notification event.

I get an error using the term “…”

Can you post your code? 

Got it to work. The docs have bits and pieces here and there. The Corona community could benefit from a step-by-step tutorial on how to use all of this. But a big shout-out and thanks to you, Rob, for sticking with this issue until it was resolved.

Hi Rob,

We still have one outstanding issue about how to receive notifications. Of course, there are three conditions in which a notification should be detected.

  1. The App is running and a notification is received.

SOLUTION: Custom data is passed from iOS through to Lua’s notification ‘custom’ property in JSON format.

  1. When the App is NOT running, but a user CLICKS on a notification and the App launches.
    SOLUTION: The Custom property is also set, but through different code entirely - into LaunchArgs.notifications.custom by simply putting “LaunchArgs = …” in our main.lua file.

  2. However, if the App is NOT running, and the user DOES NOT CLICK on a notification, and instead they independently open the App:
    PROBLEM - NO NOTIFICATION IS RECEIVED BY EITHER OF THESE METHODS. Am I missing something here? Or is this expected to be normal???

#3 this is the expected behavior. If you start the app by tapping on it’s icon from the home screen instead of interacting with the notification, your notifications will be lost. Your app will not receive them.  I’m not 100% sure that on Android, they will be cleared, but on iOS they will be cleared and not passed. This is true of every app on iOS.

Rob

Hi Rob,

What this tells me is that notifications cannot be used to send virtual items to a gamer - in other words, the OS (Android or iOS) could entirely bypass our App’s ability to get a sent notification and therefore, notifications are just that, notifications - they cannot be used to send any benefit or virtual item to a gamer. That must be handled by our game server. Thank you for your help on this. I would have gone down the wrong path using notifications as something more than a message.

But this problem is actually deeper.

Let’s suppose on Christmas, you want your gamers to get 100 Gold Bars. So, you send a notification. For those who never click on the notification, do you want them to miss out? Of course not. For this reason, the game code must communicate with it’s server to know which gamers are receiving their gold bars and what day it is - in this way, they get the Gold Bars whether they click on the notification or not.

However, let’s say that after they get their gold bars this way, they later click on the notification. The code must check with the game server to make sure the gamer had not yet received the gold bars or at least inform them that they have already received them.

This is a perfect example of how handling proper virtual items requires a game server and why notifications are completely unreliable for delivering them. Instead, they can be used to ‘notify’ the gamer. But in such cases, the developer will still need to see if they have already received the benefit mentioned in the notification.

That’s a long explanation, but hopefully it is clear enough for someone to understand and benefit from my discoveries here.

To implement notifications, you will need to setup your build.settings in accordance with the Corona API docs.

The following then assumes you have acquired a subscription to Push Woosh (www.pushwoosh.com). The basic is FREE, but at the time this forum post was written, we had a $49 per month for unlimited subscriptions for a certain number of apps.

To implement specifically with Push Woosh, here is what you do:

Step 1) In your main.lua file, put:

local launchArgs = ... --======================================================================================= -- Pushwoosh notifications functions --======================================================================================= native.setProperty( "applicationIconBadgeNumber", 0 ) -- clear number on icon pushwoosh = require("pushwoosh") pushwoosh.setup(launchArgs) --this will catch any notifications that may have been pressed by the user causing the App to launch

Step 2) Wherever in your code that your App user indicates that he/she will accept your notifications, or if you just want the system to ask him/her and set the OS flag accordingly, you will place this code:

pushwoosh.setup() -- this will only run once, assuming that you keep a boolean flag not to run this again once the user has already given you permission

Step 3) Save the following code to pushwoosh.lua:

--================================================================================================ -- pushwoosh.lua --================================================================================================ local pushwoosh = {} local pw\_app\_code = {} local TAG = "[Pushwoosh] " local function sendRequest( method, args, success, fail ) local PW\_URL = "https://cp.pushwoosh.com/json/1.3/" .. method local function networkListener( event ) if ( event.isError ) then if ( error ~= nil ) then error( event ) end print( TAG .. PW\_URL .. " request failed: " .. json.encode(event) ) else if ( success ~= nil ) then success( event ) end print ( TAG .. PW\_URL .. " Response: " .. json.encode(event.response) ) end end local jsonvar = {} jsonvar = json.encode(args) local post = jsonvar local headers = {} headers["Content-Type"] = "application/json" headers["Accept-Language"] = "en-US" local params = {} params.headers = headers params.body = post print( TAG .. "Sending request " .. jsonvar .. " to " .. PW\_URL ) network.request ( PW\_URL, "POST", networkListener, params ) end local function registerDevice( pushToken, app\_code ) local deviceType = 1 -- default to iOS if ( system.getInfo("platformName") == "Android" ) then deviceType = 3 end local commands\_json = { ["request"] = { ["application"] = app\_code, ["push\_token"] = pushToken, ["language"] = system.getPreference("ui", "language"), ["hwid"] = system.getInfo("deviceID"), ["timezone"] = 3600, -- offset in seconds ["device\_type"] = deviceType } } local function onSuccess( event ) local registrationEvent = { name="pushwoosh-registration-success" } Runtime:dispatchEvent( registrationEvent ) end local function onError( event ) local registrationEvent = { name="pushwoosh-registration-fail" } Runtime:dispatchEvent( registrationEvent ) end sendRequest( "registerDevice", commands\_json, onSuccess, onError) end local function sendAppOpen( app\_code ) local commands\_json = { ["request"] = { ["application"] = app\_code, ["hwid"] = system.getInfo("deviceID") } } sendRequest( "applicationOpen", commands\_json, nil, nil) end local function sendPushStat( app\_code, hash ) local commands\_json = { ["request"] = { ["application"] = app\_code, ["hwid"] = system.getInfo("deviceID"), ["hash"] = hash } } sendRequest( "pushStat", commands\_json, nil, nil) end local function sendDeliveryMessage( app\_code, hash ) local commands\_json = { ["request"] = { ["application"] = app\_code, ["hwid"] = system.getInfo("deviceID"), ["hash"] = hash } } sendRequest( "messageDeliveryEvent", commands\_json, nil, nil) end local function sendStat( event ) local hash = nil if ( system.getInfo("platformName") == "iPhone OS" ) then hash = event.custom.pw.p elseif ( system.getInfo("platformName") == "Android" ) then hash = event.androidGcmBundle.p end if ( hash ~= nil ) then -- We cannot track message delivery until user opens it -- But if it is opened it is definitely delivered sendDeliveryMessage ( pw\_app\_code, hash ) sendPushStat( pw\_app\_code, hash ) else print( TAG .. "Error! Missing hash in push payload" ) end end local function onNotification( event ) print(json.prettify(event)) print( TAG .. "onNotification: " .. json.encode(event) ) if event.type == "remoteRegistration" then registerDevice( event.token, pw\_app\_code ) elseif ( event.type == "remote" ) then -- filter out GCM service notification if ( event.androidGcmBundle ~= nil and event.androidGcmBundle.from == "google.com/iid" ) then print( TAG .. "Warning! GCM registration token may be invalid. Try reregister with GCM." ) else sendStat(event) local notificationEvent = { name="pushwoosh-notification", data=event } Runtime:dispatchEvent( notificationEvent ) end end end --================================================================================================ -- public methods -- --================================================================================================ function pushwoosh.registerForPushNotifications( app\_code, launchArgs ) pw\_app\_code = app\_code sendAppOpen( app\_code ) if launchArgs and launchArgs.notification then print( TAG .. "Application was launched from a cold start in response to push notification" ) onNotification( launchArgs.notification ) end Runtime:addEventListener( "notification", onNotification ) -- For iOS, the app must explicitly register for push notifications if ( system.getInfo("platformName") == "iPhone OS" ) then local notifications = require( "plugin.notifications" ) notifications.registerForPushNotifications() end end --================================================================================================ function pushwoosh.setup(launchArgs) --------------------------------------- -- On notification --------------------------------------- local function onNotification( event ) print(json.prettify(event)) native.showAlert( "Notification:", event.data.alert or "", { "OK" } ) -- reset badge number native.setProperty( "applicationIconBadgeNumber", 0 ) end --------------------------------------- -- On registration success/failed --------------------------------------- local function onRegistrationSuccess( event ) print( "Registered on Pushwoosh" ) end local function onRegistrationFail( event ) native.showAlert( "Notification Registration Failed", "An Error Contacting the Server has Occurred. Please try again later from the application settings.", {"OK"}) end if booleanFlag and booleanFlag==1 then Runtime:addEventListener( "pushwoosh-notification", onNotification ) Runtime:addEventListener( "pushwoosh-registration-success", onRegistrationSuccess ) Runtime:addEventListener( "pushwoosh-registration-fail", onRegistrationFail ) pushwoosh.registerForPushNotifications( "xxxxx-xxxxx", launchArgs ) end end --================================================================================================ return pushwoosh

Step 4) Make sure that the booleanFlag variable (whatever you called it) discussed in the ‘comment within the code’ in Step 2 above is properly set and referenced properly in pushwoosh.lua line 189 above:

if booleanFlag and booleanFlag==1 then

Step 5) Be sure to insert your pushwoosh ID into the line just 4 lines from the end of pushwoosh.lua line 193 above:

pushwoosh.registerForPushNotifications( "xxxxx-xxxxx", launchArgs )

That’s it! I hope it helps you.

You are exactly right. Push notifications were never designed to pass game play data, regardless of the time (your gold bars). I wish it wasn’t this way, but it’s how Apple and Google implemented this. It’s your responsibility to have your app talk to your online service to manage actually communications. I understand why Apple and Google did it, but for inter-game communications you do have to check with your server. Almost think of the notification as an alert telling you to check with your server and that interacting with a notification is a convenient way to start your app for you (instead of the user having to find the icon).

Rob

Wow! Can someone, anyone, please help me send data through notifications in a manner in which my Lua code can use it? See above.

I’m going to move this to the OneSignal forum. Perhaps someone that frequents that forum can answer you.

Rob, I didn’t put this into OneSignal because my question has more to do with Push-Woosh. However, I don’t care what plantform I use. I’m willing to use ANY PLATFORM that can provide cross-platform support and allow me to pass data from a notification into a Lua call-back. It’s that simple a request. Thus far, I can’t find ANY CORONA SUPPORT for such a simple need. - only basic notifications. Please help.

I am using app42 (shephertz.com) for my push notifications and although I have no need to send custom data with my notifications, according to their docs you can attach metadata to your push notifications. They support Corona on most of their APIs.

Hope this helps!

I spoke too soon. After digging into their docs a little further, I see where the “custom data” function for push notifications in Corona is still under development. However, there are examples in their forum where users have requested a feature and they have produced so it still might be worth your while to check with them. They might be able to assist.

Corona supports this. Your push needs to contain a table called “custom”. This is a table inside the main notification event table.

Thank you. I suppose its just too much to ask for any of these providers to do, at the very least, provide step-by-step instructions. I’m kicking off a project on upWork to do this for ANY push notification service in support of Corona. I’m just shocked that no one can provide me step-by-step instructions in Corona for ANY notification service to do something that virtually any successful title requires. It’s not like I’m a beginning programmer - I’m a 25 year game industry vet.