Swift is object-oriented!
I was weened long ago on BASIC (Don’t look that up or you may fall into a time vortex from which there is no escape), which flows logically from top to bottom, while Swift (or any modern programming language), flows…?
I’ll put it this way: if BASIC is the lazy river ride at your local water park, Swift is kayaking in a time machine through the Sea of Holes during a monsoon in the middle of a hurricane.
How does non-linear programming work? I understand arrays and if-then statements and loops and switches and functions, reference types vs. value types, views with parent-child relationships, etc. etc. All the parts make sense. But getting them to work together - deciding where things go - understanding when things pop in and out of existence - getting information from where it is to where it needs to be at the time when it’s needed - following asynchronous processes through a tangle of threads… I get tied up in knots.
I’m struggling to think in terms of objects, so that’s what I’ll be writing about for a bit.
Re-Evaluation
This week I came to a milestone in the app I’m working on. After much struggle, I’ve connected quite an assortment of parts and… it works! All the basic features are popping. I still need to tidy some things up, though, before I can turn this in as my final assignment in Udacity’s iOS Dev nano-degree.
So this is a good time for re-evaluation. Over the past two days, I’ve read through my entire body of code - more than 20 files - updating and adding comments and taking notes. I collected a list of 34 to-dos! I think I’ve got a strong foundation to work from, so adding and tweaking those features won’t be too very difficult.
I also took the opportunity to reflect on the big-picture organization of my app. I certainly had a lot of help getting to this point, so I want to make sure I understand how I put it together and why.
Published Property vs. Function Return?
I built several classes that provide information to my main view. In some cases that information is transferred as a property - an array of objects - passed in with an @StateObject wrapper. In other cases the information is supplied as the return value of a function. My question is, why one method over the other? Was I consistent in my thinking? Or was this a random choice each time? Is one method always better than the other? Or does it depend on context?
The act of writing those questions put my brain to work, asynchronously synthesizing information on its background thread. The answers came to me suddenly in the shower. And here they are:
Answers
- Use @StateObject for information that may change behind the scenes, regardless of what’s happening on the screen.
- Use a function return when the information depends on the current view or state and won’t change based on input from elsewhere.
For example, in my app, the EventManager is a class that collects event information from the Apple Calendar App and provides it to the main view. It keeps an array of events as a @Published property. When the user changes an event in the Apple Calendar App, the Event Manager reacts by updating its own array of events. When the view notices the change, via the @StateObject property wrapper, it updates as well. So change propagates from behind the scenes to the main view.
On the other hand, my background view is a complicated color gradient, rendered from an array of color stops. The stops depend on the current timeline, which depends on user interaction via the main view. So the view passes its timeline to the color stops function in my SolarEventsManager, which returns an array of stops for it to render.
Considering the Counter-Factual
To further understand why these are the right choices, I considered the alternatives. What if I tried to use a function to provide events from the EventManager to the main view? When events were changed by the user, the view would be unaware of the changes. It would need to call on the function every second or so just in case changes had occurred. Mostly it would be receiving the same response over and over, which is pretty inefficient.
For the background view, my timeline changes once per second and also whenever the user zooms in or out. To provide the color gradient as an @StateObject, my SolarEventsManager would need to access those changes to the view, then publish the new array, triggering the view to update again. Since the view is already updating every second, this just doesn't seem like a good idea. Would it create an endless loop of updates? I don't know. It seems a lot simpler for the view to grab a new gradient whenever it knows its own changes call for one.
(Maybe I could simplify the background by creating one big gradient, shared via the @StateObject wrapper, then have the view display only the needed portion at any given moment, rather than re-generate a whole new gradient once per second. That’s something to look into later. Copying this to my to-do list!)
To summarize:
Lessons on Learning
- It’s a great learning exercise to go back through your code and make notes.
- Writing down questions can trigger your brain to provide answers; this is a great way to synthesize and solidify what you already know.
- Examine both sides of the coin: Why does it work this way? Why does it not work the other way?
- This kind of analysis can lead to new insights and tightening of your code.
Lessons in Object-Oriented Programming
- Your view should access stored information if that information may change on its own.
- Your view should re-generate information if that information depends on your view.
Let me know what you think. If you're learning Swift, what general principals do you find tricky?