Implement Web Push notifications to mobile, without browser open
by jj
Software Development Journey Overview
Follow this step-by-step software development journey to see real progress updates, challenges overcome, and practical experience.
Progress Updates (4 total)
Update #1: Implement Web Push notifications to mobile, without browser open
In lieu of a native mobile app, I'd like to experiment implementing Web Push, which supposedly can push notifications to remind users to update their progress just from mobile browsers.
Update #2: Able to request permissions and send user subscription to backend
30% completeUsing swPush and PWA, able to store subscription data which should set me up to push notifications to users. Need to figure out sub expiration and web push from Lambda. Also set up an eventbridge to trigger the notifications on a schedule. Simple invoking seems to work from backend to user endpoints, so VAPID key pair seems to work.
Obstacles Faced: How to handle subscription data? Will VAPID keys generated online be compatible with the front end/back end? How can I add the pywebpush library to an AWS lambda? Can I just use SNS to send the notifications instead of requiring the lambda to do everything? (Seems like it could cause large lambda uptime to send all push requests in a loop)
Looking Back: Definitely use both ng web-push and ng PWA; I was confused which one to add but they're both important. The former helps with subscription and the latter with running the service worker.
Update #3: Push being received on phones after some troubleshooting
85% completeI was seeing messages come through subscribing to sw.Push.messages, but no notification was popping up. After too much debugging, it turns out my push payload format was incorrect from the backend. Confirmed this works with app closed!
Challenges Overcome: I wasn't sure why I was seeing sW.message, but not notifications displayed. Could have been VAPID key mismatch, or browser specific issues, or something else. To debug, I went into the Service Worker file (ngsw-worker.js) and logged the trace of getting a push notification. In here, I noticed sW was in fact getting the push. Further into the file, I saw notification being displayed as "notification.title", "notification.body". I was missing an outer "notification" key! This seems to be omitted most resources on the subject.
Looking Back: The correct format for the push notification payload is:
Further into the file, I noticed the expected payload structure is:
{ "notification": {
"title": "title",
"body": "body",
"icon": "icon"
} },
not:
{
"title": "title",
"body": "body",
"icon": "icon"
}
Update #4: Pywebpush added as Lambda layer
100% completeHad an interesting problem where cryptography lib installed on MacOS was not compatible with AWS Linux. But, that's been resolved and notifications are working from within Lambda!
Challenges Overcome: I tried a few different approaches to resolving the cryptography incompatibility issue. Ultimately, building the package and specifying the version in a Docker container was the way to go. I had tried bundling them as two separate layers (pywebpush and cryptography), but Lambda didn't recognize the latter to supersede the default for the former.
Overcame these compatibility errors:
[ERROR] Runtime.ImportModuleError: Unable to import module 'main': /lib64/libc.so.6: version `GLIBC_2.18' not found (required by /var/task/cryptography/hazmat/bindings/_rust.abi3.so)
/opt/python/cryptography/hazmat/bindings/_rust.abi3.so: invalid ELF header {}
Looking Back: This was my first time uploading a layer. It's super easy, just grab /site-packages/ contents, put it in a "python" directory, zip that, and upload it to Lambda console.
This had also been my first time using Docker, which I avoided doing fearing complexity. It was so easy (thanks chatGPT). I'll put the exact commands below.
Additional Notes: Steps to build pywebpush in Docker on Mac compatible with Amazon Linux:
DockFile:
# Start from the Ubuntu base image
FROM ubuntu:20.04
# Avoid prompts from apt
ENV DEBIAN_FRONTEND=noninteractive
# Update and install Python 3.8, pip, and zip
RUN apt-get update && apt-get install -y \
python3.8 \
python3-pip \
zip \
&& rm -rf /var/lib/apt/lists/*
# Upgrade pip
RUN python3.8 -m pip install --upgrade pip
# Create a directory for the Lambda layer
WORKDIR /lambda-layer
# First, install pywebpush normally to get its dependencies
RUN python3.8 -m pip install pywebpush
# Now, install all dependencies of pywebpush, except for cryptography, in the target directory
RUN python3.8 -m pip freeze | grep -v "cryptography" | xargs -n 1 python3.8 -m pip install --no-deps --target=./python/lib/python3.8/site-packages/
# Manually install a compatible version of cryptography for AWS Lambda
RUN python3.8 -m pip install \
--platform manylinux2014_x86_64 \
--implementation cp \
--only-binary=:all: \
--target=./python/lib/python3.8/site-packages/ \
cryptography
# Zip the Python packages for the Lambda layer
RUN zip -r pywebpush_lambda_layer.zip python
Then, run the following commands to zip into a Lambda layer:
docker build -t pywebpush-lambda-layer .
docker run --name pywebpush-layer-builder pywebpush-lambda-layer
docker cp pywebpush-layer-builder:/lambda-layer/pywebpush_lambda_layer.zip .