I'm try to parse a json response from a server like this
HTTP.GET(ServerPatientApi.SPLASH, parameters: nil) { response in
if let error = response.error {
listener.onException(error)
return;
}
DispatchQueue.main.async (execute: {
let res = try decoder.decode(PatientSplashModel.self, from: response.data)
listener.onSplashLoaded()
})
}
But I'm receiving the error below:
Invalid conversion from throwing function of type '() throws -> ()' to non-throwing function type '#convention(block)
at the block:
DispatchQueue.main.async (execute: {
let res = try decoder.decode(PatientSplashModel.self, from: response.data)
listener.onSplashLoaded()
})
I think the error caused by:
PatientSplashModel.self
how to fix this?
thanks
The error is a bit misleading, you have to wrap the try statement in a do catch block
DispatchQueue.main.async {
do {
let res = try decoder.decode(PatientSplashModel.self, from: response.data)
listener.onSplashLoaded()
} catch { print(error) }
}
Related
Problem
The data is successfully decoded from Reddit’s api and put into the variable theUser inside the .onAppear {…}, but when I try to use it I keep getting nil values.
Code
struct ContentView: View {
#State var didAppear = false
#State var theUser = getNilUser()
var body: some View {
Text("User Profile")
Text(theUser.data.subreddit.display_name_prefixed ?? "No name found")
.onAppear(perform: {
theUser = getUser(withName: "markregg")
})
}
func getUser(withName username: String) -> user {
if !didAppear {
if let url = URL(string: "https://www.reddit.com/user/\(username)/about.json") {
do {
let data = try Data(contentsOf: url)
do {
let decodedUser = try JSONDecoder().decode(user.self, from: data)
return decodedUser
} catch {
print(error)
}
} catch {
print(error)
}
}
didAppear = true
}
return getNilUser()
}
}
Edit: I’m very new to this so if I made a stupid mistake I’m sorry
You can't get JSON data like that. You have to execute a HTTP request using the URLSession class in order to get the data and parse it.
let username = "markregg"
let url = URL(string: "https://www.reddit.com/user/\(username)/about.json")!
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print(error)
} else if let data = data {
do {
let decodedUser = try JSONDecoder().decode(User.self, from: data)
} catch {
print(error)
}
} else {
print("Request failed")
}
}.resume()
Another thing to note is that URLSession methods are asynchronous. In simple terms it means they take some time to execute and return the result. So you can't use a simple return statement like return decodedUser to get the result right away. Look into how to use URLSession more.
I am having problems finding out why my dataTask returns an empty result.
While going through My NetworkingManager class it appeared that it never enters the URLSession.shared.dataTask. Does anyone know why?
Her is my NetworkingManager which is being used in the ContentView of the app:
class NetworkingManager: ObservableObject {
var didChange = PassthroughSubject<NetworkingManager, Never>()
var showList = ShowResultsAPI(results: []) {
didSet {
didChange.send(self)
}
}
init() {
guard let url = URL(string: "www.json.url") else {
return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else {
return }
let showList = try! JSONDecoder().decode(ShowResultsAPI.self, from: data)
DispatchQueue.main.async {
self.showList = showList
}
}.resume()
}
}
In my opinion your coding looks correct.
Keep in mind that the request is asynch. When your debugging the URLSession.shared.dataTask you will recognize that at first the debugger is skipping the dataTask. When the URLSession receives a response it will enter the URLSession.shared.dataTask again.
I would recommend to set a breakpoint in the line with your guard statement. Then debug the process again and see if the debugger enters the process.
It would also be interesting to observe the response and the error in the completion handler to see if there are errors occuring.
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print(error)
return
}
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200,
let data = data
else {
print("ResponseProblem")
print(response)
return
}
add return value in guard : return "example"
change your code to this :
}
}.resume()
sleep(2)
}
Adding a .sleep(2) at the end of the init() helped to process the JSON.
Edited:
It needed an asynchronous task which implies having a sleep or as #vadian suggested a better suited delay() from combine.
I'm building an iOS app in Swift 3 that's supposed to communicate with a JSON Rest Api that I'm also building myself. The app will get all sorts of content from the Api, but for now all I need it to do is check the availability of the Api through a handshake function.
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error)
} else {
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
if jsonResult["response"] as! String == "Welcome, come in!" {
print("************ RESPONSE IS: ************")
print(jsonResult)
return
} else {
return
}
} catch {
print("************ JSON SERIALIZATION ERROR ************")
}
}
}
}
task.resume()
This is the dataTask I've set up and it runs just fine (When I print the jsonResult, I get the "Welcome!" message as expected. The problem is that I want my handshake function to return true or false (So that I can give an alert if the case is false.) When I try to set up a return true or false within the if-statement, I get the error: Unexpected non-void return value in void function.
My question is: How do I return the data out of the dataTask so that I can perform checks with it within my handshake function? I'm very new to Swift so all help is appreciated :)
Below is the entire class:
import Foundation
class RestApiManager: NSObject {
var apiAvailability:Bool?
func handshake() -> Bool {
let url = URL(string: "https://api.restaurapp.nl/handshake.php")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error)
} else {
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
if jsonResult["response"] as! String == "Welcome, come in!" {
print("************ RESPONSE IS: ************")
print(jsonResult)
return true
} else {
return false
}
} catch {
print("************ JSON SERIALIZATION ERROR ************")
}
}
}
}
task.resume()
}
}
Because you're using an asynchronous API, you can't return the bool from your handshake function. If you want to show an alert in the false case, you would replace the return false with something like:
DispatchQueue.main.async {
self.showAlert()
}
Technically you could make the handshake function pause until the network stuff was done, and return the bool, but that defeats the purpose of being asynchronous, and it would freeze your app's UI during the network activity, so I doubt that's what you want to do.
I am receiving the error: Use of unresolved identifier 'json'
from this line: if let parseJSON = json {
This is the snippet of the code relevant to the error
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if error != nil {
print("error=\(error)")
return
}
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print(jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("Result: \(resultValue)")
if(resultValue=="Success")
{
//Login is sucessful
NSUserDefaults.standardUserDefaults().setBool(true,forKey:"isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize()
self.dismissViewControllerAnimated(true, completion: nil)
}
}
}
task.resume()
}
}
I reviewed this problem: Swift : Use of unresolved identifier 'json' but I do not know how I can fix my issue or where to put the variable of json
I think you meant to write if let parseJSON = jsonResult but anyways that is not gonna work either since the variables declared in the do are only visible at that scope. you need to move relevant code inside the do. You can assign the result of JSONSerialization directly to parsJSON.
You should also change this line var resultValue:String = parseJSON["status"] as String! to var resultValue:String = parseJSON["status"] as! String
Modify your code like this:
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if error != nil {
print("error=\(error)")
return
}
do {
if let parseJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print(parseJSON)
let resultValue:String = parseJSON["status"] as! String
print("Result: \(resultValue)")
if(resultValue=="Success")
{
//Login is sucessful
NSUserDefaults.standardUserDefaults().setBool(true,forKey:"isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize()
self.dismissViewControllerAnimated(true, completion: nil)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
task.resume()
}
}
I am currently trying to download, parse and print JSON from an URL.
So far I got to this point:
1) A class (JSONImport.swift), which handles my import:
var data = NSMutableData();
let url = NSURL(string:"http://headers.jsontest.com");
var session = NSURLSession.sharedSession();
var jsonError:NSError?;
var response : NSURLResponse?;
func startConnection(){
let task:NSURLSessionDataTask = session.dataTaskWithURL(url!, completionHandler:apiHandler)
task.resume();
self.apiHandler(data,response: response,error: jsonError);
}
func apiHandler(data:NSData?, response:NSURLResponse?, error:NSError?)
{
do{
let jsonData : NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonData);
}
catch{
print("API error: \(error)");
}
}
My problem is, that the data in
do{
let jsonData : NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonData);
}
remains empty.
When I debug,the connection starts successfully, with the given url as a parameter. But my jsonData variable doesn't get printed. Instead the catch block throws the error, stating that there is no data in my variable:
API error: Error Domain=NSCocoaErrorDomain Code=3840 "No value."
Can someone please help me with this?
What am I missing?
Thank you all very much in advance!
[Edited after switching from NSURL Connection to NSURLSession]
Here's an example on how to use NSURLSession with a very convenient "completion handler".
This function contains the network call and has the "completion handler" (a callback for when the data will be available):
func getDataFrom(urlString: String, completion: (data: NSData)->()) {
if let url = NSURL(string: urlString) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (data, response, error) in
// print(response)
if let data = data {
completion(data: data)
} else {
print(error?.localizedDescription)
}
}
task.resume()
} else {
// URL is invalid
}
}
You can use it like this, inside a new function, with a "trailing closure":
func apiManager() {
getDataFrom("http://headers.jsontest.com") { (data) in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
if let jsonDict = json as? NSDictionary {
print(jsonDict)
} else {
// JSON data wasn't a dictionary
}
}
catch let error as NSError {
print("API error: \(error.debugDescription)")
}
}
}