Maia

This project maintains a Maia::Device model and facilitates the delivery of push notifications for iOS and Android through FCM (Firebase Cloud Messaging).

Installation

gem 'maia'
bundle
bin/rake railties:install:migrations
bin/rake db:migrate

This will copy the maia_devices table into your project.

Setup

Maia relies on ActiveJob to enqueue messages. Ensure your application is properly setup with ActiveJob!

Set your FCM key in an initializer, such as config/initializers/maia.rb:

Maia::FCM.key = Rails.application.secrets[:fcm][:key]

Alternatively, you can set ENV['FCM_KEY'], which Maia will check first.

Include Maia::Model into your User model. This will attach the has_many relationship you need with Maia::Device:

class User
  include Maia::Model
  # ...
end

Create a Devices controller where you need it, which is most likely an API. The controller itself will be generated within your application so that Maia does not make any assumptions about your method of authentication, respond_with mimetypes, etc. The only requirement is that current_user exists and returns whatever model included Maia::Model.

Here's an example of getting setup with an API Devices controller that mobile apps can register with:

bin/rails g controller api/devices

After the controller is generated, include Maia::Controller:

class API::DevicesController
  include Maia::Controller
  # ...

Maia provides the create method for you, so devices can now register themselves by POSTing to that controller. If you'd like to add any other actions, feel free.

Device Registration

Devices can register with your application by submitting a POST to your devices controller with these params:

{ "device": { "token": "<TOKEN>" } }

Where <TOKEN> is the token from FCM registration.

Device Management

When FCM responds with an invalid or unregistered device token, the device record will be destroyed from the database.

When FCM responds with a canonical ID, the device record will be updated so that it's token field will be equal to the canonical ID given by FCM.

Device Expiration

Devices will expire after 14 days. This is to ensure user's who sell or otherwise give away their device will not be tied to that device forever. Each time a POST to Devices is received, the token expiration will be refreshed.

Defining Messages

Maia provides a Maia::Message class that provides an interface for defining push messages and sending them. To define a message, inherit from Maia::Message and override whatever you need to:

class ExampleMessage < Maia::Message
  # Required
  def title
    'Something happened!'
  end

  # Required, the body of the message on Android, alert on iOS
  def body
    'Something very important has happened, check it out!'
  end

  # Determines the icon to load on Android phones
  def icon
    'icn_maia'
  end

  # Will use 'default' by default. Overriding to nil will prevent sound
  def sound
    'default'
  end

  # Badge to use on iOS
  def badge
    1
  end

  # #RRGGBB formatted color to use for the Android notification icon
  def color
    '#ffffff'
  end

  # click_action on Android, category on iOS
  def on_click
    'SOMETHING_HAPPENED'
  end

  # Any additional data to send with the payload
  def data
    { foo: :bar }
  end

  # :normal or :high (:normal by default)
  def priority
    :normal
  end

  # Override to true in order to send the iOS content-available flag
  def content_available?
    false
  end

  # Override to true in order to send a dry run push. This can help debug any device errors without actually sending a push message
  def dry_run?
    false
  end
end

This message will generate the following FCM payload:

{
  "priority": "normal",
  "dry_run": false,
  "content_available": false,
  "data": {
    "foo": "bar"
  },
  "notification": {
    "title": "Something happened!",
    "body": "'Something very important has happened, check it out!'",
    "icon": "icn_maia",
    "sound": "default",
    "badge": 1,
    "color": "#ffffff",
    "click_action": "SOMETHING_HAPPENED",
  },
  "registration_ids": ["<TOKEN1>", "<TOKEN2>"]
}

Maia::Message does not define a constructor so you can construct your message however you want.

Sending messages

Maia::Message provides a send_to that pushes the message out to a user (or collection of users). The argument to send_to should be a single record or relation of records.

For example:

ExampleMessage.new(...).send_to User.first
ExampleMessage.new(...).send_to User.where(beta: true)

send_to will batch users in groups of 999 tokens (FCM limitation) and send them via ActiveJob.

Specifying job options (wait, queue, etc)

The send_to method passes it's last argument into ActiveJob's set method, for example:

ExampleMessage.new(...).send_to User.first, wait: 10.seconds, queue: :maia

Sending a test push

Maia comes with a built-in message to use to test your configuration out:

Maia::Poke.new.send_to user