My json response is coming in different format than I expected - ios

In browser my url gives results in perfect JSON format as follows
"articles": [
{
"source": {
"id": "the-times-of-india",
"name": "The Times of India"
},
"author": "Times Of India",
But Where as in Xcode output the response I am getting is as follows. How to convert this response into perfect json format
{
articles = (
{
author = "Times Of India";
content = "Hyderabad: Senior Police officials arrive at the site of the encounter. All four accused in the rape
description = "India News: All four accused in the rape and murder of woman veterinarian in Telangana have been killed in an encounter with the police. Cops claimed they tried t";
publishedAt = "2019-12-06T04:15:00Z";
source = {
name = "The Times of India";
};
},
I am using the following code to decode the json data
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do{
//here dataResponse received from a network request
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: [])
print(jsonResponse) //Response result
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
Please help me with this issue.

first you must create a Decodable struct and put it before viewController class:
struct YourArrray: Decodable {
let author: String?
let content: String?
let location: String?
let description : String?
let publishedAt : String?
let name: String?
}
declare your Url:
let jsonUrlString = "https://yourUrljson"
after that create your struct array var:
var myVar = [YourArrray]()
now you can procede to decode json:
fileprivate func fetchJsonObject() {
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, respons, err) in
guard let data = data else { return }
do {
let jsonResponse = try JSONDecoder().decode([myVar].self, from: data)
print(jsonResponse)
} catch let jsonErr {
print("Error serializing:", jsonErr)
}
}.resume()
}
Now you can simply call the function fetchJsonObject() and you're done
Hope this help :)

Related

Swift - How do I show Data in Swift View from Data.swift file?

In a separate data file called data.swift I have this code
struct Response: Decodable {
var data: Data
}
struct Data: Decodable {
var search: search
}
struct search: Decodable {
var __Typename: String
var query: String
var searchResults: searchResults
}
...and so on and so forth. I then decode the data from a Rapid-Api like so
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error!)
} else {
let products = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments)
if let water = products {
print("JSON: \n" + String(describing: water) + "\n")
}
}
})
How do I display the data elements in ProductList.swift it's a (view) file. The API works as expected and displays the JSON in the terminal. I am using XCODE 12.4 as I am not permitted to upgrade any further.
So, actually you want to receive the data in Model and show in view, either a label or image.
In your productSwift.List:
var response: Response?
Right now, you have to decode the data in the Model:
static func postApiCall<T: Decodable>(completion: #escaping (Result<T,Error>) -> Void) {
let url = URL(string: "Enter Your URL here")
let request = URLRequest(url: url!)
let dataTask = URLSession.shared.dataTask(with: request) { data, response , error in
guard let data = data else {
if error == nil {
completion(.failure(error as! Error))
}
return
}
do {
let decoder = JSONDecoder()
let json = try decoder.decode(T.self, from: data)
completion(.success(json))
} catch let error {
print(error.localizedDescription)
}
}
dataTask.resume()
}
}
Now in your ProductList.swift:
ServiceManage.postApiCall { (result : Result<Response,Error>) in
switch result {
case .success(let result):
print("result is \(result)")
self.response = response.data
self.yourLabel.text = response.data.search.query
case .failure(let failure):
print(failure)
}
}
and as Larme said, change your Struct "Data" name to something else.

Swift 5 Json parsing error "dataCorrupted"

I tried every solution but none of them resolved my problem getting below error while parsing. can anybody find the fault in this code
Error serializing json: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Unable to convert data to string around character 2643." UserInfo={NSDebugDescription=Unable to convert data to string around character 2643.})))
struct Facts:Codable {
let title: String
let rows: [Rows]
}
struct Rows:Codable {
var title: String
var description: String
var imageHref: String
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let jsonUrlString = "https://dl.dropboxusercontent.com/s/2iodh4vg0eortkl/facts.json"
guard let url = URL(string: jsonUrlString) else{return}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do{
let facts = try JSONDecoder().decode(Facts.self, from: data)
print(facts)
}catch let jsonErr{
print("Error serializing json:", jsonErr)
}
}.resume()
}
}
// problem in response type and encoding. It should be application/json and //unicode but actually it is:
//response content-type: text/plain; charset=ISO-8859-1
struct Facts:Codable {
let title: String
let rows: [Rows]!
}
struct Rows:Codable {
var title: String?
var description: String?
var imageHref: String?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let jsonUrlString = "https://dl.dropboxusercontent.com/s/2iodh4vg0eortkl/facts.json"
guard let url = URL(string: jsonUrlString) else{return}
// var request = URLRequest(url: url)
// request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") // the request is JSON
// request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept") // the expected response is also JSON
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
guard let string = String(data: data, encoding: String.Encoding.isoLatin1) else { return }
guard let properData = string.data(using: .utf8, allowLossyConversion: true) else { return }
do{
let facts = try JSONDecoder().decode(Facts.self, from: properData)
//dump(facts)
print(facts.title)
for row in facts.rows {
print(row.title ?? "no title")
print(row.description ?? "no description")
print(row.imageHref ?? "no img url")
print("---")
}
} catch let error {
print(error)
}
}.resume()
}
}
I have solved this problem by doing one extra step:
First convert the data in to string which we get in closures then again convert this string to data back and this will 100% work.
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
guard let request = requestObj.request else {
return
}
let task = session.dataTask(with: request) { (data, response, error) in
if let data = data {
let str = String(decoding: Data(data), as: UTF8.self)
**if let data = str.data(using: String.Encoding.utf8 ) {**
if error != nil {
if let err = error as NSError? {
failure(err)
}
}
if let httpResponse = response as? HTTPURLResponse {
let code = httpResponse.statusCode
switch code {
case HttpStatusCode.success:
success(data)
default:
failure(NSError(domain: "", code: code, userInfo: [NSLocalizedDescriptionKey: "Something went wrong"]))
}
}
}
}
}
// execute the HTTP request
task.resume()

How do I parse this nested JSON using Codable with Swift?

I am trying to parse this JSON using Codable:
{
"users": [
{
"id": 1,
"name": "Allen Carslake",
"userName": "acarslake0",
"profileImage": "https://source.unsplash.com/random/400x400",
"createdDate": "2019-07-08T00:00:00.000+0000"
},
{
"id": 2,
"name": "Revkah Antuk",
"userName": "rantuk1",
"profileImage": "https://source.unsplash.com/random/400x400",
"createdDate": "2019-07-05T00:00:00.000+0000"
},
{
"id": 3,
"name": "Mirna Saffrin",
"userName": "msaffrin2",
"profileImage": "https://source.unsplash.com/random/400x400",
"createdDate": "2019-05-19T00:00:00.000+0000"
},
{
"id": 4,
"name": "Haily Eilers",
"userName": "heilers3",
"profileImage": "https://source.unsplash.com/random/400x400",
"createdDate": "2019-06-28T00:00:00.000+0000"
},
{
"id": 5,
"name": "Oralie Polkinhorn",
"userName": "opolkinhorn4",
"profileImage": "https://source.unsplash.com/random/400x400",
"createdDate": "2019-06-04T00:00:00.000+0000"
}
]
}
I am keeping the URL private on here but it is returning JSON above. So far this is my code:
import UIKit
struct User: Codable {
let id: Int
let name: String
let userName: String
let profileImage: String
let createdDate: String
}
struct Users: Codable {
let users: String
}
let url = URL(string: "")!
URLSession.shared.dataTask(with: url) { data, _, _ in
if let data = data {
let users = try? JSONDecoder().decode([User].self, from: data)
print(users)
}
}.resume()
I need to be able to access the User properties but I think the nesting is making it difficult for me. Any help is awesome!! Thank you!!
First of all: Catch always the DecodingError and print it. It tells you exactly what's wrong.
The error occurs because you are ignoring the root object Users. Your code works if you decode(Users.self.
My suggestions:
Decode createdDate as Date adding a appropriate date decoding strategy.
Decode profileImage as URL (for free).
Handle all errors.
struct Root : Decodable { // `Users` and `User` is too confusing
let users: [User]
}
struct User : Decodable {
let id: Int
let name: String
let userName: String
let profileImage: URL
let createdDate: Date
}
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error { print(error); return }
do {
let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let result = try decoder.decode(Root.self, from: data!)
for user in result.users {
print(user.userName, user.id, user.createdDate)
}
} catch {
print(error)
}
}.resume()
The root of the json is a dictionary not an array you can write a root class but it will be useless , so you need
URLSession.shared.dataTask(with: url) { data, _, _ in
do {
if let data = data {
let res = try JSONSerialization.jsonObject(with: data) as! [String:Any]
let usersData = try JSONSerialization.data(withJSONObject: res["users"])
let users = try JSONDecoder().decode([User].self, from: usersData)
print(users)
}
}
catch {
print(error)
}
}.resume()
Your Users struct (I renamed it to UsersResponse) should contain a users property of type [User]. Then you can do the following:
struct UsersResponse: Codable {
let users: [User]
}
URLSession.shared.dataTask(with: url) { data, _, _ in
guard let data = data else { return }
if let users = try? JSONDecoder().decode(Users.self, from: data).users {
users.forEach { user in
print("A user called \(user.name) with an id of \(user.id).")
}
}
}.resume()
Please try the below code. This is working for me.
Model Class:
struct UsersResponse: Codable {
let users: [User]
}
struct User: Codable {
let id: Int
let name: String
let userName: String
let profileImage: String?
let createdDate: String
}
Network class:
public enum EndPoints: String {
case prod = "ProdURL"
case test = "testURL"
}
public enum Result<T> {
case success(T)
case failure(Error)
}
final public class Networking: NSObject {
// MARK: - Private functions
private static func getData(url: URL,
completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}
/// fetchUsersResponse function will fetch the User Response and returns
/// Result<UsersResponse> as completion handler
public static func fetchUsersResponse(shouldFail: Bool = false, completion: #escaping (Result<UsersResponse>) -> Void) {
var urlString: String?
if shouldFail {
urlString = EndPoints.test.rawValue
} else {
urlString = EndPoints.prod.rawValue
}
guard let mainUrlString = urlString, let url = URL(string: mainUrlString) else { return }
Networking.getData(url: url) { (data, response, error) in
if let error = error {
completion(.failure(error))
return
}
guard let data = data, error == nil else { return }
do {
let decoder = JSONDecoder()
//decoder.dateDecodingStrategy = .millisecondsSince1970
decoder.dateDecodingStrategy = .formatted(setDateFormat())
let json = try decoder.decode(UsersResponse.self, from: data)
completion(.success(json))
} catch let error {
completion(.failure(error))
}
}
}
func setDateFormat() -> DateFormatter {
let dateFormat = DateFormatter()
dateFormat.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
return dateFormat
}

Store JSON data as a variable

I'm trying to figure out how to store JSON data into a variable for later use. How do I store it, and is it possible to use the variable in another view controller, or do I have to do another request to fetch the data?
This is my code:
#IBAction func signinTapped(_ sender: UIButton) {
guard let url = URL(string: "http://XXXXXX/TestReqIOS.php") else {
return
}
let email = txtEmail.text!
let password = txtPassword.text!
let data : Data = "loginSubmit=1&email=\(email)&password=\(password)&grant_type=password".data(using: .utf8)!
var request : URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type");
request.setValue(NSLocalizedString("lang", comment: ""), forHTTPHeaderField:"Accept-Language");
request.httpBody = data
print("Calling API")
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// vs let session = URLSession.shared
// make the request
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
if let error = error {
print("error")
}
else if let response = response {
print("response")
}
else if let data = data {
print(data)
}
DispatchQueue.main.async { // Correct
guard let responseData = data else {
print("Error: did not receive data")
return
}
print(String(data: responseData, encoding: String.Encoding.utf8) ?? "")
}
})
task.resume()
}
Which will return:
{
"id": "7",
"first_name": "John",
"last_name": "Doe",
"email": "JohnDoe#text.com",
"created": "2019-03-11",
"modified": "2019-03-10",
}
It would be better to use a struct, such as in your case:
struct Data: Codable {
let id: Int
let first_name: String
let last_name: String
let email: String
let created: Date
let modified: Date
}
Then you create a variable of that struct where you will store it:
var dataVariable = [Data]()
Then you can do your URL call like:
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
let urlJSON = "URL"
guard let url = URL(string: urlJSON) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let getData = try JSONDecoder().decode([Data].self, from: data)
self.dataVariable = getData
} catch let jsonErr {
print("error serializing json: \(jsonErr)")
}
completion(arr)
}.resume()
}
Then you can access all of this from the dataVariable var. IF you do this in a Manager class you can access it from any ViewController.
To access:
let firstNameString = dataVariable[0].first_name
If there are not multiple trees of the same, then just make sure its:
let getData = try JSONDecoder().decode(Data.self, from: data)
Edit:
In your case put the above here:
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
if let error = error {
print("error")
}
else if let response = response {
print("response")
}
else if let data = data {
let getData = try JSONDecoder().decode([Data].self, from: data)
self.dataVariable = getData // <- Just decode here
print(data)
}
DispatchQueue.main.async { // Correct
guard let responseData = data else {
print("Error: did not receive data")
return
}
print(String(data: responseData, encoding: String.Encoding.utf8) ?? "")
}
})
task.resume()

Strange JSON API response SWIFT

I am sending an HTTP POST request to an API in swift and it is supposed to respond with:
{
"results": [
{
"alternatives": [
{
"transcript": "how old is the Brooklyn Bridge",
"confidence": 0.98267895
}
]
}
]
}
However, I am receiving this through the print(jsonResponse) function:
Optional({
results = (
{
alternatives = (
{
confidence = "0.9688848";
transcript = "how old is the Brooklyn Bridge";
}
);
}
);
})
Is there any reason why the response is not arriving in the correct format as indicated in the API documentation? I need to Decode the response to obtain the "transcript" value. However, I am receiving the following error:
keyNotFound(CodingKeys(stringValue: "transcript", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"transcript\", intValue: nil) (\"transcript\").", underlyingError: nil))
Maybe my request isn't optimal... Here's my code, any help is appreciated!
let parameters = ["config": ["encoding": "FLAC", "sampleRateHertz": "16000", "languageCode": "en-AU"], "audio": ["uri":"gs://cloud-samples-tests/speech/brooklyn.flac"]]
guard let url = URL(string: "https://speech.googleapis.com/v1/speech:recognize?key=AIzaSyDqYpPQIabwF5L-WibBxtVsWRBrc8uKi4w") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: []) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error.localizedDescription)
}
URLSession.shared.dataTask(with: request) { (data , response , error) in
guard let data = data else {return}
do {
let jsonResponse = (try? JSONSerialization.jsonObject(with: data, options: []))
print("\(jsonResponse)")
let course = try JSONDecoder().decode(Course.self , from : data)
print(course.transcript)
} catch {
print(error)
}
}.resume()
Here is my Course code block: Do I need to include the other components in the struct as well as the transcript?
struct Course: Decodable {
let transcript: String
enum CodingKeys: String, CodingKey {
case transcript = "transcript"
}
}
jsonResponse is an Optional Dictionary, and thus that's why it's debug description looks like what you printed rather than pure JSON as you were looking for. Your problem likely is that your Decodeable objects are not properly setup - as by the looks of it you only have one Course. You'll likely need two more Response which contains a list of Alternatives. And then in Alternative you have a list of Courses.
Structure your objects like this, and it should do the trick:
struct Response: Decodable {
let results: [Alternative]
}
struct Alternative: Decodable {
let alternatives: [Course]
}
struct Course: Decodable {
let transcript: String
let confidence: Float
}
And then swap this line:
let course = try JSONDecoder().decode(Course.self , from : data)
With this change:
let course = try JSONDecoder().decode(Response.self, from: data).results[0].alternatives[0]

Resources