Swift 4 Properly Parse JSON String to Object - ios

I'm trying to parse a JSON string coming from a URLSession request into a Swift object.
I managed to get the data for the first level properties but for nested properties something weird happens. Instead of : i get = AND strings are missing the double-quotes
How do I access the date property inside published because I can not do this: print(todo["published"]["date"])
Here is the data I get:
[
"pretty_artists": kida,
"published": {
date = "2015-12-05";
now = 1517005961;
time = "18.59";
timestamp = 1449341947;
},
"views": 36,
"yt_id": cyXbV7EUl14,
"play_start": 0,
"title": ski ide,
"duration": 235,
"video_name": skiide,
"artists": kida
]
Here is my function:
func makeGetCall(todoEndpoint: String) {
// Set up the URL request
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
// now we have the todo
// let's just print it to prove we can access it
print(todo["published"]["date"])
// the todo object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
guard let todoTitle = todo["title"] as? String else {
print("Could not get todo title from JSON")
return
}
print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}

try the SwiftyJSON library, it should help you parse the data more easily.
https://github.com/SwiftyJSON/SwiftyJSON

Related

How to call URL in swift which contain square bracket[ ]

I trying to call a web service which contain square brackets[ ].
But I don't know How do I call that api.
URL is shown below
http request method is GET.
https:"YOUR URL"/?searchCriteria[filter_groups][0][filters][0][field]=name&searchCriteria[filter_groups][0][filters][0][value]=(txtFldSearch.text!)&searchCriteria[filter_groups][0][filters][0][condition_type]=like
this is what I tried
#objc func getHintsFromTextField(textField: UITextField) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: "https:"YOUR URL"/?searchCriteria[filter_groups][0][filters][0][field]=name&searchCriteria[filter_groups][0][filters][0][value]=%\(txtFldSearch.text!)%&searchCriteria[filter_groups][0][filters][0][condition_type]=like")!
let task = session.dataTask(with: url!) { data, response, error in
// ensure there is no error for this HTTP response
guard error == nil else {
print ("error: \(error!)")
return
}
// ensure there is data returned from this HTTP response
guard let content = data else {
print("No data")
return
}
// serialise the data / NSData object into Dictionary [String : Any]
guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [String: Any] else {
print("Not containing JSON")
return
}
print("gotten json response dictionary is \n \(json)")
// update UI using the response here
}
// execute the HTTP request
task.resume()
}
What's the right way to do this?.Please help. I am not able to call this api.

Why will the object not append to the array?

class NetworkManager{
var articleList = [Article]()
func downloadJsonData() -> Void{
let jsonUrl = "someUrl"
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url) { data, response, err in
//check err
//check response status
guard let data = data else { return }
do{
let apiResults = try JSONDecoder().decode(ApiResults.self, from: data)
//article list remains empty
self.articleList = apiResults.articles
} catch let err{
print(err)
}
}.resume()
}
}
I have also tried to use a for loop to append to the array and that didn't work either. Any help will be appreciated.
First thing I would check is that the data returned is correct.
Is the guard block triggering the return or is the data fine?
Is the JSON able to decode the response correctly?
Are the articles in the apiResults object populated.
The next thing is you are not attempting to append the contents of apiResults.articles to your list, instead you are making your list become what ever apiResults.articles is.
Try the following and see how it runs:
class NetworkManager{
// better declaration syntax
var articleList: [Article] = []
func downloadJsonData() {
let jsonUrl = "https://newsapi.org/v2/everything?sources=nfl-news&apiKey=mykey"
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url) { data, response, err in
//check err
//check response status
guard let data = data else { return }
do{
let apiResults = try JSONDecoder().decode(ApiResults.self, from: data)
//article list remains empty
//appends contents instead of assignment
self.articleList.append(contentsOf: apiResults.articles)
} catch let err{
print(err)
}
}.resume()
}
}

swift JSON login REST with post and get response example

It's my first experience with REST in iOS development with swift. I couldn't find any working or straight (simple) example for doing what i need here.
I have a login backend (https://myaddress.com/rest/login), where I need to pass 2 params: login and password. When I pass good values (user exists in database) I get 2 variables as a result: token (string) and firstLogin (bool). So when I get those values I know that login is successful and I can log in into my app.
So I am begging you for an example (just a simple function) of how to achieve that. If I get working code example I will know how to use it for other rest services in my app. I tried many solutions from tutorials I found, but any of them was working for me.. So to not waste my time searching I would like someone experienced to show me the way to achieve that.
I am not sure if Alamofire is so good to use, I know that swift 4 has it's own build neetwork services and to work with json. Any solution that works would be great.
Also, side question - if I would prefer to use Alamofire, do I need to use swiftyJSON also? Or it's just for parsing?
You can use URLSession if you don't like to import Alamofire in your Project to perform a simple task.
here are some method : GET, POST, DELETE METHODS and tutorial
GET METHOD
func makeGetCall() {
// Set up the URL request
let todoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
// now we have the todo
// let's just print it to prove we can access it
print("The todo is: " + todo.description)
// the todo object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
guard let todoTitle = todo["title"] as? String else {
print("Could not get todo title from JSON")
return
}
print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
POST METHOD
func makePostCall() {
let todosEndpoint: String = "https://jsonplaceholder.typicode.com/todos"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
let newTodo: [String: Any] = ["title": "My First todo", "completed": false, "userId": 1]
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /todos/1")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: responseData,
options: []) as? [String: Any] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
guard let todoID = receivedTodo["id"] as? Int else {
print("Could not get todoID as int from JSON")
return
}
print("The ID is: \(todoID)")
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
DELETE METHOD
func makeDeleteCall() {
let firstTodoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
var firstTodoUrlRequest = URLRequest(url: URL(string: firstTodoEndpoint)!)
firstTodoUrlRequest.httpMethod = "DELETE"
let session = URLSession.shared
let task = session.dataTask(with: firstTodoUrlRequest) {
(data, response, error) in
guard let _ = data else {
print("error calling DELETE on /todos/1")
return
}
print("DELETE ok")
}
task.resume()
}
Thanks #MAhipal Singh for you answer. I'll post here example with Alamafire that I used so it's all in one stack question. It's easier than I though, solutions I tried to use before were not working cause I had problems with pinning certificate about I forgot..
func loginRest(login:String, password:String, deviceId:String){
let urlStr = restServices.REST_MAIN_URL + restServices.REST_LOGIN
let params = ["login":login, "password":password, "deviceId":deviceId]
let paramsJson = try! JSONSerialization.data(withJSONObject: params)
var headers: HTTPHeaders = ["Content-Type": "application/json"]
Alamofire.request(urlStr, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
switch response.result {
case .success:
print("SUKCES with \(response)")
case .failure(let error):
print("ERROR with '\(error)")
}
}
If the post is proper the response is (console print):
SUKCES with SUCCESS: {
firstLogin = 1;
token = "dfkafjkfdsakfadsjfksjkfaadjfkjdfkjfskjfdkafjakfjakfjsafksjdafjy878328hjh";
}

Making a re-useable function of JSON URL fetching function in SWIFT 2.0

I am stuck in a problem. I think it is all due to my weak basics. I am sure someone can help me easily and put me in the right direction.
I have different segues and all get the data from JSON via remote URL.
So in-short all segues need to open URL and parse JSON and make them into an ARRAY
I have made the first segue and it is working fine.
Now i plan to use the functions where it download JSON and turns it into ARRAY as a common function
I read in another page on stackoverflow that I can declare all common functions outside the class in ViewController
I hope everyone is with me this far.
now in ViewController i declare a function
getDataFromJson(url: String)
This function code looks like following
func getJsonFromURL(url: String)
{
// some class specific tasks
// call the common function with URL
// get an array
let arrJSON = getJsonArrFromURL(url)
for element in arrJSON
{
// assign each element in json to ur table
print("Element: \(element)")
}
// some class specific tasks
}
and this will call the common function declared outside the score of class
getArrFromJson(url: String) -> NSArray
This common function is just very generic.
Take a URL, call it, open it, parse its data into ARRAY and return it back.
The problem i am stuck is where to put the return
It returns empty array as the task is not finished and i am clueless
func getJsonArrFromURL(var url: String) -> NSArray
{
var parseJSON : NSArray?
if ( url == "" )
{
url = self.baseURLHomepage
}
print("Opening a JSON URL \(url)")
let myUrl = NSURL(string: url);
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "GET";
let postString = "";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if ( error != nil )
{
print("Error open JSON url \n\(error)")
return
}
do
{
parseJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSArray
}
catch
{
self.showAlert("Error", msg: "Error occurred while trying to process the product information data")
print("Error occured in JSON = \(error)")
}
}
task.resume()
return parseJSON!
}
You can probably add a method like below in any of your class
func post(url: String, info: String, completionHandler: (NSString?, NSError?) -> ()) -> NSURLSessionTask {
let URL = NSURL(string: url)!
let request = NSMutableURLRequest(URL:URL)
request.HTTPMethod = "GET"
let bodyData = info
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
dispatch_async(dispatch_get_main_queue()) {
guard data != nil else {
print("response String is nil")
completionHandler(nil, error)
return
}
if let dataNew = data {
completionHandler(NSString(data: (NSData(base64EncodedData: dataNew, options: NSDataBase64DecodingOptions([])))!, encoding: NSASCIIStringEncoding), nil)
}
}
}
task.resume()
return task
}
and access it anywhere like
let url = "your URL String"
let info = "The data you would like to pass"
yourClassName.post(url, info: info) { responseString, error in
guard responseString != nil else {
print("response String is nil")
print(error)
return
}
do {
if !(responseString as? String)!.isEmpty {
let json = try NSJSONSerialization.JSONObjectWithData((responseString as! String).data, options: NSJSONReadingOptions.init(rawValue: 0))
//process your json here
}
} catch {
print("Error\n \(error)")
return
}
}
Extend your string like follows
extension String {
var data:NSData! {
return dataUsingEncoding(NSUTF8StringEncoding)
}
}

Missing return in a function expected to return 'NSURLSessionDataTask'

I am following a tutorial on accessing an api and parsing the result. I am following the tutorial word for word but I cannot run the program because of 'Missing return in a function expected to return 'NSURLSessionDataTask'
so I changed the return statement to "return NSURLSessionDataTask" but then got an error saying "Cannot convert return expression of type 'NSURLSessionDataTask.Type" to return type 'NSURLSessionDataTask'
How do i figure out the return type? do I even need a return? because in the tutorial there is not return statement (i tried without return as well).
func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void)
-> NSURLSessionDataTask {
let postEndpoint: String = "http://jsonplaceholder.typicode.com/posts/1"
let urlRequest = NSURLRequest(URL: NSURL(string: postEndpoint)!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: {
(data, response, error) in
guard let responseData = data else {
print("Error: did not recieve data")
return
}
guard error == nil else {
print("error calling GET on /posts/1")
print(error)
return
}
// parse the resutl as JSON, since that's what the API provieds
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData, options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
// now we have the post, let's just print it to prove we can access it
print("The post is: " + post.description)
if let postTitle = post["title"] as? String {
print("The title is: " + postTitle)
}
})
// and send it
task.resume()
}
Did you really mean to write your own method called dataTaskWithRequest which looks just like the NSURLSession method of the same name? The problem is that you said you're writing a method that returns a NSURLSessionTask object, but you don't return anything.
I'd think you meant something like the following, renaming your own method to something else, and specifying that it's not returning anything itself, because it's not:
func performRequest() {
let postEndpoint: String = "http://jsonplaceholder.typicode.com/posts/1"
let urlRequest = NSURLRequest(URL: NSURL(string: postEndpoint)!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: {
(data, response, error) in
guard let responseData = data else {
print("Error: did not recieve data")
return
}
guard error == nil else {
print("error calling GET on /posts/1")
print(error)
return
}
// parse the resutl as JSON, since that's what the API provieds
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData, options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
// now we have the post, let's just print it to prove we can access it
print("The post is: " + post.description)
if let postTitle = post["title"] as? String {
print("The title is: " + postTitle)
}
})
// and send it
task.resume()
}

Resources