i came from javascript and now i'm studying for swift. i want to print one item of my URLSession return but i dont know how i can do this.
My code:
import Foundation
import Dispatch
enum ServiceError: Error {
case invalidURL
case decodeFail(Error)
case network(Error?)
}
struct Address: Codable {
let zipcode: String
let address: String
let city: String
let uf: String
let complement: String?
enum CodingKeys: String, CodingKey {
case zipcode = "cep"
case address = "logradouro"
case city = "localidade"
case uf
case complement = "complemento"
}
}
class Service {
private let baseURL = "https://viacep.com.br/ws"
func get(cep: String, callback: @escaping (Result<Any, ServiceError>) -> Void) {
let path = "/\(cep)/json"
guard let url = URL(string: baseURL path) else {
callback(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
callback(.failure(.network(error)))
return
}
guard let json: Address = try? JSONDecoder().decode(Address.self, from: data) else {
return
}
callback(.success(json))
}
task.resume()
}
}
do {
let service = Service()
service.get(cep: "1231234") { result in
DispatchQueue.main.async {
switch result {
case let .failure(error):
print(error)
case let .success(data):
print(data)
}
}
}
}
This is my return:
Address(zipcode: "12341231", address: "Teste", city: "Teste", uf: "TE", complement: Optional(""))
I want want my return is like:
print(data.zipcode)
Output: 12341231
CodePudding user response:
Unlike javascript, Swift is strongly typed, so return the Address
type
from your func get(cep:...)
, not Any
.
Note, there are many types of errors do deal with when doing a server request.
An error could mean that the response could not be decoded, due to a bad request, for example, as in the case with cep: "1231234". If you use cep: "01001000", you don't get an error, and all works well.
So I suggest update your code (especially the ServiceError
) to cater for the various errors you may get.
Here is the code I used to test my answer:
let service = Service()
// try also "01001000" for no errors
service.get(cep: "1231234") { result in
switch result {
case let .failure(error):
print("\n---> error: \(error)")
case let .success(data):
print("\n---> data: \(data)")
print("---> zipcode: \(data.zipcode)")
}
}
and
class Service {
private let baseURL = "https://viacep.com.br/ws"
// here return Address, not Any
func get(cep: String, callback: @escaping (Result<Address, ServiceError>) -> Void) {
let path = "/\(cep)/json"
guard let url = URL(string: baseURL path) else {
callback(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
callback(.failure(.network(error)))
return
}
// this shows you what the server is really sending you
print("\n---> data: \(String(data: data, encoding: .utf8)) \n")
guard let httpResponse = response as? HTTPURLResponse else {
callback(.failure(.apiError(reason: "Unknown")))
return
}
switch httpResponse.statusCode {
case 400: return callback(.failure(.apiError(reason: "Bad Request")))
case 401: return callback(.failure(.apiError(reason: "Unauthorized")))
case 403: return callback(.failure(.apiError(reason: "Resource forbidden")))
case 404: return callback(.failure(.apiError(reason: "Resource not found")))
case 405..<500: return callback(.failure(.apiError(reason: "client error")))
case 500..<600: return callback(.failure(.apiError(reason: "server error")))
default:
callback(.failure(.apiError(reason: "Unknown")))
}
// here use a proper do/catch
do {
let address = try JSONDecoder().decode(Address.self, from: data)
callback(.success(address))
} catch {
// could not be decoded as an Address.
callback(.failure(.decodeFail(error)))
}
}
task.resume()
}
}
enum ServiceError: Error {
case invalidURL
case decodeFail(Error)
case network(Error?)
case apiError(reason: String) // <-- here
}