Decoding data to Struct fails - swift

Currently, I have a custom struct that conforms to Codable
struct Language: Codable {
var isoCode: String
var name: String
var translations: [String: String]
}
And, then proceeding with encoding it successfully works
let lang = Language(isoCode: "en", name: "English", translation: ["greetings": "morning"])
let langEncoded = try? lang.encode()
The problem arises when I try to decode data into Language struct
let lang = Language.decode(from: langEncoded)
Producing an error message: Error message: Ambiguous use of 'decode(with: from:)'

You need to do as follow.
Code:
let lang = try JSONDecoder().decode(Language.self,from: langEncoded)

Related

Any ways of creating QR code for CoreData UUID object?

I'm trying to create qr code for UUID, but the main thing that stops me is that ways that I saw on creating qr codes require using String instead of UUID, so that's what I went for.
var uuid: UUID
var uuidString: {uuid} //cannot convert return expression of type 'UUID' to return type 'String'
Another option was
var uuid = UUID().uuidString
But It gave error later, when I was trying to use qr generating algorithm on practice (full qr gen code below)
import SwiftUI
import CoreImage.CIFilterBuiltins
struct QrCodeGen : View {
let context = CIContext()
let filter = CIFilter.qrCodeGenerator()
var uuid = UUID().uuidString
var body : some View {
Image(uiImage: createQrCodeImage(uuid))
}
func createQrCodeImage(_ uuid: String) -> UIImage{
let data = Data(uuid.utf8)
filter.setValue(data, forKey: "inputMessage")
if let qrCodeImage = filter.outputImage{
if let qrCodeCGImage = context.createCGImage(qrCodeImage, from: qrCodeImage.extent){
return UIImage(cgImage: qrCodeCGImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
Usage of generating qr:
NavigationLink(destination: QrCodeGen(uuid: Item.itemid)) //error: Value of type "NSManagedObject" has no member itemid
But later when showing text of Item.itemid everything is fine and recognised
Reeboting my machine and relaunching Xcode dint solve the issue also.
This code works to return the UUIDString for a UUID:
struct TestUUID {
var uuid: UUID
var uuidString: String {return uuid.uuidString}
}
let aTestUUID = TestUUID(uuid: UUID())
print(aTestUUID.uuidString)
It isn't clear to me what you're asking in the second part of your question. What error are you getting?
("It gave me error" isn't very helpful)

Decoding numerical snake_case keys with JSONDecoder

I have the following JSON object which is to be converted to an object using JSONDecoder:
{
"first_key": 3,
"image_1000x1000": "location"
}
This maps to the following Swift model:
struct Response: Decodable {
var firstKey: Int
var image1000x1000: String
}
By using JSONDecoder with the .convertFromSnakeCase option, the snake_case keys inside the JSON are transformed to camelCase by using the algorithm defined in the documentation:
This strategy follows these steps to convert JSON keys to camel-case:
Capitalize each word that follows an underscore.
Remove all underscores that aren't at the very start or end of the string.
Combine the words into a single string.
Therefore, in this case:
first_key becomes firstKey (as expected)
image_1000x1000 should become image1000x1000
However when attempting to decode this response, a keyNotFound error for the image1000x1000 key is thrown (see this live example):
let json = "{\"first_key\": 3, \"image_1000x1000\": \"location\"}".data(using: .utf8)!
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let response = try decoder.decode(Response.self, from: json)
print(response)
} catch let e {
print(e)
}
What is incorrect about my camel case conversion of image_1000x1000, and why can’t JSONDecoder find the corresponding key?
You can see what the algorithm is expecting by running the process in reverse; use a JSONEncoder to encoder your data and inspect the output:
struct Response: Codable {
var firstKey: Int
var image1000x1000: String
}
let test = Response(firstKey: 10, image1000x1000: "secondKey" )
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let data = try encoder.encode(test)
print(String(data: data, encoding: .utf8)!)
This will produce:
{"first_key":10,"image1000x1000":"secondKey"}
So if you have control over the JSON and can live with image1000x1000 as a key, then you're done. If not you'll have to do something like this:
struct Response: Codable {
var firstKey: Int
var image1000x1000: String
private enum CodingKeys: String, CodingKey {
case image1000x1000 = "image_1000x1000"
case firstKey = "first_key"
}
}
Another option is to implement a custom key encoding strategy. It may end up being less code. See KeyEncodingStrategy for more about this.
I am facing the same issue my json keys are iso639_1 and iso639_2. Without adding Coding key it will works.
make your variable optional
Here it's my Decodable model
struct CountryModel: Decodable{
var name: String
var borders: [String]
var region: String
var alpha2Code: String
var alpha3Code: String
var flag: String
var languages: [languages]
}
struct languages: Decodable {
var iso639_1: String?
var iso639_2: String?
var name: String
var nativeName: String
}
When I add optional to both underscore variables iso639_1 and iso639_2. Then it works fine may be due to Null Value!
Here, in your case add optional to your image1000x1000 variable. Like below
struct Response: Decodable {
var firstKey: Int
var image_1000x1000: String?
}
Hope it will works!

PropertyListDecoder handle key not found

I have a struct that is saved in user defaults as a property list. If the struct changes (perhaps after a app version update), the PropertyListDecoder().decode generates an error for key not found. For example, I've added the key "passwordProtect" to my struct. When the app gets the stored struct from user defaults and tries to decode it, it gives the error Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "passwordProtect", intValue: nil)
The behavior I want in this case is that since passwordProtect is not set, I'd like to decode to my struct but with a default value for passwordProtect. I have already declared the variables default value in my struct. How can I get this behavior?
My struct:
struct Settings: Codable {
var showTimeOutMessage: Bool = false
var browserLimit: Bool = false
var browserLimitSeconds: Int = 300
var passwordProtect: Bool = false
var metaTime: TimeInterval?
init(fromDict : [String : Any?]?){...}
}
How I save it:
var settingsStruct = Settings(fromDict: formatedDict)
settingsStruct.metaTime = Date().timeIntervalSince1970
defaults.set(try? PropertyListEncoder().encode(settingsStruct), forKey:"settings")
How I retrieve it:
if let settingsData = defaults.value(forKey:"settings") as? Data {
let settingsStruct = try! PropertyListDecoder().decode(Settings.self, from: settingsData)
dump(settingsStruct)
}
Thanks!

How do I correctly print a struct?

I'm trying to store an array of store structs within my users struct, but I can't get this to print correctly.
struct users {
var name: String = ""
var stores: [store]
}
struct store {
var name: String = ""
var clothingSizes = [String : String]()
}
var myFirstStore = store(name: "H&M", clothingSizes: ["Shorts" : "Small"])
var mySecondStore = store(name: "D&G", clothingSizes: ["Blouse" : "Medium"])
var me = users(name: "Me", stores: [myFirstStore, mySecondStore])
println(me.stores)
You’re initializing them just fine. The problem is your store struct is using the default printing, which is an ugly mangled version of the struct name.
If you make it conform to CustomStringConvertible, it should print out nicely:
// For Swift 1.2, use Printable rather than CustomStringConvertible
extension Store: CustomStringConvertible {
var description: String {
// create and return a String that is how
// you’d like a Store to look when printed
return name
}
}
let me = Users(name: "Me", stores: [myFirstStore, mySecondStore])
println(me.stores) // prints "[H&M, D&G]"
If the printing code is quite complex, sometimes it’s nicer to implement Streamable instead:
extension Store: Streamable {
func writeTo<Target : OutputStreamType>(inout target: Target) {
print(name, &target)
}
}
p.s. convention is to have types like structs start with a capital letter

Should a dictionary be converted to a class or struct in Swift?

I am working on a native iOS application that receives data in JSON format from a web-service which we are also in control of. The plan is to change out the backend database in a bout 18 months in favor of a different platform.
With that in mind, we want to be sure that that iOS app is going to be relatively easy to adapt to the new datasource, particularly as we may change the keys used in the associative array received from the server via JSON.
There are two goals:
Create a single location for each PHP request where the keys can be modified if needed. This would avoid digging through code to find things like job["jobNumber"].
Clean up our existing code to also eliminate references like job["jobNumber"].
We are both very new to Swift with no Objective-C experience, but I am was thinking a Struct or Class would be appropriate to create references like job.jobNumber.
Should a dictionary be converted into a class or struct? Sample code representing a reusable method of taking a Dictionary<String, String> as shown below and converting it to the recommended type would be extremely helpful.
Example Dictionary:
job = {
"jobNumber" : "1234",
"jobName" : "Awards Ceremony",
"client" : "ACME Productions"
}
Desired result:
println("job name is \(job.name)")
// prints: job name is Awards Ceremony
To access it like this you need to convert your dictionary to Struct as follow:
edit/update: Swift 3.x
struct Job: CustomStringConvertible {
let number: Int
let name, client: String
init(dictionary: [String: Any]) {
self.number = dictionary["jobNumber"] as? Int ?? 0
self.name = dictionary["jobName"] as? String ?? ""
self.client = dictionary["client"] as? String ?? ""
}
var description: String {
return "Job#: " + String(number) + " - name: " + name + " - client: " + client
}
}
let dict: [String: Any] = ["jobNumber": 1234,
"jobName" : "Awards Ceremony",
"client" : "ACME Productions"]
let job = Job(dictionary: dict)
print(job.number) // 1234
print(job.name) // "Awards Ceremony"
print(job.client) // "ACME Productions"
print(job) // "Job#: 1234 - name: Awards Ceremony - client: ACME Productions"""
edit/update:
Swift 4 or later you can use JSON Codable protocol:
struct Job {
let number: Int
let name, client: String
}
extension Job: Codable {
init(dictionary: [String: Any]) throws {
self = try JSONDecoder().decode(Job.self, from: JSONSerialization.data(withJSONObject: dictionary))
}
private enum CodingKeys: String, CodingKey {
case number = "jobNumber", name = "jobName", client
}
}
extension Job: CustomStringConvertible {
var description: String {
return "Job#: " + String(number) + " - name: " + name + " - client: " + client
}
}
let dict: [String: Any] = ["jobNumber": 1234,
"jobName" : "Awards Ceremony",
"client" : "ACME Productions"]
do {
let job = try Job(dictionary: dict)
print(job.number) // 1234
print(job.name) // "Awards Ceremony"
print(job.client) // "ACME Productions"
print(job) // "Job#: 1234 - name: Awards Ceremony - client: ACME Productions\n"
} catch {
print(error)
}
Definitely a job for a struct.
1. Structs are thread-safe and don't need to be managed by ARC.
2. Some studies have found them to be about 30,000x faster to work with than classes in general.
3. Structs also provide default initializers so your code will be cleaner.
4. In this case, you don't have to worry about inheritance/subclassing.
5. The Protocol Oriented Programming paradigm recommends using structs over classes if you're able.
struct Job {
let number: Int
let name: String
let client: String
}
Initializer for free:
let newJob = Job(number: 2, name: "Honey", client: "Jeff")
Or you can create a custom initializer that takes the dictionary:
struct Job {
let number: Int
let name: String
let client: String
init(json: [String: Any]) {
self.number = Int(dictionary["jobNumber"] as? String) ?? 0
self.name = dictionary["jobName"] as? String ?? ""
self.client = dictionary["client"] as? String ?? ""
}
}
usage:
let newJob = Job(json: yourDictionary)
print(newJob.number)
// outputs: 1234
You can add an extension to Dictionary like this to get generic objects:
extension Dictionary where Key == String, Value: Any {
func object<T: Decodable>() -> T? {
if let data = try? JSONSerialization.data(withJSONObject: self, options: []) {
return try? JSONDecoder().decode(T.self, from: data)
} else {
return nil
}
}
}
and use like this on any [String: Any] dictionaries:
let object: MyDecodableStruct? = dictionary.object()
I generally make use of value classes to achieve what you want to do. In my project I do something like following:
protocol Request : class {
func toDictionary() -> [String : String]
}
protocol Response : class {
init(dictionary: [String : String])
}
class MyRequest : Request {
var var1: Int
var var2: String
//Other stuff in class...
func toDictionary() -> [String : String] {
//Convert the value to dictionary and return
}
}
class MyResponse : Response {
var var1: String
var var2: Int
//You could have nested object as members
var innerObject: MyInnerResponseClass
//Other stuff in class...
var someCalculatedProperty: String {
return //Calculate property value
}
required init(dictionary: [String : String]) {
//Initialize class from dictionary
}
}
class MyInnerResponseClass: Response {
//Class definition here...
}
For objects that could be used as request and response you could implement both of the protocols.
You need to write code for translation once, but then it could be easy to use everywhere. Also by using calculated properties you may get extra ease.
I am not sure if you could just do it out of the box in Swift. I will require reflection which is not yet very well supported by Swift. Also even if there is reflection and you come up with clever way to use to achieve what you need, it could be quite slow if the data is quite large.
My two cents about "logic". )all correct about using structs and so on...)
Do NOT keep (as many from web do..) data in dict or JSON, convert it to struct always.
a lot of efficiently, think about for example about sorting in a tableview..