{"id":15647248,"url":"https://github.com/yuki24/pushing","last_synced_at":"2025-10-28T18:11:24.068Z","repository":{"id":56889674,"uuid":"84774559","full_name":"yuki24/pushing","owner":"yuki24","description":"Finally, push notification framework that does not hurt. currently supports Android (FCM) and iOS (APNs)","archived":false,"fork":false,"pushed_at":"2024-06-28T08:00:32.000Z","size":211,"stargazers_count":46,"open_issues_count":8,"forks_count":5,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-12T19:47:30.974Z","etag":null,"topics":["android","apns","fcm","gcm","ios","notification-framework","notifications","push","push-notifications","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yuki24.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-13T02:14:52.000Z","updated_at":"2023-05-25T11:16:00.000Z","dependencies_parsed_at":"2024-12-25T08:06:58.788Z","dependency_job_id":"915127b9-1854-42cf-897f-89d0b3807b93","html_url":"https://github.com/yuki24/pushing","commit_stats":{"total_commits":229,"total_committers":2,"mean_commits":114.5,"dds":"0.19650655021834063","last_synced_commit":"3addd119c9c24ee852fad9e08aed626512fba6b1"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuki24%2Fpushing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuki24%2Fpushing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuki24%2Fpushing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuki24%2Fpushing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yuki24","download_url":"https://codeload.github.com/yuki24/pushing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249154245,"owners_count":21221380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","apns","fcm","gcm","ios","notification-framework","notifications","push","push-notifications","rails","ruby"],"created_at":"2024-10-03T12:17:46.742Z","updated_at":"2025-10-28T18:11:23.950Z","avatar_url":"https://github.com/yuki24.png","language":"Ruby","readme":"# Pushing: ActionMailer for Push Notifications [![Build Status](https://travis-ci.org/yuki24/pushing.svg?branch=master)](https://travis-ci.org/yuki24/pushing)\n\nPushing is a push notification framework that implements interfaces similar to ActionMailer.\n\n * **Convention over Configuration**: Pushing brings Convention over Configuration to your app for organizing your push notification implementations.\n * **Extremely Easy to Learn**: If you know how to use ActionMailer, you already know how to use Pushing. Send notifications asynchronously with ActiveJob at no learning cost.\n * **Testability**: First-class support for push notification. No more hassle writing custom code or stubs/mocks for your tests.\n\n## Getting Started\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'pushing'\ngem 'jbuilder' # if you don't have it in your Gemfile\n```\n\nAt the time of writing, Pushing only has support for [jbuilder](https://github.com/rails/jbuilder) (Rails' default JSON constructor), but there are plans to add support for [jb](https://github.com/amatsuda/jb) and [rabl](https://github.com/nesquena/rabl).\n\n### Supported Platforms\n\nPushing itself doesn't make HTTP requests. Instead, it uses an adapter to make actual calls. Currently, Pushing has support for the following client gems:\n\n * [APNs](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) (iOS):\n   * [anpotic](https://github.com/ostinelli/apnotic) (recommended)\n   * [lowdown](https://github.com/alloy/lowdown)\n   * [houston](https://github.com/nomad/houston)\n\n * [FCM](https://firebase.google.com/docs/cloud-messaging/) (Android):\n   * [andpush](https://github.com/yuki24/andpush) (recommended)\n   * [fcm](https://github.com/spacialdb/fcm)\n\nIf you are starting from scratch, it is recommended using [anpotic](https://github.com/ostinelli/apnotic) for APNs and [andpush](https://github.com/yuki24/andpush) for FCM due to their reliability and performance:\n\n```ruby\ngem 'apnotic' # APNs\ngem 'andpush' # FCM\n```\n\n### Walkthrough to Writing a Notifier\n\n#### Generate a new notifier:\n\n```sh\n$ rails g pushing:notifier TweetNotifier new_direct_message\n```\n\n```ruby\n# app/notifiers/tweet_notifier.rb\nclass TweetNotifier \u003c ApplicationNotifier\n  def new_direct_message(message_id, token_id)\n    @message = DirectMessage.find(message_id)\n    @token   = DeviceToken.find(token_id)\n\n    push apn: @token.apn? \u0026\u0026 @token.device_token, fcm: @token.fcm?\n  end\nend\n```\n\n#### Edit the push notification payload:\n\nAPNs:\n\n```ruby\n# app/views/tweet_notifier/new_direct_message.json+apn.jbuilder\njson.aps do\n  json.alert do\n    json.title \"#{@tweet.user.display_name} tweeted:\"\n    json.body truncate(@tweet.body, length: 235)\n  end\n\n  json.badge 1\n  json.sound 'bingbong.aiff'\nend\n```\n\nFCM:\n\n```ruby\n# app/views/tweet_notifier/new_direct_message.json+fcm.jbuilder\njson.to @token.registration_id\n\njson.notification do\n  json.title \"#{@tweet.user.display_name} tweeted:\"\n  json.body truncate(@tweet.body, length: 1024)\n\n  json.icon 1\n  json.sound 'default'\nend\n```\n\n### Deliver the push notifications:\n\n```ruby\nTweetNotifier.new_direct_message(message_id, device_token.id).deliver_now!\n# =\u003e sends a push notification immediately\n\nTweetNotifier.new_direct_message(message_id, device_token.id).deliver_later!\n# =\u003e enqueues a job that sends a push notification later\n```\n\n## Advanced Usage\n\n### Pushing only to one platform\n\nPushing only sends a notification for the platforms that are given a truthy value. For example, give the following code:\n\n```ruby\npush apn: @token.device_token, fcm: false\n# =\u003e only sends a push notification to APNs\n\npush apn: @token.device_token\n# =\u003e same as above but without the `:fcm` key, only sends a push notification to APNs\n```\n\nThis will only send a push notification to APNs and skip the call to FCM.\n\n### APNs\n\nIt is often necessary to switch the environment endpoint or adjust the request headers depending on the notification you want to send. Pushing's `#push` method allows for overriding APNs request headers on a delivery-basis:\n\n#### Overriding the default environment:\n\n```ruby\npush apn: { device_token: @token.device_token, environment: @token.apn_environment }\n```\n\n#### Overriding the default APN topic:\n\n```ruby\npush apn: { device_token: @token.device_token, headers: { apns_topic: 'your.otherapp.ios' } }\n```\n\n#### Or all of the above:\n\n```ruby\npush fcm: @token.fcm?,\n     apn: {\n       device_token: @token.apn? \u0026\u0026 @token.device_token,\n       environment: @token.apn_environment,\n       headers: {\n         apns_id:          uuid,\n         apns_expiration:  7.days.from_now,\n         apns_priority:    5,\n         apns_topic:       'your.otherapp.ios',\n         apns_collapse_id: 'not-so-important-notification'\n       }\n     }\n```\n\nThe `:fcm` key, on the other hand, doesn't have any options as everything's configurable through the request body.\n\n## Error Handling\n\nLike ActionMailer, you can use the `rescue_from` hook to handle exceptions. A common use-case would be to handle a **'BadDeviceToken'** response from APNs or a response with a **'Retry-After'** header from FCM.\n\n**Handling a 'BadDeviceToken' response from APNs**:\n\n```ruby\nclass ApplicationNotifier \u003c Pushing::Base\n  rescue_from Pushing::ApnDeliveryError do |error|\n    response = error.response\n\n    if response.status == 410 || (response.status == 400 \u0026\u0026 response.json[:reason] == 'BadDeviceToken')\n      token = error.notification.device_token\n      Rails.logger.info(\"APN device token #{token} has been expired and will be removed.\")\n\n      # delete or expire device token accordingly\n    else\n      raise # Make sure to raise any other types of error to re-enqueue the job\n    end\n  end\nend\n```\n\n**Handling a 'Retry-After' header from FCM**:\n\n```ruby\nclass ApplicationNotifier \u003c Pushing::Base\n  rescue_from Pushing::FcmDeliveryError do |error|\n    if error.response\u0026.headers['Retry-After']\n      # re-enqueue the job honoring the 'Retry-After' header\n    else\n      raise # Make sure to raise any other types of error to re-enqueue the job\n    end\n  end\nend\n```\n\n## Interceptors and Observers\n\nPushing implements the Interceptor and Observer patterns. A common use-case would be to update registration ids with canonical ids from FCM:\n\n```ruby\n# app/observers/fcm_token_handler.rb\nclass FcmTokenHandler\n  def delivered_notification(payload, response)\n    return if response.json[:canonical_ids].to_i.zero?\n\n    response.json[:results].select {|result| result[:registration_id] }.each do |result|\n      result[:registration_id] # =\u003e returns a canonical id\n\n      # Update registration ids accordingly\n    end\n  end\nend\n\n# app/notifiers/application_notifier.rb\nclass ApplicationNotifier \u003c Pushing::Base\n  register_observer FcmTokenHandler.new\n\n  ...\nend\n```\n\n## Configuration\n\n##### TODO: Make this section more helpful\n\n```ruby\nPushing.configure do |config|\n  # Adapter that is used to send push notifications through FCM\n  config.fcm.adapter = Rails.env.test? ? :test : :andpush\n\n  # Your FCM servery key that can be found here: https://console.firebase.google.com/project/_/settings/cloudmessaging\n  config.fcm.server_key = 'YOUR_FCM_SERVER_KEY'\n\n  # Adapter that is used to send push notifications through APNs\n  config.apn.adapter = Rails.env.test? ? :test : :apnotic\n\n  # The environment that is used by default to send push notifications through APNs\n  config.apn.environment = Rails.env.production? ? :production : :development\n\n  # The scheme that is used for negotiating connection trust between your provider\n  # servers and Apple Push Notification service. As documented in the offitial doc,\n  # there are two schemes available:\n  #\n  #   :token       - Token-based provider connection trust (default)\n  #   :certificate - Certificate-based provider connection trust\n  #\n  # This option is only applied when using an adapter that uses the HTTP/2-based\n  # API.\n  config.apn.connection_scheme = :token\n\n  # Path to the certificate or auth key for establishing a connection to APNs.\n  #\n  # This config is always required.\n  config.apn.certificate_path = 'path/to/your/certificate'\n\n  # Password for the certificate specified above if there's any.\n  # config.apn.certificate_password = 'passphrase'\n\n  # A 10-character key identifier (kid) key, obtained from your developer account.\n  # If you haven't created an Auth Key for your app, create a new one at:\n  #   https://developer.apple.com/account/ios/authkey/\n  #\n  # Required if the +connection_scheme+ is set to +:token+.\n  config.apn.key_id = 'DEF123GHIJ'\n\n  # The issuer (iss) registered claim key, whose value is your 10-character Team ID,\n  # obtained from your developer account. Your team id could be found at:\n  #   https://developer.apple.com/account/#/membership\n  #\n  # Required if the +connection_scheme+ is set to +:token+.\n  config.apn.team_id = 'ABC123DEFG'\n\n  # Header values that are added to every request to APNs. documentation for the\n  # headers available can be found here:\n  #   https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW13\n  config.apn.default_headers = {\n    apns_priority:    10,\n    apns_topic:       'your.awesomeapp.ios',\n    apns_collapse_id: 'wrong.topicname.com'\n  }\nend\n\n```\n\n## Testing\n\nPushing provides first-class support for testing. In order to test your notifier, use the `:test` adapter in the test environment instead of an actual adapter in development/production.\n\n```ruby\n# config/initializers/pushing.rb\nPushing.configure do |config|\n  config.apn.adapter = Rails.env.test? ? :test : :apnotic\n  config.fcm.adapter = Rails.env.test? ? :test : :andpush\nend\n```\n\nNow you can call the `#deliveries` method on the notifier. Here is an example with [ActiveSupport::TestCase](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html):\n\n```ruby\nTweetNotifier.deliveries.clear # =\u003e clears the test inbox\n\nassert_changes -\u003e { TweetNotifier.deliveries.apn.size }, from: 0, to: 1 do\n  TweetNotifier.new_direct_message(message.id, apn_device_token.id).deliver_now!\nend\n\napn_message = TweetNotifier.deliveries.apn.first\nassert_equal 'apn-device-token',  apn_message.device_token\nassert_equal \"Hey coffee break?\", apn_message.payload[:aps][:alert][:body]\n\nassert_changes -\u003e { TweetNotifier.deliveries.fcm.size }, from: 0, to: 1 do\n  TweetNotifier.new_direct_message(message.id, fcm_registration_id.id).deliver_now!\nend\n\nfcm_payload = TweetNotifier.deliveries.fcm.first.payload\nassert_equal 'fcm-registration-id', fcm_payload[:to]\nassert_equal \"Hey coffee break?\",   fcm_payload[:notification][:body]\n```\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/yuki24/pushing. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyuki24%2Fpushing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyuki24%2Fpushing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyuki24%2Fpushing/lists"}