I want to parse JSON strings from different URLs in my iOS Tab Bar app:
Parsing.swift
FirstViewController.swift (INITIAL Tab Bar View Controller)
SecondViewController.swift
...
In Parsing.swift I have various struct (TopLevel) and enum schemes I have controlled in Playground: they works perfectly. In every ViewController I have a Table View that I want to popolate with results of different JSON parsing. This is my simplified code:
FirstViewController.swift viewDidLoad()
let url = // my first URL to parse
let urlObj = URL(string: url)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlObj!) { (data, response, error) in
do {
let results = try JSONDecoder().decode(TopLevel.self, from: data!)
... for ...
self.table.reloadData()
}
catch {
...
}
}
task.resume()
This code works perfectly: When app first open, Table View in FirstViewController popolates with results of JSON Parsing from url. But now its time to click on second Bar Item to open SecondViewController. The code is obviously:
SecondViewController.swift viewDidLoad()
let url2 = // my second URL to parse
let urlObj2 = URL(string: url2)
let config2 = URLSessionConfiguration.default
let session2 = URLSession(configuration: config2)
let task2 = session.dataTask(with: urlObj2!) { (data2, response2, error2) in
do {
let results2 = try JSONDecoder().decode(TopLevel.self, from: data2!)
... for ...
self.table2.reloadData()
}
catch {
...
}
}
task2.resume()
Well, when I tap on second Tab Bar Item to open the SecondViewController, Table View don't popolate and XCode gives an error: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}))) But JSON text is valid.
I have tried a lot of solutions: I've changed tasks to URLSession.shared, I have used private struct and enum, I have controlled variables and costants, well, no way to parse second URL correctly. Even if I create a NEW Single View App and I copy the SecondViewController.swift code into the viewDidLoad() func, it works perfectly, so, again, its not a problem of the second URL, the JSON strings are valid. I think there is an interference between the two parsing tasks, it looks like the first corrupted the second one. What can I do? Thanks.
EDIT: this is my JSON (all fields are valid strings, I have deleted it for simplify)
{
"attributes": {
"version": "2.0",
"nodeValue": "\n"
},
"channel": {
"title": " ",
"link": " ",
"description": " ",
"lastBuildDate": " ",
"language": " ",
"copyright": " ",
"item": [
{
"title": " ",
"link": " ",
"guid": {
"attributes": {
"isPermaLink": "false",
"nodeValue": " "
}
},
"pubDate": " ",
"category": " "
},
{
"title": " ",
"link": " ",
"guid": {
"attributes": {
"isPermaLink": "false",
"nodeValue": " "
}
},
"pubDate": " ",
"category": " "
}
]
} }
As I don't have access to JSON response and Model used.
I can assume few possibilities that may cause this issue.
1) You have your model and JSON response. When you are trying to decode, there could any field in JSON response that is null and same property in your model is not made optional.
2) The model may not have same structure (properties) as JSON response.
Well, I resolved the issue changing my second URL from "WWW.myserver.net/string2.json" to "myserver.net/string2.json", simply without WWW. In this way both tasks works and parses respective strings from different URLs.
Related
Hello I am creating an app with Xcode and I am having the following problem, I created this API (if you enter the link you'll see the JSON data) https://proyecto-idts6.epizy.com/models/getCategorias.php
If you dont want to enter the link here is how this si how the structure of the JSON looks like:
{
"items":[
{
"categorie":"Fruits",
"id_categorie":"1"
},
{
"categorie":"Animals",
"id_categorie":"2"
},
{
"categorie":"Juices",
"id_categorie":"3"
},
{
"categorie":"Vegetables",
"id_categorie":"4"
},
{
"categorie":"Alcohol",
"id_categorie":"5"
},
{
"categorie":"Desserts",
"id_categorie":"6"
}
]
}
The problem I have is that when I try to decode the data from the API it cant't be decoded properly, I am trying to recreate the same code of this youtube video, but with my API: https://www.youtube.com/watch?v=sqo844saoC4
What I want basically is to print the categories and storage each of them in variables (because i'll need to move the variables between screens)
This is how my code looks like:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = "https://proyecto-idts6.epizy.com/models/getCategorias.php"
getData(from: url)
//Here is where i want to storage the variables from the JSON
}
private func getData(from url: String) {
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("something went wrong")
return
}
do {
let result = try JSONDecoder().decode([ResultItem].self, from: data)
print(result)
}
catch {
print("failed to convert\(error)")
}
})
task.resume()
}
}
struct Response: Codable {
let items: [ResultItem]
}
struct ResultItem: Codable {
let categorie: String
}
My goal is to have variables for example like this: categorie1=("the category 1 called from the JSON"), categorie2=("the category 2 called from the JSON"), categorie3=("the category 3 called from the JSON"),...
The problem is not in the decoding but in the remote API.
Your endpoint (https://proyecto-idts6.epizy.com/models/getCategorias.php) instead of returning a JSON is returning the following HTML
<html><body><script type="text/javascript" src="/aes.js" ></script><script>function toNumbers(d){var e=[];d.replace(/(..)/g,function(d){e.push(parseInt(d,16))});return e}function toHex(){for(var d=[],d=1==arguments.length&&arguments[0].constructor==Array?arguments[0]:arguments,e="",f=0;f<d.length;f++)e+=(16>d[f]?"0":"")+d[f].toString(16);return e.toLowerCase()}var a=toNumbers("f655ba9d09a112d4968c63579db590b4"),b=toNumbers("98344c2eee86c3994890592585b49f80"),c=toNumbers("f5490e280a5e50f74932909856c3d3a3");document.cookie="__test="+toHex(slowAES.decrypt(c,2,a,b))+"; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/"; location.href="https://proyecto-idts6.epizy.com/models/getCategorias.php?i=1";</script><noscript>This site requires Javascript to work, please enable Javascript in your browser or use a browser with Javascript support</noscript></body></html>
So you are trying to decode that HTML content, which clearly leads to the error your reported
failed to convertdataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around line 1, column 0." UserInfo={NSDebugDescription=Invalid value around line 1, column 0., NSJSONSerializationErrorIndex=0})))
As per this documentation shared link below,
https://developer.apple.com/documentation/applemusicapi/create_a_new_library_playlist
I'm able to get developer token and music user token but I am getting error for 'Invalid Request Body' (400 Bad Request).
This is the function that I'm using to create apple music playlist.
func createAppleMusicPlaylist() {
let playlistURL = URL(string: Config.appleCreatePlaylist)!
var playlistRequest = URLRequest(url: playlistURL)
playlistRequest.httpMethod = "POST"
let params = ["name" : kPlaylistName,
"description" : kPlaylistDesc]
playlistRequest.httpBody = try? JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
playlistRequest.addValue("Bearer \(DeveloperToken)", forHTTPHeaderField: "Authorization")
playlistRequest.addValue("\(UserToken)", forHTTPHeaderField: "Music-User-Token")
URLSession.shared.dataTask(with: playlistRequest) { data, response, error in
guard error == nil else {
print("\(String(describing: error?.localizedDescription))")
return
}
if let data = data {
let json = try? JSONDecoder().decode(AppleCreatePlaylistModel.self, from: data)
}
}.resume()
}
API Response:
{
"errors": [
{
"id": "X4IK3UU6LYGGRSQ6U2KQANL63A",
"title": "Invalid Request Body",
"detail": "Unable to parse request body",
"status": "400",
"code": "40007"
}
]
}
Please suggest required body parameters.
Thanks!
You need to follow the API documentation, Create a New Library Playlist, and follow the HTTP Body part that causing issue in your case.
HTTP Body
LibraryPlaylistCreationRequest
The POST request containing the name, tracks and parent playlist folder for the playlist to be added.
Now, let's check LibraryPlaylistCreationRequest:
Properties
attributes LibraryPlaylistCreationRequest.Attributes (Required) A dictionary that includes strings for the name and
description of the new playlist.
relationships LibraryPlaylistCreationRequest.Relationships An optional key
including tracks for the new playlist.
So, body should like this, at this point:
{
"attributes": ..., //Required
"relationships": ... //Optional
}
Let's see now LibraryPlaylistCreationRequest.Attributes:
Properties
description string The description of the playlist.
name string (Required) The name of the playlist.description string
So, now, the JSON should look like this (I skipped the relationships part since you don't use it):
{
"attributes": {
"name": "playlist name",
"description": "playlist description"
}
}
This is my first post, so I hope everything is as structured as it should be.
I hope anybody can help me to solve my issue.
I have the following issue while decoding JSON in Swift from a downloaded file:
The vocabulary.json file contains the following:
[
{
"english": "one",
"deutsch": "eins",
"theme": "numbers"
},
{
"english": "two",
"deutsch": "zwei",
"theme": "numbers"
}
]
JSON in file
My swift 4 - code:
public struct Vocabulary: Codable{
let english: String
let deutsch: String
let theme: String
}
func decodeData(){
DataManager.getJSONFromURL("vokabeln") { (data, error) in
guard let data = data else {
return
}
let decoder = JSONDecoder()
do {
let vocabulary = try decoder.decode(Vocabulary.self, from: data)
print(vocabulary)
} catch let e {
print("failed to convert data \(e)")
}
}
}
public final class DataManager {
public static func getJSONFromURL(_ resource:String, completion:#escaping (_ data:Data?, _ error:Error?) -> Void) {
DispatchQueue.global(qos: .background).async {
let url = URL(string: "https://onedrive.live.com/xxxxx/vokabeln.json")
let data = try! Data(contentsOf: url!, options: .uncached)
completion(data, nil)
}
}
}
If I decode the Json from the following multi string:
public let vokabeln: String = """
[
{
"english": "one",
"deutsch": "eins",
"theme": "numbers"
},
{
"english": "two",
"deutsch": "zwei",
"theme": "numbers"
}
]
"""
it works, but if I try to decode it from the file I receive the following error message:
failed to convert data dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
Thank you in advance.
Kind regards,
Kai
Change this
let vocabulary = try decoder.decode(Vocabulary.self, from: data)
to this
let vocabulary = try decoder.decode([Vocabulary].self, from: data)
It will give an array of Vocabulary just like [Vocabulary].
I hope this will help you.
I was getting a very similar error:
caught: dataCorrupted(Swift.DecodingError.Context(codingPath: [],
debugDescription: "The given data was not valid JSON.",
underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840
"Badly formed object around character 64."
UserInfo={NSDebugDescription=Badly formed object around character
64.})))
But for a completely different reason:
My local json was created like this:
"""
{
"name": "Durian",
"rate": 600,
"city": "Cali"
"description": "A fruit with a distinctive scent."
}
"""
The error message is quite obvious. I had forgot to place , after "Cali".
If I understand this correctly, to count 64 characters you have to start counting from the beginning of the line where "name" is. Meaning there are 4 empty characters before in each line. Hence the number ~64. You don't need to count the spaces at the line of { :)
Placing the comma resolved the issue.
I am trying to pull data from an api to populate my table but nothing is being shown. How can I fix it?
UPDATED
When I place the GET request in either viewDidLoad() or viewDidAppear() I would receive an empty array
result of GET is []
I have tried adding self.tableView.reload() to the viewDidLoad() but would be thrown this error
Ambiguous reference to member 'tableView(_:numberOfRowsInSection:)'
This is my JSON response
[
{
"schedule_job_id": 23,
"start_date": "2016-05-01",
"end_date": "2016-05-08",
"building_id": 2,
"building_name": "Republic Plaza",
"building_block_no": "69A",
"building_street_name": "9 Raffles Place Downtown Core",
"building_postal_code": "233645",
"checklist_id": "1",
"checklist_name": "Lightning protection",
"checklist_due_date": "2016-04-12 17:00:00",
"tasks": [
{
"submission_id": 105,
"task_id": "1",
"task_name": "Inspect Carpark",
"task_checked": true,
"task_submitted_date": "2016-05-04 04:39:01",
"task_comment": null,
"task_building_level": "1",
"task_description": "",
"task_status": "OK",
"total_rating": null,
"safety_ratings": [
{
"submission_id": 105,
"safety_rating_id": 79,
"task_id": "1",
"name": "Equipment",
"points": "10",
"score": "0"
}
],
"photos": []
}
]
This is my GET request
override func viewDidLoad() {
usernameLabel.text = Data.sharedInstance.userName
let url = "http://anyapi.com"
Alamofire.request(.GET, url).validate().responseJSON { response in
switch response.result {
case .Success(let data):
let json = JSON(data)
if let data = json["task_name"].arrayValue as [JSON]?{
self.datas = data
self.displayTask.reloadData()
print("result of GET is \(self.datas)")
}
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
}
The entire tableview code can be seen here https://codeshare.io/aKknb
Seems like json["task_name"] is not an array and isn't at first level, instead is a property of the objects in the tasks array, thats why you are getting an empty array.
You should do something like this if you intent to show the taks in your tableview
if let data = json["tasks"].arrayValue as [JSON]?{
Because the code which makes your HTTP request is in the wrong method. You intended it to be in viewDidLoad(), but you actually put it in didReceiveMemoryWarning()
Instead of putting the population in viewDidLoad try it in viewDidAppear... that way the tableview is all setup and able to correctly populate.
you can start HTTP request in ViewWillAppear and reload the tableview.That way tableview gets properly loaded.
I am using a WKUserScript for communications between a UIWebView and a server. This particular code will allow user's to search for geographical places. I receive a message back just fine in the function
func userContentController(userContentController: WKUserContentController,
didReceiveScriptMessage message: WKScriptMessage)
The variable message has an AnyObject body. That value I want to convert to a JSONObject so I can access it's contents. Here is the message.body:
{
"status":"OK",
"predictions":[
{
"description":"Dallas, TX, United States",
"id":"fa589a36153613fc17b0ebaebbea7c1e31ca62f0",
"matched_substrings":[{"length":6,"offset":0}],
"place_id":"ChIJS5dFe_cZTIYRj2dH9qSb7Lk",
"reference":"CkQxAAAAJNbPZRkdsyxuKT4FzFmgpBx9HWnZLNhxprRQB0zy62sHCXo3tkHfV_M5dK4Cabp2KL43nIKAAyrv_RI4qbvNfRIQ1dzEGuqywMIAlNg_1AKvoRoUQN32C2uNo4KzZ9j58lB-wjPpjJw",
"terms":[
{"offset":0,"value":"Dallas"},
{"offset":8,"value":"TX"},
{"offset":12,"value":"United States"}
],
"types":["locality","political","geocode"]},
{
"description":"Dallas Athletic Club Drive, Dallas, TX, United States",
"id":"37c4f8d416b9d3975ad57662eb022a0d410e8f76",
"matched_substrings":[{"length":6,"offset":0}],
"place_id":"EjVEYWxsYXMgQXRobGV0aWMgQ2x1YiBEcml2ZSwgRGFsbGFzLCBUWCwgVW5pdGVkIFN0YXRlcw",
"reference":"CkQ5AAAArHSWkIVO6uTH4qE6LxRHshWAfgSnMfxXiBxqf_ZO3O-xQ8RIKKHA9QT7LKwf6Ic788Bzy_I2FpemvcQhE6o5ZRIQ5td4XsjIiyX6D6_dgI3YIxoURu_oROPuOguuorK3Tw11veN7XJI",
"terms":[
{"offset":0,"value":"Dallas Athletic Club Drive"},
{"offset":28,"value":"Dallas"},
{"offset":36,"value":"TX"},
{"offset":40,"value":"United States"}
],
"types":["route","geocode"]
}
]
}
The JSONObject has a status, which lets me know if the results are valid or if an error occurred. I am using SwiftyJSON to create and access my JSONs. I create the JSONObject:
let json = JSON(message.body as! NSString)
and I attempt to access the status key like:
if let status = json["status"].string {
print("status: \(status)")
}
But I am no able to reach the print statement. I have noticed that NSDictionary and JSON have new line characters when you print them out to the console, but I didn't think that would make a difference. Does anyone know why I am unable to retrieve the status variable from the JSON?
You can't initalize a SwiftyJSON object directly with a string, you have to convert this string to data (or use the data you got in the first place if that's the case).
Assuming message.body is a string:
if let dataFromString = message.body.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
if let status = json["status"].string {
print("status: \(status)")
}
}