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

Time:10-05

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?

CodePudding user response:

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