Hi i am a newbie to tvOS. I have an TV application which is registered for APNS.
But while i push a notification i am not able to get the notifications.
i am getting the device token but not the notification.
While i try with the Mobile Devices i am getting the notifications,But not in the tvOS why is it so...?
How can i solve this..?
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
if granted == true
{
print("Allow")
UIApplication.shared.registerForRemoteNotifications()
}
else
{
print("Don't Allow")
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print("DEVICE TOKEN = \(deviceTokenString)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
print(userInfo)
}
tvOS supports only 2 types of notifications: badges and content-available. So you need to send one of these two types to APNS. Any of these types notification only changes badge number on App Icon. And only the lastest notification will be delivered to your application when you open the app. There is no visual presentation of notification as it was on iOS
How it looks see on presentation from WWDC 2016/Session 206/tvOS, start watching from 21:20
UPDATE:
On tvOS 11 appeared Silent notifications which wakes the application up and allow to refresh content in background
WWDC 2017 watch from 7:50
This is my solution for Notifications in tvOS.
in AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// set self (AppDelegate) to handle notification
UNUserNotificationCenter.current().delegate = self
// Request permission from user to send notification
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { authorized, error in
if authorized {
DispatchQueue.main.async(execute: {
application.registerForRemoteNotifications()
})
}
})
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
//print(userInfo)
print("Notification Received")
let nc = NotificationCenter.default
nc.post(name: Notification.Name("foo"), object: nil)
}
The first function provide the permission necessary for notification.
And the second function received the notification and send a notification to the current viewcontroller and make the magic happpend.
This is the viewcontroller
//viewload NotificationCenter.default.addObserver(self, selector: #selector(updateTable(_ :)), name: Notification.Name("foo"), object: nil)
I'm trying to receive changes with CKDatabaseSubscription but I'm stuck that I don't receive any push notifications from that subscription.
I'm subscribing to changes in private record zone via such method:
let container = CKContainer.default()
func subscribeChanges() {
let subscription = CKDatabaseSubscription(subscriptionID: "test")
let notificationInfo = CKNotificationInfo()
notificationInfo.shouldSendContentAvailable = true
subscription.notificationInfo = notificationInfo
let operation = CKModifySubscriptionsOperation(subscriptionsToSave: [subscription], subscriptionIDsToDelete: [])
operation.modifySubscriptionsCompletionBlock = { savedSubscriptions, deletedSubscriptionIDs, operationError in
if operationError != nil {
print(operationError)
return
} else {
print("Subscribed")
}
}
container.privateCloudDatabase.add(operation)
}
Subscribe is successful. But I don't receive any notifications on device, I've checked with such method:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
application.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
NSLog("Notification received")
}
I'm testing on real device connected to XCode, tried to push changes via simulator and Apple's dashboard.
Update
As I've found CKDatabaseSubscription only works for shared record zones. For public & private zone I can use something like that and it works:
CKQuerySubscription(recordType: "TestRecordType", predicate: NSPredicate(format: "TRUEPREDICATE"), options: CKQuerySubscriptionOptions.firesOnRecordCreation)
As for know CKDatabaseSubscription will fire notifications only for record zones with CKRecordZoneCapabilityFetchChanges, all private custom zones have that capability (shared I think also), default zones not.
So only way now is to create new custom zone.
I have problems getting my app to receive notifications while in background or shutdown mode. I have followed Firebase guide on how to implement firebase messaging in my app. Previously I have used GCM (google cloud messaging) and it all worked well, but since upgrading to Firebase I can't get it to work. As soon as I start my app, all the notifications that I sent (through firebase console notifications) while in background or shutdown are delivered.
I have:
Created project on Firebase console
Added my app to the firebase project with the correct bundle id
Created an APNS Development certificate
Uploaded the certificate to my app on Firebase console
Worth mentioning is that I have disabled swizzling by setting FirebaseAppDelegateProxyEnabled to NO in my Info.plist file.
Relevant code:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let types: UIUserNotificationType = [UIUserNotificationType.Badge, UIUserNotificationType.Alert, UIUserNotificationType.Sound]
let settings: UIUserNotificationSettings = UIUserNotificationSettings( forTypes: types, categories: nil )
application.registerUserNotificationSettings( settings )
application.registerForRemoteNotifications()
FIRApp.configure()
}
func connectToFcm() {
FIRMessaging.messaging().connectWithCompletion { (error) in
if (error != nil) {
print("Unable to connect with FCM")
} else {
print("Connected to FCM.")
self.refreshToken()
}
}
}
func refreshToken(){
if let refreshedToken = FIRInstanceID.instanceID().token() {
gcmToken = refreshedToken
userDefaults.setValue(gcmToken, forKey: CONSTANTS.GCM_TOKEN)
if(userDefaults.boolForKey("UserLoggedIn")){
pushGcmToken() //push the token to server
}
}
}
func onTokenRefresh() {
refreshToken()
// Connect to FCM since connection may have failed when attempted before having a token.
connectToFcm()
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
NSLog("didReceiveRemoteNotification \(userInfo)")
FIRMessaging.messaging().appDidReceiveMessage(userInfo)
handleRemoteNotification(userInfo)
}
func handleRemoteNotification(userInfo: [NSObject : AnyObject]){
if let notification = userInfo["notification"] as? [NSObject : AnyObject]{
let bodyNot = notification["body"] as! String
var titleNot = "Ändring"
var category = "UNIFIED_OTHER_CATEGORY"
if(notification["title"] != nil){
titleNot = (notification["title"] as! String == "Call" ? "Inkomande samtal" : notification["title"]) as! String
category = "UNIFIED_CALL_CATEGORY"
}
let notis = UILocalNotification()
notis.alertTitle = titleNot
notis.alertBody = bodyNot
notis.soundName = UILocalNotificationDefaultSoundName // play default sound
notis.userInfo = ["UUID": "122" ] // assign a unique identifier to the notification so that we can retrieve it later
notis.category = category
notis.fireDate = NSDate()
UIApplication.sharedApplication().scheduleLocalNotification(notis)
}
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
NSLog("didRegisterForRemoteNotificationsWithDeviceToken \(deviceToken)")
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox)
}
I have even tried with swizzling on. Same thing happening. I would very much appreciate any help or hint into the right direction.
I configured below things and its work fine for my application,
In info.plist set below two keys
<key>FIRMessagingAutoRegisterEnabledflag</key>
<true/>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
In appdelegate set below code for test notification development mode
[[FIRInstanceID instanceID] setAPNSToken:deviceToken type:FIRInstanceIDAPNSTokenTypeSandbox];
Download GoogleService-Info.plist from firebase console and put it on your application
You have to add "content-available" : true in the JSON payload. Otherwise you won't get push notification in the background mode.
"notification" : {
"content-available" : true,
"body" : "this is body",
"title" : "this is title"
}
I'm not receiving Push Notifications I expect from CloudKit Subscriptions.
Here's what I've done so far:
Enabled the CloudKit and Remote Notifications capabilities.
Created a 'Test' Record Type using the CloudKit dashboard.
Created a subscription for the appropriate record type (Test), which
I can see in the CloudKit dashboard.
Use a physical device to test, which is signed in to iCloud and
connected to the internet.
Set up the app delegate to receive notifications.
Manually Inserted/Updated/Deleted records via the CloudKit portal.
Unfortunately I never receive any push notifications, ever. The code involved is shown below. Literally, this is the only code in a brand new blank project.
// MARK: - SUBSCRIPTIONS
func subscribeToRecordChangesWithRecordType (recordType:String, database:CKDatabase) {
let predicate = NSPredicate(value: true)
let subscription = CKSubscription(recordType: recordType, predicate: predicate, options: CKSubscriptionOptions.FiresOnRecordCreation|CKSubscriptionOptions.FiresOnRecordDeletion|CKSubscriptionOptions.FiresOnRecordUpdate)
database.saveSubscription(subscription, completionHandler: { (savedSubscription, error) -> Void in
if let _error = error {
NSLog("ERROR saving '%#' subscription %#",recordType, _error)
} else {
NSLog("SUCCESS creating '%#' subscription: %#", recordType, savedSubscription)
}
})
}
func createSubscriptions () {
let privateDB = CKContainer.defaultContainer().privateCloudDatabase
let publicDB = CKContainer.defaultContainer().publicCloudDatabase
// NOTE: create a Record Type called 'Test' in the CloudKit dashboard
self.subscribeToRecordChangesWithRecordType("Test", database: privateDB)
self.subscribeToRecordChangesWithRecordType("Test", database: publicDB)
}
// MARK: - PUSH NOTIFICATIONS
func registerForPushNotifications (application: UIApplication) {
self.createSubscriptions()
let settings = UIUserNotificationSettings(forTypes: .Alert, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
NSLog("Registered for Push Notifications with token: %#", deviceToken);
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
NSLog("FAILED to register for Push Notifications. %#", error)
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
NSLog("RECEIVED Push Notification")
NSNotificationCenter.defaultCenter().postNotificationName("PushNotificationReceived", object: userInfo)
}
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
NSLog("RECEIVED LOCAL Push Notification")
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
NSLog("RECEIVED Push Notification with fetchCompletionHandler")
NSNotificationCenter.defaultCenter().postNotificationName("PushNotificationReceived", object: userInfo)
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.registerForPushNotifications(application)
return true
}
Thanks in advance for any tips or suggestions. I hope this isn't a bug and that I'm doing something wrong here ... it should 'just work'!
Cheers
Make sure
You have enabled Push Notification besides CloudKit (and Background Mode if needed) in App's Capabilities tab. And if needed, find the push certificates (one for Dev, one for production) from Developer Portal, download them and install them (by double clicking on them);
You're testing the app on a device. Apple does not push to the simulator.
I am trying to set up a push notification system for my application. I have a server and a developer license to set up the push notification service.
I am currently running my app in Swift. I would like to be able to send the notifications remotely from my server. How can I do this?
Swift 2:
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
UIApplication.sharedApplication().registerForRemoteNotifications()
While the answer is given well to handle push notification, still I believe to share integrated complete case at once to ease:
To Register Application for APNS, (Include the following code in didFinishLaunchingWithOptions method inside AppDelegate.swift)
IOS 9
var settings : UIUserNotificationSettings = UIUserNotificationSettings(forTypes:UIUserNotificationType.Alert|UIUserNotificationType.Sound, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
UIApplication.sharedApplication().registerForRemoteNotifications()
After IOS 10
Introduced UserNotifications framework:
Import the UserNotifications framework and add the UNUserNotificationCenterDelegate in AppDelegate.swift
To Register Application for APNS
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
// If granted comes true you can enabled features based on authorization.
guard granted else { return }
application.registerForRemoteNotifications()
}
This will call following delegate method
func application(application: UIApplication,didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
//send this device token to server
}
//Called if unable to register for APNS.
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
println(error)
}
On Receiving notification following delegate will call:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
println("Recived: \(userInfo)")
//Parsing userinfo:
var temp : NSDictionary = userInfo
if let info = userInfo["aps"] as? Dictionary<String, AnyObject>
{
var alertMsg = info["alert"] as! String
var alert: UIAlertView!
alert = UIAlertView(title: "", message: alertMsg, delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
}
To be identify the permission given we can use:
UNUserNotificationCenter.current().getNotificationSettings(){ (setttings) in
switch setttings.soundSetting{
case .enabled:
print("enabled sound")
case .disabled:
print("not allowed notifications")
case .notSupported:
print("something went wrong here")
}
}
So the checklist of APNS:
Create AppId allowed with Push Notification
Create SSL certificate with valid certificate and app id
Create Provisioning profile with same certificate and make sure to add device in case of sandboxing(development provisioning)
Note: That will be good if Create Provisioning profile after SSL Certificate.
With Code:
Register app for push notification
Handle didRegisterForRemoteNotificationsWithDeviceToken method
Set targets> Capability> background modes> Remote Notification
Handle didReceiveRemoteNotification
To register to receive push notifications via Apple Push Service you have to call a registerForRemoteNotifications() method of UIApplication.
If registration succeeds, the app calls your app delegate object’s application:didRegisterForRemoteNotificationsWithDeviceToken: method and passes it a device token.
You should pass this token along to the server you use to generate push notifications for the device. If registration fails, the app calls its app delegate’s application:didFailToRegisterForRemoteNotificationsWithError: method instead.
Have a look into Local and Push Notification Programming Guide.
registerForRemoteNotification() has been removed from ios8.
So you should use UIUserNotification
CODE EXAMPLE:
var type = UIUserNotificationType.Badge | UIUserNotificationType.Alert | UIUserNotificationType.Sound;
var setting = UIUserNotificationSettings(forTypes: type, categories: nil);
UIApplication.sharedApplication().registerUserNotificationSettings(setting);
UIApplication.sharedApplication().registerForRemoteNotifications();
Hope this will help you.
To support ios 8 and before, use this:
// Register for Push Notitications, if running iOS 8
if application.respondsToSelector("registerUserNotificationSettings:") {
let types:UIUserNotificationType = (.Alert | .Badge | .Sound)
let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
} else {
// Register for Push Notifications before iOS 8
application.registerForRemoteNotificationTypes(.Alert | .Badge | .Sound)
}
Swift 4
I think this is the correct way for setup in iOS 8 and above.
Turn on Push Notifications in the Capabilities tab
Import UserNotifications
import UserNotifications
Modify didFinishLaunchingWithOptions
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let notification = launchOptions?[.remoteNotification] as? [String: AnyObject] {
// If your app wasn’t running and the user launches it by tapping the push notification, the push notification is passed to your app in the launchOptions
let aps = notification["aps"] as! [String: AnyObject]
UIApplication.shared.applicationIconBadgeNumber = 0
}
registerForPushNotifications()
return true
}
It’s extremely important to call registerUserNotificationSettings(_:) every time the app launches. This is because the user can, at any time, go into the Settings app and change the notification permissions. application(_:didRegisterUserNotificationSettings:) will always provide you with what permissions the user currently has allowed for your app.
Copy paste this AppDelegate extension
// Push Notificaion
extension AppDelegate {
func registerForPushNotifications() {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
[weak self] (granted, error) in
print("Permission granted: \(granted)")
guard granted else {
print("Please enable \"Notifications\" from App Settings.")
self?.showPermissionAlert()
return
}
self?.getNotificationSettings()
}
} else {
let settings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
UIApplication.shared.registerForRemoteNotifications()
}
}
#available(iOS 10.0, *)
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { data -> String in
return String(format: "%02.2hhx", data)
}
let token = tokenParts.joined()
print("Device Token: \(token)")
//UserDefaults.standard.set(token, forKey: DEVICE_TOKEN)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If your app was running and in the foreground
// Or
// If your app was running or suspended in the background and the user brings it to the foreground by tapping the push notification
print("didReceiveRemoteNotification /(userInfo)")
guard let dict = userInfo["aps"] as? [String: Any], let msg = dict ["alert"] as? String else {
print("Notification Parsing Error")
return
}
}
func showPermissionAlert() {
let alert = UIAlertController(title: "WARNING", message: "Please enable access to Notifications in the Settings app.", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) {[weak self] (alertAction) in
self?.gotoAppSettings()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alert.addAction(settingsAction)
alert.addAction(cancelAction)
DispatchQueue.main.async {
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
private func gotoAppSettings() {
guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.openURL(settingsUrl)
}
}
}
Check out: Push Notifications Tutorial: Getting Started
Thanks for the earlier answers. Xcode has made some changes and here's the SWIFT 2 code that passes XCode 7 code check and supports both iOS 7 and above:
if #available(iOS 8.0, *) {
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
UIApplication.sharedApplication().registerForRemoteNotifications()
} else {
let settings = UIRemoteNotificationType.Alert.union(UIRemoteNotificationType.Badge).union(UIRemoteNotificationType.Sound)
UIApplication.sharedApplication().registerForRemoteNotificationTypes(settings)
}
Swift 4
Import the UserNotifications framework and add the UNUserNotificationCenterDelegate in AppDelegate
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate
To Register Application for APNS, (Include the following code in didFinishLaunchingWithOptions method inside AppDelegate.swift)
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
application.registerForRemoteNotifications()
This will call following delegate method
func application(_ application: UIApplication,didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
//send this device token to server
}
//Called if unable to register for APNS.
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error)
}
On Receiving notification following delegate will call:
private func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
print("Recived: \(userInfo)")
//Parsing userinfo:
}
Swift 3:
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
UIApplication.shared.registerForRemoteNotifications()
Make sure to import UserNotifications at the top of your view controller.
import UserNotifications
You can send notification using the following code snippet:
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
if(UIApplication.sharedApplication().currentUserNotificationSettings() == settings ){
//OK
}else{
//KO
}
I use this code snip in AppDelegate.swift:
let pushType = UIUserNotificationType.alert.union(.badge).union(.sound)
let pushSettings = UIUserNotificationSettings(types: pushType
, categories: nil)
application.registerUserNotificationSettings(pushSettings)
application.registerForRemoteNotifications()
100% Working... You Can read onesignal document and setup properly https://documentation.onesignal.com/docs/ios-sdk-setup
OneSignal below code import in AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
OneSignal.setLogLevel(.LL_VERBOSE, visualLevel: .LL_NONE)
// OneSignal initialization
let onesignalInitSettings = [kOSSettingsKeyAutoPrompt: false, kOSSettingsKeyInAppLaunchURL: false]
OneSignal.initWithLaunchOptions(launchOptions,
appId: "YOUR_ONE_SIGNAL_ID",
handleNotificationAction: nil,
settings: onesignalInitSettings)
OneSignal.inFocusDisplayType = OSNotificationDisplayType.inAppAlert;
// promptForPushNotifications will show the native iOS notification permission prompt.
// We recommend removing the following code and instead using an In-App Message to prompt for notification permission (See step 8)
OneSignal.promptForPushNotifications(userResponse: { accepted in
print("User accepted notifications: \(accepted)")
})
return true
}