Home > Back-end >  SwiftUI NavigationLink cannot find 'json' in scope
SwiftUI NavigationLink cannot find 'json' in scope

Time:01-02

I'm new to SwiftUI and have worked through the server requests and JSON. I now need to programmatically transition to a new view which is where I get stuck with a "Cannot find 'json' in scope" error on the NavigationLink in ContentView.swift. I've watched videos and read articles but nothing quite matches, and everything I try just seems to make things worse.

JSON response from server

{"status":{"errno":0,"errstr":""},
"data":[
{"home_id":1,"name":"Dave's House","timezone":"Australia\/Brisbane"},
{"home_id":2,"name":"Mick's House","timezone":"Australia\/Perth"},
{"home_id":3,"name":"Jim's House","timezone":"Australia\/Melbourne"}
]}

JSON Struct file

import Foundation

struct JSONStructure: Codable {
    struct Status: Codable {
        let errno: Int
        let errstr: String
    }

    struct Home: Codable, Identifiable {
        var id = UUID()
        let home_id: Int
        let name: String
        let timezone: String
    }

    let status: Status
    let data: [Home]
}

ContentView file

import SwiftUI

struct ContentView: View {

    @State private var PushViewAfterAction = false
    
    var body: some View {

        NavigationLink(destination: ListView(json: json.data), isActive: $PushViewAfterAction) {
            EmptyView()
        }.hidden()
        
        Button(action: {
            Task {
                await performAnAction()
            }
        }, label: {
            Text("TEST")
                .padding()
                .frame(maxWidth: .infinity)
                .background(Color.blue.cornerRadius(10))
                .foregroundColor(.white)
                .font(.headline)
        })
        
    }

    func performAnAction() {
        PushViewAfterAction = true
        return
    }

}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

ListView file

import SwiftUI

struct ListView: View {
    
    @State var json: JSONStructure

    var body: some View {

        VStack {
             
            List (self.json.data) { (home) in
                HStack {
                    Text(home.name).bold()
                    Text(home.timezone)
                }
            }
             
        }.onAppear(perform: {
            
            guard let url: URL = URL(string: "https://... ***removed*** ") else {
                print("invalid URL")
                return
            }
             
            var urlRequest: URLRequest = URLRequest(url: url)
            urlRequest.httpMethod = "POST"
            
            URLSession.shared.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
                // check if response is okay
                 
                guard let data = data, error == nil else { // check for fundamental networking error
                    print((error?.localizedDescription)!)
                    return
                }
                            
                let httpResponse = (response as? HTTPURLResponse)!
                            
                if httpResponse.statusCode != 200 { // check for http errors
                    print("httpResponse Error: \(httpResponse.statusCode)")
                    return
                }
                 
                // convert JSON response
                do {
                    self.json = try JSONDecoder().decode(JSONStructure.self, from: data)
                } catch {
                    print(error.localizedDescription)
                    print(String(data: data, encoding: String.Encoding.utf8)!)
                }
                
                print(json)
                
                if (json.status.errno != 0) {
                    print(json.status.errstr)
                }
                
                print("1. \(json.data[0].name)), \(json.data[0].timezone)")
                print("2. \(json.data[1].name)), \(json.data[1].timezone)")
                
            }).resume()
        })
        
    }
}

struct ListView_Previews: PreviewProvider {
    static var previews: some View {
        ListView()
    }
}

I've tried to keep the code to a minimum for clarity.

CodePudding user response:

It's because there is no "json" in ContentView, you need to pass json object to ListView, but since you load json in ListView, then you need to initialize json in ListView like:

struct ListView: View {

    @State var json: JSONStructure = JSONStructure(status: JSONStructure.Status(errno: 0, errstr: ""), data: [JSONStructure.Home(home_id: 0, name: "", timezone: "")])

    var body: some View {

and remove it form NavigationLink in ContentView like:

NavigationLink(destination: ListView(), isActive: $PushViewAfterAction) {

or you could build your JSONStructure to accept optional like:

import Foundation

struct JSONStructure: Codable {
    struct Status: Codable {
        let errno: Int?
        let errstr: String?
        init() {
            errno = nil
            errstr = nil
        }
    }

    struct Home: Codable, Identifiable {
        var id = UUID()
        let home_id: Int?
        let name: String?
        let timezone: String?

        init() {
            home_id = nil
            name = nil
            timezone = nil
        }
    }

    let status: Status?
    let data: [Home]

    init() {
        status = nil
        data = []
    }
  }

but then you need to check for optionals or provide default value like:

struct ListView: View {

@State var json: JSONStructure = JSONStructure()

var body: some View {

    VStack {

        List (self.json.data) { (home) in
            HStack {
                Text(home.name ?? "Could not get name").bold()
                Text(home.timezone ?? "Could not get timeZone")
            }
        }

    }.onAppear(perform: {

        guard let url: URL = URL(string: "https://... ***removed*** ") else {
            print("invalid URL")
            return
        }

        var urlRequest: URLRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"

        URLSession.shared.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
                // check if response is okay

            guard let data = data, error == nil else { // check for fundamental networking error
                print((error?.localizedDescription)!)
                return
            }

            let httpResponse = (response as? HTTPURLResponse)!

            if httpResponse.statusCode != 200 { // check for http errors
                print("httpResponse Error: \(httpResponse.statusCode)")
                return
            }

                // convert JSON response
            do {
                self.json = try JSONDecoder().decode(JSONStructure.self, from: data)
            } catch {
                print(error.localizedDescription)
                print(String(data: data, encoding: String.Encoding.utf8)!)
            }

            print(json)

            if (json.status?.errno != 0) {
                print(json.status?.errstr)
            }

            print("1. \(json.data[0].name)), \(json.data[0].timezone)")
            print("2. \(json.data[1].name)), \(json.data[1].timezone)")

        }).resume()
    })

}

}

  • Related