Firebase. Swift :Retrieve a message after 5 hours of the post - ios

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")
}
}
}
})

Related

Converting Firestore Timestamp to Date data type

Few days ago, I just updated my firebase pod to the latest version, and in my debugging area, I got message that said that I have to update from timestamp data type to Date (something like that, I forget actually).
and I also have to change the DB settings like below
let db = Firestore.firestore()
let settings = db.settings
settings.areTimestampsInSnapshotsEnabled = true
db.settings = settings
after add the code above, the error message in my debugging area in Xcode disappears.
As far as I remember, I also had to change the data type in my client from Timestamp to Date data type, I haven't changed this because the error message in my debugging area in Xcode have disappeared.
As a result, I get not correct Date in my app.
Could you please share again the conversion step from TimeStamp to Date ? because as far as I remember I had to do some steps to follow. I can't find it in the firebase documentation.
Thank you very much :)
This is the message from the debugger
The behavior for system Date objects stored in Firestore is going to change AND YOUR APP MAY BREAK.
To hide this warning and ensure your app does not break, you need to add the following code to your app before calling any other Cloud Firestore methods:
let db = Firestore.firestore()
let settings = db.settings
settings.areTimestampsInSnapshotsEnabled = true
db.settings = settings
With this change, timestamps stored in Cloud Firestore will be read back as Firebase Timestamp objects instead of as system Date objects. So you will also need to update code expecting a Date to instead expect a Timestamp. For example:
// old:
let date: Date = documentSnapshot.get("created_at") as! Date
// new:
let timestamp: Timestamp = documentSnapshot.get("created_at") as! Timestamp
let date: Date = timestamp.dateValue()
Please audit all existing usages of Date when you enable the new behavior. In a future release, the behavior will be changed to the new behavior, so if you do not follow these steps, YOUR APP MAY BREAK.

iOS Firebase - Crash in simulator but not device w/ persistence

I tried googling this problem but it seems like everyone has the opposite problem where the app runs on simulator but not their device. I've been struggling a LOT all week with firebase asynchronous calls returning null and linked the issue to persistence being enabled. All my problems go away if I disable persistence, but I want it enabled. I learned recently about synchronous issues with the different listeners/persistence and have been struggling with firebase returning outdated/nil values for a while.
Simulator was working just a week or two ago and I'm not sure what's changed. I've tried messing with / switching out .observeSingleEvent for .observe and still crashes at this code:
let synced = ref.child("profiles").child((FIRAuth.auth()?.currentUser?.uid)!).child("level")
synced.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
print(snapshot)
print(snapshot.ref)
if (snapshot.value as! String == "One") {
........//CRASH
With the message:
Could not cast value of type 'NSNull' (0x10b7cf8c8) to 'NSString' (0x10a9dfc40).
When I try to print snapshot, it shows me an empty snapshot. But when I print the ref, the link works and takes me to the right place in my db (where I can see the data exists)
Any ideas how to fix/get around this without disabling persistence? Or more importantly I guess, should I care that it doesn't work in simulator if it works on a device? Will this matter for app store approval / affect future users?
If you'd like to see for yourself that this is an issue of firebase getting a nil/outdated value when the reference exists, here is what I see when I follow the printed ref link
The error seems fairly explicit: there is no value, so you can't convert it to a string.
synced.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
if (snapshot.exists()) {
if (snapshot.value as! String == "One") {
........//CRASH

How to save information ( Swift application )

I am developing an iOS application which generate random inspirational quote every day. At the moment when I close the app, open it again and click on the button which generates the daily quote, it shows me a new one.
Can you help me, how can I save the same quote all over the day and when the day is over generate a new quote. I mean at 00:00 o'clock in the morning.
I want to keep 1 quote per day, not 1 quote for every time I open the app.
Okay, I have some time on my hands and I can provide you a small tutorial on how to save data to the user defaults.
You will want to save the date on which you create a quote right after you created it and then each time your app goes to foreground check against that. Obviously you want to only remember the day, not the hours, minutes and seconds. Here's a small function for that:
func makeCleanToday() -> NSDate {
let today = NSDate()
let comps = NSCalendar.currentCalendar().components([.Day, .Month, .Year], fromDate: today)
return NSCalendar.currentCalendar().dateFromComponents(comps)!
}
Note that this forcibly unwraps the date on the last line, but this is fine here, since I do nothing that could lead to dateFromComponents returning nil.
The next two lines you will always call right after you created your quote:
let cleanToday = makeCleanToday()
NSUserDefaults.standardUserDefaults().setObject(cleanToday, forKey: "MyAppDateKey")
Obviously you should use a better key to identify this (I suggest defining a constant somewhere for this). This saves your date (only day, month and year) in the app's user defaults.
Next time your app goes into the foreground (use the app delegate for this), then do this here:
if let savedDate:NSDate = NSUserDefaults.standardUserDefaults().objectForKey("MyAppDateKey") as? NSDate {
let cleanToday = makeCleanToday()
if savedDate.earlierDate(cleanToday) == savedDate {
// create new sentence
NSUserDefaults.standardUserDefaults().setObject(cleanToday, forKey: "MyAppDateKey")
}
} else {
// create new sentence
NSUserDefaults.standardUserDefaults().setObject(makeCleanToday(), forKey: "MyAppDateKey")
}
Please note that I added the two NSUserDefaults.standardUserDefaults().setObject.... lines here just to illustrate what happens. You will probably have that code already in your quote creation method, like I told you above to do. Also, the else part of this is for when you run the app the first time. Then you don't have anything saved in the user defaults, so savedDateis nil and you have to create your quote as normal.
Lastly a general remark: Using NSCalendar is relatively expensive (though it is designed for this scenario), so you shouldn't use it, for example, when filling cells in a table or something (because that might negatively affect frame rate during scrolling). In this case it's perfectly fine (relatively being the keyword here), I just wanna let you know before you eventually advance with your coding to a point where you run into this. :) Same is true for date formatters.
Oh, and regarding the comment: No need to say sorry, I just wanted to let you know how things here on SO are and cushion the fall a bit when you see your question getting downvoted. I hope this could help you.

How to send multiple columns of information using CKSubscription

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

iOs - Parse - Handle many push notifications

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.

Resources