firstly I am really new to iOS development and Swift (2 weeks coming here from PHP:))
I am creating a application that has a callout to my very simple api. I am using the below code but an unable to access the 'if let response' part of the code and unable to get the value of 'Comments' from the returned data from api. Wondering if anyone can help on this.
The data come back fine as
//Print out fine
print(str)
will print my response of {"Comments":"success"}
so just wondering how I can properly use this data and check if success etc
Thanks
func loadData() {
guard let url = URL(string: "myapi.php") else {
print("Your API end point is Invalid")
return
}
let request = URLRequest(url: url)
print("hello1")
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
print("hello2")
let str = String(decoding: data, as: UTF8.self)
//Print out fine
print(str)
// Cannot enter this if statement
if let response = try? JSONDecoder().decode([TaskEntry].self, from: data) {
DispatchQueue.main.async {
print("hello3")
print(response)
}
return
}
}
}.resume()
}
struct TaskEntry: Codable {
public var Comments: String
}
With the description provided by you, the JSON that returns your API is the following:
{
"Comments": "success"
}
Is that right?
If it is, this part of your code
if let response = try? JSONDecoder().decode([TaskEntry].self, from: data)
Marks that you are decoding data fetched from your app as Array. You specify an array of TaskEntry right here [TaskEntry].self
for your data that's not true, you need to decode json with try? JSONDecoder().decode(TaskEntry.self, from: data)
try? JSONDecoder().decode(TaskEntry.self, from: data)
If you hope for an array your api must to return something like this
[
{
"Comments": "success"
}
]
For check if it is right with status code I provide a restructuration of your code explained.
First, protocol Codable it's used to decode and encode data with Encoders and Decoders. In that case, you are working with JSONs so you use JSONDecoder to decode data. You only need decode, so with Decodable protocol It's enough. You don't need to mark Comments with public, because you are working with TaskEntry in your project, so by default (no specified) It's internal. Moreover you don't need var, because, you are not making changes of the property. Also, properties as standard starts with lowercase, so you can use CodingKeys to keep a quality code standard.
You can check it in Apple Developer site
struct TaskEntry: Decodable {
let comments: String
enum CodingKeys: String, CodingKey {
case comments = "Comments"
}
}
Then, you should to think about your Api errors and defined it. I give you an example:
enum NetworkErrors: Error {
case responseIsNotHTTP
case noDataProvided
case unknown
}
Your load function, should to communicate to external class or struct the TaskEntry or the error. You could use a closure with a Result. Please check the link Hacking with Swift . This has two cases with associated values, failure and success.
Further the response of your UrlSessionTask need to be cast to HTTPURLResponse to get the response http status code. I provide an example of that with your code.
func loadData(result: @escaping (Result<TaskEntry, Error>) -> Void) {
guard let url = URL(string: "myapi.php") else {
result(.failure(URLError(.badURL)))
return
}
let request = URLRequest(url: url) // your method is Get, if not you need to set the http method of the request
URLSession.shared.dataTask(with: request) { data, response, error in
guard let response = response as? HTTPURLResponse else {
if let error = error {
print(error.localizedDescription)
result(.failure(error))
} else {
result(.failure(NetworkErrors.responseIsNotHTTP))
}
return
}
switch response.statusCode {
case 200...299:
if let data = data {
do {
let taskEntry = try JSONDecoder().decode(TaskEntry.self, from: data)
result(.success(taskEntry))
} catch {
result(.failure(error))
}
} else {
result(.failure(NetworkErrors.noDataProvided))
}
default:
if let error = error {
print(error.localizedDescription)
result(.failure(error))
} else {
result(.failure(NetworkErrors.unknown))
}
}
}.resume()
}
Additionally, you could pass this code to async/await form with a new function in iOS 15 or MacOS 12 (async/await it's a feature of swift 5.5, but the urlsession function that i've used it's only from ios 15 and macos 12 to up):
@available(macOS 12.0, iOS 15.0, *)
func loadData() async throws -> Result<TaskEntry,Error> {
guard let url = URL(string: "myapi.php") else {
throw URLError(.badURL)
}
do {
let (data, urlResponse) = try await URLSession.shared.data(from: url, delegate: nil)
guard let response = urlResponse as? HTTPURLResponse else {
return .failure(NetworkErrors.responseIsNotHTTP)
}
switch response.statusCode {
case 200...299:
do {
let taskEntry = try JSONDecoder().decode(TaskEntry.self, from: data)
return .success(taskEntry)
} catch {
return .failure(error)
}
default:
return .failure(NetworkErrors.unknown)
}
} catch {
return .failure(error)
}
}
Please, vote up if it's clarify you.
Have a good day:)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.