How to fix: Cannot convert value of type: GameData - Codeable, to expected argument Data - swift

I am attempting to decode my JSON response data from type: AnyObject? back into something that can be printed out in the console / interacted with.
reading back the data, before decoding prints projectName.GameData
Here is the breakdown, data comes back from the response as type: Any? Because it sent up as
class GameData : Codable {
var isPlayerOneTurn: Bool!
var wasCreated: Bool!
var playerOne: String!
var playerTwo: String!
var board: [[Int]]!
init() {
}
}
The current error I am getting when attempting to decode is Cannot convert value of type 'GameData' to expected argument type 'Data'
code :
let decoder = JSONDecoder()
let dataTest = try? decoder.decode(GameData.self, from: data)
Am I missing a correct init() method on the GameData class?
UPDATE:
data was changed to type Data here: thank you #rmaddy for the comment pointing this out.
let data = data as? Data
let decoder = JSONDecoder()
let dataTest = try? decoder.decode(GameData.self, from: data!)
print("data: \(String(describing: dataTest))")
the print line still shows data: Optional(projectName.GameData)
What is wrong here still, not allowing me to view the values of the class GameData?

The print line mentioned in the question, was the value of the game object decode.. That was all XCode would print out - the name of the original object before decoding. Using dataTest.myValue worked when accessing data from the object.

Related

Error: "Expected to decode Dictionary<String, Any> but found an array instead." — but I haven't defined a dictionary? [duplicate]

This question already has answers here:
Decoding Error -- Expected to decode Dictionary<String, Any> but found an array instead
(2 answers)
Closed 1 year ago.
I'm working on a creative project, and I'm trying to decode content from an API database using Swift's JSONDecoder() function. I've built my structs, a getData() function, and I've set up a do-try-catch for the JSONDecoder() function. I'm having difficulty understanding what I'm doing to get the error I'm getting.
Here are my structs:
struct Response: Codable {
let foundRecipes: [Recipe]
let foundIngredients: [Ingredient]
}
struct Recipe: Codable {
let id: Int
let title: String
let image: String
let imageType: String
let usedIngredientCount: Int
let missedIngredientCount: Int
let missedIngredients: [Ingredient]
let usedIngredients: [Ingredient]
let unusedIngredients: [Ingredient]
let likes: Int
}
struct Ingredient: Codable {
let id: Int
let amount: Int
let unit: String
let unitLong: String
let unitShort: String
let aisle: String
let name: String
let original: String
let originalString: String
let origianalName: String
let metaInformation: [String]
let meta: [String]
let image: String
}
Here's my getData() function:
func getData(from url: String) {
URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("something went wrong.")
return
}
var result: Response?
do {
result = try JSONDecoder().decode(Response.self, from: data)
}
catch {
print("")
print(String(describing: error)) // Right here is where the error hits.
}
guard let json = result else {
return
}
print(json.foundRecipes)
}).resume()
}
Here's a link to the API's documentation. The URL I'm calling in getData() links to the same structure of search as shown in their example: https://spoonacular.com/food-api/docs#Search-Recipes-by-Ingredients — and here's a screenshot of the url results for the exact search I'm working on: https://imgur.com/a/K3Rn9SZ
And finally, here's the full error that I'm catching:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
My understanding of this error is that it's saying I told the JSONDecoder() to look for a Dictionary of <String, Any>, but it's at the link and only seeing an array. I'm confused, because I don't know where it thinks I'm providing a dictionary. Where am I screwing up? Not looking for specific code changes, just some guidance on what I'm missing.
Thanks in advance :)
As you can see in your image of the API data and in the API documentation you linked to, the API is returning an array (in the documentation, for example, you can see that it is surrounded by [...]). In fact, it looks like the API returns an array of Recipe.
So, you can change your decoding call to this:
var result: [Recipe]?
do {
result = try JSONDecoder().decode([Recipe].self, from: data)
print(result)
} catch {
print(error)
}
Perhaps your idea for Response came from somewhere else, but the keys foundRecipes or foundIngredients don't show up in this particular API call.
Also, thanks to #workingdog's for a useful comment about changing amount to a Double instead of an Int in your model.

Issue with UserDefaults (converting data to array and back)

What I want to do:
I want to get an array from UserDefaults that I saved beforehand and append a custom object to it. Afterwards I want to encode it as a Data-type again and set this as the UserDefaults Key again.
My problem:
The encoding part is what is not working as intended for me.
It says: -[__SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x60000011a540
But I do not know how to fix this.
Below is my code for more context:
do {
let decoded = defaults.object(forKey: "ExArray") as! Data
var exo = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decoded) as! [Exerc]
exo.append(datas[indexPath.row])
let enco = try NSKeyedArchiver.archivedData(withRootObject: exo, requiringSecureCoding: false) <- Here is the error
defaults.set(enco, forKey: "ExArray")
} catch {
print("Error encoding custom object NOSEARCHO")
}
This is how Exerc looks:
struct Exerc: Codable {
var title: String
var exID: String
}
Seems like you are not using the archiver features, so why don't you just use the codable?
do {
let key = "ExArray"
let decoded = defaults.data(forKey: key)!
var exo = try JSONDecoder().decode([Exerc].self, from: decoded)
exo.append(datas[indexPath.row])
let enco = try JSONEncoder().encode(exo)
defaults.set(enco, forKey: key)
} catch {
print("Error encoding/decoding custom object NOSEARCHO", error)
}
It just a simple refactored MVP of the original code, but you can even work a bit on this and make it human readable right in the plist file!

Conversion of JSON String to Object always returns nil

I'm fairly new to this. Anyway, here we go:
I have JSON data that comes from an API. For the sake of this question, I have simplified it greatly. You can run the following code in a Playground.
import UIKit
struct Book: Codable {
let image: String
}
// this comes from my API
let jsonString = "{ \"image\" = \"someURL\" }"
print(jsonString) // { "image" = "someURL" }
// convert String to Data
let jsonData = jsonString.data(using: .utf8)
// decode data (in my project, I catch the error, of course)
let decoder = JSONDecoder()
let decodingResult = try? decoder.decode(Book.self, from: jsonData!)
print(decodingResult) // nil
As you can see, I'm trying to decode my JSON-String into an Object (my Struct), but the Decoder always returns nil.
Can someone point me in the right direction?
Thank you.
Your current jsonString isn't a proper JSON. Change it to "{ \"image\": \"someURL\" }", and it should work. For more information on JSON syntax, check this manual.

Can't unarchive custom Object with NSKeyedUnarchiver in AR Multiuser

I'm using MultipeerConnectivity to share SCNNodes position in a multiuser AR session.
When I archive (with NSKeyedArchiver.archivedData(withRootObject: someARNode, requiringSecureCoding: true) )
And unarchive (with if let node = try NSKeyedUnarchiver.unarchivedObject(ofClass:SCNNode.self, from: data) {)
Everything works fine, but now I'm trying to send a custom Object like this:
struct ARUser: Codable {
var idUser : String
var position: [Double]
}
When I try to unarchive the object received with the NSKeyedUnarchiver.unarchivedObject it let me error.
if let node = try NSKeyedUnarchiver.unarchivedObject(ofClass:ARUser.self, from: data) {...}
I get the syntax error: Incorrect argument label in call (have 'ofClass:from:', expected 'ofClasses:from:')
But if I change the function as suggested by the compiler:
if let node = try NSKeyedUnarchiver.unarchivedObject(ofClasses:[ARUser.self], from: data) {..}
I get the next syntax error: Cannot convert value of type 'ARUser.Type' to expected element type 'AnyObject.Type'
So, the question here is, what's the correct way to unarchive custom Objects?
Since here you use Codable
struct ARUser: Codable {
Then
do {
let dataToObject = try JSONDecoder().decode(ARUser.self,from:data)
let objectToData = try JSONEncoder().encode(dataToObject)
}
catch {
print(error)
}
NSKeyedUnarchiver is an old Objective-C stuff

Alamofire XML request to PropertyList

I am trying to parse an XML data using Codable from the sample https://www.w3schools.com/xml/note.xml.
My struct is
struct Note: Codable {
var to: String?
var from: String?
var heading: String?
var body: String?
}
However if I make the following request I get the error responseSerializationFailed : ResponseSerializationFailureReason "PropertyList could not be serialized because of error:\nThe data couldn’t be read because it isn’t in the correct format."
let url = URL(string: "https://www.w3schools.com/xml/note.xml")
Alamofire.request(url!, method: .get, encoding: PropertyListEncoding.default).responsePropertyList { (response) in
guard response.error == nil else {
print(response.error!)
exp.fulfill()
return
}
print(response)
if let data = response.data {
print(data)
let decoder = PropertyListDecoder()
let note = try! decoder.decode(Note.self, from: data)
print(note)
}
}
How do you exactly work with the responsePropertyList in Alamofire?
Currently, Apple's Codable protocol does not have a way to decode XML. While a Plist is XML, XML is not necessarily a Plist unless it follows a certain format.
While there are plenty of third party libraries, I would suggest you take a look at the XMLParsing library. This library contains a XMLDecoder and a XMLEncoder that uses Apple's own Codable protocol, and is based on Apple's JSONEncoder/JSONDecoder with changes to fit the XML standard.
Link: https://github.com/ShawnMoore/XMLParsing
W3School's XML To Parse:
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
Swift Struct conforming to Codable:
struct Note: Codable {
var to: String
var from: String
var heading: String
var body: String
}
XMLDecoder:
let data = Data(forResource: "note", withExtension: "xml") else { return nil }
let decoder = XMLDecoder()
do {
let note = try decoder.decode(Note.self, from: data)
} catch {
print(error)
}
XMLEncoder:
let encoder = XMLEncoder()
do {
let data = try encoder.encode(self, withRootKey: "note")
print(String(data: data, encoding: .utf8))
} catch {
print(error)
}
There are a number of benefits for using Apple's Codable protocol over that of a third-party's protocol. Take for example if Apple decides to begin supporting XML, you would not have to refactor.
For a full list of examples of this library, see the Sample XML folder in the repository.
There are a few differences between Apple's Decoders and Encoders to fit the XML standard. These are as follows:
Differences between XMLDecoder and JSONDecoder
XMLDecoder.DateDecodingStrategy has an extra case titled keyFormatted. This case takes a closure that gives you a CodingKey, and it is up to you to provide the correct DateFormatter for the provided key. This is simply a convenience case on the DateDecodingStrategy of JSONDecoder.
XMLDecoder.DataDecodingStrategy has an extra case titled keyFormatted. This case takes a closure that gives you a CodingKey, and it is up to you to provide the correct data or nil for the provided key. This is simply a convenience case on the DataDecodingStrategy of JSONDecoder.
If the object conforming to the Codable protocol has an array, and the XML being parsed does not contain the array element, XMLDecoder will assign an empty array to the attribute. This is because the XML standard says if the XML does not contain the attribute, that could mean that there are zero of those elements.
Differences between XMLEncoder and JSONEncoder
Contains an option called StringEncodingStrategy, this enum has two options, deferredToString and cdata. The deferredToString option is default and will encode strings as simple strings. If cdata is selected, all strings will be encoded as CData.
The encode function takes in two additional parameters than JSONEncoder does. The first additional parameter in the function is a RootKey string that will have the entire XML wrapped in an element named that key. This parameter is required. The second parameter is an XMLHeader, which is an optional parameter that can take the version, encoding strategy and standalone status, if you want to include this information in the encoded xml.
PropertyList files although are in XML format, they need to follow Apple's PropertyList DTD: http://www.apple.com/DTDs/PropertyList-1.0.dtd
If you want to map a regular XML file (that do not follow PropertyList DTD) into a model object and you don't mind using an external library you can try XMLMapper.
You model for this XML should look like this:
class Note: XMLMappable {
var nodeName: String!
var to: String?
var from: String?
var heading: String?
var body: String?
required init(map: XMLMap) { }
func mapping(map: XMLMap) {
to <- map["to"]
from <- map["from"]
heading <- map["heading"]
body <- map["body"]
}
}
And you can map it from string using XMLMapper:
let note = XMLMapper<Note>().map(XMLString: xmlString)
Or if you install Requests subspec you can use responseXMLObject(queue:keyPath:mapToObject:completionHandler:) function like:
let url = URL(string: "https://www.w3schools.com/xml/note.xml")
Alamofire.request(url!, method: .get, encoding: XMLEncoding.default).responseXMLObject { (response: DataResponse<Note>) in
let note = response.result.value
print(note?.from ?? "nil")
}
Hope this helps.