SwiftUI NavigationLink 在范围内找不到“json”

Posted

技术标签:

【中文标题】SwiftUI NavigationLink 在范围内找不到“json”【英文标题】:SwiftUI NavigationLink cannot find 'json' in scope 【发布时间】:2022-01-01 03:05:52 【问题描述】:

我是 SwiftUI 的新手,并且已经处理过服务器请求和 JSON。我现在需要以编程方式转换到一个新视图,这是我在 ContentView.swift 的 NavigationLink 上遇到“无法在范围内找到 'json'”错误的地方。我看过视频和阅读文章,但没有什么完全符合的,而且我尝试的一切似乎都让事情变得更糟。

来自服务器的 JSON 响应

"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 结构文件

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]

内容视图文件

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()
    

列表视图文件

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()
    

为了清晰起见,我尽量将代码保持在最低限度。

【问题讨论】:

【参考方案1】:

这是因为ContentView中没有“json”,所以需要将json对象传递给ListView,但是既然在ListView中加载了json,那么就需要在ListView中初始化json如:

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 

并将其从 ContentView 中的 NavigationLink 中删除,例如:

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

或者您可以构建您的 JSONStructure 以接受可选的,例如:

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 = []
    
  

但是您需要检查选项或提供默认值,例如:

结构列表视图:查看

@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()
    )


【讨论】:

谢谢穆斯塔法。第一个选项似乎是最简单的,并且在成功构建代码和查看转换工作方面效果很好。但是,当我返回数据数组中的数据时,更改破坏了 JSONDecoder,我无法弄清楚。 数据丢失,无法读取。 "status":"errno":1,"errstr":"Test message","data":["home_id":1,"name":"Dave's House","timezone":"Australia\ /布里斯班","home_id":2,"name":"Mick's House","timezone":"澳大利亚\/珀斯","home_id":3,"name":"Jim's House"," timezone":"Australia\/Melbourne"] 打印 JSON JSONStructure(status: jsontest.JSONStructure.Status(errno: 0, errstr: ""), data: [jsontest.JSONStructure.Home(id: 42D7B889-46F4-40C8 -8E2D-C740CBE26751, home_id: 0, name: "", timezone: "")]) @Mick 将 print(error.localizedDescription) 替换为 print(error)。这显示了一条详细的错误消息,即 JSON 中没有键 id 替换 Home 谢谢@vadian 我现在更好地理解了可识别性,并更改了 mysql 给我一个“id”,直到我更好地掌握 SwiftUI。现在一切正常。 不客气@Mick,对不起,我忘了告诉你id,你要么从服务器提供它,要么在解码时使用 UUID()(这就是我所做的,因为我不这样做拥有我正在使用的服务器)

以上是关于SwiftUI NavigationLink 在范围内找不到“json”的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:NavigationLink 奇怪的行为

SwiftUI 上的 NavigationLink 两次推送视图

SwiftUI:NavigationLink 内的水平 ScrollView 会中断导航

有没有办法向 NavigationLink 添加额外的功能? SwiftUI

无法单击列表中的 NavigationLink (SwiftUI)

需要帮助使 NavigationLink 在 SwiftUI 中工作