Home > OS >  How can access one item of struct in swift
How can access one item of struct in swift

Time:11-08

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
}
  • Related