Convert NSDate to Date - swift

this might be a stupid question, but I canĀ“t find the information. I'm using CoreData in my app, and I save an array of structs. The problem is when I fetch and try to restore it into the struct array, I have a problem with my Date variable; I can't find a way to convert it from NSDate to Date, I try using as Date, but it makes me force downcast and I'm not sure if it's safe. Is it correct? or is there another way?
This is my Struc:
struct MyData {
var value = Int()
var date = Date()
var take = Int()
var commnt = String()
}
This is how I'm fetchin the data:
func fetchRequestInfo() -> [MyData] {
let fetchRequest: NSFetchRequest<GlucoseEvents> = GlucoseEvents.fetchRequest()
do {
let searchResults = try DatabaseController.getContext().fetch(fetchRequest)
for result in searchResults as [GlucoseEvents] {
let value = Int(result.value)
let date = result.date as Date
let take = Int(result.take)
let commnt = String(describing: result.commnt)
let data = MyData(value: value, date: date, take: take, commnt: commnt)
self.dataArray.append(data)
}
} catch {
print ("error: \(error)")
}
let orderArray = self.dataArray.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
return orderArray
}
And this is the how I set the properties of my CoreDataClass:
#NSManaged public var commnt: String?
#NSManaged public var date: NSDate?
#NSManaged public var value: Int16
#NSManaged public var take: Int16

result.date is an optional NSDate, so you can bridge it
to an optional Date:
result.date as Date?
Then use optional binding to safely unwrap it. In your case that
could be
guard let date = result.date as Date? else {
// date is nil, ignore this entry:
continue
}
You might also want to replace
let commnt = String(describing: result.commnt)
with
guard let commnt = result.commnt else {
// commnt is nil, ignore this entry:
continue
}
otherwise you'll get comment strings like Optional(My comment).
(Rule of thumb: String(describing: ...) is almost never what you
want, even if the compiler suggests it to make the code compile.)

Just make implicit casting like:
let nsdate = NSDate()
let date = nsdate as Date

You can use a function or just an extension:
let nsDate = NSDate()
let date = Date(timeIntervalSinceReferenceDate: nsDate.timeIntervalSinceReferenceDate)

Related

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.

Converting timestamp from Firebase and adding to array

I'm trying to read a timeStamp from Firebase and append it to an array.
I have made some progress:
var orderDateHistoryArray = [String:Int]()
func getOrderDates() {
let uid = Auth.auth().currentUser!.uid
let orderDateHistoryRef = Database.database().reference().child("users/\(uid)/Orders/")
orderDateHistoryRef.observeSingleEvent(of: .value, with: { (snapshot) in
// Get dates
let value = snapshot.value as? NSDictionary
if let orderDate = value?["Date"] as? [String:Int] {
self.orderDateHistoryArray += Array(orderDate.values)//This does not conform
print(orderDate)
}
self.tableView.reloadData()
// ...
}) { (error) in
print(error.localizedDescription)
}
}
The print(orderDate)statement prints:
["-LQYspEghK3KE27MlFNE": 1541421618601,
"-LQsYbhf-vl-NnRLTHhK": 1541768379422,
"-LQYDWAKlzTrlTtO1Qiz": 1541410526186,
"-LQsILjpNqKwLl9XBcQm": 1541764115618]
This is childByAutoID : timeInMilliseconds
So, I want to read out the timeInMilliseconds, convert it to a readable timestampand append it to the orderDateHistoryArray
Those are timestamps. Parse it as a Date object then use .toLocaleDateString to get date.
alert( new Date(1541421618601).toLocaleDateString("en-US") );
In order to transform your timestamp you must, first remove milliseconds on each the values returned by the dictionary.
self.orderDateHistoryArray += Array(orderDate.values).map { Date(timeIntervalSince1970: TimeInterval($0/1000)) }
In order to get it in a "human way", you need to have a DateFormatter. It's on this object where you define how it's presented.
extension Date {
func format(_ dateFormat: String = "dd/MMMM")
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "MMMM dd"
return formatter.string(from: self)
}
}
and on a Date element you can just call it by date.format() or by passing a string date.format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX")

Determine Swift object type from Any Object in a more Swifty way

I have a dictionary of [String:Any] and when i load from UserDefaults I get back a Date object, but from Firebase I get back a Timestamp object.
Surely there is a my Swifty way to write this code ?
var correctDate:Date!
if let t = dictionary[key.timestamp] as? Timestamp {
print("as Timestamp")
correctDate = t.dateValue()
}
else if let t = dictionary[key.timestamp] as? Date {
print("as Date")
correctDate = t
}
else {
fatalError("no date object found")
}
print(correctDate)
Is a switch more Swifty?:
switch dictionary[key.timestamp] {
case let timestamp as Timestamp:
print("as TimeStamp")
correctDate = timestamp.dateValue()
case let date as Date:
print("as Date")
correctDate = date
default:
fatalError("no date object found")
}
Use a protocol
You have two objects which both represent date objects. Why not unify them with a protocol and give them a common interface?
Use a protocol to create a DateObject that both Date and Timestamp adopt:
protocol DateObject {
var date: Date { get }
}
extension Date: DateObject {
var date: Date { return self }
}
extension Timestamp: DateObject {
var date: Date { return self.dateValue() }
}
// Added this extension for NSDate after comments
// indicated that NSDate objects were coming back
// from UserDefaults
extension NSDate: DateObject {
var date: Date { return self as Date }
}
if let date = (dictionary[key.timestamp] as? DateObject)?.date {
print("has date")
correctDate = date
} else {
fatalError("no date object found")
}
By using ?? Nil-Coalescing operator, you could:
let timeStamp = dictionary[key.timestamp] as? Timestamp
let date = dictionary[key.timestamp] as? Date
if let correctDate = timeStamp?.dateValue() ?? date {
print(correctDate)
} else {
fatalError("no date object found")
}
Assuming that timeStamp.dateValue() returns Date type.

Unwrapping long datatype in Swift Linux

I'm trying to get a record from MongoDB which has a DateTime property. This property is ISODate but is received as a long data type (milliseconds since 1970) through the Perfect-MongoDB API.
The code looks like this:
if var something = dictionary["Something"] as? [String:Any], var intDate = something["$date"] as? Int64
{
let date = Date(timeIntervalSince1970: TimeInterval(intDate/1000))
}
This code is working fine in Mac OSX. However in Linux, created["$date"] as? Int64 is always nil.
I've tried a couple of things, including using Double and NSNumber instead of Int64 but it is still nil.
Any ideas on how I can access this number? I need to convert it to a readable date, and the way I'm doing this is through TimeInterval() which needs a Double value for seconds after 1970, so it needs to be divisible by 1000 and convertible to Double during that step.
Edit: This is the NSNumber code where intDate is still nil and thus doesn't fall through the let date line. something is not nil
if var something = dictionary["Something"] as? [String:Any], var intDate = something["$date"] as? NSNumber
{
let date = Date(timeIntervalSince1970: TimeInterval(NSDecimalNumber(decimal:intDate.decimalValue/1000).doubleValue))
}
Edit 2: Sample Dictionary for this case:
var dictionary : [String:Any] = ["SomethingElse":"SomeOtherData","Something":["$date": 1507710414599]]
Apparently there is only limited conversion between integer types and NSNumber in Swift on Linux, so you have to cast to the exact type,
which is Int in this case:
let dictionary : [String: Any] = ["SomethingElse":"SomeOtherData","Something":["$date": 1507710414599]]
if let something = dictionary["Something"] as? [String:Any],
let numDate = something["$date"] as? Int {
let date = Date(timeIntervalSince1970: Double(numDate)/1000)
print("Date:", date)
}

conformity requirements for userInfo object of UILocalNotification

Using Swift-2.2,
I would like to pass a 'struct' or a 'class object' to userInfo of a UILocalNotification. (see code-illustration below).
Can you tell me how this struct needs to be changed in order to be conform to the requirements of the UserInfo ?
I read something about
a) UserInfo can't be a struct (but I also tried with a class - it did not work either)
b) "plist type" conformity --> but how would I do so ?
c) "NSCoder" and "NSObject" conformity --> but how would I do so ?
The error message I get running the code below is:
"unable to serialize userInfo"
Thank you for any help on this.
struct MeetingData {
let title: String
let uuid: String
let startDate: NSDate
let endDate: NSDate
}
let notification = UILocalNotification()
notification.category = "some_category"
notification.alertLaunchImage = "Logo"
notification.fireDate = NSDate(timeIntervalSinceNow: 10)
notification.alertBody = "Data-Collection Request!"
// notification.alertAction = "I want to participate"
notification.soundName = UILocalNotificationDefaultSoundName
let myData = MeetingData(title: "myTitle",
uuid: "myUUID",
startDate: NSDate(),
endDate: NSDate(timeIntervalSinceNow: 10))
// that's where everything crashes !!!!!!!!!!!!!!
notification.userInfo = ["myKey": myData] as [String: AnyObject]
As the documentation for UILocalNotification.userInfo says:
You may add arbitrary key-value pairs to this dictionary. However, the keys and values must be valid property-list types; if any are not, an exception is raised.
You'll need to convert your data to this type yourself. You might want to do something like this:
enum Keys {
static let title = "title"
static let uuid = "uuid"
static let startDate = "startDate"
static let endDate = "endDate"
}
extension MeetingData {
func dictionaryRepresentation() -> NSDictionary {
return [Keys.title: title,
Keys.uuid: uuid,
Keys.startDate: startDate,
Keys.endDate: endDate]
}
init?(dictionaryRepresentation dict: NSDictionary) {
if let title = dict[Keys.title] as? String,
let uuid = dict[Keys.uuid] as? String,
let startDate = dict[Keys.startDate] as? NSDate,
let endDate = dict[Keys.endDate] as? NSDate
{
self.init(title: title, uuid: uuid, startDate: startDate, endDate: endDate)
} else {
return nil
}
}
}
Then you can use myData.dictionaryRepresentation() to convert to a dictionary, and MeetingData(dictionaryRepresentation: ...) to convert from a dictionary.