CKQuery with Todays Date - cloudkit

Would really value some support.
I am designing an tvOS app which displays certain CloudKit content in a tableView, the data is different depending on the current date.
Each calendar date correspondents to the data within that Type.
e.g. RecordType "A13032019" relates to 13th March 2019.
I generate the date in the correct format using a func fired within ViewDidLoad (generateDate():
*
func generateDate() {
let formatter = DateFormatter()
//2016-12-08 03:37:22 +0000
formatter.dateFormat = "ddMMyyyy"
let now = Date()
let dateString = formatter.string(from:now)
NSLog("%#", dateString)
let generateOperationalDate = ("A\(dateString)")
print(generateOperationalDate)
}
I then try to use generateOperationalData to run in the below CKQuery:
func queryDatabase() {
let query = CKQuery(recordType: "\(generateDate())", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
let sort = NSSortDescriptor(key: "trainDepartureTime", ascending: true)
query.sortDescriptors = [sort]
database.perform(query, inZoneWith: nil) { (records, _) in
guard let records = records else { return }
let sortedRecords = records
When I try to run this it throws a Thread Error with reason "* Terminating app due to uncaught exception 'CKException', reason: 'recordType can not be empty'
* "
So it appears to me that the queryDatabase function is running before the generateDate function, however I have tried delaying the queryDatabase function and this still throws the same error!
Is there anyway I can generate the date (via generateDate) before the queryDatabase function runs?
Thanks

You want to write generateDate() so it returns a String. Then, inside queryDatabase(), call generateDate() so you're guaranteed to have a value to pass to "recordType".
I also condensed the calls a little bit. Hope this helps.
func generateDate() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "ddMMyyyy"
let dateString = formatter.string(from:Date())
return "A\(dateString)"
}
func queryDatabase() {
// this will make sure you have a String value
let type = generateDate()
// now you can pass it
let query = CKQuery(recordType: type, predicate: NSPredicate(value: true))
//...
}

Related

How to read a Firestore timestamp in Swift

I'm trying to read a timestamp from my Firestore database and ultimately convert it to a date, but for some reason my code doesn't seem to be returning anything. Instead, it only seems to use the default value that I provide, which is 0, so it always reads as Jan 1, 1970.
The document I'm trying to read in Firestore includes a field called date, which has a type of timestamp. There are other fields in the document, but to keep things simple I've left those out from this question. For reference, the other fields from the document are successfully read.
I've tried the below code. Note that I have imported Firebase to the class:
surveyDataCollectionRef.whereField("uid", isEqualTo: Auth.auth().currentUser?.uid ?? "").getDocuments { (snapshot, error) in
if let err = error {
debugPrint("Error fetching docs: \(err)")
} else {
guard let snap = snapshot else { return }
for document in snap.documents {
let data = document.data()
let timestamp = data["date"] as? TimeInterval ?? 0
let date = Date(timeIntervalSince1970: timestamp)
dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
dateFormatter.locale = NSLocale.current
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
let strDate = dateFormatter.string(from: date)
let newSurvey = Survey(date: strDate)
self.surveys.append(newSurvey!)
self.currentSurveys = self.surveys
}
self.tableView.reloadData()
}
}
}
After even more trial and error I found that the below code seemed to be able to get me a date as a String value (and in the correct format that I was aiming for:
if let timestamp = data["date"] as? Timestamp {
let date = timestamp.dateValue()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
strDate = "\(dateFormatter.string(from: date))"
}

How can I filter by specific date from realm object in swift?

I have a realm object with date property type of Date , and want to get list of items with specific date.
If I click specific date from calendar, for example 2020-03-06 , then it will present list of items which was created in 2020-03-06.
:: EDITED ::
Here is my realm object named "Profile" and there are dates from
2020-03-05 to 2020-03-08 .
Here is my Profile object and ProfileManager Singleton.
class Profile: Object {
#objc dynamic var date: Date!
#objc dynamic var content: String!
convenience init(_ content: String) {
self.init()
self.content = content
self.date = Date()
}
}
class ProfileManager {
static let shared = ProfileManager()
private var realm = try! Realm()
var profileList: Results<Profile>?
private init() {
profileList = realm.objects(Profile.self)
}
func save(_ object: Profile) {
do {
try realm.write {
realm.add(object)
}
} catch {
print(error)
}
}
func addNewProfile(_ content: String) {
let newProfile = Profile(content)
save(newProfile)
}
}
And lastly, here is a viewController which has to buttons. One for
adding new Profile, and one for printing filtered profile list.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addProfilePressed(_ sender: Any) {
ProfileManager.shared.addNewProfile("profile content")
}
#IBAction func filterButtonPressed(_ sender: Any) {
let stringDate = "2020-03-09"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let searchDate:Date = dateFormatter.date(from: stringDate)!
let results = ProfileManager.shared.profileList!.filter("date == %#", searchDate)
print(searchDate)
print(results)
for profile in results {
print(profile.content!)
}
}
}
the result on the console, when filterButtonPressed method called.
2020-03-08 15:00:00 +0000
Results<Profile> <0x7f9b36f160a0> (
)
How can I fix this problem?
And here is another problem.
I set to 'stringDate' a value of "2020-03-09"
but when I print converted date 'searchDate' , it prints "2020-03-08"
why this happens?
Hope now my questions is more clear to understand.
My original answer is below which, after a lot of research was only somewhat correct.
The actual answer has to do with the timestamp portion of the date.
So... if we create a date object using the below code and set it to a known date,
let stringDate = "2020-03-08"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let searchDate:Date = dateFormatter.date(from: stringDate)!
the actual object will look like this
2020-03-08T05:00:00.000Z
However, how the Profile object is being created is like this
convenience init(_ content: String) {
self.init()
self.content = content
self.date = Date()
}
and that date object looks like this
2020-03-08T16:10:25.123Z
so as you can see, if we filter for a specific date these are not equal
2020-03-08T05:00:00.000Z != 2020-03-08T16:10:25.123Z
which is why this
let stringDate = "2020-03-08"
let searchDate:Date = dateFormatter.date(from: stringDate)!
let searchResults = realm.objects(Profile.self).filter("date == %#", searchDate)
could not find the date because it's filtering for this
2020-03-08T05:00:00.000Z
To fix, change the profile class with a date stamp with a default time stamp
class Profile: Object {
#objc dynamic var date: Date!
#objc dynamic var content: String!
convenience init(_ content: String) {
self.init()
self.content = content
let formatter = DateFormatter()
formatter.timeStyle = .none
formatter.dateFormat = "MM/dd/yy"
let today = Date()
let s = formatter.string(from: today)
let d = formatter.date(from: s)
self.date = d
}
}
or, store your dates as a string yyyymmdd which will remove the ambiguity completely.
-- ORIGINAL ANSWER BELOW ---
Filtering by date is fully supported on date objects. Here's two quick examples. One for filtering for a specific date (for your question) and one for a date range using BETWEEN.
Note, I have a function makeDate that casts a string to a date object. This example uses a Realm DogClass object that has a dog_birthdate Date property.
This filters for objects with a specific date
let searchDate = self.makeDate(fromString: "06/01/2019")
let specificDateResults = realm.objects(DogClass.self)
.filter("dog_birthdate == %#", searchDate)
for dog in specificDateResults {
print(dog.dog_name)
}
This filters for objects within a date range
let startDate = self.makeDate(fromString: "06/01/2019")
let endDate = self.makeDate(fromString: "06/20/2019")
let dateRangeResuls = realm.objects(DogClass.self)
.filter("dog_birthdate BETWEEN {%#,%#}", startDate, endDate)
for dog in dateRangeResuls {
print(dog.dog_name)
}
EDIT: Using the code in the comment from the OP for testing
let stringDate = "2019-06-01"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let searchDate:Date = dateFormatter.date(from: stringDate)!
let result = realm.objects(DogClass.self).filter("dog_birthdate == %#", searchDate)
for dog in result {
print(dog.dog_name)
}
which works perfectly.

Check Core Data for an entry with Today's Date & perform function

I am fairly new to Swift & hoping someone knows the answer to this - nothing I try seems to work!
I have a Swift app which has a Core Data entity called "Drink" with 2 keys: a Date & then one called "drinkWater" which stores a value of "1" when a button is pushed.
I am trying to write a separate function where I can check if an entry exists for todays date and, if so, perform an action (in this case change an imageview).
I realise the below isn't the answer but it's as far as I got! Basically I can get all of the entries based on the value of drinkWater (this would need to be by Date I am guessing?) and I can get today's date all printing to the console. Now I'm stuck ...
private func updateMyImageView() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Drink")
request.predicate = NSPredicate(format: "drinkWater = %#", "1")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "timestamp") as! Date)
}
} catch {
print("Failed")
}
let dateNow = Date()
print("Date is \(dateNow)")
}
This returns:
2018-12-29 01:27:27 +0000
Date is 2018-12-29 12:21:21 +0000
Any ideas on how to turn this all into the correct function would be greatly appreciated!!
You need to use a date range from start of day to end of day (midnight to midnight) in a compound predicate. Here is a solution based on a similar SO question
var calendar = Calendar.current
calendar.timeZone = NSTimeZone.local
let dateFrom = calendar.startOfDay(for: Date())
let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)
let fromPredicate = NSPredicate(format: "timestamp >= %#", dateFrom as NSDate)
let toPredicate = NSPredicate(format: "timestamp < %#", dateTo! as NSDate)
let waterPredicate = NSPredicate(format: "drinkWater = %#", "1")
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Drink")
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [waterPredicate, fromPredicate, toPredicate])
Thank you for all of your help. I have it all working now. My final code is:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
var calendar = Calendar.current
calendar.timeZone = NSTimeZone.local
let dateFrom = calendar.startOfDay(for: Date())
let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)
let fromPredicate = NSPredicate(format: "timestamp >= %#", dateFrom as NSDate)
let toPredicate = NSPredicate(format: "timestamp < %#", dateTo! as NSDate)
let waterPredicate = NSPredicate(format: "drinkWater = %#", "1")
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Drink")
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [waterPredicate, fromPredicate, toPredicate])
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "timestamp") as! Date)
drinkMoreButton.setBackgroundImage(UIImage(named: "watericonselected.png"), for: UIControlState.normal)
}
} catch {
print("Failed")
}

Swift: how to fetch objects made today from parse?

I'm a Beginner so please be friendly while explaining, Thanks.
So basically i'm trying to fetch objects made today using createdAt.
let parseQuery = PFQuery(className: "request")
parseQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.reqDates.append(object.createdAt!)
}
}
}
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm"
let formatteddate = formatter.stringFromDate(self.reqDates[indexPath.row])
myCell.reqDate.text = formatteddate
When i do this fetch it shows me all objects with their dates correctly. But how can i display objects of today only not all of them created.
I hope i explained good enough.
Request object where createdAt is greater than today (midnight)
let parseQuery = PFQuery(className: "request")
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let components = cal.components([.Day , .Month, .Year ], fromDate: NSDate())
let todayDate = cal.dateFromComponents(components)
parseQuery.whereKey("createdAt", greaterThan:todayDate!)
parseQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
print(object.createdAt)
}
}
}

Swift - timeIntervalSinceNow Only want to show up to minutes (excluding the seconds part)

I'm using a timeIntervalSinceNow function to determine the difference between a certain time and the current time. I have used the "ZeroFormattingBehaviour" to .DropAll to get rid of any 0's in the date. However I want to make it so the difference doesn't include the seconds difference. For example if the difference is 2 days , 5 hours , 25min and 40 sec I want it to only show 2days,5h,25min. (e.g. excluding the seconds part) Is there any way to do so?
Code is as follows:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Values")
let sortDescriptor = NSSortDescriptor(key: "time", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
Values = results as! [NSManagedObject]
for result in results as! [NSManagedObject] {
times = result.valueForKey("time") as! String
let timeFormatter = NSDateFormatter()
timeFormatter.locale = NSLocale.currentLocale()
timeFormatter.dateFormat = "HH:mm dd/MM/yy"
let otherTime = timeFormatter.dateFromString(times)
let dateComponentsFormatter = NSDateComponentsFormatter()
dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyle.Abbreviated
dateComponentsFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehavior.DropAll
let difference = otherTime?.timeIntervalSinceNow
let diffAbs = abs(difference!)
let stringDiff = dateComponentsFormatter.stringFromTimeInterval(diffAbs)
// "\n" skips a line in the text
TimerLabel.text = stringDiff!
}
} catch let error as NSError {
TimerLabel.text = "There seems to be an error. Please try again later"
print("Could not fetch \(error), \(error.userInfo)")
}
Thanks
Specify the units you want to display
dateComponentsFormatter.allowedUnits = [.Day, .Hour, .Minute]