Swift is a modern, multiparadigm language that combines imperative, object-oriented, protocol-oriented, and functional programming. It was developed by Apple and announced in 2014 as a replacement for Objective-C, an object-oriented programming language that has been employed within Apple since 1996 and used to be the only choice for building MacOS and iOS apps. It also seems to be fairly popular. I have a strong interest in Swift. In the past, I’ve written in this blog about its error handling mechanism, how apps use that mechanism, and why it’s become open source. Here I’d like to talk about my thoughts on using Swift as a first programming language, based on my personal experience with 12 programming novices.
Recently, I’ve had the opportunity of being an instructor of a course on app development for the iOS ecosystem as part of the Apple Developer Academy program at UFPE. The course includes classes on coding, design, and business. More information about its syllabus is available here (Portuguese only). One of the distinguishing features of this course is that it is also an Introduction to Programming course. Our students exhibit backgrounds that range from programming novices to last-year, programming contest-level undergrads. By the end of the course, the students organized in 4-person teams are expected to build a working iOS app. The teams are formed so that novices cannot be in the same group as students with programming experience. In this manner, we hope that every programming novice will have to learn how to code, otherwise they won’t be able to finish their apps. Overall, the first instance of the course had 20 students, 4 of them without any prior programming experience. We are currently running the second instance of the course, this time with 8 novices out of 21 students.
Since the focus of this course is on iOS development, we use Swift as our main programming language. The course covers typical programming 101 subjects, such as variables, functions, loops, etc., and also some less often addressed topics (at least in Programming 101 courses), such as optional data types and closures. We start with command-line Swift, no GUIs, and only after more than 20h of classes transition to GUI-based app development. Our reasoning is that this reduces subject conflation at the expense of having to organize the course in two blocks (more on this later).
In the remainder of this post, I discuss some impressions and opinions about our experience of using Swift as a first language, organized in terms of Obstacles and Aids. I’d like to emphasize that this is a personal perspective. At the same time, it also leverages opinions and views of the students themselves and what I and some colleagues observed during the course.
In our course, I believe these were the greatest obstacles to using Swift. I try to stick to issues that (I think) are specific to Swift.
Optionals: In my opinion, optionals are the greatest obstacle to adopting Swift as a first language. Optional types and optional values are not simple concepts and they have to be introduced very early on since they are employed pervasively in Swift and in Apple’s frameworks and libraries. For example, an
Int initializer that takes a
String and produces an
Int (equivalent to C’s
atoi function) yields an optional value. The same goes for the
readLine: method for reading user input from the command line. Unwrapping optionals requires students to learn additional syntax when they are still learning basic Swift constructs. In particular, optional binding is a hurdle for programming novices, since they are still learning to use basic constructs, such as
if statements. In our experience, suggesting that they use forced unwrapping and only afterwards teaching
if-let blocks seems to lower the entry barrier. Another obstacle is understanding the different kinds of optional types (regular and implicitly unwrapped) and unwrapping approaches (forced unwrapping, optional chaining, and optional binding). We believe the best approach is to use regular optional types (the ones including the
? character), since they more explicitly embody the notion of being optional, and forced unwrapping, since it has a lower syntactic and cognitive overhead. More advanced subjects should only be introduced after they have mastered control structures and the two aforementioned subjects. Furthermore, it is difficult for novices to see the difference between an expression producing nothing (
nil) and producing 0 or an empty
String. It is important to provide multiple examples.
There is a lot to learn if students are expected to develop apps. Optional types, closures, and object-oriented programming are all mandatory subjects. In some case, generics also become visible (
Array). Similary, error handling often appears in the form of
rethrows clauses in libraries. This means that a large number of concepts must be grasped in a relatively short amount of time. If the goal is, as in our case, to develop apps, it is also necessary to learn at least the
UIKit framework and, in most practical situation, something else, such as
CloudKit, and other frameworks.
String manipulation could be more direct. Languages such as C, Java, Haskell, Python, and others attempt to treat strings similarly to their most popular collection type (lists in Haskell, lists in Python, arrays in C, arrays+objects in Java). Swift, on the other hand, probably due to its powerful Unicode support, handles strings a bit awkwardly. Even though it is possible to traverse a string very straightforwardly using
for loops, getting the character at the nth position of a string is a bit more work. One cannot simply index a string using a position, as would be possible to do in all the aforementioned languages. For example, in Python, the code to get the 5th character of a string looks like this:
string. In Swift, it looks like this:
string.index (string.startIndex, offsetBy: 4). It may be a bit overwhelming because they have to think about (i) a method named
index, (ii) a property named
startIndex, which is an object of an internal class of
Index, and (iii) the offset (not the position!). This is not a big problem for a programmer, but it hinders the use of simple string manipulation exercises that are commonplace in programming 101 courses. Moreover, if you want to start from an index other than the first one, it is not possible to just add an
Int to and
Index, which is not intuitive, since the an
Index should correspond to a position.
Transition from basic programming to App Development. In spite of these obstacles, Swift is a straightforward language to learn, or at least the students in the course thought so. However, their feedback about app development was consistent: it is hard to make the transition from basic, command-line Swift programming to app development. It is necessary to learn new concepts. Moreover, the app development process is split between coding and building a user interface. The latter part usually involves interface builder, a separate tool whose connection with the code requires links between UI and program elements (
IBActions). Furthermore, program actions are started by the
UIKitframework (because of user interactions) whereas, if we start with command line tools, students feel more in control of what their programs do. We could probably have reduced this obstacle if we started with app development from the outset. However, this could have lead to a different set of problems, since there would be a large conflation of subjects. Maybe it is the right way to go, though.
Swift also has features that make it easier to learn. I use other languages such as Python, Java, C, and Pascal, as a baseline for comparison. These are all languages that I’ve used or seen used as programming 101 languages. I am not claiming that Swift is better or generally easier to learn than these languages. Instead, I think that the feature set listed in this section sets Swift apart. In the remainder of this section I discuss the main aids Swift presents to novice programmers.
Type inference makes programs easier to read and write. I do not think that types such as
Bool, and the overall notion of values having types is difficult to understand. However, not having to write types everywhere makes coding more agile. Swift code looks like it was written in a dynamically type language, like Python, even though Swift is actually statically type. It also postpones the need to introduce the notion that variables also have types, which in my experience is a bit less intuitive.
Playgrounds are a powerful learning resource. Even though REPL-based tools such as Node.js, Python’s IDLE, and JShell (Java’s interpreter, which will appear in Java 9) are very useful for learners, Swift’s Playgrounds go a step further by providing a user-friendly interpreter that makes it easy to examine how loops unfold, the results of each different instruction in a program’s text, and even the graphics that a program produces. Furthermore, Swift playgrounds can be saved for later use and be annotated in a Literate Programming style. This means that they are a very rich learning resource that can be used to write tutorials that developers can experiment with. A great list of interesting Swift playgrounds is available here. I am not familiar with the more advanced tools usually associated with Python and other scripting languages, but Swift playgrounds are the best REPL tool I am aware of by far.
Value-based semantics for assignment. Swift uses both value and reference semantics. Nonetheless, unlike Java, the former is not constrained to values of simple, built-in types, such as
Bool. Arrays, dictionaries, and structs also use value semantics. Thus, it is possible to explain collections and structured data without having to mention pointers or the heap. This differs from Python, for example, which uses a reference semantics for lists. The same goes for Java arrays, which are just objects and thus use a reference semantics. In our prior experiences with introductory programming using Java and Python, by the time we reach arrays (lists in Python), students must have a grasp of both reference and value semantics, which is not ideal.
Structs also reduce the learning curve. This is related to the previous topic. Swift supports both objects, which use a reference semantics, and structs, which employ a value semantics. Since structs can have both fields (“properties”) and functions, they serve as an intermediate step between purely structured programming and object-oriented programming. A struct with state and functions is similar to an object (or a class, if you’re thinking about it at development time), but without subclassing and dynamic dispatch. If we add protocols, we also have dynamic dispatch. In this manner, object-oriented programming can be explained in terms of something the students are already familiar with, and concepts such as inheritance and inclusion polymorphism can be introduced incrementally.
I hope these observations can be useful to other instructors thinking about using Swift as a first programming language. In the next year, there’ll be at least four more instances of this course and, hopefully, a lot a new lessons will arise.