Class: Gravel::APNS

Inherits:
Object
  • Object
show all
Defined in:
lib/gravel/apns.rb,
lib/gravel/apns/auto_token.rb,
lib/gravel/apns/notification.rb,
lib/gravel/apns/notification/localization.rb

Overview

Manages the connection to APNS. You should keep this instance somewhere instead of recreating it every time you need to send notifications.

Defined Under Namespace

Classes: AutoToken, Notification

Constant Summary collapse

PRODUCTION_URL =

The production APNS server URL.

'api.push.apple.com'
DEVELOPMENT_URL =

The development APNS server URL.

'api.development.push.apple.com'
DEFAULT_PORT =

The default APNS port.

443
ALTERNATIVE_PORT =

The alternative APNS port.

2197

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Gravel::APNS

Create a new APNS instance.

Parameters:

  • alternative_port (Boolean)

    (optional) Should we use the default (443) or the alternative (2197) port?

  • concurrency (Integer)

    (optional) How many connections to APNS should we open?

  • environment (Symbol)

    (optional) :production or :development

  • key (OpenSSL::PKey::EC)

    An elliptic curve key (APNS authentication).

  • key_id (String)

    The key’s identifier.

  • team_id (String)

    The team’s identifier.

  • topic (String)

    The topic to pass to APNS.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/gravel/apns.rb', line 57

def initialize(options = {})
  options = {
    alternative_port: false,
    concurrency: 1,
    environment: :development
  }.merge(options)

  unless options[:topic].is_a?(String)
    raise 'The APNS topic is required.'
  end

  unless [true, false].include?(options[:alternative_port])
    raise 'The alternative port should be a boolean value.'
  end

  unless options[:concurrency].is_a?(Integer) && options[:concurrency] > 0
    raise 'Concurrency should be specified as an Integer greater than zero.'
  end

  unless [:development, :production].include?(options[:environment])
    raise 'The environment should be either :production or :development.'
  end

  host = case options[:environment]
  when :development
    DEVELOPMENT_URL
  when :production
    PRODUCTION_URL
  end

  port = options[:alternative_port] ? ALTERNATIVE_PORT : DEFAULT_PORT

  @auto_token = Gravel::APNS::AutoToken.new(options[:team_id], options[:key_id], options[:key])
  @queue = Queue.new
  @topic = options[:topic]
  @url = "https://#{host}:#{port}"

  @workers = options[:concurrency].times.map do
    client = NetHttp2::Client.new(@url)

    thread = Thread.new(client) do |client|
      Thread.current.abort_on_exception = true

      loop do
        Thread.current[:processing] = false

        notification, block = @queue.pop

        Thread.current[:processing] = true

        process_notification(notification, client, &block)
      end
    end

    { client: client, thread: thread }
  end
end

Instance Attribute Details

#topicString (readonly)

The topic to pass to APNS. This is usually the bundle identifier of the application you’re sending push notifications to.

Returns:

  • (String)

    The APNS topic.



44
45
46
# File 'lib/gravel/apns.rb', line 44

def topic
  @topic
end

Class Method Details

.key_from_file(path) ⇒ OpenSSL::PKey::EC

Attempt to load a key from the specified path.

Parameters:

  • path (String)

    The path to the key file.

Returns:

  • (OpenSSL::PKey::EC)

    The key.



29
30
31
32
33
34
35
# File 'lib/gravel/apns.rb', line 29

def key_from_file(path)
  unless File.file?(path)
    raise "A key could not be loaded from: #{path}"
  end

  OpenSSL::PKey::EC.new(File.read(path))
end

Instance Method Details

#closeObject

Close all clients and terminate all workers.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/gravel/apns.rb', line 159

def close
  @queue.clear

  if @workers.is_a?(Array)
    @workers.each do |payload|
      payload[:thread].kill
      payload[:client].close
    end

    @workers = nil
  end

  nil
end

#key_idString

The identifier for the APNS key.

Returns:

  • (String)

    The key’s identifier.



127
128
129
# File 'lib/gravel/apns.rb', line 127

def key_id
  @auto_token.key_id
end

#send(notification, &block) ⇒ Boolean

Push a notification onto the send queue.

Parameters:

  • notification (Gravel::APNS::Notification)

    The notification to send.

  • block (Proc)

    The block to call when the request has completed.

Returns:

  • (Boolean)

    Whether or not the notification was sent.



137
138
139
140
141
142
143
144
145
# File 'lib/gravel/apns.rb', line 137

def send(notification, &block)
  if @workers.nil? || @workers.empty?
    raise "There aren't any workers to process this notification!"
  end

  @queue.push([notification, block])

  nil
end

#team_idString

The identifier for the developer’s team.

Returns:

  • (String)

    The team’s identifier.



119
120
121
# File 'lib/gravel/apns.rb', line 119

def team_id
  @auto_token.team_id
end

#waitObject

Wait for all threads to finish processing.



149
150
151
152
153
154
155
# File 'lib/gravel/apns.rb', line 149

def wait
  threads = @workers.map { |w| w[:thread] }

  until @queue.empty? && threads.all? { |t| t[:processing] == false }
    # Waiting for the threads to finish ...
  end
end