Send Apple Push Notifications with HTTP2 using the apnotic
gem. The benefit of HTTP2 is that we can receive feedback for invalid device tokens without running a separate feedback service like RPush does.
bundle add "apnotic"
Token-based authentication is used for APNS.
- A single key can be used for every app in your developer account.
- Token authentication never expires, unlike certificate authentication which must be renewed annually.
Follow these docs for setting up Token-based authentication. https://github.com/ostinelli/apnotic#token-based-authentication https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns
class CommentNotifier < ApplicationNotifier
deliver_by :ios do |config|
config.device_tokens = -> { recipient.notification_tokens.where(platform: :iOS).pluck(:token) }
config.format = ->(apn) {
apn.alert = "Hello world"
apn.custom_payload = {url: root_url(host: "example.org")}
}
config.bundle_identifier = Rails.application.credentials.dig(:ios, :bundle_id)
config.key_id = Rails.application.credentials.dig(:ios, :key_id)
config.team_id = Rails.application.credentials.dig(:ios, :team_id)
config.apns_key = Rails.application.credentials.dig(:ios, :apns_key)
end
end
-
format
Customize the Apnotic notification object
See https://github.com/ostinelli/apnotic#apnoticnotification
-
bundle_identifier
The APN bundle identifier
-
apns_key
The contents of your p8 apns key file.
-
key_id
Your APN Key ID
-
team_id
Your APN Team ID
-
pool_size: 5
- OptionalThe connection pool size for Apnotic
-
development
- OptionalSet this to
true
to use the APNS sandbox environment for sending notifications. This is required when running the app to your device via Xcode. Running the app via TestFlight or the App Store should not use development.
A recipient can have multiple tokens (i.e. multiple iOS devices), so make sure to return them all.
Here, the recipient has_many :notification_tokens
with columns platform
and token
.
deliver_by :ios do |config|
config.device_tokens = -> { recipient.notification_tokens.where(platform: :iOS).pluck(:token) }
end
Apple Push Notifications may fail delivery if the user has removed the app from their device. Noticed allows you
class CommentNotifier < ApplicationNotifier
deliver_by :ios do |config|
config.invalid_token = ->(token) { NotificationToken.where(token: token).destroy_all }
end
end
If you're managing the iOS app badge, you can pass it along in the format
class CommentNotifier < ApplicationNotifier
deliver_by :ios do |config|
config.format = ->(apn) {
apn.alert = "Hello world"
apn.custom_payload = {url: root_url(host: "example.org")}
apn.badge = recipient.notifications.unread.count
}
end
end
Another common action is to update the badge after a user reads a notification.
This is a great use of the Noticed::Ephemeral class. Since it's all in-memory, it will perform the job and not touch the database.
class NativeBadgeNotifier < Noticed::Ephemeral
deliver_by :ios do |config|
config.format = ->(apn) {
# Setting the alert text to nil will deliver the notification in
# the background. This is used to update the app badge on the iOS home screen
apn.alert = nil
apn.custom_payload = {}
apn.badge = recipient.notifications.unread.count
}
end
end
Then you can simply deliver this notifier to update the badge when you mark the notification as read
notification.mark_as_read!
NativeBadgeNotifier.with(record: notification).deliver(notification.recipient)
If you wish to send notifications to both sandboxed and real devices from the same application, you can configure two iOS delivery methods A user has_many tokens that can be generated from both development (sandboxed devices), or production (not sandboxed devices) and is unrelated to the rails environment or endpoint being used. I
deliver_by :ios do |config|
config.device_tokens = -> { recipient.notification_tokens.where(environment: :production, platform: :iOS).pluck(:token) }
end
deliver_by :ios_development, class: "Noticed::DeliveryMethods::Ios" do |config|
config.development = true
config.device_tokens = ->{ recipient.notification_tokens.where(environment: :development, platform: :iOS).pluck(:token) }
end