the main issue that I am having is decoding the response data into a
struct / class
Hmmm, looking at your code it’s clear why that’s happening. URLSession tasks are asynchronous. When you call resume the task starts and it doesn’t complete until some time in the future (once it’s got the response off the network). So your control flow runs like this:
-
Someone calls GetShellyProp.
-
It sets shelly_dev to the empty value.
-
It starts a URLSession data task.
-
It returns the empty shelly_dev value.
-
At some point in the future the URLSession data task completes and sets shelly_dev, but no one is looking at that value.
The fundamental problem here is that you can’t return the results of an network request synchronously because the network request might block for long periods of time (minutes!). The fix is twofold:
The first part has a number of standard solutions:
-
You can add a completion handler to getShellyProp(ip:) that you call when it has a final ShellyProperties value.
-
You can use a Combine publisher for this.
-
If you’re not shipping immediately, you can use the new Swift concurrency feature. See WWDC 2021 Session 10192 What‘s new in Swift.
The second part of the fix is trickier because it depends on your app’s UI. If, for example, this network request takes 30 seconds, what do you want your app to be doing in the interim?
Oh, some other general suggestions…
If you’re working in Swift it’s important to follow Swift’s naming conventions. For example:
-
Type names are always InterCaps starting with an uppercase letter (so ShellyProperties).
-
Other identifies are InterCaps starting with a lowercase letter (so getShellyProp(ip:)).
-
And that includes variables (so shellyDev).
For more on this, see the Swift API Design Guidelines.
Also, try to avoid building URLs using string APIS because there are many ways that it can go wrong. A better option is URLComponents. For example:
var uc = URLComponents()
uc.scheme = "http"
uc.path = "/rpc/Shelly.GetInfo"
uc.host = ip
let url = uc.url!
print(url)
// prints: http://1.2.3.4/rpc/Shelly.GetInfo
Or you can use a string literal as a placeholder:
var uc = URLComponents(string: "http://placeholder/rpc/Shelly.GetInfo")!
uc.host = ip
let url = uc.url!
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"