Swift 4 - Convert 1 or 0 from mysql to bool - ios

I have this variable here, its an Any
item["completed"]
Its either 1 or 0 which is true or false from mysql database, how can I covert the 1 or 0 to true or false?
I have tried this:
(item["completed"] as! NSNumber).boolValue)
but I get this error
Could not cast value of type 'NSTaggedPointerString' (0x10f906560) to
'NSNumber' (0x10e455d40).

You can try below extension which i have implemented,
extension String {
var bool: Bool {
return self == "1" ? true : false
}
}
(item["completed"] as? String).bool
Hope this way may help you.

Just use
let isCompleted = "1" == (item["completed"] as? String)
If you are using Decodable for json parsing, you can use following ApiBool struct instead of Bool
struct ApiBool: Codable {
let value: Bool
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self.value = string == "1"
} else {
let intVal = try container.decode(Int.self)
self.value = intVal == 1
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.value ? 1 : 0)
}
}
Note As mentioned in comments, you got an String data type and it is expected to be an integer, this is caused by libmysql driver, you can connect to MySQL using mysqlnd driver instead, this will result in a JSON with proper numeric data types, following posts might help you configure the driver
https://stackoverflow.com/a/46690317/1244597
https://stackoverflow.com/a/45366230/1244597

#ahemadabbas-vagh shared a good and reusable approach. But if we talk about computer science, a string's Boolean equality should not be measured like that.
This approach is the same as his, but it asks Bool if the value is true or false, not to String. Bool should know is a value is true or false, not String.
var str = "1"
extension Bool {
static func from(stringValue str: String) -> Bool {
return str == "1"
}
}
if Bool.from(stringValue: str) {
print("TRUE")
} else {
print("FALSE")
}
But it is still not correct. In computer science, null/nil or/and an empty string should be false and any other strings should be true. You can add the string "0" to this set.
You can implement like this; better approaches still might exist.
The idea is; globally a string can only be converted to true if it is not "0", not "" (empty string), not null/nil or if it is "true".
var str = "1" // check for nil, empty string and zero; these should be false; any other string should be true.
extension Bool {
static func from(stringValue str: String) -> Bool {
if str == "true" {
return true
}
guard let intVal = Int(str) else {
return false
}
return Bool(truncating: intVal as NSNumber)
}
}
if Bool.from(stringValue: str) {
print("TRUE")
} else {
print("FALSE")
}
If you use Decodable protocol; I know you know how to handle but:
struct aStruct: Decodable {
let boolVal: Bool
enum CodingKeys: String, CodingKey {
case completed
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let boolString = try container.decode(String.self, forKey: .completed)
self.boolVal = Bool.from(stringValue: boolString)
}
}

Related

working with similar json objects, casting int to string [duplicate]

This question already has answers here:
Using codable with value that is sometimes an Int and other times a String
(5 answers)
Closed last year.
i am working with a service that have both a websocket for live data and a api for historical data
the JSON looks similar and i would like to decode it to the same object
the only difference is that in the live one variable is a number but as a string and with the historical data the number is an int.
and preferably i would like to not have to create 2 almost identical decodable objects.
have anyone tried something similar.
You have to define a single type (Int or String) for your data structure and use init with Decoder to make a custom parsing.
struct MyData: Decodable {
let value: Int // Could be Int or String from different services
}
extension MyData {
enum CodingKeys: String, CodingKey {
case value
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
value = try container.decode(Int.self, forKey: .value)
} catch {
let stringValue = try container.decode(String.self, forKey: .value)
if let valueInt = Int(stringValue) {
value = valueInt
} else {
var codingPath = container.codingPath
codingPath.append(CodingKeys.value)
let debugDescription = "Could not create Int from String \(stringValue) of field \(CodingKeys.value.rawValue)"
let context = DecodingError.Context(codingPath: codingPath, debugDescription: debugDescription)
throw DecodingError.dataCorrupted(context)
}
}
}
}
I think you need a wrapper for that case of some sort. To make it as convenient as possible you could use a property wrapper for this
#propertyWrapper
struct NormalOrStringyInt: Decodable {
var wrappedValue: Int?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let value = try? container.decode(Int.self) {
wrappedValue = value
} else if
let string = try? container.decode(String.self),
let value = Int(string)
{
wrappedValue = value
} else {
wrappedValue = nil // default value
}
}
}
struct Model: Codable {
#NormalOrStringyInt var id: Int?
var someInt: Int
var someString: String
...
}
let model = try! JSONDecoder().decode(Model, from: data)
let id: Int? = model.id.wrappedValue

Swift property wrapper String to Numeric type

I want to convert a value might be string or number to number finally by property wrapper.
For example:
"12" -> 12
12 -> 12
I achieved it by specific type Int, Double etc. (with below code, change T to specific type):
import Foundation
#propertyWrapper
struct NumericOrString<T: Numeric & Decodable> {
var wrappedValue: T?
init(wrappedValue: T?) {
self.wrappedValue = wrappedValue
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let value = try? container.decode(String.self) {
wrappedValue = T(value) // if T is Double then Double(value) works.
} else if let value = try? container.decode(T.self) {
wrappedValue = value
} else {
wrappedValue = 0
}
}
}
But generic type should be the right direction. So I tried the code above by generic type.
The issue is in this part:
wrappedValue = T(value)
No exact matches in call to initializer .
So what can I do right now is:
if T.self is Double.Type {
wrappedValue = Double(value) as? T
}
if T.self is Int.Type {
wrappedValue = Int(value) as? T
}
Then I have to add many ifs to check the type.
Not sure if Numeric is the right one for both String and Numeric, Any better solutions?
In order for this to work, you need either concrete types to instantiate, or at least a protocol with an initializer requirement for this.
My recommendation would be to go with the latter option, of using a protocol, and push the Numeric requirement to the protocol:
protocol NumericOrStringDecodable: Numeric {
init(numericOrStringContainer: SingleValueDecodingContainer) throws
}
#propertyWrapper struct NumericOrString<T: NumericOrStringDecodable>: Decodable {
var wrappedValue: T
init(from decoder: Decoder) throws {
wrappedValue = try T(numericOrStringContainer: decoder.singleValueContainer())
}
}
Using a protocol will help you a lot, as you'll be able to detect at compile time if the property wrapper was applied to an incompatible type, instead of detecting this at runtime (via a crash):
struct MyStruct {
#NumericOrString var age: Int
#NumericOrString var weight: Double
// the below won't compile
#NumericOrString var visitedCities: [String]
}
Now, coming to the protocol conformance, as others have said in the comments, you only need to support Int or Double if you're decoding from JSON.
extension Int: NumericOrStringDecodable {
init(numericOrStringContainer container: SingleValueDecodingContainer) throws {
if let int = try? container.decode(Int.self) {
self = int
} else if let string = try? container.decode(String.self), let int = Int(string) {
self = int
} else {
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: "Invalid int value"))
}
}
}
extension Double: NumericOrStringDecodable {
init(numericOrStringContainer container: SingleValueDecodingContainer) throws {
if let double = try? container.decode(Double.self) {
self = double
} else if let string = try? container.decode(String.self), let double = Double(string) {
self = double
} else {
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: "Invalid double value"))
}
}
}

Decoding empty string into nil with Codable

normal case:
{
"maintenance": true
}
{
"maintenance": false
}
If there is no maintenance station then it will become empty string
{
"maintenance": ""
}
i want to have nil if maintenance is empty string in json
struct Demo: Codable {
var maintenance: Bool?
}
Is there a good way to do it?
What you need is to try to decode your Bool, catch the error, try to decode a string and check if it is an empty string otherwise throw the error. This will make sure you don't discard any decoding error even if it is a string but not empty:
struct Demo: Codable {
var maintenance: Bool?
}
struct Root: Codable {
var maintenance: Bool?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
maintenance = try container.decode(Bool.self, forKey: .maintenance)
} catch {
guard try container.decode(String.self, forKey: .maintenance) == "" else {
throw error
}
maintenance = nil
}
}
}
Playground testing:
let json1 = """
{
"maintenance": true
}
"""
let json2 = """
{
"maintenance": false
}
"""
let json3 = """
{
"maintenance": ""
}
"""
let json4 = """
{
"maintenance": "false"
}
"""
do {
let root1 = try JSONDecoder().decode(Root.self, from: Data(json1.utf8))
print("root1", root1)
} catch {
print(error)
}
do {
let root2 = try JSONDecoder().decode(Root.self, from: Data(json2.utf8))
print("root2", root2)
} catch {
print(error)
}
do {
let root3 = try JSONDecoder().decode(Root.self, from: Data(json3.utf8))
print("root3", root3)
} catch {
print(error)
}
do {
let root4 = try JSONDecoder().decode(Root.self, from: Data(json4.utf8))
print("root4", root4)
} catch {
print(error)
}
This will print
root1 Root(maintenance: Optional(true))
root2 Root(maintenance: Optional(false))
root3 Root(maintenance: nil)
typeMismatch(Swift.Bool, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "maintenance", intValue: nil)], debugDescription: "Expected to decode Bool but found a string/data instead.", underlyingError: nil))
You can try
struct Root: Codable {
var maintenance: Bool?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
self.maintenance = try container.decode(Bool.self, forKey: .maintenance)
}
catch {
}
}
}
In most cases, I'd probably use SeaSpell or Sh_Khan's solutions. Bool? is usually not the right type, but in this case it seems precisely what you mean (you seem to want to keep track of whether the value was set or not, rather than defaulting to something). But those approaches do require a custom decoder for the whole type which might be inconvenient. Another approach would be to define a new type just for this 3-way value:
enum Maintenance {
case `true`, `false`, unset
}
(Maybe "enabled" and "disabled" would be better here than taking over the true and false keywords. But just showing what Swift allows.)
You can then decode this in a very strict way, checking for true, false, or "" and rejecting anything else ("false" for example).
extension Maintenance: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let value = try? container.decode(Bool.self) {
self = value ? .true : .false
} else if let value = try? container.decode(String.self), value.isEmpty {
self = .unset
} else {
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Unable to decode maintenance")
}
}
}
Depending on how the rest of your code work, this three-way enum may be more convenient or less convenient than an Optional Bool, but it's another option.
With this, you don't need anything special in Demo:
struct Demo: Decodable {
var maintenance: Maintenance
}
An important distinction is that maintenance here is not optional. It is required. There are just three acceptable values. Even if using Bool? you should think hard about whether there is a difference between "" and missing.
You really only have a couple of decent options that I know of.
do codable init yourself and loose the free one.
struct Root: Codable {
var maintenance: Bool?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let value = (try? container.decode(Bool.self, forKey: .maintenance) ?? "")
self.maintenance = (value.isEmpty() || value == "false" ) ? nil : true
}
}
For obvious reasons this may not be ideal, especially if you have a lot of other variables to decode. The other option is to use a getter and add a variable to store the string optional.
Calculated var
private var _maintenance: String?
var maintenance: Bool {
get {
((_maintenance ?? "").isEmpty || _maintenance == "false") ? false : true
}
}
This solution is more ideal because you only need to change coding keys and add a var.
Bool? is a very bad idea unless you know exactly what you are doing, because you have three possible values: true, false and nil.
You can’t use it directly in an if statement. You can compare b == true, b == false or b == nil and I expect that b == false will not produce the result you expect.
You want to use a three-value type for two possible values, which is asking for trouble.
I'd get the API changed because that's just bad design to have a field with two different types. It'd be much more sensible to have it as something like
maintenance: { active: true }
And for the case where there's no station, either set it as null or remove the field

Cast Any to RawRepresentable where RawValue == String

I am trying to encode data. For the sake of the example I'll use JSON encoding even though there are other solutions for this out there.
I would like to handle enums that are backed by String or an Int as if they were simply String and Int:
struct UnhandledValueError: Error {}
enum Foo: String {
case bar
}
func encode(_ data: Any?) throws -> String {
guard let data = data else {
return "null"
}
if let string = data as? String {
return "\"\(string)\""
}
if let int = data as? Int {
return String(int)
}
// represent String and Int based enums there
if let array = data as? [Any?] {
return try "[\(array.map({ try encode($0) }).joined(separator: ","))]"
}
if let dict = data as? [String: Any?] {
return try "{\(dict.map({ "\"\($0.key)\": \(try encode($0.value))" }).joined(separator: ","))}"
}
throw UnhandledValueError()
}
let value: Any? = ["foo": Foo.bar]
try encode(value)
Because RawRepresentable is a generic protocol I don't think I can do either
if let value = data as? RawRepresentable
or
if let value = data as? RawRepresentable where RawValue == String
How can I access the raw value (as Any?) from an object that might be RawRepresentable ?
You can create a generic method and constrain its type to RawRepresentable where its RawValue conform to LosslessStringConvertible:
func encode<T>(object: T) -> String where T: RawRepresentable, T.RawValue: LosslessStringConvertible {
.init(object.rawValue)
}
Playground testing:
enum IntEnum: Int {
case a = 1, b, c
}
enum StringEnum: String {
case a, b , c
}
let int: IntEnum = .c
let string: StringEnum = .c
encode(object: int) // "3"
encode(object: string) // "c"

swift : shortcut for guard "let self = ..."?

I have to parse json server response into a swift object. I use this code :
struct MyGPSCoords {
var latitude:Double
var longitude:Double
var accuracy:Int
var datetime:NSDate
init?(infobrutFromJson_:[String:String]?)
{
guard let infobrut = infobrutFromJson_ else {
// first time the user sign up to the app, php server returns "null" in Json
return nil
}
guard
let lat:Double = Double(infobrut["latitude"] ?? "nil"),
let lng = Double(infobrut["longitude"] ?? "nil"),
let acc = Int(infobrut["accuracy"] ?? "nil"),
let dtm = NSDate(timeIntervalSince1970: Double(infobrut["time"] ?? "nil"))
else {
print("warning : unable to parse data from server. Returning nil");
return nil ; // position not NIL but format not expected => = nil
}
self.latitude = lat
self.longitude = lng
self.accuracy = acc
self.datetime = dtm
}
}
I want to make the "guard" statement as short as possible. First, I added ?? "nil" so if one of the keys doesn't exist, Double("nil") gets nil and guard statement can handle. For NSDate, I made an extension with a convenience init? returning nil if its input is nil, so I can do the same.
My question is, can i do it even shorter by assigning directly to self.latitude the values right in the guard statement ? I tried that :
guard self.latitude = Double(infobrut["latitude"] ?? "nil"), ...
It says it cannot cast from Double? to Double. Is there any way to make this guard even shorter and avoiding me to assign lat, lng, acc and dtm buffering variables ?
First, you should of course try to fix the JSON, since this JSON is malformed. Strings are not numbers in JSON. Assuming you cannot correct this broken JSON, the tool you want is flatMap, which converts T?? to T? (which is what guard-let expects).
guard
let lat = infobrut["latitude"].flatMap(Double.init),
let lng = infobrut["longitude"].flatMap(Double.init),
let acc = infobrut["accuracy"].flatMap(Int.init),
let dtm = infobrut["time"].flatMap(TimeInterval.init).flatMap(Date.init(timeIntervalSince1970:))
else {
print("warning : unable to parse data from server. Returning nil")
return nil // position not NIL but format not expected => = nil
}
I saw a lot of comments that Codable won't work here, but it absolutely will, and it's really what you should use. Here's one way (this is a little sloppy about its error messages, but it's simple):
struct MyGPSCoords: Decodable {
var latitude:Double
var longitude:Double
var accuracy:Int
var datetime:Date
enum CodingKeys: String, CodingKey {
case latitude, longitude, accuracy, datetime
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
guard
let lat = Double(try container.decode(String.self, forKey: .latitude)),
let lng = Double(try container.decode(String.self, forKey: .longitude)),
let acc = Int(try container.decode(String.self, forKey: .accuracy)),
let dtm = TimeInterval(try container.decode(String.self,
forKey: .datetime)).flatMap(Date.init(timeIntervalSince1970:))
else {
throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Could not decode"))
}
self.latitude = lat
self.longitude = lng
self.accuracy = acc
self.datetime = dtm
}
}
Or you can get really fancy with an internal helpful function and get rid of all the temporary variables and optionals through the power of throws.
struct MyGPSCoords: Decodable {
var latitude:Double
var longitude:Double
var accuracy:Int
var datetime:Date
enum CodingKeys: String, CodingKey {
case latitude, longitude, accuracy, datetime
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
func decodeBrokenJSON<T>(_ type: T.Type,
forKey key: CodingKeys) throws -> T
where T: Decodable & LosslessStringConvertible {
return try T.init(container.decode(String.self, forKey: key)) ?? {
throw DecodingError.dataCorruptedError(forKey: key,
in: container,
debugDescription: "Could not decode \(key)")
}()
}
self.latitude = try decodeBrokenJSON(Double.self, forKey: .latitude)
self.longitude = try decodeBrokenJSON(Double.self, forKey: .longitude)
self.accuracy = try decodeBrokenJSON(Int.self, forKey: .accuracy)
self.datetime = Date(timeIntervalSince1970: try decodeBrokenJSON(TimeInterval.self, forKey: .datetime))
}
}
(IMO, this is a great example of how throws really shines and should be used much more than it commonly is.)
What you want to do is not possible. The compiler already tells you so, even though the error message is a bit misleading. You can either use a guard let that creates a new variable, or you can use a guard with a boolean expression. In your case there is no let so the compiler tries to parse a boolean expression. Instead it sees the assignment and produces the error message that the types don’t match. If the types would match (as in guard self.latitude = 12.0) the error message would be clearer: error: use of '=' in a boolean context, did you mean '=='?
The other solutions seem overly complicated. Simply make it
struct MyGPSCoords: Codable {
var latitude: Double?
var longitude: Double?
var accuracy: Int?
var datetime: Date?
var isValid {
return [latitude, longitude, accuracy, datetime].filter { $0 == nil }.isEmpty
}
}
// jsonData is whatever payload you get back from the URL request.
let coords = JSONDecoder().decode(jsonData, type: MyGPSCoords.self)
if !coords.isValid {
print("warning : unable to parse data from server.")
}
Since all of your properties are Optional, parsing can't fail if one or more of them is missing. The isValid check is much simpler than the guard let... clause in your original code.
EDIT: If, as Rob Napier suggests, all the JSON values are encoded as Strings, then here's another way to structure your MyGPSCoords:
struct MyGPSCoords: Codable {
// These are the Codable properties
fileprivate var latitudeString: String?
fileprivate var longitudeString: String?
fileprivate var accuracyString: String?
fileprivate var datetimeString: String?
// Default constant to use as a default check for validity
let invalid = Double.leastNonzeroMagnitude
// And these are the derived properties that you want users to use
var latitude: Double {
return Double(latitudeString ?? "\(invalid)") ?? invalid
}
var longitude: Double {
return Double(longitudeString ?? "\(invalid)") ?? invalid
}
var accuracy: Int {
return Int(accuracyString ?? "\(invalid)") ?? Int(invalid)
}
var date: Date {
return <whatever-formatter-output-you-need>
}
var isValid {
return [latitudeString, longitudeString, accuracyString, datetimeString].filter { $0 == nil }.isEmpty
&& latitude != invalid && longitude != invalid
&& accuracy != Int(invalid) /* && however you compare dates */
}
}
I know that question is old but I have to admit that I didn't understand very well the in-built Decodable/Decoder system in Swift (especially this notion of "Container", can't figure out what it represents exactly)
Anyway, I made my own decoder which enable to handle this situation in quite the same way as Android does (to decode JSONObject). I made an extension of Dictionary like this :
protocol Decodable {
init(from raw:[String:Any]) throws
}
extension Dictionary where Key == String
{
enum DecodableError : Error {
case unknownKey(key:String)
case keyWrongType(key:String, expectedType:String, actualValue:String)
case nullValueAtKey(key:String)
}
func getSafe<T>(_ key:String, forType t:T.Type) throws -> T
{
if(self[key] != nil)
{
if(self[key] is NSNull) // corresponds to the JSON null value (by experience)
{
throw DecodableError.nullValueAtKey(key:key)
}
else if(self[key] is T) // for raw type
{
return self[key] as! T
}
// try to parse self[key] to provided type if it's decodable
else if(self[key] is [String:Any] && t is Decodable.Type)
{
return try (t as! Decodable.Type).init(from: self[key] as! [String:Any]) as! T
}
throw DecodableError.keyWrongType(key: key,
expectedType: String(describing: T.self),
actualValue: String(describing:self[key]!))
}
throw DecodableError.unknownKey(key:key)
}
func getSafeOpt<T>(_ key:String, forType t:T.Type) throws -> T?
{
if(self[key] != nil)
{
if(self[key] is NSNull)
{
return nil
}
return try getSafe(key, forType: t)
}
throw DecodableError.unknownKey(key:key)
}
}
I use it like that :
struct Position : Decodable {
let latitude:Double
let longitude:Double
let accuracy:Int?
let member:Member
init(from raw:[String:Any]) throws
{
// getSafe throw exception whenever node are json<null> or if node doesn't exist
latitude = try raw.getSafe("lat", forType: Double.self)
longitude = try raw.getSafe("lng", forType: Double.self)
// getSafeOpt returns nil if the JSON value of node acc is null,
// but it still throw an exception if there is no "acc" node
accuracy = try raw.getSafeOpt("acc", forType: Int.self)
// you can use it to decode other objects that implement my Decodable protocol too :
member = try raw.getSafeOpt("member", forType: Member.self)
}
}
do {
try app.position = Position(from: rawDic)
}
catch {
print("Unable to parse Position : \(error) ")
return
}
This does not handle yet the JSON arrays, I'll do it later, or feel free to update my answer if you wish to add a JSON array handling mechanism.

Resources