I have a iPhone app using Swift 2 and Parse as a back-end. In my app people can like everybody else pictures (the same way you will do it in Instagram). The notification system works well and everytime someone like a picture, I'm sending a Parse notification to the picture owner like this :
let pushQuery:PFQuery = PFInstallation.query()
pushQuery.whereKey("user", equalTo: receiver)
let push:PFPush = PFPush()
let data = [
"alert" : message,
"badge" : "Increment",
"sound" : "default"
]
push.setData(data)
push.setQuery(pushQuery)
push.sendPushInBackground()
I'm facing a problem when I have a lot of users liking a picture in a short amount of time. The problem is that the receiver is flooded by Notifications and I don't want the receiver to have his phone vibrating (or ringing) 100 times in 5 minutes.
As I'm a new user using parse I have no idea if I have to modify some parameters in the server (Parse) of I have to do it programmatically (and I have no idea how to do this).
Anyone has an idea ? Thank you !
A possible solution. create an extra column in your parse table called lastPush. In this store the date and time of the last push. So when you send a push first check to see if there is a lastPush value, if not send one and store the time. If there is a date then check if it's in your range, you can decide if you want to send a push or not. If you do remember to save the new time.
Related
I work on an iPhone app (Swift 4), I do some updates on my database at few nodes at the same time.
Both following methods work but I'm wondering what's the most "clean" one ?
Way 1 :
let idNotification = BaseViewController.database.child("notifications").childByAutoId().key
BaseViewController.database.child("notifications").child(idNotification).updateChildValues(["content" : "some content"])
BaseViewController.database.child("users").child(userID).child("notifications").updateChildValues(["something" : true])
Way 2 :
let idNotification = BaseViewController.database.child("notifications").childByAutoId().key
let createNotif = ["content" : "some content"]
let notifToUser = ["something" : true]
BaseViewController.database.updateChildValues(["/notifications/\(idNotification)" : createNotif, "/users/\(userID)/notifications" : notifToUser])
And if that makes any difference in case of crash ? With the first one, if one of the two update request fail the other one will not be impacted. What happens with method 2 if there is a fail just for one of the two ?
Thanks !
The first snippet send multiple write operations to the database (one for each call to updateChildValues). The second snippet sends exactly one write operation.
Both are completely valid, and either could be what you want. For example, if there's no relation between the two updates, then it makes sense to send them separately. But if the updates are related, it makes more sense to send them in one go, since that allows the security rules of your database to allow/reject them as one write operation.
I am developing a messaging app in iOS that could have an option for user to delay sending message for a specific amount of time ( ex. Send this text 5 hours latter) and I am using Firebase as my database. Anyone know how to achieve this functionality?
Specifically:
Every time sending a message I use Firebase.child(XXX).setValue(message)
to add a new message into firebase
Then use
Firebase.child(XXX).observeEventType(.ChildAdded,...
to get newly send message
Is there any way I could delay the setValue action for an amount of time and therefore achieve the functionality? Or there is better way to do it.
There is nothing like that in Firebase out of the box.
The easies solution for you here is to add filed like validFrom to your message and on client side to filter all messages by this field.
You can even add validTo field and make messages expire!
Its probably not a good idea to delay setting a value to your Firebase Database, You can with Timer but then you have to be sure that your app has to be still in running mode five hours after you have started the Timer. Which i think even you understand is a stupid idea.
But what you can do is set your value to your Database with a timestamp as a key.
While setting the value :-
let timeStamp = Int(NSDate().timeIntervalSince1970)
FIRDatabase.database().reference().child("your_path").setValue(["\(timeStamp)":"yourMessage"])
Where
timeStamp :- is your current timeStamp
yourMessage :- is your message
Whenever you wanna retrieve your message retrieve that message's timeStamp and check wether or not 5 hours have passed.
FIRDatabase.database().reference().child("your_path").observeSingleEventOfType(.Value, withBlock:{(snap) in
if let messageDict = snap.value as? [String:AnyObject]{
for each in messageDict{
let timeKey = Int(each.0) //your TimeStamp
let datePosted = NSDate(timeIntervalSince1970: TimeInterval(timeKey))
let elapsedTime_Hours = Int(NSDate().timeIntervalSince(datePosted as Date)/3600)
if elapsedTime_Hours >= 5 {
print("Five hours have passed and now the user can see the message")
}
}
}
})
I can get CKSubscription work using CKNotificationInfo() and CKNotificationInfo.alertBody. So I can send one piece of information. I'm trying to make CKSubscription send the user something like message, username, location, etc in a dictionary format. I've dabbled with CKNotificationInfo.alertLocaliztionKey and CKNotificationInfo.alertLocaliztionArgs but just can't seem to make it work. It feels like i'm missing something small because CKSubscription shouldn't be this troublesome to make it work.
Because that is not what is intended in the notification framework. What you do get back is information about WHAT has changed, and then you have to fetch this data and do what ever you want to do. I have made an app which both tells the user that something has changed and silently in the back refreshes the local data:
let cloudKitNotifiction = CKQueryNotification(fromRemoteNotificationDictionary: uif)
if cloudKitNotifiction.notificationType == CKNotificationType.Query{
if let recordId = cloudKitNotifiction.recordID{
let ccs = CloudCoreSynchronizer()
ccs.syncOneCustomerFromCloudToCore(recordId)
resetBadgeCounter()
}
}
To make this work you have to enable push notifications and background modes, if you want it to happen when the app is in the background.Hope this helps. PS: Just disregard the inapp purchase thing, it has nothing to do with this
I've spent two days no reading and testing as there is a lot of info about this topic.
Unfortunately I've found no solution yet. I can't implement my own authentication as this doesn't help with the issue I want to solve (see Backgrounding at the end of the question).
Here is my current best approach:
I'm generating a UUID thanks to https://stackoverflow.com/a/8677177/1443733 and storing it in the KeyChain as suggested with SwiftKeychainWrapper (https://github.com/jrendel/SwiftKeychainWrapper)
The short nice and sweet code for that is:
let stored = KeychainWrapper.stringForKey("UUID")
if stored != nil {
Helper.log(TAG, msg: "retrieved from keychain: \(stored!)")
} else {
let theUUID = CFUUIDCreate(nil)
let str = CFUUIDCreateString(nil, theUUID)
Helper.log(TAG, msg: "generated UUID: \(str)")
let ret = KeychainWrapper.setString(str, forKey: "UUID")
Helper.log(TAG, msg: "setkeychain: \(ret)")
}
But the UUID stored in the keychain seems to be per device and not per store ID as well.
When I store the UUID like above and login with a different Store ID on the device KeychainWrapper.stringForKey("UUID")still returns the value of the other user.
Isn't their a way to store a value in a store-id keychain?
It seems that I'm so close so I hope someone could point me in the right direction.
If that approach (with a keychain) can't succeed please let me know as well.
I reckon you can ask a different question as well: Is there some cryptic data I can read/generate or a store which changes with the Store Id currently used on a device?
Ohh... and Swift examples preffered ;)
Backgroundinfo:
I use IAPs in my app and want to save the Store-Id of the user once a refresh of the receipt is valid.
On each start of the app I check if the current Store-Id is the same as the saved one. If not I trigger immediately a refresh of the receipt. If it fails I fall back to the free version of the app.
iOS devices do not support multiple users.
If you want to differentiate between users you will have to do that in your app, perhaps with a user login. Then save a UUID per userID in the Keychain.
As NSUserdefaults temporarily stores the UUID.so,after unistalling the app and again installing,the UID changes. So, UUID must be stored in keychain
i got an application, using parse.com i established a back-end
my problem is this; is there a way to send customized push notifications based on the information i gathered?
Something like this;
[#"Hello %#, Don't forget to brush your teeth",userName]
or
a system that i can use the gathered data to send push notifications. for example
CTTelephonyNetworkInfo *netInfo =[[CTTelephonyNetworkInfo alloc]init];
CTCarrier *carrier =[netInfo subscriberCellularProvider];
testUser[#"Carrier"]=[carrier carrierName];
I get carrier of the user like this and lets say i want to send push notification for those who use carrier A
is it possible?I tired to figure out with the apple documentations but it didn't help me.
If not with parse.com is there any system that let me do it?
Yes you can use the Parse CloudCode. Keep in mind that you can create channels, each channel in your case could be a carrier, so if the first user tries to subscribe to the channel you have to ensure that the channel exists, if not, just create it. Then you can target notifications for the channels you want from the Parse push console or from the Parse Cloud Code. It's all in the docs.
So, if you're currently using Parse, to subcribe a user to the channel you can use:
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObject:#"TMobile" forKey:#"channels"];
[currentInstallation saveInBackground];
Then from the Parse Push console you will see that the "TMobile" channel has been added and you can send push just to the users subscribed to that channel.
Otherwise if you want to send dinamically the notifications from your webserver, you can call a CloudFunction similar to this:
Parse.Push.send({
channels: [ "TMobile" ],
data: {
alert: "Alert message"
}
}, { success: function() {
// success!
}, error: function(err) {
console.log(err);
}
});
It looks like you are asking two different questions.
For the first question, Apple Push Notifications support this.
...the client application can store in its bundle the alert-message
strings translated for each localization it supports. The provider
specifies the loc-key and loc-args properties in the aps dictionary of
the notification payload. When the device receives the notification
(assuming the application isn’t running), it uses these aps-dictionary
properties to find and format the string localized for the current
language, which it then displays to the user.
Here’s how that second option works in a little more detail.
An application can internationalize resources such as images, sounds,
and text for each language that it supports, Internationalization
collects the resources and puts them in a subdirectory of the bundle
with a two-part name: a language code and an extension of .lproj (for
example, fr.lproj). Localized strings that are programmatically
displayed are put in a file called Localizable.strings. Each entry in
this file has a key and a localized string value; the string can have
format specifiers for the substitution of variable values. When an
application asks for a particular resource—say a localized string—it
gets the resource that is localized for the language currently
selected by the user. For example, if the preferred language is
French, the corresponding string value for an alert message would be
fetched from Localizable.strings in the fr.lproj directory in the
application bundle. (The application makes this request through the
NSLocalizedString macro.)
To make this clearer, let’s consider an example. The provider
specifies the following dictionary as the value of the alert property:
{"aps" :
{"alert" : {
"loc-key" : "GAME_PLAY_REQUEST_FORMAT",
"loc-args" : [ "Jenna", "Frank"]
}
}
When the device receives the notification, it uses
"GAME_PLAY_REQUEST_FORMAT" as a key to look up the associated string
value in the Localizable.strings file in the .lproj directory for the
current language. Assuming the current localization has an
Localizable.strings entry such as this:
"GAME_PLAY_REQUEST_FORMAT" = "%# and %# have invited you to play
Monopoly";
the device displays an alert with the message “Jenna and Frank have
invited you to play Monopoly”.
In addition to the format specifier %#, you can %n$# format specifiers
for positional substitution of string variables. The n is the index
(starting with 1) of the array value in loc-args to substitute.
(There’s also the %% specifier for expressing a percentage sign (%).)
So if the entry in Localizable.strings is this:
"GAME_PLAY_REQUEST_FORMAT" = "%2$# and %1$# have invited you to play
Monopoly";
the device displays an alert with the message “Frank and Jenna have
invited you to play Monopoly”.