I am trying to get a variable from the function that represents the boolean status of the charge_port_door_open variable, something like this: let ChargePortStatus = (DATA.response.charge_state.charge_port_door_open) The function does print a correct boolean, however, 'DATA' cannot be found in scope outside the function.
Edit: Thank you all very much for the help! This is my first question posted on this website and I wasnt expecting for help to arrive so quickly!
import Foundation
import UIKit
struct Root: Codable {
let response: TEST1
}
struct TEST1: Codable {
let charge_state: TEST2
}
struct TEST2: Codable {
let charge_port_door_open: Bool
}
public func RequestVehicleData() {
let url = URL(string: "https://owner-api.teslamotors.com/api/1/vehicles/:id/vehicle_data")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue( "Bearer \(token)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
let DATA = try! JSONDecoder().decode(Root.self, from: data!)
print(DATA.response.charge_state.charge_port_door_open)
}
task.resume()
}
CodePudding user response:
Since dataTask
is asynchronous, you have to modify your method to return a value in a closure. A simple version without any error handling can look like this:
public func requestVehicleData(completion: @escaping ((Bool?) -> Void) {
let url = URL(string: "https://owner-api.teslamotors.com/api/1/vehicles/:id/vehicle_data")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue( "Bearer \(token)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let responseData = data, let response = try? JSONDecoder().decode(Root.self, from: responseData) {
completion(response.response.charge_state.charge_port_door_open)
} else {
completion(nil)
}
}
task.resume()
}
You can also play around with the Swift Concurrency and user new async/await
API for the same purpose.
P.S. When you have a chance, take a look at some basics and API Design Guideline.
CodePudding user response:
Two simple approaches (if you don't want the call-site to have to use a completion block - which is also a perfectly good solution described in Maksym's answer) are..
perform any actions you need to perform with the value inside of the closure
create an optional property with a
didSet
on whatever ownsRequestVehicleData
. That way you can set the value within the closure and trigger the desired actions. An example for this would be:class VehicleDataManager { private var root: Root? { didSet { doSomething(with: root) } } public func RequestVehicleData() { let url = URL(string: "https://owner-api.teslamotors.com/api/1/vehicles/:id/vehicle_data")! var request = URLRequest(url: url) request.httpMethod = "GET" request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue( "Bearer \(token)", forHTTPHeaderField: "Authorization") let task = URLSession.shared.dataTask(with: request) { data, response, error in root = try! JSONDecoder().decode(Root.self, from: data!) print(DATA.response.charge_state.charge_port_door_open) } task.resume() } }
The value is fetched asynchronously, so it will only be safely accessible in places that are guaranteed to get run after it has been returned.
Side note: I know this might have just been for the sake of a "quick and dirty" test function, but I would highly recommend not getting in the habit of force-unwrapping values that very well could be nil