5 things I wish I knew sooner about Swift
The places where Swift isn’t so intuitive.
It usually is. Intuitive, that is. Usually Swift is intuitive, with optionals and drag-and-drop kind of building of GUI elements.
But other times it isn’t intuitive at all. It redefines keywords from popular languages like
Python , and the division between Storyboards and code can be incredibly confusing.
Here are 5 places that confused me when I started out. We will use Swift 5 in Xcode 11.5 using Storyboards — note that SwiftUI redefines the wheel again, but maybe for the better.
- Classes persist, structs do not
- Segues and unwind segues are done differently
- Protocol and delegate magic
- Prevent inheritance with final
- Public, internal, fileprivate and private, but no protected
1. Classes persist, structs do not
Never mind what
C++ has already established about the meanings of classes and structs (structs are classes with no private members). Just forget everything that already exists and learn it again — the
Swift, the key difference is: structs are passed by value, while classes are passed by reference.
Let’s take a look at an example using
This fails to compile in the method that modifies the struct, because it is passed by value, and therefore cannot be modified:
class, on the other hand, works just fine:
2. Segues and unwind segues are done differently
- The first step in defining a normal segue is done in the storyboard.
- The first step in defining an unwind segues is done in code.
Let’s start by creating two view controllers:
MainViewControllerhas a simple message text field and a button.
SetDataViewControllerhas a text field and a button.
Let’s hook it up such that pressing the button goes to the
SetDataViewController . Click and drag from the button to the view controller to create a segue.
We can now name the segue:
And then add additional logic in the navigation section in
That works for the forward segue —but what about an unwind segue to go back?
To unwind, you start in the code. In the destination view controller
MainViewController, add a
Then in the storyboard, hook up the unwind segue in the
SetDataViewController . Click and drag from the button to the
Exit at the top:
and select the
You can find the segue in the left hand side, and give it a name like
returnedSegue as before:
- For normal segues, first add the segue in the Storyboard. You can add an identity and modify it by name later in the code.
- For unwind segues, first add the unwind function in the code in the destination view controller, then hook it up in the Storyboard.
3. Protocol and delegate magic
Very frequently you will want to call a method of one view controller from another view controller. This is best done using protocols and delegates. If you are used to the idea of abstract base classes in
protocols are like an interface that a class promises to implement.
Let’s use the example from the last section.
- First define a new protocol called
MainProtocol, which declares a method
handleData. It inherites from
AnyObjectsuch that only classes can inherit from it.
2. Then let
MainViewController inherit from
MainProtocol , and add an implementation for
3. Add an
SetDataViewController , and call it in the navigation code for the
returnedSegue. Note that it must be weak to break the strong reference cycle (if you don’t use
weak, the reference counts of the view controllers do not reach zero, so they don’t get deallocated).
4. Finally, hook up the
delegate in the
This is the most general and powerful method for calling methods in different view controllers and passing data back and forth. The final
MainViewController looks like this:
4. Prevent inheritance with final
By default, you can inherit from any class you wish. The
open keyword makes this explicit (but redundant):
The console will show
Goodbye . We can change
final to prevent inheritance:
This will prevent inheritance:
You can also apply it to a method rather than a whole class:
This allows inheritance but prevents
override of the
final you can control closely how your classes can be reused.
5. Public, internal, fileprivate and private, but no protected
Python you may be used to
import statements, or
C++ to include headers defining different methods.
Well it turns out in
Swift , by default everything is just
internal p̵u̵b̵l̵i̵c̵, meaning every class/method you define is available from any other file in your project (although not outside a module). Obviously, this is almost never what you want!
Even if you don’t care about it explicitly, note that leaving everything as internal increases compilation time. So it’s worthwhile using the other access levels appropriately:
fileprivate , and
publico̵r̵ ̵n̵o̵t̵ ̵g̵i̵v̵i̵n̵g̵ ̵a̵n̵y̵ ̵a̵c̵c̵e̵s̵s̵ ̵l̵e̵v̵e̵l means the method/class is available from anywhere in the project.
internalor not giving any access level means things can only be used from within the same module.
fileprivatemeans that things can only be used from within the same file.
privatemeans you can only access it from within the same class you defined it in.
private also has the usual meaning from
C++ for classes, in that subclasses cannot access
If you are wondering what the equivalent of the
protected keyword from
C++ is (
protected attributes can only be accessed from an inherited class): there is none. It’s just
private because…. Yep.
Yes, Swift has lots of nice features and makes iOS programming like a breeze. Yes, it feels like Swift reinvented the wheel sometimes, and redefines keywords you already know. Yes, simple things can be so unintuitive between code and storyboards.
And yes, I wish I had known these things sooner.