Hello I'm new to swift and this is giving me some problems.
I have a function from SDK that returns Any as a result, this is what the actual data looks like:
/*{
code = 1;
msg = success;
result = {
macAddress = "E6:1D:4D:71:64:9B";
};
}*/
I figured out a way to get the data I need, but it seems quite convoluted.
sucBlock: {
(mac) in
if let macAddress = ((mac as AnyObject)["result"] as AnyObject )["macAddress"] as! Optional<String>{
print("macAddress:" + macAddress)
}
}
Is there a better way to achieve this result? I tried to create a struct and type cast this object, but somehow it always failed.
You need to avoid using AnyObject and as! in such cases
if let macAddress = mac as? [String:Any] , let item = macAddress["result"] as? [String:String] , let str = item["macAddress"] {
print("str:" + str)
}
If you need a struct ( Don't think it really deserves for your simple json )
do {
let data = try JSONSerialization.data(withJSONObject:mac)
let res = try JSONDecoder().decode(Root.self, from:data)
print(res.result.macAddress)
}
catch {
print(error)
}
struct Root: Codable {
let code: Int
let msg: String
let result: Result
}
// MARK: - Result
struct Result: Codable {
let macAddress: String
}
Related
I am developing a swift 5 project using Freddy FrameWork (for parsing). I have a requirement like when user search for the zipcode in autocomplete it will display the zipcodes from Array. But my code is getting crash.I am retrieving the zipcode from "JSON"(using Freddy). using the below code
struct FreddyJson: JSONDecodable {
let zipCodes: [JSON]
}
init(json: JSON) throws {
do{
let zipCodes = try? json.getArray(at: "WarningConfigurationUS", "ZipCode")
self.zipCodes = zipCodes!
}catch{
print(error)
}
}
After that I am calling this in ViewController.swift file like as below
func FreddyDataParsing()
{
let data = DataService.shared.readCustomizerMappingJson()
guard let freddy = try? FreddyJson(json: data) else {return}
self.autoCompletionPossibilities = freddy.zipCodes // I am getting an error "Cannot assign value of type '[JSON]' to type '[NSString]'"
}
The below code is fo getting the autosearch result
func getAutocompleteSuggestions(userText: String) -> [String]{
var possibleMatches: [String] = []
for item in autoCompletionPossibilities { //2
let myString:NSString! = item as NSString. //Here also I am getting the same error
let substringRange :NSRange! = myString.range(of: userText)
if (substringRange.location == 0)
{
possibleMatches.append(item as String)
}
}
return possibleMatches
}
Can anyone help me to resolve the above issue. Thanks in advance.
I am having the json response in which "products" key sometime having the int value and some cases it had an array?
How to check whether it is having array or Int?
"products": 25
or
"products": [77,80,81,86]
I am using this
self.productsCount = mResp["products"] as! [Int]
but it is crashed every time when it is not having array.
Now i am not getting how to check this because i have the different option for Int and Array?
Please help me.Thanks
There is no need to fall back to Any here. Even problematic JSON like this can be handled with Codable. You just need to keep trying the different types until one works.
struct Thing: Decodable {
let products: [Int]
enum CodingKeys: String, CodingKey {
case products
}
init(from decoder: Decoder) throws {
// First pull out the "products" key
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
// Then try to decode the value as an array
products = try container.decode([Int].self, forKey: .products)
} catch {
// If that didn't work, try to decode it as a single value
products = [try container.decode(Int.self, forKey: .products)]
}
}
}
let singleJSON = Data("""
{ "products": 25 }
""".utf8)
let listJSON = Data("""
{ "products": [77,80,81,86] }
""".utf8)
let decoder = JSONDecoder()
try! decoder.decode(Thing.self, from: singleJSON).products // [25]
try! decoder.decode(Thing.self, from: listJSON).products // [77, 80, 81, 86]
It crashes because you force unwrap as an Integer Array, even though you just have an integer. The solution is to check for both:
self.productsCount = mResp["products"] as? [Int] ?? mResp["products"] as? Int
Other Solution
if let proCount = mResp["products"] as? [Int] {
self.productsCount = proCount
} else {
self.productsCount = mResp["products"] as? Int
}
This is temporary solution as you want. Check for possible type with "Any" type.
var anyType : Any!
anyType = "123"
anyType = ["Test","Test1"]
anyType = 1
if anyType is Array {
print("is Array")
}else if anyType is String {
print("is String")
}else if anyType is Int {
print("is Int")
}
let dict = [77,80,81,86]//Pass your parameter or parsed json value
if dict is Array<Any> {
print("Yes, it's an Array")
}
else{
print("NO, it's not an Array")
}
let's assume your json name is jsonData
Check for Int and Array Int:
if let intVal = jsonData["products"] as? Int {
print("Products is a Integer: ", intVal)
} else if let jsonArr = jsonData["products"] as? [Int] {
var intVals = [Int]()
for json in jsonArr {
intVals.append(json)
}
print("Json is array of Int: ", intVals)
}
Generic solution would be like this,
let products = mResp["products"] as? Any
if let item = products as? [Int] {
print("array", item)
} else if let item = products as? Int {
print("Integer", item)
}
Use Generics for obtaining a better solution and provide the type at the time of decoding this model.
struct Product<T: Codable>: Codable {
let products: T?
}
And you can use it with nested try catch:
do {
let product = try JSONDecoder().decode(Product<Int>.self, from: data)
print(product)
} catch {
do {
let product = try JSONDecoder().decode(Product<[Int]>.self, from: data)
print(product)
} catch {
print(error)
}
}
Note: This solution assumes there is not more than a few different type-varying properties in the codable struct. If there are multiple type-varying properties I'd recommend use a custom init(decoder:) as provided in the accepted answer which would be much better design instead of having a try-catch tree.
I have the json like this:
{"result":0,"data":[{\"ID":7,"TITLE":"123"},{\"ID":8,"TITLE":"123"}]}
I have the struct like this:
struct ResponseResult: Decodable {
let result: Int
let data: [IdResponseResult]
}
struct IdResponseResult: Decodable {
let ID: Int
let TITLE: String
}
So, when I run request like this:
Alamofire.request("https://xxx.xxx.xxx",headers:headers).responseJSON { response in
if let json = response.data {
let decoder = JSONDecoder()
let result = try? decoder.decode(ResponseResult.self, from: json)
print(response.value)
completion(result)
}
}
and print response.value I'm getting this:
data = (
{
"ID" = 7;
TITLE = 123;
},
{
"ID" = 8;
TITLE = 123;
}
);
result = 0;
}
And I cant parse it. How can i resolve it??
the decoding failure is caused by the struct resp2
struct resp2: Decodable {
let ID: Int
let TITLE: String
}
you are defining TITLE:String, but in the JSON you have an Int "TITLE":123.
In case you really want a String (make sense, since is a "Title"), you may need to fix the server side.
edit:
I tried the Decodable as it is now and I am able to recover your structures, you may check with:
let string = "{\"result\": 0,\"data\": [{\"ID\": 7,\"TITLE\": \"123\"}, {\"ID\": 8,\"TITLE\": \"123\"}]}"
let data = string.data(using: .utf8)
do {
let decoder = JSONDecoder()
let result = try? decoder.decode(ResponseResult.self, from: data!)
print(result?.data.first?.TITLE ?? "") // 123
} catch _ {
}
Then I guess there has to be something weird when you receive the data from the service.
I'm trying to make a generic sterilizer function using SwitfyJson.
Currently every where I'm getting server response, I'm parsing is like this:
let json = JSON(data : networkResponse.data!)
let usersJson = json["data"]
var users = [User]()
for (_,subJson):(String, JSON) in usersJson {
let user = User(json: subJson)
users.append(user)
}
Now this is repeatedly every were I'm getting a response.
I'm trying to use a generic function insted. It should look somthing like this:
protocol Serializable {
init(json: JSON)
}
func serializeToArray(data: NSData, serializable: Serializable)->serializable {
let json = JSON(data : data)
let jsonObjects = json["data"]
var serializedObjects = [serializable.classType]()
for (_,subJson):(String, JSON) in jsonObjects {
let serializedObject = User(json: subJson)
serializedObjects.append(user)
}
return serializedObjects
}
But this "classType" is not available in Swift.
Is there a way I can achieve this?
You can make your serialize-method use a generic type.
It looks like you only need the type of the array.
func serializeToArray<SerializableType : Serializable>(data: NSData, serializableType: SerializableType.Type)-> [SerializableType] {
// stuff
var serializedObjects = [SerializableType]()
// more stuff
return serializedObjects
}
This way, you can pass your type to the function without needing to instantiate an object first:
let users = serializeToArray(data: data, serializableType: User.self)
The returned result will then be of the type [User]
Maybe this is more what you're looking for:
static func serializeToArray(json:[String:AnyObject]) {
var serializedObjects = [Serializable]()
guard let jsonArray = json["data"] as? Array<[String:AnyObject]> else {
return [Serializable]()
}
for jsonDict in jsonArray {
serializedObjects.append(Serializable(init:jsonDict))
}
return serializedObjects
}
I'm tring to parse a JSON format like this:
{
"key_1" : {
"key_2" : "value"
}
}
and then assign "value" to a variable.
Here is my code:
var variableShouldBeAssigned: String
if let x = (jsonResult["key_1"]? as? NSDictionary) {
if let y = (x["key_2"]? as? String) {
variableShouldBeAssigned = y
}
}
However, an error occurs when I try to downcast from x["key_2"]? to a String, but it's fine to downcast from jsonResult["key_1"]? to an NSDictionary.
I can solve the error by using x["key_2"] to replace x["key_2"]?, but I don't really know why it only works for jsonResult["key_1"]?.
Can anybody tell me the reason?
String does not conform to NSCopying, but surely NSString does!
Also, going from NSString to String is instantaneously implied...
So I would say try something like this... Change String to NSString
here is a sample, assuming that you handle the jsonResult as a NSDictionary...
func giveDictionary(jsonResult:NSDictionary) -> String?
{
if let x = (jsonResult["key_1"]? as? NSDictionary)
{
if let y = (x["key_2"]? as? NSString)
{
return y
}
}
return nil
}
You can simplify all your type checking by using a Swift dictionary at the beginning:
var variableShouldBeAssigned: String
if let dict = jsonResult as? [String:[String:String]] {
if let key1Dict = dict["key_1"] {
if let value = key1Dict["key_2"] {
variableShouldBeAssigned = value
}
}
}
In fact, you can even combine the two last if statements:
var variableShouldBeAssigned: String
if let dict = jsonResult as? [String:[String:String]] {
if let value = dict["key_1"]?["key_2"] {
variableShouldBeAssigned = value
}
}
In general, you should using Swift Dictionaries instead of NSDictionary