iosjsonswift

JSON API transfer data in Swift


How to pass data from JSON API to static func getAllStats()

I need to transfer only two parameters P and W from JSON

look at the screenshot

struct Stat: Codable, Identifiable {
    
    var id = UUID()
    
    let image: String
    let name: String
    
    var p: String
    var w: String
    
    let t: String
}

extension Stat {

    static func getAllStats() -> [Stat] {

        return [

            Stat(image: "1", name: "Name1", p: "", w: "", t: "12"),

            Stat(image: "71", name: "Name2", p: "", w: "", t: "13"),

            Stat(image: "2", name: "Name3", p: "", w: "", t: "14")
        ]
    }
}

class Api {
    func getPost(completion: @escaping ([Stat]) -> ()) {
        
        guard let url = URL(string: "") else {return}
        
        URLSession.shared.dataTask(with: url) { (data, _, _) in
            
            let posts = try! JSONDecoder().decode([Stat].self, from: data!)
            
            DispatchQueue.main.async {
                completion(posts)
            }
        }
        .resume()
        
    }
}

Solution

  • It is not clear to me with what criteria you will be inserting the variables p and w from an array of Stat within other objects of type Stat: do they need to match the id? Do they need to be read in order?

    The code I propose here below creates a dedicated struct only for those 2 variables, I called it PW. This will avoid mixing the type you use in your project with the type downloaded from the API. PW is the type that will be decoded.

    p and w coming from the api are injected into the Stat object based on the order of the downloaded JSON just for example purposes - however, this is not the best practice, so I recommend to adapt your code accordingly.

    The example uses async to avoid completion handlers and DispatchQueue, but that is not necessary - just use your favourite approach.

    // This is where the API will store the downloaded values
    struct PW: Codable {
        let p: String?
        let w: String?
    }
    
    struct Stat: Codable, Identifiable {
        
        var id = UUID()
        
        let image: String
        let name: String
    
        // Replace 2 variables of type string with one variable of type PW
        var pw: PW
        
        let t: String
    }
    
    extension Stat {
    
        // This will allow the code to read p and w in a convenient way
        var p: String { pw.p ?? "" }
        var w: String { pw.w ?? "" }
    
        // async just to match the function in the Api class, but you
        // can use your own code
        static func getAllStats() async -> [Stat] {
            
            // Get the array of PW (with p and w values) from the api
            let pws = await Api().getPost()
    
            return [
    
                // Each object will store p and w values based on the order received:
                // this is NOT a best practice, a clear and unique criteria should be used
                Stat(image: "1",  name: "Name1", pw: pws[0], t: "12"),
                Stat(image: "71", name: "Name2", pw: pws[1], t: "13"),
                Stat(image: "2",  name: "Name3", pw: pws[2], t: "14")
            ]
        }
    }
    
    // This class is just for example purposes, implement your own code as you wish
    class Api {
        
        // async just to avoid completion and DispatchQueue
        func getPost() async -> [PW] {
            
            guard let url = URL(string: "https://...") else { return [] }
            
            let request = URLRequest(url: url)
            
            do {
                let (data, response) = try await URLSession.shared.data(for: request)
                
                guard let httpResponse = response as? HTTPURLResponse,
                      (200...299).contains(httpResponse.statusCode) else {
                    print("Error: HTTP response =\n\(response.debugDescription)")
                    return []
                }
                
                // Decode type PW, not Stat
                let posts = try! JSONDecoder().decode([PW].self, from: data)
                return posts
            } catch {
                print("Oops: something went wrong!")
                return []
            }
        }
    }
    
    // Example of a view, just to show how to use the code
    struct MyView: View {
        
        @State private var stats = [Stat]()
    
        var body: some View {
            VStack {
                ForEach(stats) { stat in
                    VStack {
                        Text(stat.name)
                        Text(stat.p)
                        Text(stat.w)
                    }
                }
            }
            .task {
                stats = await Stat.getAllStats()
            }
        }
    }