0

End Goal: To have a macOS app to monitor the temperature values from my bluetooth bbq probe, refreshing the data every X mins to keep an eye on it whilst at my desk.

macOS App

JSON structure

{
    "status": "OK",
    "statusCode": 200,
    "data": {
        "devices": [
            {
                "id": "xxxxxxxxxxxxxxxxx",
                "temperature": {
                    "internal": 25.8,
                    "ambient": 25.8
                },
                "cook": {
                    "id": "xxxxxxxxxxxxxxxxx",
                    "name": "Whole Chicken",
                    "state": "Configured",
                    "temperature": {
                        "target": 74,
                        "peak": 25.8
                    },
                    "time": {
                        "elapsed": 232,
                        "remaining": -1
                    }
                },
                "updated_at": 1622816968 
            }
        ]
    },
    "meta": {}
}

ContentView.swift

import SwiftUI

struct ContentView: View {

    @State var probeData = [Meater]()


    var body: some View {
        
        VStack {
            
              HStack{
            
            List{
                Text("Current Cook: \(probeData.Devices.cook.name)")
                    .font(.system(size: 20))
                    .fontWeight(.light)
                    .foregroundColor(.primary)
                
                Text("Target Temparature: \(probeData.Devices.cook.temperature.target) as String")
                    .font(.callout)
                    .fontWeight(.light)
                    .foregroundColor(.primary)
                Text("Internal Temperature: \(probeData.Devices.cook.temperature.internal) as String")
                    .font(.callout)
                    .fontWeight(.light)
                    .foregroundColor(.primary)   
                Text("Ambient Temperature: \(probeData.Devices.cook.temperature.ambient) as String")
                    .font(.callout)
                        .fontWeight(.light)
                        .foregroundColor(.primary)                      
            }

Text("Time Elapsed: \(probeData.Devices.cook.time.elapsed)")
                        .font(.system(size: 20))
                        .fontWeight(.light)
                        .foregroundColor(.primary)
                    
                    Text("Time Remaining: \(probeData.Devices.cook.time.remaining)")
                        .font(.callout)
                        .fontWeight(.light)
                        .foregroundColor(.primary)
          
        }
        }
    }

    //MARK: - Web Service

    func loadData() {
        
        let url = URL(string: "https://xxxxxxxx")
               var request = URLRequest(url: url!)
               request.httpMethod = "GET"
            request.addValue("Bearer xxxxxxxxxxxxxx", forHTTPHeaderField: "Authorization")

               let task = URLSession.shared.dataTask(with: request) { data, response, error in
               
                do {
                    if let d = data {
                        let decodedLists = try JSONDecoder().decode(Meater.self, from: d)

                       
                        DispatchQueue.main.async {

                            self.probeData = [decodedLists]
                            print(probeData)
                        }
                    }else {
                        print("No Data")
                    }
                }catch DecodingError.keyNotFound(let key, let context) {
                    Swift.print("could not find key \(key) in JSON: \(context.debugDescription)")
                } catch DecodingError.valueNotFound(let type, let context) {
                    Swift.print("could not find type \(type) in JSON: \(context.debugDescription)")
                } catch DecodingError.typeMismatch(let type, let context) {
                    Swift.print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
                } catch DecodingError.dataCorrupted(let context) {
                    Swift.print("data found to be corrupted in JSON: \(context.debugDescription)")
                } catch let error as NSError {
                    NSLog("Error in read(from:ofType:) domain= \(error.domain), description= \(error.localizedDescription)")
                }
               }
               task.resume()

    }

}

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

// MARK: - Data Model

struct Meater: Codable {
  struct Data: Codable {
    struct Devices: Codable {
      struct Temperature: Codable {
        let `internal`: Double
        let ambient: Double
      }

      struct Cook: Codable {
        struct Temperature: Codable {
          let target: Int
          let peak: Double
        }

        struct Time: Codable {
          let elapsed: Int
          let remaining: Int
        }

        let id: String
        let name: String
        let state: String
        let temperature: Temperature
        let time: Time
      }

      let id: String
      let temperature: Temperature
      let cook: Cook
      let updatedAt: Date

      private enum CodingKeys: String, CodingKey {
        case id
        case temperature
        case cook
        case updatedAt = "updated_at"
      }
    }

    let devices: [Devices]
  }

  struct Meta: Codable {
  }

  let status: String
  let statusCode: Int
  let data: Data
  let meta: Meta
}

Debugger seems to print the correct info from probeData but can't seem to work out why the values aren't displaying.

any pointers in the right direction would be appreciated. Thank you

4
  • You seem to have two questions here: how to display the data and how to use a timer. For the first one, you haven't provided any information on how you want that data displayed. For the second, there are plenty of resources that are easily searchable about using a Timer in Swift. What have you tried so far? Commented Jun 4, 2021 at 15:34
  • Apologies, yes my head was thinking 1 step forward without solving the now, I have amended the question slightly to remove the Timer bit, focus on getting the data showing. Commented Jun 4, 2021 at 15:46
  • It doesn't appear that you have -- no edits are showing to the original post. Commented Jun 4, 2021 at 15:50
  • Just made the edits sorry I couldn't get the formatting correctly Commented Jun 4, 2021 at 15:58

1 Answer 1

1

A few things had to change in order to get this to compile and work:

  1. probeData shouldn't be an Array -- it should be an optional property
  2. That means when you decode, you shouldn't put it inside [ ]
  3. Then, in your list, you have to address each item of the devices property (see the ForEach)
struct ContentView: View {
    
    @State var probeData : Meater? //<-- Here
    
    var body: some View {
        VStack {
            HStack{
                List {
                    ForEach(probeData?.data.devices ?? [], id: \.id) { item in //<-- Here
                        Text("Current Cook: \(item.cook.name)")
                            .font(.system(size: 20))
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                        
                        Text("Target Temparature: \(item.cook.temperature.target)")
                            .font(.callout)
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                        Text("Internal Temperature: \(item.temperature.internal)")
                            .font(.callout)
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                        Text("Ambient Temperature: \(item.temperature.ambient)")
                            .font(.callout)
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                        
                        Text("Time Elapsed: \(item.cook.time.elapsed)")
                            .font(.system(size: 20))
                            .fontWeight(.light)
                            .foregroundColor(.primary)
        
                        Text("Time Remaining: \(item.cook.time.remaining)")
                            .font(.callout)
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                    }
                }
            }
        }
    }
    
    //MARK: - Web Service
    
    func loadData() {
        
        let url = URL(string: "https://xxxxxxxx")
        var request = URLRequest(url: url!)
        request.httpMethod = "GET"
        request.addValue("Bearer xxxxxxxxxxxxxx", forHTTPHeaderField: "Authorization")
        
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            
            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode(Meater.self, from: d)
                    
                    
                    DispatchQueue.main.async {
                        self.probeData = decodedLists //<-- Here
                        print(probeData)
                    }
                }else {
                    print("No Data")
                }
            }catch DecodingError.keyNotFound(let key, let context) {
                Swift.print("could not find key \(key) in JSON: \(context.debugDescription)")
            } catch DecodingError.valueNotFound(let type, let context) {
                Swift.print("could not find type \(type) in JSON: \(context.debugDescription)")
            } catch DecodingError.typeMismatch(let type, let context) {
                Swift.print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
            } catch DecodingError.dataCorrupted(let context) {
                Swift.print("data found to be corrupted in JSON: \(context.debugDescription)")
            } catch let error as NSError {
                NSLog("Error in read(from:ofType:) domain= \(error.domain), description= \(error.localizedDescription)")
            }
        }
        task.resume()
        
    }
    
}

In general, I'd also suggest moving your loadData function to an ObservableObject view model -- doing async work in a View in SwiftUI can be potentially unstable if the view reloads during an async call.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much for the explanation, just added it and worked perfectly, needs a little tweaking but it’s displaying now. I looked up Timer in swift and now have that updating the view every 30seconds.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.