Push notification from AWS SNS console - ios

I'm trying to get notification work on my ios app using SNS. I follow the instruction on AWS mobile hub integration page. I checked the SNS page and I can see my app subscribes to the topic. But when I publish notification from the console by selecting the topic, my app does not get any notification. I've been trying for hours now and I appreciate any clue and theory on this matter.
This is the output log from my app after subscribing to the topic :
<SubscribeResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
<SubscribeResult>
<SubscriptionArn>arn:aws:sns:****:NotifySharing:****</SubscriptionArn>
</SubscribeResult>
<ResponseMetadata>
<RequestId>***</RequestId>
</ResponseMetadata>
</SubscribeResponse>
I replaced some info with **** just to make sure that I'm not disclosing sensitive data. Anyway, what could be the problem?
This is my code that does subscription :
class PushNotificationHandler: NSObject {
func setupPushManager() {
let pushManager: AWSPushManager = AWSPushManager.defaultPushManager()
pushManager.delegate = self
pushManager.registerForPushNotifications()
if let topicARNs = pushManager.topicARNs {
pushManager.registerTopicARNs(topicARNs)
}
if pushManager.enabled {
for topic in pushManager.topics {
topic.subscribe()
}
}
}
}

Related

Some registrations don't seem to be registered in Notification Hub

I'm trying to integrate my Android and iOS native apps with Notification Hub.
For example, on iOS, as soon as I receive the deviceToken and if my user is already authenticated, I register directly with NotificationHub:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
log.debug("Received device token \(deviceToken.hexEncodedString()). Saving device token to user defaults.", "AppDelegate.didRegisterForRemoteNotificationsWithDeviceToken")
let defaults = UserDefaults.standard
defaults.set(deviceToken, forKey: UserNotificationDeviceTokenKey)
defaults.synchronize()
if let authenticatedUser = Session.shared.authenticatedUser {
log.debug("Trying to register device token \(deviceToken.hexEncodedString()) to user \(authenticatedUser.id) on notification hub with hub name \(String(describing: hubName)) and connection string \(String(describing: connectionString)). Notification hub null? \(notificationHub == nil)")
self.notificationHub?.registerNative(withDeviceToken: deviceToken, tags: Set<String>([authenticatedUser.id]), completion: { error in
if (error != nil) {
log.error("Error registering for notifications: \(error.debugDescription)", "AppDelegate.didRegisterForRemoteNotificationsWithDeviceToken");
} else {
log.debug("Successfully registered device token \(deviceToken.hexEncodedString()) for user with ID \(authenticatedUser.id) and email \(authenticatedUser.emailAddress)", "AppDelegate.didRegisterForRemoteNotificationsWithDeviceToken")
}
})
}
}
And whatever happens, I start by saving the device token to the user defaults so that when a user does log in, he can retrieve the deviceToken from user defaults and call the same registerNative method to create a registration associating this deviceToken with a tag that is the user's identifier:
func userDidSignIn(user: User) {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let deviceToken = UserDefaults.standard.data(forKey: UserNotificationDeviceTokenKey){
log.debug("Trying to register device token \(deviceToken.hexEncodedString()) to user \(user.id) on notification hub with hub name \(String(describing: appDelegate.hubName)) and connection string \(String(describing: appDelegate.connectionString)). Notification hub null? \(appDelegate.notificationHub == nil)")
appDelegate.notificationHub?.registerNative(withDeviceToken: deviceToken, tags: Set<String>([user.id]), completion: { error in
if (error != nil) {
log.error("Error registering for notifications: \(error.debugDescription)");
} else {
log.debug("Successfully registered device token \(deviceToken.hexEncodedString()) for user with ID \(user.id) (\(user.emailAddress)) who just logged in", context: "HTRootViewController.userDidSignIn")
}
})
} else {
log.warning("No device token found in settings.", "HTRootViewController.userDidSignIn")
}
}
I have all my logs remotely sent to Bugfender, which lets me check the logs for all the devices using this app installed in TestFlight, so I can see that all the registerNative's are successful:
Trying to register device token
0fea9a4d99ec37dc4f3ac252c35fa4e1617004fd14740973d80a7dfdaeacc857 to
user 77bfb1c6-b05a-440b-a7a0-71ae5a91bbb2 on notification hub with hub
name Optional("[my notification hub name]") and connection string
Optional("[my notification hub connection string]").
Notification hub null? false
Successfully registered device
token 0fea9a4d99ec37dc4f3ac252c35fa4e1617004fd14740973d80a7dfdaeacc857
for user with ID 77bfb1c6-b05a-440b-a7a0-71ae5a91bbb2
([my user's email]) who just logged in
But then, when I load the list of all the registrations using this .NET C# code:
public async Task<List<RegistrationDescription>> GetRegistrations()
{
var hub = NotificationHubClient.CreateClientFromConnectionString([my notification hub connection string], [my notification hub name]);
var result = await hub.GetAllRegistrationsAsync(1000);
return result.ToList();
}
I can see that some registrations are there, but others are nowhere to be found, even though they succeeded according to my mobile logs.
Does anyone know what else could be going on? What else can I check? Why else would registrations not be saved even though the call to registerNative seems to succeed?
EDIT: As a matter of fact, and after checking more precisely what is happening, the registration first appears in the list of registrations returned by Notification Hub, but as soon as we try to send a notification, it's as if Notification Hub could not reach the corresponding device token and deleted the registration altogether. Is there a way to see the logs of Notification Hub to see what is going on and why it decides to delete this registration?
I figured it out. It turns out it was working on my device because I ran the beta in debug mode, so it was getting its device token from the Sandbox APS environment, which matched the Sandbox endpoint configured in Notification Hub, but other users were using the Production APS environment configured in the archive build, so they got device tokens for a different environment and notification hub correctly identified as incorrect device tokens. Once we switched the Notification Hub to the production APNS endpoint, all the TestFlight users were able to receive notifications because their registrations stayed in Notification Hub. Now that does mean that my debug builds trying to use that environments won't receive notifications, but that's OK, that's why we have a different environment and Notification Hub for development purposes. So if anyone sees something similar, don't forget to double check your Notification Hub configuration matches the type of build your users are using, and don't forget that TestFlight builds are now production-type builds, same as App Store builds.

Firebase Cloud Messaging to Android works but iOS fails. How to structure payload for iOS?

I can send test notifications from Firebase console to my app both on iOS and Android. Therefore, my app is set up properly to receive push notifications on both platforms. However, when I use a cloud function to send notifications. Only, notifications on the Android device are received. No notifications show up on the iOS device. I suspect this may be related to the way that I create the payload in my cloud function. Maybe I am missing something for iOS. If you could give me some tips, that would be great.
I checked if the deviceToken for the iOS device is correct and it was correct.
I sent a test message using firebase console to the same deviceToken for the iOS device and the notification was delivered.
Therefore, I concluded my problem may arise from the cloud function I wrote. Thus, I share below the cloud function:
exports.notifToApp = functions.database.
ref(`/memInfo/{memId}/notifChoice/`).onWrite((snap, context) => {
//send only if exists and new notification OR if doesn't exist
if ((snap.before.exists() && (snap.after.val() !== snap.before.val())) || !snap.before.exists()) {
//get notification body
const notificationTitle = snap.after.val().memName;
const notificationText = snap.after.val().notifText;
//get and loop over notification subscribers
return admin.database().ref(`/notifics/${context.params.memId}/notifSubs/`).once("value", subs => {
if (subs.exists()) {
return subs.forEach(sub => {
//payload for notification
const payload = {
"notification":{
"title": notificationTitle,
"body": notificationText,
"sound": "default",
"click-action": "FCM_PLUGIN_ACTIVITY",
"priority": "high"
}
}
//deliver notification
return admin.messaging().sendToDevice(sub.val().deviceToken, payload).catch(e => {console.log(e);});
});
} else { //end: if returned any value
return 0;
}
});// end: get and loop over notification subscribers
} else { //end: send only if exists and new notification OR if doesn't exist
return 0;
}
});
I do not get any error messages. Function completes successfully with status "OK."
I test using two devices: one android and one iOS. Both device tokens are saved correctly in the database for the cloud function to retrieve and use for sending messages.
I see the notification on the Android device running my app. I wish the notification to show up on the iOS device running the same app.
Test message notification sent from the firebase console shows up on both devices correctly.
I realized that sendToDevice() used the legacy version of payload. I used send() in my function to use the newer version. (see answer: stackoverflow)
admin.messaging().send(payload).catch(e => console.log(e));
I changed the payload to include platform specific fields according to the latest guidelines (see firebase docs)
const payload = {
"token": sub.val().deviceToken,
"notification":{"title": notificationTitle,"body": notificationText},
"android": {"notification": {"sound": "default"}},
"apns": {"payload": {"aps": {"sound": "default"}}}
};
Now it works on both platforms.

Stripe SDK integration using swift and flutter

I am trying to integrate Apple Pay in iOS using flutter. I am using method channels to communicate with swift and get the payment process completed. I have followed the documentation which is in this link
However, I believe I have stuck in the very ending part which I don't understand how to continue the flow. Since I am using flutter UIs, I don't need iOS ViewControllers.
This is the code that I have tried so far in the AppDelegate.swift:
func handleApplePayButtonTapped(result: FlutterResult){
let merchantIdentifier = "my.apple.merchant.id"
let paymentRequest = Stripe.paymentRequest(withMerchantIdentifier:merchantIdentifier, country:"US", currency:"USD")
paymentRequest.paymentSummaryItems = [
PKPaymentSummaryItem(label:"Fancy Hat", amount:50.00),
PKPaymentSummaryItem(label:"iHats, Inc", amount:50.00),
]
if Stripe.canSubmitPaymentRequest(paymentRequest){
//next steps ???
result(String("Can submit payment request"))
}else{
result(String("Can't submit payment request"))
}
}
I am calling this code in flutter UI using this code:
Future<void> _doPayment() async {
String returnMsg;
try {
final bool result = await platform.invokeMethod('checkIfDeviceSupportsApplePay');
if(result){
final String status = await platform.invokeMethod('handleApplePayButtonTapped');
print(status);
}
returnMsg = '$result';
} on PlatformException catch (e) {
returnMsg = "Failed: '${e.message}'.";
}
print(returnMsg);}
I already have a Stripe publishable key as well as a Heroku deployed backend. If you checked my swift code, you will see where I am stuck at the moment.
As I have understood the flow, what is remaining to be done is
send the card details to the backend and get a token
using the token, send the payment details to the Stripe server
I am very new to swift language and code samples will be greatly helpful for me to continue with.
Thank you.
It looks like you're following the Stripe Custom iOS Integration, using the native PKPaymentAuthorizationViewController.
You should read through the integration steps here: https://stripe.com/docs/mobile/ios/custom#apple-pay
Basically, your next steps would be
instantiate a PKPaymentAuthorizationViewController with the paymentRequest
Set yourself to its delegate
present the PKPaymentAuthorizationViewController
implement the relevant delegate methods to get back an Apple Pay token (PKToken)
convert PKToken to STPToken (a Stripe token)
All these steps and more are detailed in the link above.

Azure Push Notification Error from IOS Xcode

I feel I am the first one in the universe trying to get iOS swift working with Azure, not much help out there.
I followed this Create an iOS app
and then Add Push Notifications to your iOS App. I am supposed to be able to do a successful push notification from iPhone, but I get this error. btw: I can get my C# code to trigger in visual studio in my pc (using this tutorial), so the request seems to be working, but the response sucks. Any one knows how to fix it!!
Error registering for notifications: Optional("Error Domain=com.Microsoft.MicrosoftAzureMobile.ErrorDomain Code=-1302 \"{\"message\":\"An error has occurred.\"}\" UserInfo={com.Microsoft.MicrosoftAzureMobile.ErrorRequestKey=<NSMutableURLRequest: 0x14cebf780> { URL: http://<mysite>.azurewebsites.net/push/installations/1E32E9B5-E976-4CCD-BD61-D026D3F4FF1C }, com.Microsoft.MicrosoftAzureMobile.ErrorResponseKey=<NSHTTPURLResponse: 0x14cec54b0> { URL: http://<mysite>.azurewebsites.net/push/installations/1E32E9B5-E976-4CCD-BD61-D026D3F4FF1C } { status code: 500, headers {\n \"Content-Length\" = 36;\n \"Content-Type\" = \"application/json; charset=utf-8\";\n Date = \"Wed, 11 May 2016 21:39:39 GMT\";\n Server = \"Microsoft-IIS/8.0\";\n \"Set-Cookie\" = \"ARRAffinity=8d79cd782ff16b44f7f280b76e2bc5564d86e0d1b228227b8e0033f4bb1c4582;Path=/;Domain=<mysite>.azurewebsites.net\";\n \"X-Powered-By\" = \"ASP.NET\";\n} }, NSLocalizedDescription={\"message\":\"An error has occurred.\"}}")
UPDATE #1
The only url I have is the one per the tutorial. The rest of the code is identical to the ones I mentioned in the links (I copied it character by character):
class ClientManager {
static let sharedClient = MSClient(applicationURLString: "http://<mysite>.azurewebsites.net")
}
UPDATE #2
#Pau Senabre I am working with swift not Objective-C per my question (see my tags under question), so I don't have an .m file per your step #1. I also don't have the logErrorIfNotNil you mentioned. My method (which is generated by Azure before modifications) looks like this:
#IBAction func addItem(sender : AnyObject) {
self.performSegueWithIdentifier("addItem", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!)
{
if segue.identifier == "addItem" {
let todoController = segue.destinationViewController as! ToDoItemViewController
todoController.delegate = self
}
}
UPDATE #3
#Pau Senabre My goal is to do mobile apps/services, not mobile engagement. See the difference here. btw: I had followed the azure engagement example when I started playing with it and had worked for me. But my need now is web/mobile apps. So, does what you suggested still apply for my need?
Could you please post some code? I think you may be using a wrong URL in a certain place.
To UPDATE #2
Check the following link:
https://github.com/Azure/azure-content/blob/master/articles/mobile-engagement/mobile-engagement-ios-swift-get-started.md
In section Modify your Application Delegate make sure you create a reach module and your existing Engagement initialization has all the init Values.
EngagementAgent.init("Endpoint={YOUR_APP_COLLECTION.DOMAIN};SdkKey={YOUR_SDK_KEY};AppId={YOUR_APPID}", modulesArray:[reach])
The error Code provided Error Domain=com.Microsoft.MicrosoftAzureMobile.ErrorDomain Code=-1302 matches to a bad request. If you are entering some data, make beforehand a Data Input Validation:
1 In the TodoService.m file, locate the addItem method search for the [self logErrorIfNotNil:error]; line of code. Beneath that line of code, replace the remainder of the completion block with the following code that checks to see if there was an error in the request and if that error code was –1302, indicating a bad request:
BOOL badRequest = ((error) && (error.code == -1302));
// detect text validation error from service.
if (!badRequest) // The service responded appropriately
{
NSUInteger index = [itemscount];
[(NSMutableArray *)itemsinsertObject:result atIndex:index];
// Let the caller know that we finished
completion(index);
}
2 Build and run; you can see in the Xcode output window that the bad request error from the service was handled:
2012-10-23 22:01:32.169 Quickstart[5932:11303] ERROR Error Domain=com.Microsoft.WindowsAzureMobileServices.ErrorDomain Code=-1302 “Text length must be under 10″ UserInfo=0x7193850 {NSLocalizedDescription=Text length must be under 10, com.Microsoft.WindowsAzureMobileServices.ErrorResponseKey=, com.Microsoft.WindowsAzureMobileServices.ErrorRequestKey=https://task.azure-mobile.net/tables/TodoItem>}
3 Finally, in the TodoService.m file, locate the logErrorIfNotNil method, which handles the logging of errors to the output window. Inside the if code block, just below the line NSLog(#”ERROR %#”, error); add the following if block:
// added to display description of bad request
if (error.code == -1302){
UIAlertView *av =
[[UIAlertView alloc]
initWithTitle:#”Request Failed”
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#”OK”
otherButtonTitles:nil
];
[av show];
}
Aditionally, review the following steps in the Azure Setup, maybe you are missing something at some point:
https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-ios-get-started-push/
1 Create a Notification Hub
This creates a new notification hub and connects it to your mobile app. If you have an existing notification hub, you can choose to connect it to your Mobile App backend instead of creating a new one.
2 Register app for push notifications
Register an App ID for your app. Create an explicit App ID (not a wildcard App ID) and for Bundle ID, use the exact Bundle ID that is in your Xcode quickstart project. It is also crucial that you check the Push Notifications option.
Next, configuring push notifications. You may create either a "Development" or "Distribution" SSL certificate (remember to select the corresponding option in the Azure portal later.)
3 Configure Azure to send push notifications
In the Azure portal, click Browse All > App Services > your Mobile App backend > Settings > Mobile > Push > Apple Push Notification Services > Upload Certificate. Upload the .p12 file, selecting the correct Mode (corresponding to whether the client SSL certificate you generated earlier was Development or Distribution.)
4 Update server project to send push notifications
Replace the PostTodoItem method with the following code:
public async Task<IHttpActionResult> PostTodoItem(TodoItem item)
{
TodoItem current = await InsertAsync(item);
// Get the settings for the server project.
HttpConfiguration config = this.Configuration;
MobileAppSettingsDictionary settings =
this.Configuration.GetMobileAppSettingsProvider().GetMobileAppSettings();
// Get the Notification Hubs credentials for the Mobile App.
string notificationHubName = settings.NotificationHubName;
string notificationHubConnection = settings
.Connections[MobileAppSettingsKeys.NotificationHubConnectionString].ConnectionString;
// Create a new Notification Hub client.
NotificationHubClient hub = NotificationHubClient
.CreateClientFromConnectionString(notificationHubConnection, notificationHubName);
// iOS payload
var appleNotificationPayload = "{\"aps\":{\"alert\":\"" + item.Text + "\"}}";
try
{
// Send the push notification and log the results.
var result = await hub.SendAppleNativeNotificationAsync(appleNotificationPayload);
// Write the success result to the logs.
config.Services.GetTraceWriter().Info(result.State.ToString());
}
catch (System.Exception ex)
{
// Write the failure result to the logs.
config.Services.GetTraceWriter()
.Error(ex.Message, null, "Push.SendAsync Error");
}
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
this is the proper answer from another question I had earlier, which fixes both: Registration and Receiving push notifications. I said this in here:
I finally have been able to receive notifications last night. I went ahead and redid an entire walk-through of all apple-side installation steps using this tutorial: Sending push notifications to iOS with Azure Notification Hubs then the azure-side of things using this: Create an iOS app and Add Push Notifications to your iOS App. That took care of the registering the app of the device successfully, which I was able to verify using the note of AdrianHall in this thread. But that wasn't enough. The Azure tutorials fell short detailing the steps needed in Xcode, which I found here: [How To] Setup Remote Push Notification in iOS - Swift 2.0 Code I didn't have to setup any "push notification" in Xcode or anything like that.
I hope this detailed answer will save you many hours of digging through.

how to send verification code by sms in swift 2

i build a register form for my app and i need to send the user a verifiation code by sms in order to complete the registration proccess.
i tried to use MFMessageComposeViewController but its open the dialog sms on the device so the user can see the code.
i also checked the web for 3party of sending sms but there is a problem with the country code. i know its posible becuse whatsapp do it to confirm the user.
what it the right way to do it?
this is the topic the i tried:
Sending SMS in iOS with Swift
The best way to achieve this is by creating some views for allowing the user to enter the phone number with the country code which can be used by a server to send a request for initiating the OTP verification. To achieve this you need to:
Create View Controllers.
Upload Phone Number and Country code to the server.
Validate the requests by verifying the OTP.
As mentioned by Dan, you can use Digits in Fabric for that purpose, and create custom views for a great UX.
On the other hand, you can also use a service called as SendOTP from MSG91 - you can use it for internal testing and development ideas as they provide you with 5,000 free OTP SMS. The service has a complete set of APIs which you can implement on the backend as well on the app front. Also, they provide a framework so that you don't need to create the views, but only presentViewController and call delegate methods for knowing what happened during the verification process - such as Cancelled or Verified or Failed.
Swift implementation of the same looks like this:
class OTPFrame: UIViewController, sendOTPAuthenticationViewControllerDelegate {
func loadOTPFramework() {
SendOTP.sharedManager().startWithApiId("yourAppID")
let frameworkPath: NSString = NSBundle.mainBundle().privateFrameworksPath!
let frameworkBundlePath: NSString = frameworkPath.stringByAppendingPathComponent("SendOTPFramework.framework")
let frameworkBundle : NSBundle
= NSBundle(path: frameworkBundlePath as String)!
let authenticationViewController: AuthenticationViewController = AuthenticationViewController(nibName: "AuthenticationViewController", bundle: frameworkBundle)
authenticationViewController.delegate = self self.presentViewController(authenticationViewController, animated: true, completion: nil)
}
func authenticationisSuccessfulForMobileNumber(mobNo: String!, withCountryCode countryCode: String!) {
print("Success")
}
func canceledAuthentication() {
print("Cancelled")
}
func authenticationisFailedForMobileNumber(mobNo: String!, withCountryCode countryCode: String!) {
print("Failed")
}
}
Disclaimer: I, in no way, endorse the services mentioned above - you are free to choose whatever you like.
Thank You!
I would give digits a try! It's part of the Twitter Fabric package and it's very simple to use. The user enters their phone number and Fabric takes care of validating the number.

Resources