How to save information ( Swift application ) - ios

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.

Related

How to determine the number of days a user has used an app using swift?

I want to determine the number of days a user has used an iOS app. I want to trigger an amplitude event the first to fourth days but not after the fourth day he uses it. How can I track how many days the user used the app?
One approach is using UserDefaults to count how many times users open the app:
var userOpenApp: Int {
get {
UserDefaults.standard.integer(forKey: "UserOpenApp")
}
set {
UserDefaults.standard.set(newValue, forKey: "UserOpenApp")
}
}
then check the number in sceneDelegate or appDelegate, and update it:
userOpenApp // check the number
userOpenApp = userOpenApp + 1 // update the number
Store the needed data in UserDefaults and call it when you need to do the calculation.

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.

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

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

Cloudkit fetch data (strings and image asset) take a long time to appear after call

I was hoping that someone can help a coding newbie with what might be considered a stupid question. I'm making a blog type app for a community organization and it's pretty basic. It'll have tabs where each tab may be weekly updates, a table view with past updates and a tab with general information.
I setup cloudkit to store strings and pictures, and then created a fetchData method to query cloud kit. In terms of the code (sample below) it works and gets the data/picture. My problem is that it takes almost 5-10 seconds before the text and image update when I run the app. I'm wondering if that's normal, and I should just add an activity overlay for 10 seconds, or is there a way to decrease the time it takes to update.
override func viewDidLoad() {
fetchUpcoming()
}
func fetchUpcoming() {
let container = CKContainer.defaultContainer()
let publicData = container.publicCloudDatabase
let query = CKQuery(recordType: "Upcoming", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
publicData.performQuery(query, inZoneWithID: nil) { results, error in
if error == nil { // There is no error
println(results)
for entry in results {
self.articleTitle.text = entry["Title"] as? String
self.articleBody.text = entry["Description"] as? String
let imageAsset: CKAsset = entry["CoverPhoto"] as! CKAsset
self.articlePicture.image = UIImage(contentsOfFile: imageAsset.fileURL.path!)
self.articleBody.sizeToFit()
self.articleBody.textAlignment = NSTextAlignment.Justified
self.articleTitle.adjustsFontSizeToFitWidth = true
}
}
else {
println(error)
}
}
}
Another question I had is about string content being stored on cloud kit. If I want to add multiple paragraphs to a blood entry (for example), is there a way to put it in one record, or do I have to separate the blog entry content into separate paragraphs? I may be mistaken but it seems like CloudKit records don't recognize line breaks. If you can help answer my questions, I'd be really appreciative.
It looks like you might be issuing a query after creating the data, which isn't necessary. When you save data, as soon as your completion block succeeds (with no errors) then you can be sure the data is stored on the server and you can go ahead and render it to the user.
For example, let's say you're using a CKModifyRecordsOperation to save the data and you assign a block of code to the modifyRecordsCompletionBlock property. As soon as that block runs and no errors are passed in, then you can render your data and images to your user. You have the data (strings, images, etc.) locally because you just sent them to the server, so there's no need to go request them again.
This provides a quicker experience for the user and reduces the amount of network requests and battery you're using on their device.
If you are just issuing normal queries when your app boots up, then that amount of time does seem long but there can be a lot of factors: your local network, the size of the image you're downloading, etc. so it's hard to say without more information.
Regarding the storage of paragraphs of text, you should consider using a CKAsset. Here is a quote from the CKRecord's documentation about string data:
Use strings to store relatively small amounts of text. Although
strings themselves can be any length, you should use an asset to store
large amounts of text.
You'll need to make sure you're properly storing and rendering line break characters between the user input and what you send to CloudKit.

MonoTouch DateTime.ToLocalTime does not update when time zone is changed

When I change the time zone on my computer (when testing on the simulator) or on the phone (when testing directly on the phone), DateTime.ToLocalTime() does not return updated results.
I register and successfully receive notification events for UIApplication.Notifications.ObserveSignificantTimeChange. In it I call TimeZoneInfo.ClearCachedData() which allows me to detect the time zone has in fact changed but this has no effect on ToLocalTime(). The DateTime I am converting is definitely of kind Utc (tried DateTime.UtcNow for example).
I tried calling CultureInfo.CurrentCulture.ClearCachedData() as suggested in this question: SetTimeZoneInformation does not update DateTime.Now for another .NET Application. That only causes the application to crash when the CurrentCulture is nulled.
Digging a bit deeper into the implementation of ToLocalTime(), it seems to use the object TimeZone.CurrentTimeZone. This object is reset only when the difference between DateTime.GetNow() and TimeZone.timezone_check. I suspect that TimeZone.timezone_check is not getting updated when TimeZoneInfo.ClearCachedData() is called.
Is there any way I can force ToLocalTime() to take into consideration the time zone change?
You can use DateTimeOffset for this. See also this question.
Edit - What I have working is this:
var dateTimeUtc = new System.DateTime(2013, 2, 4, 21, 30, 0,
System.DateTimeKind.Utc);
var dateTimeOffsetLocal = new System.DateTimeOffset(dateTimeUtc.ToLocalTime());
var formattedLocalTime = string.Format("{0:HH:mm}", dateTimeOffsetLocal);
On my phone my formattedLocalTime changes whenever I change my TimeZone on it, without clearing any cache. I do have to reload the view, but no other tricks.
To display/verify the local TimeZone I use:
var localTimeZone = System.TimeZoneInfo.Local.StandardName;
I hope this somehow points you in the right direction. I don't have any better than this "works on my machine(s)"-anwer.

Resources