firebase-ios-sdk icon indicating copy to clipboard operation
firebase-ios-sdk copied to clipboard

FR: Update iOS 16 Live Activities using FCM

Open mbuchetics opened this issue 1 year ago • 44 comments

Feature proposal

  • Firebase Component: Cloud Messaging

Apple just added iOS 16 Live Activities to Xcode 14 Beta 4: https://developer.apple.com/documentation/activitykit/displaying-live-data-on-the-lock-screen-with-live-activities

Updating a Live Activity is done using remote push notifications (see "Update or end a Live Activity with a remote push notification" in the documentation). Each active Live Activity gets its own push token and is updated whenever a remote notification is sent to that token with the app-specific payload, e.g.

{
    "aps": {
        "timestamp": 1650998941,
        "event": "update",
        "content-state": {
            "driverName": "Anne Johnson",
            "estimatedDeliveryTime": 1659416400
        }
    }
}

Currently, FCM maps the APNS device token to its own registration token. It would be great to use FCM to also send pushes to the Live Activities of an app. Is there a way to use FCM to send an Apple remote notification to a specific APNS token (instead of using a FCM registration token)?

mbuchetics avatar Jul 27 '22 20:07 mbuchetics

Adding for consideration to the Firebase 10 milestone.

cc: @leojaygoogle

paulb777 avatar Aug 19 '22 22:08 paulb777

Are there any updates on the fix for this?

meherdattaC avatar Sep 28 '22 21:09 meherdattaC

We are still investigating a Live Activities and FCM integration. In the meantime, if you have any feedback or suggestions please feel free to follow up.

If you can share the kind of integration you are considering, that would be helpful for us to ensure that we consider all the different use cases.

aashishpatil-g avatar Oct 17 '22 17:10 aashishpatil-g

@aashishpatil-g we are working on Taxi.eu, a taxi booking app and would like to use Live Activities to show the user the status of the ride (approaching taxi, ...). FCM is used for all push related things and we are sending push notifications for each status change. We would like to keep using FCM for Live Activities as well but would need support to send a notification to a specific push token.

For us, it would be fine to just send the custom APNS push token with the push payload and FCM would send the push notification to that specific token and not the user's general FCM token.

mbuchetics avatar Oct 18 '22 07:10 mbuchetics

Anyone think of a workaround for this?

ericagredo avatar Oct 23 '22 16:10 ericagredo

Would love to see a workaround for this without having to set up our own apns stuff!

bodhihawken avatar Nov 09 '22 12:11 bodhihawken

Thanks for the interest @ericagredo and @Hawki101. We are still investigating this.

If live activity support is important for you, please be sure to give a thumbs up to the original post if you haven't done so already.

aashishpatil-g avatar Nov 09 '22 20:11 aashishpatil-g

I attempted a workaround today generating the fcm token on the server, however, ran into Invalid [apns-push-type] set for apns platform for "apns-push-type": "liveactivity" so wasn't able to use this as a way around!

bodhihawken avatar Nov 10 '22 02:11 bodhihawken

I have a working workaround. It’s using Firebase functions. I’ll post a write up tomorrow for everyone.

ericagredo avatar Nov 10 '22 04:11 ericagredo

Epic! currently working on a direct send via APNS instead! but interested to read through some alternatives!

bodhihawken avatar Nov 10 '22 04:11 bodhihawken

I have a working workaround. It’s using Firebase functions. I’ll post a write up tomorrow for everyone.

To begin, you want to make sure you've gone through the process of enabling push notifications and downloaded the p8 file. https://medium.com/@syumak/how-to-send-push-notifications-to-ios-devices-via-p8-key-using-amazon-pinpoint-f08efb2a6b7 Then, create a firebase function. In our function we're going to be using the http2 and jsonwebtoken library. Here is my code with what you need:

app.post("/startTrigger", async (request, response) => {
         var amount = request.body.amount
         var deviceToken = request.body.deviceToken;
         var privateKey = fs.readFileSync('YOUR_P8_FILE.p8');
        const secondsSinceEpoch = Math.round(Date.now() / 1000)
        var payload = {
            iss: 'YOUR_TEAM_ID',
            iat: secondsSinceEpoch
         }
        var finalEncryptToken = jwt.sign(payload, privateKey, {algorithm: 'ES256', keyid: 'YOUR_KEY_ID'});
        const session = http2.connect('https://api.sandbox.push.apple.com:443/3/device/' + deviceToken);
       const body = {
            "aps": {
            "timestamp": secondsSinceEpoch,
            "event": "update",
            "content-state": {
                 'value': amount
              }
        }
    };
    functions.logger.log("token:", finalEncryptToken);

    var buffer = new Buffer.from(JSON.stringify(body));
  
    const req = session.request({
      ":method": "POST",
      ":path": "/3/device/" + deviceToken,
      "authorization": "bearer " + finalEncryptToken,
      "apns-push-type": "liveactivity",
      "apns-topic": "YOUR_BUNDLE_ID.push-type.liveactivity",
      "Content-Type": 'application/json',
      "Content-Length": buffer.length
    });
    functions.logger.log()
    req.on('response', (headers) => {
      console.log(headers[http2.constants.HTTP2_HEADER_STATUS]);
    });
    let data = '';
    req.setEncoding('utf8');
    req.on('data', (chunk) => data += chunk);
    req.on('end', () => {
      console.log(`The server says: ${data}`);
    });
    req.end(JSON.stringify(body));

    
};

When you create your p8 file, you'll see that you get the file but on the page you downloaded it they'll also give you a KEY ID. That's what you use to fill in for KEY_ID. "YOUR_P8_FILE" is the path to your downloaded p8 file. My p8 file was deployed with my function. You also need your team ID, which you get from the developer page and should be under your name. Device token is the activity push token that your activity produces. You need to send that to the function as a post request. Follow this guide as well to make sure you have the payload just like your content state https://developer.apple.com/documentation/activitykit/update-and-end-your-live-activity-with-remote-push-notifications

ericagredo avatar Nov 10 '22 14:11 ericagredo

I have a working workaround. It’s using Firebase functions. I’ll post a write up tomorrow for everyone.

To begin, you want to make sure you've gone through the process of enabling push notifications and downloaded the p8 file. https://medium.com/@syumak/how-to-send-push-notifications-to-ios-devices-via-p8-key-using-amazon-pinpoint-f08efb2a6b7 Then, create a firebase function. In our function we're going to be using the http2 and jsonwebtoken library. Here is my code with what you need:

app.post("/startTrigger", async (request, response) => {
         var amount = request.body.amount
         var deviceToken = request.body.deviceToken;
         var privateKey = fs.readFileSync('YOUR_P8_FILE.p8');
        const secondsSinceEpoch = Math.round(Date.now() / 1000)
        var payload = {
            iss: 'YOUR_TEAM_ID',
            iat: secondsSinceEpoch
         }
        var finalEncryptToken = jwt.sign(payload, privateKey, {algorithm: 'ES256', keyid: 'YOUR_KEY_ID'});
        const session = http2.connect('https://api.sandbox.push.apple.com:443/3/device/' + deviceToken);
       const body = {
            "aps": {
            "timestamp": secondsSinceEpoch,
            "event": "update",
            "content-state": {
                 'value': amount
              }
        }
    };
    functions.logger.log("token:", finalEncryptToken);

    var buffer = new Buffer.from(JSON.stringify(body));
  
    const req = session.request({
      ":method": "POST",
      ":path": "/3/device/" + deviceToken,
      "authorization": "bearer " + finalEncryptToken,
      "apns-push-type": "liveactivity",
      "apns-topic": "YOUR_BUNDLE_ID.push-type.liveactivity",
      "Content-Type": 'application/json',
      "Content-Length": buffer.length
    });
    functions.logger.log()
    req.on('response', (headers) => {
      console.log(headers[http2.constants.HTTP2_HEADER_STATUS]);
    });
    let data = '';
    req.setEncoding('utf8');
    req.on('data', (chunk) => data += chunk);
    req.on('end', () => {
      console.log(`The server says: ${data}`);
    });
    req.end(JSON.stringify(body));

    
};

When you create your p8 file, you'll see that you get the file but on the page you downloaded it they'll also give you a KEY ID. That's what you use to fill in for KEY_ID. "YOUR_P8_FILE" is the path to your downloaded p8 file. My p8 file was deployed with my function. You also need your team ID, which you get from the developer page and should be under your name. Device token is the activity push token that your activity produces. You need to send that to the function as a post request. Follow this guide as well to make sure you have the payload just like your content state https://developer.apple.com/documentation/activitykit/update-and-end-your-live-activity-with-remote-push-notifications

@ericagredo This is awesome! So you just included your p8 file in the functions folder (e.g. next to index.js) & it will be uploaded & read just like that? I'm not really a Node dev so I don't know if that's how it will work. You also reference api.sandbox.push.apple.com but I'm guessing we should use api.push.apple.com for production? Sorry, I've never read all the APNs docs & don't know how much I can just copy/paste. Thanks again for posting your solution!

twhitt14 avatar Nov 15 '22 06:11 twhitt14

I have a working workaround. It’s using Firebase functions. I’ll post a write up tomorrow for everyone.

To begin, you want to make sure you've gone through the process of enabling push notifications and downloaded the p8 file. https://medium.com/@syumak/how-to-send-push-notifications-to-ios-devices-via-p8-key-using-amazon-pinpoint-f08efb2a6b7 Then, create a firebase function. In our function we're going to be using the http2 and jsonwebtoken library. Here is my code with what you need:

app.post("/startTrigger", async (request, response) => {
         var amount = request.body.amount
         var deviceToken = request.body.deviceToken;
         var privateKey = fs.readFileSync('YOUR_P8_FILE.p8');
        const secondsSinceEpoch = Math.round(Date.now() / 1000)
        var payload = {
            iss: 'YOUR_TEAM_ID',
            iat: secondsSinceEpoch
         }
        var finalEncryptToken = jwt.sign(payload, privateKey, {algorithm: 'ES256', keyid: 'YOUR_KEY_ID'});
        const session = http2.connect('https://api.sandbox.push.apple.com:443/3/device/' + deviceToken);
       const body = {
            "aps": {
            "timestamp": secondsSinceEpoch,
            "event": "update",
            "content-state": {
                 'value': amount
              }
        }
    };
    functions.logger.log("token:", finalEncryptToken);

    var buffer = new Buffer.from(JSON.stringify(body));
  
    const req = session.request({
      ":method": "POST",
      ":path": "/3/device/" + deviceToken,
      "authorization": "bearer " + finalEncryptToken,
      "apns-push-type": "liveactivity",
      "apns-topic": "YOUR_BUNDLE_ID.push-type.liveactivity",
      "Content-Type": 'application/json',
      "Content-Length": buffer.length
    });
    functions.logger.log()
    req.on('response', (headers) => {
      console.log(headers[http2.constants.HTTP2_HEADER_STATUS]);
    });
    let data = '';
    req.setEncoding('utf8');
    req.on('data', (chunk) => data += chunk);
    req.on('end', () => {
      console.log(`The server says: ${data}`);
    });
    req.end(JSON.stringify(body));

    
};

When you create your p8 file, you'll see that you get the file but on the page you downloaded it they'll also give you a KEY ID. That's what you use to fill in for KEY_ID. "YOUR_P8_FILE" is the path to your downloaded p8 file. My p8 file was deployed with my function. You also need your team ID, which you get from the developer page and should be under your name. Device token is the activity push token that your activity produces. You need to send that to the function as a post request. Follow this guide as well to make sure you have the payload just like your content state https://developer.apple.com/documentation/activitykit/update-and-end-your-live-activity-with-remote-push-notifications

@ericagredo This is awesome! So you just included your p8 file in the functions folder (e.g. next to index.js) & it will be uploaded & read just like that? I'm not really a Node dev so I don't know if that's how it will work. You also reference api.sandbox.push.apple.com but I'm guessing we should use api.push.apple.com for production? Sorry, I've never read all the APNs docs & don't know how much I can just copy/paste. Thanks again for posting your solution!

yes for both questions. https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns

ericagredo avatar Nov 16 '22 16:11 ericagredo

Bump for supporting Live Activities via FCM, would really help with a feature we're working on.

@paulb777 👋🏼

inkytibbs avatar Dec 13 '22 16:12 inkytibbs

@inkytibbs Make sure to upvote the OP at the top.

charlotteliang avatar Dec 13 '22 23:12 charlotteliang

Bump for supporting Live Activities via FCM, would really help with a feature we're working on.

@paulb777 👋🏼

@inkytibbs why not just use firebase functions like I showed above? It works perfectly fine.

ericagredo avatar Dec 14 '22 23:12 ericagredo

Bump for supporting Live Activities via FCM, would really help with a feature we're working on. @paulb777 👋🏼

@inkytibbs why not just use firebase functions like I showed above? It works perfectly fine.

We're going to give that solution a shot. Just thought it would be cool for FCM to officially support Live Activities with documentation and support (assuming Live Activities becomes a popular Apple API).

Thanks for sharing this workaround!

inkytibbs avatar Dec 19 '22 22:12 inkytibbs

any news here @aashishpatil-g @paulb777 ?

mbuchetics avatar Feb 22 '23 10:02 mbuchetics

FCM doesnt work with apns type = liveactivity in headers? @ericagredo Is there such a life hack with С# libs? may be you know?

shevzoom avatar Mar 15 '23 07:03 shevzoom

It's really bad that FCM still doesn't have a plan to support live activity, and that we have to change a provider because of this, all other providers support it for months now.

Any update on this that i have missed?

dejanr avatar Apr 25 '23 08:04 dejanr

Any news here @eldhosembabu ?

vscafuto87 avatar Apr 28 '23 10:04 vscafuto87

@here I am writing to inquire about your plans to support the new feature introduced in iOS 16 live activity. As a Firebase user, I am interested in utilising this feature in my app, and I would greatly appreciate it if Firebase could provide support for it. can you please guys give any timeline?

Azeem00786 avatar May 08 '23 07:05 Azeem00786

Sorry. We can't say anything about future plans on this feature. Please keep adding 👍 to help us with prioritization.

paulb777 avatar May 08 '23 14:05 paulb777

there's no progress on that yet?

ambogatyrev avatar May 24 '23 06:05 ambogatyrev

Since Live Activities are expanding on the iOS ecosystem (iOS 17) it would totally make sense to bump up the prioritisation of this feature 😌 Would love to get an update on any plans 🙂

kalmicka avatar Jul 08 '23 20:07 kalmicka

@here I fully understand that implementing new features takes time and effort. I want to express my unwavering trust in Firebase and its exceptional service. While I appreciate the ongoing commitment and value Firebase brings to my app's development, I kindly request any information you can provide regarding a potential timeline for the support of the 'live activity' feature. This will help me plan and align my app's roadmap accordingly. Thank you for your attention and ongoing commitment to improving Firebase. I look forward to any information or updates you can provide regarding the support for the "live activity" feature in iOS 16.

Azeem00786 avatar Jul 10 '23 14:07 Azeem00786

It's now over a year since this post was created. I would really love to see some updates from Firebase about this. Apple is really pushing this feature so it would be nice to have it supported.

rlee1990 avatar Sep 12 '23 19:09 rlee1990

++1

MichaelTendoSsemwanga avatar Oct 19 '23 05:10 MichaelTendoSsemwanga

Please prioritize it!!

usamaeltmsah avatar Nov 06 '23 05:11 usamaeltmsah

Based on previous comments the prioritization (partly) determined by a thumbs-up for the issue, although this has been the most requested feature for a long time, there is not even an update is shared.

To me, it seems like they don't want to lose users by bluntly saying, we don't want to implement or spend time on an iOS only feature because we are Google which makes sense, implementing this only helps iOS because it will open the doors for a cool future. I guess until they come up with something like a temporary widget for Android, this future is not going to be implemented.

I'd love to be wrong, please prove me wrong at any point.

PS: This comment is not about any individual or firebase itself. Merely a reality check for anyone who is waiting and eagerly checking this issue.

eminsr avatar Nov 06 '23 10:11 eminsr