Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 4 Codable - API provides sometimes an Int sometimes a String

I have Codables running now. But the API has some String entries that can sometimes have an Int value of 0 if they are empty. I was searching here and found this: Swift 4 Codable - Bool or String values But I'm not able to get it running

My struct

struct check : Codable {
    let test : Int
    let rating : String?  
}

Rating is most of the time something like "1Star". But if there is no rating I get 0 as Int back.

I'm parsing the data like this:

enum Result<Value> {
    case success(Value)
    case failure(Error)
}

func checkStar(for userId: Int, completion: ((Result<check>) -> Void)?) {
    var urlComponents = URLComponents()
    urlComponents.scheme = "https"
    urlComponents.host = "xyz.com"
    urlComponents.path = "/api/stars"
    let userIdItem = URLQueryItem(name: "userId", value: "\(userId)")
    urlComponents.queryItems = [userIdItem]
    guard let url = urlComponents.url else { fatalError("Could not create URL from components") }

    var request = URLRequest(url: url)
    request.httpMethod = "GET"


    let config = URLSessionConfiguration.default
    config.httpAdditionalHeaders = [
        "Authorization": "Bearer \(keytoken)"
    ]

    let session = URLSession(configuration: config)
    let task = session.dataTask(with: request) { (responseData, response, responseError) in
        DispatchQueue.main.async {
            if let error = responseError {
                completion?(.failure(error))
            } else if let jsonData = responseData {
                // Now we have jsonData, Data representation of the JSON returned to us
                // from our URLRequest...

                // Create an instance of JSONDecoder to decode the JSON data to our
                // Codable struct
                let decoder = JSONDecoder()

                do {
                    // We would use Post.self for JSON representing a single Post
                    // object, and [Post].self for JSON representing an array of
                    // Post objects
                    let posts = try decoder.decode(check.self, from: jsonData)
                    completion?(.success(posts))
                } catch {
                    completion?(.failure(error))
                }
            } else {
                let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
                completion?(.failure(error))
            }
        }
    }

    task.resume()
}

Loading it:

func loadStars() {
    checkStar(for: 1) { (result) in
        switch result {
        case .success(let goo):
            dump(goo)
        case .failure(let error):
            fatalError(error.localizedDescription)
        }
    }
}

I hope someone can help me there, cause I'm not completely sure how this parsing, etc. works.

like image 673
Nxt97 Avatar asked Jun 04 '18 15:06

Nxt97


1 Answers

you may implement your own decode init method, get each class property from decode container, during this section, make your logic dealing with wether "rating" is an Int or String, sign all required class properties at last.

here is a simple demo i made:

class Demo: Decodable {
    var test = 0
    var rating: String?

    enum CodingKeys: String, CodingKey {
        case test
        case rating
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let test = try container.decode(Int.self, forKey: .test)
        let ratingString = try? container.decode(String.self, forKey: .rating)
        let ratingInt = try? container.decode(Int.self, forKey: .rating)
        self.rating = ratingString ?? (ratingInt == 0 ? "rating is nil or 0" : "rating is integer but not 0")
        self.test = test
    }
}

let jsonDecoder = JSONDecoder()
let result = try! jsonDecoder.decode(Demo.self, from: YOUR-JSON-DATA)
  • if rating API's value is normal string, you will get it as you wish.
  • if rating API's value is 0, rating will equal to "rating is nil or 0"
  • if rating API's value is other integers, rating will be "rating is integer but not 0"

you may modify decoded "rating" result, that should be easy.

hope this could give you a little help. :)

for more info: Apple's encoding and decoding doc

like image 171
vg0x00 Avatar answered Oct 23 '22 05:10

vg0x00