0

I don't know why there's nil on the JSON structure. Below is the code. The structure is relatively simple, but somehow I just can't parse the decoded JSON.

ContentView.swift

import SwiftUI
var fetchTotal = getTotalValue()
struct ContentView: View {

var body: some View {
   Text(fetchTotal)
    //Text("gg")
   }
}

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

Decoded_structure.swift:

import Foundation
import SwiftUI

struct PostTotal: Codable {
var rows: [Layer0]
struct Layer0: Codable, Hashable {

    var totalmarketvalue: String
    var totaltodaysprofit: String
    var todaysprofitpercent: String
    var totalasset: String
    var maxpurchasepower: String

  }
}
   let webstringtotal: String = "http://gsx2json.com/api?id=1z9WvS25hDvLaNPX1HwTE-CiJ46UvmqlUWT-iWVT0aJY&sheet=1&integers=false&column=false"


 func getTotalValue() -> String {
 let urlTotal = URL(string: webstringtotal)
 var results: String = ""
    URLSession.shared.dataTask(with: urlTotal!) { (datas, _, _) in
        let postss = try! JSONDecoder().decode([PostTotal.Layer0].self, from: datas!)
        //let layer0 = [postss.rows]
        results = postss[0].totalasset
}
.resume()
return results

}

And the error message:

2020-06-11 15:51:46.211463+0800 PureTestProject[28884:988078] [Agent] Received remote injection
2020-06-11 15:51:46.211753+0800 PureTestProject[28884:988078] [Agent] Create remote injection Mach transport: 600001500770
2020-06-11 15:51:46.212150+0800 PureTestProject[28884:988022] [Agent] No global connection handler, using shared user agent
2020-06-11 15:51:46.212339+0800 PureTestProject[28884:988022] [Agent] Received connection, creating agent
2020-06-11 15:51:46.897118+0800 PureTestProject[28884:988022] [Agent] Received message: < DTXMessage 0x600001a150e0 : i2.0e c0 object:(__NSDictionaryI*) {
    "products" : <NSArray 0x600003c141b0 | 1 objects>
    "id" : [0]
    "scaleFactorHint" : [3]
    "providerName" : "15PureTestProject20ContentView_PreviewsV"
    "updates" : <NSArray 0x7fff8062d430 | 0 objects>
} > {
    "serviceCommand" : "forwardMessage"
    "type" : "display"
}
2020-06-11 15:51:46.938191+0800 PureTestProject[28884:988035] App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
2020-06-11 15:51:46.938349+0800 PureTestProject[28884:988035] Cannot start load of Task <EF8F8A3E-E1FC-4E80-808A-1B576BBCDBD6>.<1> since it does not conform to ATS policy
2020-06-11 15:51:46.940051+0800 PureTestProject[28884:988033] Task <EF8F8A3E-E1FC-4E80-808A-1B576BBCDBD6>.<1> finished with error [-1022] Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSUnderlyingError=0x6000030045d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1022 "(null)"}, NSErrorFailingURLStringKey=http://gsx2json.com/api?id=1z9WvS25hDvLaNPX1HwTE-CiJ46UvmqlUWT-iWVT0aJY&sheet=1&integers=false&column=false, NSErrorFailingURLKey=http://gsx2json.com/api?id=1z9WvS25hDvLaNPX1HwTE-CiJ46UvmqlUWT-iWVT0aJY&sheet=1&integers=false&column=false, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.}
Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/benny/temp_files/test_xcode_11/PureTestProject/PureTestProject/Decoded_struct.swift, line 31
2020-06-11 15:51:46.957521+0800 PureTestProject[28884:988033] Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/benny/temp_files/test_xcode_11/PureTestProject/PureTestProject/Decoded_struct.swift, line 31
    (lldb)
Aleksey Potapov
  • 3,683
  • 5
  • 42
  • 65
Benny Chen
  • 27
  • 1
  • 7
  • 2
    "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection". That's the main reason. Read the logs. `datas!` that's the one making the crash. Avoid using force unwrap (ie `!`) if you don't understand them. Or at least, unwrap them line by line to understand which one failed, and then causes the crash. – Larme Jun 11 '20 at 08:01

1 Answers1

2

the error says that you are connecting using http:// . Apple does not like this, it expect https:// . But you can provide an exception in the info.plist to get around this. Because of this your "datas" is nil, and as mentioned you are trying to unwrap it, triggering the error message.

Here is one example to put into info.plist:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
    <key>example.com</key>
    <dict>
        <key>NSExceptionAllowsInsecureHTTPLoads</key>
        <true/>
        <key>NSIncludesSubdomains</key>
        <true/>
    </dict>
</dict>
</dict>

Edit: the code I use for testing, updated with an async example:

import Foundation
import SwiftUI

struct ContentView: View {
@State var results: PostTotal?

var body: some View {
    VStack (alignment: .leading) {
        Text("totalasset: \(results?.rows[0].totalasset ?? " fetching data ...") ")
        Text("maxpurchasepower: \(results?.rows[0].maxpurchasepower ?? " fetching data ..." )")
    }.onAppear(perform: loadData)
}

func loadData() {
    getTotalValue() { res in
        self.results = res
    }
}
}

struct PostTotal: Codable {
var columns: Layer1
var rows: [Layer0]

 struct Layer0: Codable, Hashable {
     var totalmarketvalue: String
     var totaltodaysprofit: String
     var todaysprofitpercent: String
     var totalasset: String
     var maxpurchasepower: String
 }

struct Layer1: Codable, Hashable {
    var totalmarketvalue: [String]
    var totaltodaysprofit: [String]
    var todaysprofitpercent: [String]
    var totalasset: [String]
    var maxpurchasepower:[String]
}
}

let webstringtotal: String = "http://gsx2json.com/api?id=1z9WvS25hDvLaNPX1HwTE-CiJ46UvmqlUWT-iWVT0aJY&sheet=1&integers=false&column=false"

func getTotalValue(handler: @escaping (PostTotal?) -> Void) {
let urlTotal = URL(string: webstringtotal)
URLSession.shared.dataTask(with: urlTotal!) { (datas, _, _) in
    if let theData = datas, let postss = try? JSONDecoder().decode(PostTotal.self, from: theData) {
        return handler(postss)
    } else {
        return handler(nil)
    }
}
.resume()
}
  • Thanks a lot ! It seems that the "found nil" problem is gone, but I got another complain Thread 3: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil)) – Benny Chen Jun 11 '20 at 08:58
  • look at the json, it starts with "{"columns":{ ... but your PostTotal struct does not have that – workingdog support Ukraine Jun 11 '20 at 10:07
  • I've updated my answer to show the test code I'm using. – workingdog support Ukraine Jun 11 '20 at 10:32
  • Thanks a lot! Finally, I get rid of the "found nil" problem and it did return the correct value. But somehow I just can't see the value in ContentView .... – Benny Chen Jun 11 '20 at 12:57
  • well that understandable. getTotalValue() is an asynchronous function. You need to wait till it has finished before you can use the results. The way you are using getTotalValue() is not going to work. Study up on asynchronous coding. Best of luck. – workingdog support Ukraine Jun 11 '20 at 13:21
  • I've updated the answer with an async example code. – workingdog support Ukraine Jun 11 '20 at 13:50
  • No error found ! But I admit the resolution is far beyond my level of understanding ^_^. The Text View shows a whole bunch of JSON data. How can I fetch say totalasset, maxpurchasepower in 2 Text Views? – Benny Chen Jun 11 '20 at 14:38
  • I've updated the answer again to show how to display some info. You will have to figure out how you want to display the results the way you want it to appear using your own code. All the best. – workingdog support Ukraine Jun 11 '20 at 22:40
  • The code fetched correct information ! Thanks for your kind help. I will do the rest. – Benny Chen Jun 12 '20 at 01:08
  • I implement this code to a more complex UI which contains another ForEach List View with data fetched from web api. But one error found that state "Cannot assign value of type 'PostTotal?' to type 'PostTotal?.Type'". Looks like the the type of '@State var results' to be PostTotal?Type instead of PostToal? in the worked simpler project. – Benny Chen Jun 12 '20 at 03:36
  • I also have another question, tape of 'results.rows[0].totalasset' is String, why in the Text View I have to use '\(...)" -- escaping string? I use 'Text(results.rows[0].totalasset)' but the compiler complains... – Benny Chen Jun 12 '20 at 14:34