Problem with receiving topic notifications in Swift - ios

I am having some problems with sending fcm notifications directly from my SwiftUI app.
When I try to send a notification to token like this, it will work ok:
let request1 = NSMutableURLRequest(url: URL(string: "https://fcm.googleapis.com/fcm/send")!)
request1.httpMethod = "POST"
request1.setValue("application/json", forHTTPHeaderField: "Content-Type")
request1.setValue("\(serverKey)", forHTTPHeaderField: "Authorization")
let json = [
"to": "my token",
"notification": [
"title": "Notification Title",
"body": "Notification Body"
]
] as [String : Any]
let jsonData = try! JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
request1.httpBody = jsonData
URLSession.shared.dataTask(with: request1 as URLRequest) { (data, response, error) in
if error != nil {
print(error!)
return
}
print(response!)
}.resume()
This code works, but when I change the line "to": "my token" into
"topic": "/topics/test_topic"
or
"topic": "test_topic"
It won't send notification to any of the devices subscribed to topic.
Devices are connected to topic, because if I send notification to topic from Firebase console, it works OK.
Is there any way to send a notification to topic directly from a SwiftUI app?
I would like to be able to send notifications from a SwiftUI app directly, without backend servers.

Related

how to schedule push notifications sent between users for iOS

so far I was able to send notifications from userA to UserB using url and json but I couldn't figure a way to schedule a time for the notification to be sent and I would like to repeat it every week or a month according to the user's needs, this is the code I have so far :
func sendNotification(to token: String, title: String, body: String) {
let urlString = "https://fcm.googleapis.com/fcm/send"
let url = NSURL(string: urlString)!
let paramString: [String : Any] = ["to" : token,
"notification" : ["title" : title, "body" : body],
"data" : ["user" : "test_id"]
]
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject:paramString, options: [.prettyPrinted])
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("key=\(legacyServerKey)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
do {
if let jsonData = data {
if let jsonDataDict = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] {
NSLog("Received data:\n\(jsonDataDict))")
}
}
} catch let err as NSError {
print(err.debugDescription)
}
}
task.resume()
}
how can I send the notification at a specific time and how can I repeat it ?
I haven't tried it, you can schedule LocalNotifications for a specific time and date, on receiving of localnotification you can do it by enabling background mode.

Push notification Localization are displayed in the sending device language, not in the receiving one. Swift

I am translating my app and I'm having an issue with remote notifications.
Following documentation
Storing Localized Content in Your App Bundle
If you use a consistent set of messages for your notifications, you
can store localized versions of the message text in your app bundle
and use the loc-key and loc-args keys in your payload to specify which
message to display. The loc-key and loc-args keys define the message
content of the notification. When present, the local system searches
the app’s Localizable.strings files for a key string matching the
value in loc-key. It then uses the corresponding value from the
strings file as the basis for the message text, replacing any
placeholder values with the strings specified by the loc-args key.
(You can also specify a title string for the notification using the
title-loc-key and title-loc-args keys.)
I have NSLocalizedString for title, subtitle and body. The problem is that when I send the notification from a English set device, I get an English remote notification on an Italian set device. I'm storing all Localized key:value pair in the Localizable.string file so as I understood it to work, on receiving the notification the values from the current device language should be taken from the Localizable.string file and get displayed. I do the same for all the alert pups and it works perfectly there, but on remote notifications it keeps the language of the sending device. I'm using FCM for the remote notifications, so I might miss to pass some value in the payload. Can you see something missing in the payload?
Many thanks.
static func sendPushNotification(to receiverToken: String, title: String, subtitle: String, body: String) {
print("PushNotifications.sendPushNotification Started")
let serverKey = firebaseServerKey
let url = NSURL(string: "https://fcm.googleapis.com/fcm/send")
let postParams: [String : Any] = [
"to": receiverToken,
"mutable_content": true,
"content_available": true,
"priority": "high",
"notification": [
// "badge" : 1, sendig the badge number, will cause aglitch
// receiving device localized parameters
"title_loc_key" : title,
"subtitle_loc_key" : subtitle,
"body_loc_key" : body,
"sound" : true, // or specify audio name to play
],
"data" : [
"data": "ciao",
]
]
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST"
request.setValue("key=\(serverKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
do {
// request.httpBody = try JSONSerialization.data(withJSONObject: postParams, options: JSONSerialization.WritingOptions())
request.httpBody = try JSONSerialization.data(withJSONObject: postParams, options: [.prettyPrinted]) // working
print("My paramaters: \(postParams)")
} catch {
print("Caught an error: \(error)")
}
let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
if let realResponse = response as? HTTPURLResponse {
if realResponse.statusCode != 200 {
print("Not a 200 response")
}
}
if let posData = data {
if let postString = String(data: posData, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) as String? {
print("POST: \(postString)")
}
}
}
task.resume()
}
This is the function calling:
PushNotifications.sendPushNotification(to: customerFcmToken, title: String(format: NSLocalizedString( "ORDER_DELETED_PUSH_TITLE", comment: ""), orderId), subtitle: String(format: NSLocalizedString( "ORDER_DELETED_PUSH_SUBTITLE", comment: ""), UserDetails.fullName!), body: String(format: NSLocalizedString("ORDER_DELETED_PUSH_BODY", comment: "") , customerName))
I got it you calling
PushNotifications.sendPushNotification(to: customerFcmToken, title: String(format: NSLocalizedString( "ORDER_DELETED_PUSH_TITLE", comment: ""), orderId), subtitle: String(format: NSLocalizedString( "ORDER_DELETED_PUSH_SUBTITLE", comment: ""), UserDetails.fullName!), body: String(format: NSLocalizedString("ORDER_DELETED_PUSH_BODY", comment: "") , customerName))
And you are doing the localization, instead of sending the keys for localization.
I post an answer so to make a good recap for others stumbling upon this.
Main problem, as stated in the accepted answer, was that I was passing a "localised" String instead of a "reference" String for Localisation to get values from proper Localizable.string file.
As my Localizable.string strings have a format, loc-argsare also needed to be passed with the payload in order to swap placeholders for values. I had them in %1#, %2#, etc etc form but the correct form is a plain and simple %#.
So after modifications the code is:
static func sendPushNotification(to receiverToken: String, title: String, titleArgs: [String], subtitle: String, subtitleArgs: [String], body: String, bodyArgs: [String]) {
print("PushNotifications.sendPushNotification Started")
let serverKey = firebaseServerKey
let url = NSURL(string: "https://fcm.googleapis.com/fcm/send")
let postParams: [String : Any] = [
"to": receiverToken,
"mutable_content": true,
"content_available": true,
"priority": "high",
"notification": [
// "badge" : 1, sendig the badge number, will cause aglitch
// receiving device localized parameters
"title_loc_key" : title,
"title_loc_args" : titleArgs,
"subtitle_loc_key" : subtitle,
"subtitle_loc_args" : subtitleArgs,
"body_loc_key" : body,
"body_loc_args" : bodyArgs,
"sound" : true, // or specify audio name to play
],
"data" : [
"data": "ciao",
]
]
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST"
request.setValue("key=\(serverKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
do {
// request.httpBody = try JSONSerialization.data(withJSONObject: postParams, options: JSONSerialization.WritingOptions())
request.httpBody = try JSONSerialization.data(withJSONObject: postParams, options: [.prettyPrinted]) // working
print("My paramaters: \(postParams)")
} catch {
print("Caught an error: \(error)")
}
let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
if let realResponse = response as? HTTPURLResponse {
if realResponse.statusCode != 200 {
print("Not a 200 response")
}
}
if let posData = data {
if let postString = String(data: posData, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) as String? {
print("POST: \(postString)")
}
}
}
task.resume()
}
and it's called as :
PushNotifications.sendPushNotification(to: customerFcmToken, title: "BOOKING_DELETED_PUSH_TITLE", titleArgs: [self.bookingId], subtitle: "BOOKING_DELETED_PUSH_SUBTITLE", subtitleArgs: [UserDetails.fullName!], body: "BOOKING_DELETED_PUSH_BODY", bodyArgs: [customerName])
Thank you again.

Request working properly with postman but fails with protocol error when requesting with iOS

I am trying to run this request and the reason I am doing is that from here is that I want to test push notifications (I mean a lot of them) with different strings in a loop.
When I try to do it with Postman it works perfectly fine I mean I had tried sending 300 requests and they worked fine but the request is giving an error when I am doing it with URLSession.
The error which I get on console is-
error is there in data task
The operation couldn’t be completed. Protocol error
2018-01-10 08:48:47.735403+0530 Universal Time Table[18533:1210462] TIC Read Status [4:0x0]: 1:57
2018-01-10 08:48:47.735555+0530 Universal Time Table[18533:1210462] TIC Read Status [4:0x0]: 1:57
My code for sending requests is -
func testNotifications(times: Int, completionHandler: #escaping (String) -> Void) {
print("test notifications called")
let url = "https://fcm.googleapis.com/fcm/send"
var urlRequest = URLRequest(url: URL(string: url)!)
urlRequest.httpMethod = "POST"
let session = URLSession(configuration: .ephemeral)
urlRequest.setValue("Content-Type", forHTTPHeaderField: "application/json")
urlRequest.setValue("Authorization", forHTTPHeaderField: "Key=my_API_KEY")
do{
let jsonData = try JSONSerialization.data(withJSONObject: notificationJson, options: .prettyPrinted)
urlRequest.httpBody = jsonData
} catch let err as Error{
print("error is", err.localizedDescription)
}
session.dataTask(with: urlRequest) { (data, response, error) in
if error != nil{
print("error is there in data task\n", error?.localizedDescription ?? "Error Hard Coded String")
return
}
print("response is", response)
print("Shoud be a success")
DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.main.async {
completionHandler("responseString")
print("success")
}
}
}.resume()
}
let notificationJson = [
"notification":[
"body" : "This week's edition is now available.",
"title" : "NewsMagazine.com",
],
"data" : [
"volume" : "3.21.15",
"contents" : "http://www.news-magazine.com/world-week/21659772"
],
"android":[
"priority":"normal"
],
"apns":[
"headers":[
"apns-priority":"5",
"apns-collapse-id": "ON"
]
],
"webpush": [
"headers": [
"Urgency": "high"
]
]
,
"to" : "device_token"
] as [String : Any]
Your headers are incorrect. You should exchange the key-value pair like this:
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.setValue("Key=my_API_KEY", forHTTPHeaderField: "Authorization")

Http POST request in swift to send an email via Mailjet

I'm trying to send an email with the Mailjet API v3 with a http post request but I'm getting an error 400.
I used the exact same body with success in Javascript, but I guess the error 400 is related with it...
Any ideas ?
var recipients = [Any]()
recipients.append(["Email": "email#gmail.com"])
var body: [String: Any] = [
"FromEmail": "anEmail#gmail.com",
"FromName": "Me",
"Subject": "YEEES",
"Text-part": "Greetings from IOS ;)",
"Recipients": recipients
]
var request = URLRequest(url: self.apiURL)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Authorization", forHTTPHeaderField: "Basic <keysInBase64>")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])
}
catch {
print("error during JSON serialization")
dump(error)
return
}
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
print(error)
print(response)
print(data)
})
task.resume()
Headers was wrong...
I was doing :
request.setValue("Authorization", forHTTPHeaderField: "Basic <keysInBase64>")
Instead of :
request.setValue("Basic <keysInBase64>", forHTTPHeaderField: "Authorization")
Using the Charles Proxy as suggested by #LouFranco, I was able to find the mistake.

send push notification to device directly from a device swift 3

Is there any way to send push notification from a device to device directly using https://fcm.googleapis.com/fcm/send.
My code:
func sendPushNotification(toUser: String, message: String) {
let urlString = "https://fcm.googleapis.com/fcm/send"
let url = NSURL(string: urlString)!
let paramString = "to="
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = "POST"
request.httpBody = paramString.data(using: String.Encoding.utf8)!
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
do {
if let jsonData = data {
if let jsonDataDict = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] {
NSLog("Received data:\n\(jsonDataDict))")
}
}
} catch let err as NSError {
print(err.debugDescription)
}
}
task.resume()
}
let paramString = ["to" : FCM_ID/UDID, "notification" : ["title" : NOTIFICATION_TITLE, "body" : NOTIFICATION_BODY], "data" : ["user" : USER_ID, "image" : IMAGE_URL, "extrainfo" : ANY_STRING]]
just replace your line with this one and push notification should deliver.
Read more about it here... https://firebase.google.com/docs/cloud-messaging/server
Old post but it's worth to add to accepted solution as it can be helping others. You can check my post Push Notifications are delivered but didReceiveRemoteNotification is never called Swift or here didReceiveRemoteNotification function doesn't called with FCM notification server.
You must have "content_available": truein your notification definition or push notification service(with underscore) or your didReceiveRemoteNotification won't be called and you won't be able to use userInfo.

Resources