0

I'd like to load a URL that is string interpolated to fetch a static JSON. I have tried declaring the variables inside the init() but haven't had any luck. I am very new to Swift and SwiftUI, so there is a def a gap in knowledge.

class FetchVersion: ObservableObject {
    @Published var version = String() {
        didSet {
            print("\(self.version) created in VersionView")
        }
    }
    
    init() {
        guard let url = URL(string: "https://ddragon.leagueoflegends.com/realms/na.json") else {
            fatalError("Invalid URL")
        }
        URLSession.shared.dataTask(with: url) {(data, response, error) in
            do {
                if let todoData = data {
                    let decodedData = try JSONDecoder().decode(ClientVersion.self, from: todoData)
                    DispatchQueue.main.async {
                        self.version = decodedData.patch
                    }
                } else {
                    print("No data")
                }
            } catch {
                print("Error")
            }
        }.resume()
    }
}

So the purpose of that class is to decode the JSON mentioned and return the latest client version. That seems to be working fine. The problem is in the next part.

class FetchChampion: ObservableObject {
    @ObservedObject var clientVersion = FetchVersion()
    @Published var datas = [String()] {
        didSet {
            print("\(self.datas.count) champions logged in ChampionData")
        }
    }
    
    init() {
        print(clientVersion.version) // Prints nothing
        var ver : String = "10.15.1"
        var urlString : String {
                return "https://ddragon.leagueoflegends.com/cdn/\(ver)/data/en_US/champion.json"
        }
//        var urlString : String {
//                return "https://ddragon.leagueoflegends.com/cdn/\(self.clientVersion.version)/data/en_US/champion.json"
//        }
        print(urlString)
        let url = URL(string: urlString)!
        URLSession.shared.dataTask(with: url) {(dataGappu, response, error) in
            do {
                if let jsonData = dataGappu {
                    let decodedData = try JSONDecoder().decode(ChampionData.self, from: jsonData)
                    DispatchQueue.main.async {
                        let sortedData = decodedData.data.keys.sorted()
                        self.datas = sortedData
                        //print(self.version)
                    }
                } else {
                    print("No data")
                }
            } catch {
                print("Error")
            }
        }.resume()
    }
    
}

When I try to use the published var from FetchVersion I am unable to use it during the string interpolation when creating urlString, and it prints out the "Error". The init() works fine when I manually define the patch version (i.e., "10.15.1").

I know Swift has 2 phase initialization, but I just don't seem to be able to understand if that is why the code won't work as I need or if I am missing something else.

All criticism and tips would be constructive since I am very new to the language and iOS as a whole.

Thanks!

AK-Vitae
  • 61
  • 2
  • You are doing an asynchronous call in the init of FetchVersion so the data has not been returned yet when you try to access it in the other class. Overall I think it is a bad idea to download data like this in an init method so my suggest is to re-design your solution to avoid it. – Joakim Danielson Jul 28 '20 at 06:14
  • Does this answer your question? [Returning data from async call in Swift function](https://stackoverflow.com/questions/25203556/returning-data-from-async-call-in-swift-function) – Joakim Danielson Jul 28 '20 at 06:15

1 Answers1

0

As @Joakim Danielson mentioned, you're trying to access the data before the request comes through. You're essentially retrieving the init-value of FetchVersion.version, which in you case is String().

If you would like to print the value when it comes through, you could do this with combine as you use @ObservableObject. This subscriber can be created in the init function of FetchChampion. Make sure, however, to store the subscriber in a cancellable.

You can read more about this in the Combine docs. Here's a link to a great tutorial on Combine by Ray Wenderlich.

clientVersion.$version.sink { value in
  print(value)
}.store(in: &cancellables)
Fredrik
  • 971
  • 8
  • 23