So, waking up and reaching for my freshly set up iPhone 15, I realised my SuperConnector app wasn’t working. Thats an irritating thing with developer-side-loading your app and not going through the Testflight alpha/beta dance. Still, it reminded me there were still things to do with it.
And if I rotate my phone…
And if I rotate my phone it all goes to poo. I was really ignoring this, but basically I left it to SwiftUI to figure out what was going on and it went “Dunno, want a HUGE icon, cos this is how you get a huge icon”. And it was unusable in landscape.
Browsing some tutorials (Good old Hacking with Swift) pointed the way, first showing there’s an environment value which changes according to the orientation of the device. All you had to do was add this at the start of a SwiftUI view:
@Environment(\.horizontalSizeClass) var horizontalSizeClass
Then when it came to the body of the view, add:
let layout = horizontalSizeClass == .regular ? AnyLayout(HStackLayout()) : AnyLayout(VStackLayout())
And this bit of magic uses AnyLayout to make the Horizontal Stack Layout and the Vertical Stack Layout interchangeable. And here it uses the horizontalSizeClass
to decide which. But then how to put that into action…
Literally put layout
where your may have had a HStack or VStack in your SwiftUI code. In my code there were two boxy objects which would be on top of each other vertically and besides each other when in landscape….
Inside the main view, there’s a bit like this:
VStack {
layout {
Button() {
...
} label: {
...
}
List {
...
}
}
That layout variable directly influences how the UI lays out. This is where SwiftUI is different to so may other GUI builders; you are declaring the UI in active code. It’s rather neat though it involves a lot of programmatic thinking to lay out your UI.
Waiting to find the radio
Previously, I gave the app ten seconds to do some SSDP discovery to find the SuperConnect radio on the network. But as time went on this has got irritating. The solution was simple on paper; when the app has scanned and found a radio, save the value and persist it to storage. When the app starts up next time, if the value is set, use that value.
Oh great, lots of fiddling with file writing and managing variable state and parsing values and… no? Look at this:
@AppStorage("lastfoundip") var lastfoundip=""
There. We’re done. That makes a thing called lastfoundip
in the UserDefaults for the app, and then the declaration is used to define the variable in use.
Now, we just change when we scan for the address and…
if lastfoundip=="" {
while(superDiscovery.address=="") {
sleep(1)
}
lastfoundip=superDiscovery.address
}
setIpaddress(address:lastfoundip)
That’s it. That’s the code. Reading and writing lastfoundip
all happens transparently and well, that’s some sweet time saving.
Now the app just snaps into activity as soon as it launched.
If the radio IP moved between runs, maybe there’d be a problem but I’ll leave that till I (a) move the radio or (b) SuperConnector gets um, another user. It’s lonely in the niche maker corner.