├── .gitignore ├── .travis.yml ├── Gemfile ├── ProMotion-push.gemspec ├── README.md ├── Rakefile ├── app ├── app_delegate.rb └── test_delegate.rb ├── lib ├── ProMotion-push.rb └── ProMotion │ ├── delegate_notifications.rb │ └── push_notification.rb ├── resources └── Default-568h@2x.png └── spec └── push_notification_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .repl_history 2 | build 3 | tags 4 | app/pixate_code.rb 5 | resources/*.nib 6 | resources/*.momd 7 | resources/*.storyboardc 8 | .DS_Store 9 | nbproject 10 | .redcar 11 | #*# 12 | *~ 13 | *.sw[po] 14 | *.gem 15 | .eprj 16 | .sass-cache 17 | .idea 18 | .dat*.* 19 | .ruby-version 20 | Gemfile.lock 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.2 3 | before_install: 4 | - sudo chown -R travis ~/Library/RubyMotion 5 | - mkdir -p ~/Library/RubyMotion/build 6 | - sudo motion update 7 | gemfile: 8 | - Gemfile 9 | script: 10 | - bundle install 11 | - bundle exec rake clean 12 | - bundle exec rake spec 13 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Define all dependencies in your .gemspec file 4 | gem "ProMotion", github: "clearsightstudio/ProMotion", branch: "master" 5 | gemspec 6 | -------------------------------------------------------------------------------- /ProMotion-push.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | Gem::Specification.new do |spec| 3 | spec.name = "ProMotion-push" 4 | spec.version = "0.4.0" 5 | spec.authors = ["Jamon Holmgren"] 6 | spec.email = ["jamon@infinite.red"] 7 | spec.description = %q{Adds push notification support to ProMotion.} 8 | spec.summary = %q{Adds push notification support to ProMotion.} 9 | spec.homepage = "https://github.com/infinitered/ProMotion-push" 10 | spec.license = "MIT" 11 | 12 | files = [] 13 | files << 'README.md' 14 | files.concat(Dir.glob('lib/**/*.rb')) 15 | spec.files = files 16 | spec.test_files = spec.files.grep(%r{^(spec)/}) 17 | spec.require_paths = ["lib"] 18 | 19 | spec.add_dependency "ProMotion", "~> 2.0" 20 | spec.add_development_dependency "motion-stump", "~> 0.3" 21 | spec.add_development_dependency "motion-redgreen", "~> 1.0" 22 | spec.add_development_dependency "rake" 23 | end 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProMotion-push 2 | 3 | ProMotion-push is push notification support, extracted from the 4 | popular RubyMotion gem [ProMotion](https://github.com/infinitered/ProMotion) and was created by [Infinite Red](https://infinite.red), a web and mobile development company based in Portland, OR and San Francisco, CA. While you're welcome to use it, please note that we rely on the community to maintain it. We are happy to merge pull requests and release new versions but are no longer driving primary development. 5 | 6 | ## Installation 7 | 8 | ```ruby 9 | gem 'ProMotion-push' 10 | ``` 11 | 12 | ## Usage 13 | 14 | ### AppDelegate 15 | 16 | Include PM::DelegateNotifications to add a few methods to PM::Delegate. 17 | 18 | ```ruby 19 | # app/app_delegate.rb 20 | class AppDelegate < PM::Delegate 21 | include PM::DelegateNotifications 22 | 23 | def on_load(app, options) 24 | register_for_push_notifications :badge, :sound, :alert, :newsstand 25 | PM.logger.info registered_push_notifications 26 | # ... 27 | end 28 | 29 | def on_unload 30 | unregister_for_push_notifications 31 | end 32 | 33 | def on_push_registration(token, error) 34 | PM.logger.info token.description 35 | end 36 | 37 | def on_push_notification(notification, launched) 38 | PM.logger.info notification.to_json 39 | end 40 | end 41 | ``` 42 | 43 | #### register_for_push_notifications(*types) 44 | 45 | Method you can call to register your app for push notifications. You'll also want to implement 46 | `on_push_notification` and `on_push_registration`. 47 | 48 | ```ruby 49 | def on_load(app, options) 50 | register_for_push_notifications :badge, :sound, :alert, :newsstand # or :all 51 | # ... 52 | end 53 | ``` 54 | 55 | #### unregister_for_push_notifications 56 | 57 | Unregisters from all push notifications. 58 | 59 | ```ruby 60 | def logging_out 61 | unregister_for_push_notifications 62 | end 63 | ``` 64 | 65 | **NOTE:** From a screen you'll have to reference the app_delegate: 66 | 67 | ```ruby 68 | def log_out 69 | app_delegate.unregister_for_push_notifications 70 | end 71 | ``` 72 | 73 | #### on_push_registration(token, error) 74 | 75 | Method that is called after you attempt to register for notifications. Either `token` or `error` 76 | will be provided. 77 | 78 | ```ruby 79 | def on_push_registration(token, error) 80 | if token 81 | # Push token to your server 82 | else 83 | # Display the error 84 | end 85 | end 86 | ``` 87 | 88 | #### on_push_notification(notification, launched) 89 | 90 | Method called when the app is launched via a notification or a notification is received 91 | in-app. `notification` is a 92 | [PM::PushNotification](https://github.com/clearsightstudio/ProMotion/wiki/API-Reference:-ProMotion::PushNotification) 93 | object which is a thin wrapper around the notification hash provided by iOS. `launched` 94 | is a boolean letting you know whether the notification initiated your app's launch (true) or 95 | if your app was already running (false). 96 | 97 | ```ruby 98 | def on_push_notification(notification, launched) 99 | notification.to_json # => '{"aps":{"alert":"My test notification","badge":3,"sound":"default"}, "custom": "Jamon Holmgren"}' 100 | notification.alert # => "My test notification" 101 | notification.badge # => 3 102 | notification.sound # => "default" 103 | notification.custom # => "Jamon Holmgren" 104 | end 105 | ``` 106 | 107 | ProMotion-push automatically adds support for handling push notifications while your app is in the background. If your push notification payload includes `content-available: 1` then you may have an opportunity to pre-fetch data. The return value of the `on_push_notification` method should one of the following that best matches the result: `:new_data`, `:no_data`, `:failed`. The default is `:no_data`, so you don't need to return anything if you did not fetch any data. For example: 108 | 109 | ```ruby 110 | # Payload: 111 | # { 112 | # "aps": { 113 | # "content-available": 1, 114 | # "alert": "My test notification", 115 | # "badge": 3, 116 | # "sound": "default" 117 | # }, 118 | # "type": "new_messages" 119 | # } 120 | 121 | def on_push_notification(notification, launched) 122 | if notification.type == "new_messages" 123 | MessagesScreen.load_data 124 | return :new_data 125 | end 126 | end 127 | ``` 128 | 129 | #### registered_push_notifications 130 | 131 | Returns the currently registered notifications as an array of symbols. 132 | 133 | ```ruby 134 | def some_method 135 | registered_push_notifications # => [ :badge, :sound, :alert, :newsstand ] 136 | end 137 | ``` 138 | 139 | ### Actionable Notifications 140 | As of IOS 8, notifications can include action buttons in the lock screen, notification banners and notification center. This is called the "Minimal" context and supports 2 actions. The "Default" context supports up to 4 Actions and applies when alerts are opened as popups. 141 | 142 | To use these features with ProMotion-push you need to: 143 | 144 | Define each action you plan to include in a notification for either context. 145 | ```ruby 146 | approve_action = UIMutableUserNotificationAction.new.tap do | action | 147 | action.identifier = "APPROVE_ACTION" 148 | action.title = "Approve" 149 | action.activationMode = UIUserNotificationActivationModeBackground 150 | action.authenticationRequired = false 151 | end 152 | ``` 153 | 154 | Register your actions by calling `register_push_notification_category` from your AppDelegate code prior to `register_for_push_notifications`, for each category of action you intend to use. Note that you must include a separate array for the actions to show in the minimal context. 155 | ```ruby 156 | def on_load(app, options) 157 | register_push_notification_category 'APPROVAL_ACTIONS', [approve_action, deny_action, self_destruct_action], minimal: [approve_action] 158 | register_push_notification_category 'SECOND_CATEGORY_NAME', # ... 159 | # ... 160 | register_for_push_notifications :badge, :sound, :alert, :newsstand # or :all 161 | # ... 162 | end 163 | ``` 164 | 165 | Include one of the categories you just defined in your push payload 166 | ```json 167 | { 168 | "aps" : { 169 | "alert" : "Do you approve?", 170 | "category" : "APPROVAL_ACTIONS" 171 | } 172 | } 173 | ``` 174 | 175 | Implement `on_push_notification_action` in your AppDelegate to handle the selected action 176 | ```ruby 177 | def on_push_notification_action(action, notification) 178 | # handle the action 179 | case action 180 | when 'APPROVE_ACTION' 181 | # approve 182 | when 'DENY_ACTION' 183 | # deny 184 | end 185 | end 186 | ``` 187 | 188 | ### ProMotion::PushNotification 189 | 190 | You receive this object in your AppDelegate's `on_push_notification` method. 191 | 192 | ```ruby 193 | def on_push_notification(notification, launched) 194 | notification.to_json # => '{"aps":{"alert":"My test notification","badge":3,"sound":"default"}, "custom": "Jamon Holmgren"}' 195 | notification.alert # => "My test notification" 196 | notification.badge # => 3 197 | notification.sound # => "default" 198 | notification.custom # => "Jamon Holmgren" 199 | end 200 | ``` 201 | 202 | The best way to test push notifications is on a device, but it's often useful to test 203 | them in the simulator. We provide a way to do that from the REPL or in code. 204 | 205 | ```ruby 206 | # In REPL 207 | PM::PushNotification.simulate(alert: "My test", badge: 4, sound: "default", custom: "custom", launched: true) 208 | ``` 209 | ```ruby 210 | def on_push_notification(notification, launched) 211 | notification.aps # => { alert: "My test", badge: sound: "default"} 212 | notification.alert # => "My test" 213 | notification.custom # => 'custom' 214 | end 215 | ``` 216 | 217 | #### alert 218 | 219 | Returns the alert string for the push notification object. 220 | 221 | ```ruby 222 | notification.alert # => "My test notification" 223 | ``` 224 | 225 | #### badge 226 | 227 | Returns the badge number for the push notification object, if it exists. 228 | 229 | ```ruby 230 | notification.badge # => 3 231 | ``` 232 | 233 | #### sound 234 | 235 | Returns a string representing the sound for the push notification object, if it exists. 236 | 237 | ```ruby 238 | notification.sound # => "sound" 239 | ``` 240 | 241 | #### to_json 242 | 243 | Returns a json string representation of the push notification object. 244 | 245 | ```ruby 246 | notification.to_json # => '{"aps":{"alert":"My test notification","sound":"default"},"custom":"something custom"}' 247 | ``` 248 | 249 | #### (custom methods) 250 | 251 | A `method_missing` implementation will respond to all methods that are keys in the notification hash. It 252 | will raise a NoMethodError if there isn't a corresponding key. 253 | 254 | ```ruby 255 | # Given: '{"aps":{"alert":"My test notification","sound":"default"}, "my_custom_key": "My custom data"}' 256 | notification.my_custom_key # => "My custom data" 257 | notification.no_key_here # => NoMethodError 258 | ``` 259 | 260 | #### notification 261 | 262 | Returns the raw notification object provided by iOS. 263 | 264 | ```ruby 265 | notification.notification # => Hash 266 | ``` 267 | 268 | ## Contributing 269 | 270 | 1. Fork it 271 | 2. Create your feature branch (`git checkout -b my-new-feature`) 272 | 3. Commit your changes (`git commit -am 'Add some feature'`) 273 | 4. Make some specs pass 274 | 5. Push to the branch (`git push origin my-new-feature`) 275 | 6. Create new Pull Request 276 | 277 | ## Premium Support 278 | 279 | [ProMotion](https://github.com/infinitered/ProMotion) and [ProMotion-push](https://github.com/infinitered/ProMotion-push), as open source projects, are free to use and always will be. [Infinite Red](https://infinite.red/) offers premium ProMotion and ProMotion-push support and general mobile app design/development services. Email us at [hello@infinite.red](mailto:hello@infinite.red) to get in touch with us for more details. 280 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | $:.unshift("/Library/RubyMotion/lib") 3 | require 'motion/project/template/ios' 4 | require 'bundler' 5 | require 'motion/project/template/gem/gem_tasks' 6 | Bundler.require(:development) 7 | require 'ProMotion' 8 | require 'ProMotion-push' 9 | 10 | Motion::Project::App.setup do |app| 11 | # Use `rake config' to see complete project settings. 12 | app.name = 'ProMotion-push' 13 | end 14 | -------------------------------------------------------------------------------- /app/app_delegate.rb: -------------------------------------------------------------------------------- 1 | class AppDelegate < ProMotion::Delegate 2 | include PM::DelegateNotifications 3 | 4 | def on_load(app, options) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/test_delegate.rb: -------------------------------------------------------------------------------- 1 | class OtherDelegate; end 2 | class TestDelegate < OtherDelegate 3 | include ProMotion::DelegateModule 4 | 5 | def on_load(app, options) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/ProMotion-push.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | unless defined?(Motion::Project::Config) 4 | raise "ProMotion-push must be required within a RubyMotion project." 5 | end 6 | 7 | Motion::Project::App.setup do |app| 8 | lib_dir_path = File.dirname(File.expand_path(__FILE__)) 9 | app.files << File.join(lib_dir_path, "ProMotion/push_notification.rb") 10 | app.files << File.join(lib_dir_path, "ProMotion/delegate_notifications.rb") 11 | app.info_plist['UIBackgroundModes'] ||= [] 12 | app.info_plist['UIBackgroundModes'] << "remote-notification" 13 | end 14 | -------------------------------------------------------------------------------- /lib/ProMotion/delegate_notifications.rb: -------------------------------------------------------------------------------- 1 | module ProMotion 2 | # @requires class:PushNotification 3 | module DelegateNotifications 4 | 5 | attr_accessor :aps_notification 6 | 7 | def check_for_push_notification(options) 8 | if options && options[UIApplicationLaunchOptionsRemoteNotificationKey] 9 | received_push_notification options[UIApplicationLaunchOptionsRemoteNotificationKey], true 10 | end 11 | end 12 | 13 | def register_push_notification_category(category_name, actions, options = {}) 14 | return unless actionable_notifications? 15 | 16 | @push_notification_categories ||= [] 17 | UIMutableUserNotificationCategory.new.tap do |category| 18 | minimal = options[:minimal] 19 | category.setActions(minimal, forContext: UIUserNotificationActionContextMinimal) if minimal 20 | category.setActions(actions, forContext: UIUserNotificationActionContextDefault) 21 | category.identifier = category_name 22 | @push_notification_categories << category 23 | end 24 | end 25 | 26 | def register_for_push_notifications(*notification_types) 27 | notification_types = Array.new(notification_types) 28 | notification_types = [ :badge, :sound, :alert, :newsstand ] if notification_types.include?(:all) 29 | 30 | types = UIRemoteNotificationTypeNone 31 | notification_types.each { |t| types = types | map_notification_symbol(t) } 32 | 33 | register_for_push_notification_types(types) 34 | end 35 | 36 | def register_for_push_notification_types(types) 37 | UIApplication.sharedApplication.tap do |application| 38 | if actionable_notifications? 39 | settings = UIUserNotificationSettings.settingsForTypes(types, categories: @push_notification_categories) 40 | application.registerUserNotificationSettings settings 41 | application.registerForRemoteNotifications 42 | else 43 | application.registerForRemoteNotificationTypes types 44 | end 45 | end 46 | end 47 | 48 | def actionable_notifications? 49 | UIApplication.sharedApplication.respond_to?(:registerUserNotificationSettings) 50 | end 51 | 52 | def unregister_for_push_notifications 53 | UIApplication.sharedApplication.unregisterForRemoteNotifications 54 | end 55 | 56 | def registered_push_notifications 57 | types = [] 58 | if UIApplication.sharedApplication.respond_to?(:currentUserNotificationSettings) 59 | mask = UIApplication.sharedApplication.currentUserNotificationSettings.types 60 | else 61 | mask = UIApplication.sharedApplication.enabledRemoteNotificationTypes 62 | end 63 | 64 | types << :badge if mask & UIRemoteNotificationTypeBadge > 0 65 | types << :sound if mask & UIRemoteNotificationTypeSound > 0 66 | types << :alert if mask & UIRemoteNotificationTypeAlert > 0 67 | types << :newsstand if mask & UIRemoteNotificationTypeNewsstandContentAvailability > 0 68 | 69 | types 70 | end 71 | 72 | def received_push_notification_with_action(notification, action) 73 | @aps_notification = ProMotion::PushNotification.new(notification) 74 | on_push_notification_action(action, @aps_notification) if respond_to?(:on_push_notification_action) 75 | end 76 | 77 | def received_push_notification(notification, was_launched) 78 | @aps_notification = ProMotion::PushNotification.new(notification) 79 | on_push_notification(@aps_notification, was_launched) if respond_to?(:on_push_notification) 80 | end 81 | 82 | # CocoaTouch 83 | 84 | def application(application, didRegisterForRemoteNotificationsWithDeviceToken: device_token) 85 | on_push_registration(device_token, nil) if respond_to?(:on_push_registration) 86 | end 87 | 88 | def application(application, didFailToRegisterForRemoteNotificationsWithError: error) 89 | on_push_registration(nil, error) if respond_to?(:on_push_registration) 90 | end 91 | 92 | def application(application, didReceiveRemoteNotification: notification) 93 | received_push_notification(notification, application.applicationState != UIApplicationStateActive) 94 | end 95 | 96 | def application(application, didReceiveRemoteNotification: notification, fetchCompletionHandler: callback) 97 | result = received_push_notification(notification, application.applicationState == UIApplicationStateInactive) 98 | callback.call(background_fetch_result(result)) 99 | end 100 | 101 | def application(application, handleActionWithIdentifier: action_identifier, forRemoteNotification: notification, completionHandler: callback) 102 | received_push_notification_with_action(notification, action_identifier) 103 | callback.call 104 | end 105 | 106 | protected 107 | 108 | def map_notification_symbol(symbol) 109 | { 110 | none: UIRemoteNotificationTypeNone, 111 | badge: UIRemoteNotificationTypeBadge, 112 | sound: UIRemoteNotificationTypeSound, 113 | alert: UIRemoteNotificationTypeAlert, 114 | newsstand: UIRemoteNotificationTypeNewsstandContentAvailability 115 | }[symbol] || UIRemoteNotificationTypeNone 116 | end 117 | 118 | def background_fetch_result(result) 119 | options = { 120 | new_data: UIBackgroundFetchResultNewData, 121 | no_data: UIBackgroundFetchResultNoData, 122 | failed: UIBackgroundFetchResultFailed 123 | } 124 | return options[result] if options[result] 125 | 126 | return result if options.values.include?(result) 127 | 128 | UIBackgroundFetchResultNoData 129 | end 130 | 131 | end 132 | end 133 | -------------------------------------------------------------------------------- /lib/ProMotion/push_notification.rb: -------------------------------------------------------------------------------- 1 | module ProMotion 2 | class PushNotification 3 | 4 | attr_accessor :notification 5 | 6 | def initialize(n) 7 | self.notification = n 8 | end 9 | 10 | def to_s 11 | self.notification.inspect 12 | end 13 | 14 | def to_json 15 | ProMotion.logger.warn "ProMotion::PushNotification.to_json not implemented yet." 16 | end 17 | 18 | def aps 19 | self.notification["aps"] 20 | end 21 | 22 | def alert 23 | aps["alert"] if aps 24 | end 25 | 26 | def badge 27 | aps["badge"] if aps 28 | end 29 | 30 | def sound 31 | aps["sound"] if aps 32 | end 33 | 34 | def method_missing(meth, *args, &block) 35 | aps[meth.to_s] || aps[meth.to_sym] || self.notification[meth.to_s] || self.notification[meth.to_sym] || super 36 | end 37 | 38 | # For testing from the REPL 39 | # > ProMotion::PushNotification.simulate alert: "My test message", badge: 4 40 | def self.simulate(args = {}) 41 | UIApplication.sharedApplication.delegate.on_push_notification self.fake_notification(args), args[:launched] 42 | end 43 | 44 | def self.fake_notification(args = {}) 45 | self.new({ 46 | "aps" => { 47 | "alert" => args.delete(:alert) || "Test Push Notification", 48 | "badge" => args.delete(:badge) || 2, 49 | "sound" => args.delete(:sound) || "default" 50 | }, 51 | "channels" => args.delete(:channels) || [ 52 | "channel_name" 53 | ] 54 | }.merge(args)) 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /resources/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinitered/ProMotion-push/646397d9811186861c264ec532cf56f2c146465e/resources/Default-568h@2x.png -------------------------------------------------------------------------------- /spec/push_notification_spec.rb: -------------------------------------------------------------------------------- 1 | describe "push notifications" do 2 | before { @subject = AppDelegate.new } 3 | 4 | it "should handle push notifications" do 5 | @subject.mock!(:on_push_notification) do |notification, was_launched| 6 | notification.should.be.kind_of(ProMotion::PushNotification) 7 | notification.alert.should == "Eating Bacon" 8 | notification.badge.should == 42 9 | notification.sound.should == "jamon" 10 | @subject.aps_notification.should == notification 11 | end 12 | 13 | launch_options = { UIApplicationLaunchOptionsRemoteNotificationKey => ProMotion::PushNotification.fake_notification(alert: "Eating Bacon", badge: 42, sound: "jamon").notification } 14 | @subject.application(UIApplication.sharedApplication, willFinishLaunchingWithOptions: nil) 15 | @subject.application(UIApplication.sharedApplication, didFinishLaunchingWithOptions:launch_options ) 16 | end 17 | 18 | it "should return false for was_launched if the app is currently active on screen" do 19 | @subject.mock!(:on_push_notification) do |notification, was_launched| 20 | was_launched.should.be.false 21 | end 22 | 23 | fake_app = Struct.new(:applicationState).new(UIApplicationStateActive) 24 | remote_notification = PM::PushNotification.fake_notification.notification 25 | @subject.application(fake_app, didReceiveRemoteNotification: remote_notification) 26 | end 27 | 28 | it "should return true for was_launched if app was launched from background" do 29 | @subject.mock!(:on_push_notification) do |notification, was_launched| 30 | was_launched.should.be.true 31 | end 32 | 33 | fake_app = Struct.new(:applicationState).new(UIApplicationStateBackground) 34 | remote_notification = PM::PushNotification.fake_notification.notification 35 | @subject.application(fake_app, didReceiveRemoteNotification: remote_notification) 36 | end 37 | 38 | it "should return true for was_launched if the app wasn't running" do 39 | @subject.mock!(:on_push_notification) do |notification, was_launched| 40 | was_launched.should.be.true 41 | end 42 | 43 | launch_options = { UIApplicationLaunchOptionsRemoteNotificationKey => PM::PushNotification.fake_notification.notification } 44 | @subject.application(UIApplication.sharedApplication, didFinishLaunchingWithOptions:launch_options ) 45 | end 46 | 47 | describe "on a version lower than iOS 8" do 48 | before do 49 | @app = IOS7Application.new 50 | UIApplication.stub!(:sharedApplication, return: @app) 51 | end 52 | 53 | it "should allow registration for push notifications on < iOS 8" do 54 | @subject.register_for_push_notifications(:badge) 55 | @app.types.should.equal(UIRemoteNotificationTypeBadge) 56 | end 57 | 58 | it "should return nil for :register_push_notification_category" do 59 | @subject.register_push_notification_category("category", [], {}).should.be.nil 60 | end 61 | 62 | it "should return the registered push notification types as an array" do 63 | @subject.registered_push_notifications.should == [] 64 | bits = 0 65 | types = [] 66 | { 67 | badge: UIRemoteNotificationTypeBadge, 68 | sound: UIRemoteNotificationTypeSound, 69 | alert: UIRemoteNotificationTypeAlert, 70 | newsstand: UIRemoteNotificationTypeNewsstandContentAvailability }.each do |symbol, bit| 71 | @app.stub!(:enabledRemoteNotificationTypes, return: bit) 72 | @subject.registered_push_notifications.should == [symbol] 73 | 74 | bits |= bit 75 | types << symbol 76 | @app.stub!(:enabledRemoteNotificationTypes, return: bits) 77 | @subject.registered_push_notifications.should == types 78 | end 79 | end 80 | end 81 | 82 | describe "on iOS 8 +" do 83 | before do 84 | @app = IOS8Application.new 85 | UIApplication.stub!(:sharedApplication, return: @app) 86 | end 87 | 88 | it "should allow registration of push notifications on iOS 8+" do 89 | @subject.register_for_push_notifications(:badge) 90 | 91 | @app.registered_for_notifications.should.be.true 92 | @app.settings.types.should.equal(UIRemoteNotificationTypeBadge) 93 | @app.settings.categories.count.should.equal(0) 94 | end 95 | 96 | it "should work properly when using register_push_notification_category" do 97 | @method_call = @subject.register_push_notification_category("my category", [], {}) 98 | @method_call.class.should.equal(UIMutableUserNotificationCategory) 99 | @method_call.identifier.should.equal("my category") 100 | end 101 | 102 | it "should take into consideration the categories used with register_push_notification_category" do 103 | @subject.register_push_notification_category("my category", [], {}) 104 | @subject.register_for_push_notifications(:badge) 105 | @app.settings.categories.count.should.equal(1) 106 | @app.settings.categories.allObjects[0].identifier.should.equal("my category") 107 | end 108 | 109 | it "should call on_push_notification_action if its implemented" do 110 | @subject.mock!(:on_push_notification_action) do |action, notification| 111 | notification.should.be.kind_of(ProMotion::PushNotification) 112 | notification.alert.should == "Eating Bacon" 113 | notification.badge.should == 42 114 | notification.sound.should == "jamon" 115 | @subject.aps_notification.should == notification 116 | end 117 | 118 | notification = ProMotion::PushNotification.fake_notification(alert: "Eating Bacon", badge: 42, sound: "jamon").notification 119 | @subject.application(UIApplication.sharedApplication, handleActionWithIdentifier: "my category", forRemoteNotification: notification, completionHandler: -> {}) 120 | end 121 | 122 | it "should return the registered push notification types as an array" do 123 | @subject.registered_push_notifications.should == [] 124 | bits = 0 125 | types = [] 126 | { 127 | badge: UIRemoteNotificationTypeBadge, 128 | sound: UIRemoteNotificationTypeSound, 129 | alert: UIRemoteNotificationTypeAlert, 130 | newsstand: UIRemoteNotificationTypeNewsstandContentAvailability }.each do |symbol, bit| 131 | @app.stub!(:currentUserNotificationSettings, return: stub(:types, return: bit)) 132 | @subject.registered_push_notifications.should == [symbol] 133 | 134 | bits |= bit 135 | types << symbol 136 | @app.stub!(:currentUserNotificationSettings, return: stub(:types, return: bits)) 137 | @subject.registered_push_notifications.should == types 138 | end 139 | end 140 | end 141 | 142 | class IOS7Application 143 | attr_accessor :types 144 | def registerForRemoteNotificationTypes(types) 145 | self.types = types 146 | end 147 | def enabledRemoteNotificationTypes 148 | 0 149 | end 150 | end 151 | class IOS8Application 152 | attr_accessor :settings 153 | attr_accessor :registered_for_notifications 154 | 155 | def registerUserNotificationSettings(value) 156 | self.settings = value 157 | end 158 | 159 | def registerForRemoteNotifications 160 | self.registered_for_notifications = true 161 | end 162 | def currentUserNotificationSettings 163 | mock(:types, return: 0) 164 | end 165 | end 166 | end 167 | --------------------------------------------------------------------------------