JSON data couldn’t be read - ios

I am trying to replicate following GET request in my project:
curl -XGET 'https://api2.branch.io/v1/url?url=https://example.app.link/WgiqvsepqF&branch_key=key_live_kaFuWw8WvY7yn1d9yYiP8gokwqjV0Swt'
Here is my final code:
if let url = URL(string: "https://example.app.link/WgiqvsepqF&branch_key=key_live_kaFuWw8WvY7yn1d9yYiP8gokwqjV0Swt"){
let urlRequest = URLRequest(url: url)
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error == nil{
do{
if let dataReceived = data, let jsonData = try JSONSerialization.jsonObject(with: dataReceived, options: .mutableLeaves) as? [String : Any]{
print(jsonData)
}
} catch let error{
print(error.localizedDescription)
}
}
}.resume()
}
I am getting following error:
The data couldn’t be read because it isn’t in the correct format.
I tried other Stackoverflow and Reddit solution but nothing seems to be working.

API call is returning data keys in wrong format. You are receiving the keys starting from '$' or '~' e.g. '$og_title', '~stage'. That's why it's showing you an error.
You can use Codable to parse the API. For that first, you need to
make a Model struct for your API Call.
Change the variable names according to Swift Syntax. I've replaced
'$' with 'l' and '~' with 'i'. You can customize the code
according to your needs.
This is your API Call
let url = URL(string: "https://api2.branch.io/v1/url?url=https://example.app.link/WgiqvsepqF&branch_key=key_live_kaFuWw8WvY7yn1d9yYiP8gokwqjV0Swt")
let request = NSMutableURLRequest(url: url!)
request.httpMethod = "GET"
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) in
guard ((data) != nil), let _: URLResponse = response, error == nil else {
print("error")
return
}
if let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue) {
print(dataString)
do {
//here dataResponse received from a network request
let decoder = JSONDecoder()
let model = try decoder.decode(ResponseData.self, from: data!) //Decode JSON Response Data
// Access your json data from Swift Structs e.g. model.type
print(model.data?.iurl)
} catch let parsingError {
print("Error", parsingError)
}
}
})
task.resume()
This is your Model Struct
struct ResponseData : Codable {
let data : MyData?
let type : Int?
let tags : [String]?
let campaign : String?
let feature : String?
let channel : String?
let stage : String?
enum CodingKeys: String, CodingKey {
case data = "data"
case type = "type"
case tags = "tags"
case campaign = "campaign"
case feature = "feature"
case channel = "channel"
case stage = "stage"
}
}
struct MyData : Codable {
let custom_array : [Int]?
let log_title : String?
let custom_boolean : Bool?
let custom_integer : Int?
let icreation_source : Int?
let log_description : String?
let log_image_url : String?
let istage : String?
let custom_string : String?
let ifeature : String?
let url : String?
let custom_object : Custom_object?
let iurl : String?
let ldesktop_url : String?
let itags : [String]?
let lcanonical_identifier : String?
let lone_time_use : Bool?
let iid : String?
let icampaign : String?
let ichannel : String?
enum CodingKeys: String, CodingKey {
case custom_array = "custom_array"
case log_title = "$og_title"
case custom_boolean = "custom_boolean"
case custom_integer = "custom_integer"
case icreation_source = "~creation_source"
case log_description = "$og_description"
case log_image_url = "$og_image_url"
case istage = "~stage"
case custom_string = "custom_string"
case ifeature = "~feature"
case url = "url"
case custom_object = "custom_object"
case iurl = "+url"
case ldesktop_url = "$desktop_url"
case itags = "~tags"
case lcanonical_identifier = "$canonical_identifier"
case lone_time_use = "$one_time_use"
case iid = "~id"
case icampaign = "~campaign"
case ichannel = "~channel"
}
}
struct Custom_object : Codable {
let random : String?
enum CodingKeys: String, CodingKey {
case random = "random"
}
}

The decoding code is correct.
The link is wrong, it should be https://api2.branch.io/v1/url?url=https://example.app.link/WgiqvsepqF&branch_key=key_live_kaFuWw8WvY7yn1d9yYiP8gokwqjV0Swt and not https://example.app.link/WgiqvsepqF&branch_key=key_live_kaFuWw8WvY7yn1d9yYiP8gokwqjV0Swt
Only decoding:
import UIKit
var json = """
{
"data": {
"custom_array": [
1,
2,
3,
4,
5,
6
],
"$og_title": "Title from Deep Link",
"custom_boolean": true,
"custom_integer": 1243,
"~creation_source": 0,
"$og_description": "Description from Deep Link",
"$og_image_url": "http://www.lorempixel.com/400/400/",
"~stage": "new user",
"custom_string": "everything",
"~feature": "onboarding",
"url": "https://example.app.link/WgiqvsepqF",
"custom_object": {
"random": "dictionary"
},
"+url": "https://example.app.link/WgiqvsepqF",
"$desktop_url": "http://www.example.com",
"~tags": [
"one",
"two",
"three"
],
"$canonical_identifier": "content/123",
"$one_time_use": false,
"~id": "423196192848102356",
"~campaign": "new product",
"~channel": "facebook"
},
"type": 0,
"tags": [
"one",
"two",
"three"
],
"campaign": "new product",
"feature": "onboarding",
"channel": "facebook",
"stage": "new user"
}
"""
let data = json.data(using: .utf8)!
do {
if let jsonData = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String : Any] {
print(jsonData)
}
} catch let error {
print(error)
}

Related

The data couldn’t be read because it isn’t in the correct format. Swift 5

I am trying to decode data from https://swapi.dev/. I get json correctly with response code 200, but the decoder could not read the data because the format is incorrect. I tried it in a lot of different ways. I am trying to get info on peoples.
Here is my code:
Model File
struct people: Codable {
let count: Int
let next: String?
let previous: String?
let results: [result]
}
struct result: Codable{
let name: String
let height: Int
let mass: Int
let hair_color: String
let skin_color: String
let eye_color: String
let birth_year: String
let gender: String
let homeworld: String
let films: [String]
let species: [String]
let vehicles: [String]
let starships: [String]
let created: String
let edited: String
let url: String
}
struct APIError: Codable {
let detail: String
}
Network Services
typealias OnApiSucces = (people) -> Void
typealias OnApiError = (String) -> Void
struct ApiService {
static let shared = ApiService()
let URL_BASE = "https://swapi.dev/api"
let URL_PEOPLE = "/people"
let session = URLSession(configuration: .default)
func getResults(onSuccess: #escaping OnApiSucces, onError: #escaping OnApiError) {
let url = URL(string: "\(URL_BASE)\(URL_PEOPLE)")!
var request = URLRequest(url: url)
request.httpMethod = "GET" // GET, PUT, POST, DELETE for some different api
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let task = session.dataTask(with: request) { (data, response, error) in
if let error = error {
onError(error.localizedDescription)
return
}
guard let data = data, let response = response as? HTTPURLResponse else {
onError("Invalid data or response")
return
}
do{
if response.statusCode == 200 {
print("Code is \(response.statusCode)")
let results = try JSONDecoder().decode(people.self, from: data)
onSuccess(results)
} else {
let err = try JSONDecoder().decode(APIError.self, from: data)
print("Code is \(response.statusCode)")
onError(err.detail)
}
}
catch {
onError(error.localizedDescription)
}
}
task.resume()
}
}
** Getting data on ViewController**
func getResults() {
ApiService.shared.getResults { (people) in
self.results = people.results
} onError: { (error) in
debugPrint(error.description)
}
}
First, your data can't be read because height and mass are represented as String in the Star Wars API, while you represent them as Int in your Codable struct.
Also, try adding CodingKeys to your Codable struct so your struct adheres to naming conventions (specifically regarding your attribute_color variants) e.g.
struct result: Codable{
let name: String
let height: String
let mass: String
let hairColor: String // changed from hair_color
let skinColor: String
let eyeColor: String
let birthYear: String
let gender: String
let homeworld: String
let films: [String]
let species: [String]
let vehicles: [String]
let starships: [String]
let created: String
let edited: String
let url: String
enums CodingKeys: String, CodingKey {
case name = "name"
case height = "height"
case mass = "mass"
case hairColor = "hair_color"
case skinColor = "skin_color"
case eyeColor = "eye_color"
case birthYear = "birth_year"
case gender = "gender"
case homeworld = "homeworld"
case films = "films"
case species = "species"
case vehicles = "vehicles"
case starships = "starships"
case created = "created"
case edited = "edited"
case url = "url"
}
}

Making an api request using URLSession.shared.dataTask

I'm making an api request:
var urlRaw = bookSummaryReadsDomainUrl + apiKey;
let url = URL(string: urlRaw.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)
let task = URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if let error = error {
print("Error with fetching book summary reads: \(error)")
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("Error with the response, unexpected status code: \(response)")
return
}
if let data = data,
let flurryItems = try? JSONDecoder().decode(FlurrySummary.self, from: data) {
completionHandler(flurryItems.rows ?? [])
}
})
task.resume()
to an endpoint that returns the following data
{
"rows": [
{
"dateTime": "2020-07-04 00:00:00.000-07:00",
"event|name": "BookSummaryRead",
"paramName|name": "bookId",
"paramValue|name": "elon-musk",
"count": 12
},
...
]
import Foundation
struct FlurrySummary: Codable {
var rows: [FlurryItem]?
enum CodingKeys: String, CodingKey {
case rows = "rows"
}
}
struct FlurryItem: Codable {
var name: String?
var event: String?
var value: String?
var count: String?
var date: String?
enum CodingKeys: String, CodingKey {
case name = "paramName|name"
case event = "event|name"
case value = "paramValue|name"
case count = "count"
case date = "dateTime"
}
}
For some reason the JSONDecoder.decode part is not working. It's not filling up the flurryItems and flurryItems.rows = nil. What am I doing wrong?
The property count in FlurryItem has to be of type Int.
var count: Int?
You have to catch the Error that are thrown.
do {
if let data = data,
let flurryItems = try JSONDecoder().decode(FlurrySummary.self, from: data) {
completionHandler(flurryItems.rows ?? [])
}
} catch { print(error) }
Also, you don't need the CodingKeys in FlurrySummary since the property name is the same.
struct FlurrySummary: Codable {
var rows: [FlurryItem]?
}
Note: Also, avoid using optional declaration if the property never becomes null.

Parsing json data using Codable

I am new to using Codable for parsing data from JSON and I am having trouble with the format of my JSON. I am not able to parse the correct fields into my Employee object. This is my first time using codable and dealing with a complex URL. This is how my JSON url is structured: https://ibb.co/WgDNMNT
{
"students": [
{
"uuid": "0djkdjjf734783749c",
"full_name": "Joe Morris",
"phone_number": "44445399",
"email_address": "jm99#jfgj.com",
"biography": "student of arts"
},
{
"uuid": "0djkdjjf734783749c",
"full_name": "Joe Morris",
"phone_number": "44445399",
"email_address": "jm99#jfgj.com",
"biography": "student of arts"
}
]
}
Here is my code:
struct Students: Codable {
var uuid: String?
var fullName: String?
var phoneNumber: String?
var emailAddress: String?
var biography: String?
}
//Custom Keys
enum CodingKeys: String, CodingKey{
case uuid
case fullname = "full_name"
case phoneNumber = "phone_number"
case emailAddress = "email_address"
case biography = "biography"
}
func parseData(){
guard let url = URL(string: "xxxxxxxxxx") else {return}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Error")
return }
do{
let decoder = JSONDecoder()
let model = try decoder.decode([Students].self, from: dataResponse)
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
Replace
let model = try decoder.decode([Students].self, from: dataResponse)
With
let model = try decoder.decode([String:[Students]].self, from: dataResponse)
print(model["students"])

struct for nested dictionary API in swift

i'm trying to import JSON data from the v2 of coinmarketcap API. I had it working with v1 as it was an array, however the new version is a dictionary and i cant quite get my struct correct.
The API im using is : https://api.coinmarketcap.com/v2/ticker/?convert=AUD
My struct is set up as below:
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case id = "rank", symbol, name, priceAUD = "quotes"
}
var id: String
var symbol : String
var name : String
var priceAUD : quoteStruct
}
struct quoteStruct{
let aud : priceStruct
}
struct priceStruct{
let price : String
}
My code for fetching the data is:
var coins = [Coin]()
func getCoinData() {
let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
My code for fetching the data i have used the same as previously which worked with v1 of the API, however i don't think i made my struct correctly.
Thanks in advance!
Your response Changed i try to configure it by converting it to array of dictionary you will need to change quotes to be [String:priceStruct]
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case id,rank,symbol, name, priceAUD = "quotes"
}
var id: Int
var rank: Int
var symbol : String
var name : String
var priceAUD : [String: priceStruct]
}
struct priceStruct : Decodable{
let price : Double
}
func getCoinData() {
var coins = [Coin]()
let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], let resultData = json["data"] as? [String:Any] {
let dataObject = try JSONSerialization.data(withJSONObject: resultData.values.map({$0}) , options: .prettyPrinted)
coins = try JSONDecoder().decode([Coin].self, from: dataObject)
print(coins.count)
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
You response in data parameter should be an array rather than a dictionary. You will not be able to iterate a dictionary over undefined keys. It would be good to get your response of API updated first.
But, If you wish to continue with the existing API response, first you need to convert your response in an array and use your Decodable structs as:
struct Coin: Decodable {
var id: String
var symbol : String
var name : String
var priceAUD : QuoteStruct
private enum CodingKeys: String, CodingKey {
case id = "rank", symbol, name, priceAUD = "quotes"
}
}
struct QuoteStruct: Decodable {
let aud : PriceStruct
}
struct PriceStruct: Decodable {
let price : String
}
Update your data parsing in API block as:
guard let responseData = data else { return }
do {
let json = try? JSONSerialization.jsonObject(with: responseData, options: [])
if let jsonData = json as? [String: Any], let dataObject = jsonData["data"] as? [Int: Any] {
let coinArray = dataObject.map { $0.1 }
if let jsonData = try? JSONSerialization.data(withJSONObject: coinArray, options: .prettyPrinted) {
coins = try JSONDecoder().decode([Coin].self, from: jsonData)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
} catch {
print("Error is : \n\(error)")
}

Values are not updating in my text label from api calling

I have one api calling, and I am passing one parameter value to that api. And I am doing append to one model data and from there I am trying to display in my label. But when I do api calling itself and try to print the label name , Its showing crash index out of range
func showprofileapi () {
let headers = [
"cache-control": "no-cache",
"postman-token": "4c933910-0da0-b199-257b-28fb0b5a89ec"
]
let jsonObj:Dictionary<String, Any> = [
"customerID" : "5"
]
if (!JSONSerialization.isValidJSONObject(jsonObj)) {
print("is not a valid json object")
return
}
if let postData = try? JSONSerialization.data(withJSONObject: jsonObj, options: JSONSerialization.WritingOptions.prettyPrinted) {
let request = NSMutableURLRequest(url: NSURL(string: "http://MyProfile.php")! as URL,
cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
///print(error)
} else {
DispatchQueue.main.async(execute: {
if let json = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? Dictionary<String,AnyObject>
{
let status = json["status"] as? Int;
if(status == 1)
{
print("SUCCESS....")
if (json["myprofile"] as? NSDictionary) != nil
{
print("SUCCESS ......22....")
print(json)
DispatchQueue.main.async(execute: {
print("INSIDE CATEGORIES")
self.Profileddatas.append(MyProfiledData(json:json as NSDictionary))
print("Product Name : ", self.Profileddatas[0].custName)
})
}
}
}
})
}
})
dataTask.resume()
}
}
My above code is my api calling, And when I try to print the value in my console its crashing :
// print("Product Name : ", self.Profileddatas[0].custName)
My json output after api calling is :
{
"status": 1,
"message": "My Profile Details are.",
"myprofile": {
"CustomerName": "ram",
"CustomerEmail": "ram#gmail.com",
"CustomerMobile": "",
"CustomerAddress": "",
"CustomerUsername": "",
"CustomerPassword": " "
}
}
My append data model class is :
class MyProfiledData
{
var custName : String?
var custEmail : String?
var custMobile : String?
var custAddress : String?
var custUsername : String?
var custPassword : String?
init(json:NSDictionary)
{
self.custName = json["CustomerName"] as? String
self.custEmail = json["CustomerEmail"] as? String
self.custMobile = json["CustomerMobile"] as? String
self.custAddress = json["CustomerAddress"] as? String
self.custUsername = json["CustomerUsername"] as? String
self.custPassword = json["CustomerPassword"] as? String
}
}
Please help me out.
Thanks
change if (json["myprofile"] as? NSDictionary) != nil
to if let json = json["myprofile"] as? NSDictionary because your 'json' in the context of initializing MyProfiledData went wrong
You're accessing the JSON Data by it's wrong keys in Your MyProfileData Class. You have either pass the ["myprofile"] dict in the init(json) call by
if let myProfile = json["myprofile"] as? NSDictionary {
DispatchQueue.main.async(execute: {
self.Profiledatas.append(MyProfileData(json:myProfile))
})
}
or access it by their right Keys:
class MyProfiledData {
var custName : String?
var custEmail : String?
var custMobile : String?
var custAddress : String?
var custUsername : String?
var custPassword : String?
init(json:NSDictionary) {
self.custName = json["myprofile"]["CustomerName"] as? String
self.custEmail = json["myprofile"]["CustomerEmail"] as? String
self.custMobile = json["myprofile"]["CustomerMobile"] as? String
self.custAddress = json["myprofile"]["CustomerAddress"] as? String
self.custUsername = json["myprofile"]["CustomerUsername"] as? String
self.custPassword = json["myprofile"]["CustomerPassword"] as? String
}
}
In your init function it structure is not ok, it will be work if you send only my profile node of your json
{
"CustomerName": "ram",
"CustomerEmail": "ram#gmail.com",
"CustomerMobile": "",
"CustomerAddress": "",
"CustomerUsername": "",
"CustomerPassword": " "
}
use
self.Profileddatas.append(MyProfiledData(json:Json["myprofile"] as NSDictionary))
if (json["myprofile"] as? NSDictionary) != nil
{
print("SUCCESS ......22....")
print(json)
DispatchQueue.main.async(execute: {
print("INSIDE CATEGORIES")
self.Profileddatas.append(MyProfiledData(json:json["myprofile"] as! NSDictionary))
print("Product Name : ", self.Profileddatas[0].custName)
self.getvalue ()
})
}

Resources