Search code examples
jsonswiftdata-handling

Swift - Creating a stack (Text view) from a function that returns an array


I am trying to create a list of text objects out of a function that returns an array of params. Everything seems to be working fine, getting the data, console shows the correct results, except the list itself which remains empty.

The function call:

import UIKit
import SwiftUI

struct SubdomainsList: View {

    @State var SubDomains = VTData().funky(XDOMAIN: "giphy.com")
    
    var body: some View {
            VStack {
                List{
                    Text("Subdomains")
                    ForEach(SubDomains, id: \.self) { SuDo in
                        Text(SuDo)
                    }
                }
            }
        }
}

struct SubdomainsList_Previews: PreviewProvider {
    static var previews: some View {
        SubdomainsList()
    }
}

The Json handlers:

struct VTResponse: Decodable {
    let data: [VT]
}

struct VT: Decodable {
    var id: String
}

The class:

class VTData {
    func funky (XDOMAIN: String) -> Array<String>{
        var arr = [""]
        getDATA(XDOMAIN: "\(XDOMAIN)", userCompletionHandler: { (SubDomain) in
            print(SubDomain)
            arr.append(SubDomain)
            return SubDomain
        })
        return arr
    }
    
    
    func getDATA(XDOMAIN: String, userCompletionHandler: @escaping ((String) -> String))  {
        let token = "<TOKEN>"
        guard let url = URL(string: "https://www.lalalla.com/subdomains") else {fatalError("Invalid URL")}
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("\(token)", forHTTPHeaderField: "x-apikey")
        
        let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
            guard let data = data else { return }
            let decoder = JSONDecoder()
            let result = try? decoder.decode(VTResponse.self, from: data)
            if let result = result {
                for SubDo in result.data {
                    let SubDomain = SubDo.id
                    userCompletionHandler(SubDomain)
                }
            }
            else {
                fatalError("Could not decode")
            }
        })
            task.resume()
    }
    
}

I'm getting no errors whatsoever, and the console output shows the correct results:

support.giphy.com
cookies.giphy.com
media3.giphy.com
i.giphy.com
api.giphy.com
developers.giphy.com
media.giphy.com
x-qa.giphy.com
media2.giphy.com
media0.giphy.com

It is also worth mentioning that when I add print(type(of: SubDomain)) to the code I'm getting a String rather than an array.

The preview: preview

Any ideas?


Solution

  • try this approach, again, to extract the list of subdomain from your API, and display them in a List using the asynchronous function getDATA(...):

    class VTData {
        
        // `func funky` is completely useless, remove it
        
        func getDATA(XDOMAIN: String, completion: @escaping ([String]) -> Void) { // <-- here
            let token = "<TOKEN>"
            guard let url = URL(string: "https://www.virustotal.com/api/v3/domains/\(XDOMAIN)/subdomains") else {
                print("Invalid URL")
                return
            }
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue("\(token)", forHTTPHeaderField: "x-apikey")
            
            URLSession.shared.dataTask(with: request) { data, response, error in
                guard let data = data else { return } // todo return some error msg
                do {
                    let results = try JSONDecoder().decode(VTResponse.self, from: data)
                    return completion(results.data.map{ $0.id }) // <-- here
                } catch {
                    print(error) // <-- here important
                }
            }.resume()
        }
        
    }
    
    struct SubdomainsList: View {
        @State var subDomains: [String] = []  // <--- here
        
        var body: some View {
            VStack {
                List{
                    Text("Subdomains")
                    ForEach(subDomains, id: \.self) { SuDo in
                        Text(SuDo)
                    }
                }
            }
            .onAppear {
                // async function
                VTData().getDATA(XDOMAIN: "giphy.com") { subs in  // <--- here
                    subDomains = subs
                }
            }
        }
    }