Unwrapping long datatype in Swift Linux - swift

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

Related

How to avoid this Force Cast

I think that a force cast I have in my app is causing it to crash, (userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int?)...but I really don't know how to avoid it. Any guidance is greatly appreciated!
func getProductionTime(store: Bool = false) {
let userDefaults = UserDefaults.standard
let productionTimeFormatter = DateFormatter()
productionTimeFormatter.timeZone = TimeZone(abbreviation: defaultTimeZone)
productionTimeFormatter.dateFormat = defaultTimeFormat
if let defaultTimeDiffSeconds: Int = userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int? {
timeDiffSeconds = defaultTimeDiffSeconds
}
let productionTime = Calendar.current.date(byAdding: .second, value: timeDiffSeconds, to: Date())!
if store {
storeDateComponents(nowProdTime: productionTime)
}
productionTimeString = productionTimeFormatter.string(from: productionTime)
liveCounterButton.setTitle(productionTimeString, for: .normal)
}
Use the dedicated API which returns a non-optional
timeDiffSeconds = userDefaults.integer(forKey: "timeDiffSecondsDefault")
If a default value != 0 is required register it.
Note: Never use value(forKey with UserDefaults unless you really need KVC
When the key is absent, you are trying to force-cast an empty Any? to Int?, and thus, the if condition is not executed:
if let defaultTimeDiffSeconds: Int = userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int? {
timeDiffSeconds = defaultTimeDiffSeconds
}
And if timeDiffSeconds was not initialized elsewhere, it will cause the crash when you try to use it.
The appropriate way would be conditional casting with as?:
if let defaultTimeDiffSeconds = userDefaults.object(forKey: "timeDiffSecondsDefault") as? Int { ... }
object(forKey:) was kindly suggested by Mr Leonardo.
Using userDefaults.integer(forKey: "timeDiffSecondsDefault") might be confusing when using timeDiffSeconds later, since integer(forKey:) would return 0 if the key is absent in user defaults, and returns an integer even if the value is a string or a boolean.

Convert NSDate to Date

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)

Init has been renamed to init(describing) error in Swift 3

This code works fine in Swift 2:
guard let userData = responseData["UserProfile"] as? [String : AnyObject] else { return }
var userProfileFieldsDict = [String: String]()
if let profileUsername = userData["Username"] as? NSString {
userProfileFieldsDict["username"] = String(profileUsername)
}
if let profileReputationpoints = userData["ReputationPoints"] as? NSNumber {
userProfileFieldsDict["reputation"] = String(profileReputationpoints)
}
But, in Swift 3 it throws an error on userProfileFieldsDict["reputation"] saying
init has been renamed to init(describing:)
My question is why does it trigger on that line and not on the userProfileFieldsDict["username"] assignment line, and how to go about fixing it? I'm assuming it's because I'm casting a NSNumber to a String, but I can't really understand why that matters.
NSNumber is a very generic class. It can be anything from a bool to a long to even a char. So the compiler is really not sure of the exact data type hence it's not able to call the right String constructor.
Instead use the String(describing: ) constructor as shown below
userProfileFieldsDict["reputation"] = String(describing: profileReputationpoints)
Here's more info about it.
You need to drop your use of Objective-C types. This was always a bad habit, and now the chickens have come home to roost. Don't cast to NSString and NSNumber. Cast to String and to the actual numeric type. Example:
if let profileUsername = userData["Username"] as? String {
userProfileFieldsDict["username"] = profileUsername
}
if let profileReputationpoints = userData["ReputationPoints"] as? Int { // or whatever
userProfileFieldsDict["reputation"] = String(profileReputationpoints)
}

Assign Value of NSNumber to AnyObject

I have a segment of code that gets info from an API, and I need to add it to a Dictionary. The code is below:
typealias JSONdic = [String: AnyObject]
var weatherData: AnyObject = StorageManager.getValue(StorageManager.StorageKeys.WeatherData)!
let json: AnyObject = ["Any": "Object"]
if let json = json as? JSONdic, history = json["history"] as? JSONdic, tempi = history["tempi"] as? Int, hum = history["hum"] as? String, precip = history["precipi"] as? String{
println("Temperature:\(tempi) Humidity:\(hum) Precipitation:\(precip)")
weatherData = [NSDate: AnyObject]()
let temp = tempi as NSNumber
weatherData[(The Current Date)] = temp
}
I want to first add "temp" to the weatherData Dictionary, but even after casting it to NSNumber, I am told that an NSNumber value cannot be assigned to the AnyObject?! type. Can anyone help me fix this?
Your weatherData variable is of type AnyObject. Despite the fact that you later assign it a value of type [NSDate: AnyObject], the variable itself is still considered by the compiler to be AnyObject. You then hit problems because you try to subscript it, assigning an NSNumber, which is obviously not possible on AnyObject.
Your declaration of weatherData should ensure it is the type you intend. If you are sure that your StorageManager will return you the appropriate dictionary type for the weather data key, you can force downcast it to the correct type:
var weatherData = StorageManager.getValue(StorageManager.StorageKeys.WeatherData) as! [NSDate: NSObject]

Swift 1.2 NSDate?! is not convertible to NSDate

After upgrading to 1.2, I have the compile error:
NSDate?! is not convertible to NSDate
Code:
let dateCreated = photoCommentObjects[indexPath.row].createdAt as NSDate
Other attempt:
I also tried:
let dateCreated = photoCommentObjects[indexPath.row].createdAt as? NSDate
I get error:
Downcast from NSDate?! to 'NSDate' only unwraps optionals
This is a type problem. You have an array with AnyObject type and you cannot read a property on AnyObject.
//sample data
class PhotoComment {
let createdAt = NSDate()
}
let photoCommentObjects: [AnyObject] = [PhotoComment()]
//let's get the indexed object first and let's cast it from AnyObject
let photoComment = photoCommentObjects[indexPath.row] as! PhotoComment
//now trivially
let dateCreated = photoComment.createdAt
or
//let's cast the whole array first
let photoComments = photoCommentObjects as! [PhotoComment]
let dateCreated = photoComments[indexPath.row].createdAt
Have you tried?
let dateCreated = photoCommentObjects[indexPath.row].createdAt as! NSDate
Since Sift 1.2 you have to explicitly mark forced casting with !.
This as a reminder for you that if it fails, your app will crash.
Apparently photoCommentObjects[indexPath.row].createdAt is returning a type of NSDate?! which is an implicitly wrapped optional of an optional. To unwrap that, first cast the result to NSDate? with as to remove the implicitly wrapped optional, and then use optional binding to unwrap the resulting NSDate?:
if let dateCreated = photoCommentObjects[indexPath.row].createdAt as NSDate? {
// use dateCreated which is of type NSDate
}
The result is that dateCreated will be a plain NSDate.
Alternatively, you can use:
if let temp = photoCommentObjects[indexPath.row].createdAt, dateCreated = temp {
// each assignment unwraps one layer of Optional
// use dateCreated which is of type NSDate
} else {
println("something is nil")
}