Swift for Programmers

Running it

Running from XCode is much too slow if you have a laptop. You can create a new Swift project, type, and see very detailed pop-up error messages. These give very helpful syntax errors and interesting warnings.

But XCode takes minutes to compile even the simplest code, even if you use the simulator (this took me days to figure out: the dropdown always tries to run on a physical device. Only the "Forward" button, off to the side, runs a simulator).

The best way to run Swift is to download it and run from shell.You already have Swift with XCode. This other download is still required, but I think it merely completes the final link.

Command-line swift take 30 seconds to start. You can't import simple written files -- cut and paste is all you get. You're allowed to redeclare (it overwrites).

Overview

General things to know: 1) Semi-colons are optional. 2) Parens around if and loop conditions are optional. But curly-braces for bodies are non-optional. 3) An underscore, all by itself, is the "don't care" symbol (used in various contexts).

The internal strings "evaluate me" is \(evalMe). For example: "x is \(x) and cow weighs \(4*x) pounds"

It's funny about spacing. Assignment operators must have the same spacing on either side (n=5 of n = 5, but not n= 5). Some uses of = simply require spaces. The ?: operator requires spaces (b?1:2 is an error. b ? 1 : 2 is fine). The problem is ! and ? have extra meanings.

Variables

The type goes on the end, after a colon, where you can easily leave it off. The required keyword is a leading var or let (for a constant):

var a : Int
var a = 4 // inferred to be Int

All types start with caps: Int, Float, Double, String, Bool

Character literals use double-quotes. They're inferred as chars: var a : Char ="x". Number literals don't have a type: 3/4 is 0, but 3/4+0.0 is 0.75. The actual rule is that literals never have types -- they have possible types which are inferred from context.

You're strongly encouraged to use let for constants. For pointers, it's a const pointer to non-const data:

let c1 : Cat = Cat(7) // c1 is constant pointer to non-const Cat
c1.age=12; // this is fine
c1 = c2; // error - let is const pointer

variable/operator misc

Strings also use variable-sized characters (for the longer unicodes). You can't use an index and are expected to step through, using built-ins to check the sizes as you go.

Functions, pt-1

func is in front. Return type is at the back: ex:->Int. Use () for void. Parameters look like (n:Int, w:String).

Function calls need to add the parameter name:

doStuff(5,"earl") // error
doStuff(n:5, name:"earl") // need to add actual parm names in front

func doStuff(n:Int, name:String)->() { ... } 

Function prototypes are written with the names, but not the types, and with colons. doStuff(n:name:) or doStuff(_:_:) (which we'll see later). Seriously. You'll see max(x:y:) written with a straight face.

Required names are sometimes use to distinguish overloads. For example W.append("hat") and W.append(contentsOf:["sok","fur]) (the name is required).

There are two opposite ways to modify call-with-name. In front of any parm: underscore eliminates it, or a second name replaces it:

func doStuff(_ n:Int, _ name:String)->() {...} // underscore = no names
doStuff(5,"earl") // now this is legal

func doStuff(num n:Int, buddy name:String)->() {...} // alternate name for caller
doStuff(num:5, buddy:"earl") // must use alternate names

All parameters are constants. The error tells you they are "let"'s. I gotta say, this is an OK rule. Functions tweaking their input parms cause more errors than they save time.

Call-by-reference uses inout before the type, and & in the call:

func add5(_ n : inout Int) { n+=5 }

There a super-oddball "catch" for functions, designed to fix problems caused by return-from-middle: defer { ... } runs it's body just before leaving the function:

defer { print("I run after any return, esp early returns") }

// or for loops with break/continue:
while i<10 {
  defer { i+=1 } // fake the semantics of a FOR loop
    ...
}

nullable types / "optional"

All types can be modified so they can be null, or so they can never be null. No decoration can't be: var n : Int can't be nil, which is what we'd expect.

The weird thing is var c : Cat also can't be nil. It's a pointer, but assigning nil is an error. c = nil won't compile, and c = getCat() crashes immediately if it returns nil.

This sort-of lets you pretend classes are value types, and allows you to distinguish real pointers:

var c1 : Cat = Cat() // we just want two Cat, conceptually and ...
var c2 : Cat = Cat() // ...syntactically, c1 and c are not pointers
vat currentCat : Cat? = c1 // an actual pointer

var Barn : [Cat] // basic Cat-array can't have null-Cats
var Guards : [Cat?] = [nil,nil,nil] // but I can be missing some guard-cats

If you're used to C++, this fixes the mashing problem with reference types. Use Cat when you wanted stack/automatic data, and Cat? for the few times you actually wanted a Cat* pointer.

But note: no matter what, classes always use pointers. c1=c2 is always aiming a pointer, no matter Cat or Cat?. Likewise value types are never pointers. You can never have int's p1 and p2 pointing to the same int. p1=p2 is always a value copy.

Using ? or ! after the type allows nil. The difference is syntax. ? is preferred, and requires a fake dereference, which does nothing except to flag risky code:

var num : Int? = 8
a = num! + 1 // the ! after num is a warning of a possible nil

var c : Cat?
num = c!.age // the ! warns that c could be nil 

The main use is a warning: barn!.Cats![0]!.tail!.len has 4 possible nil pointers. A ! after len would let us know the the tail might exist but the length is allowed to be undefined

Value types use ? simply to flag undefined. Often functions return Int? to allow nil for failure

var n : Int? = A.first // list might be empty. first returns an Int?
if (n < 5) ... // error. Use n! < 5
//  ... then see the ! and realize you should check for nil

Note that value types are never pointers. The misleading term for n! is unwrapping. But this is not not boxing.

With classes, ! isn't like a real dereference. ca! = cb! feels like it should copy the contents of cb into ca, but doesn't. It's a decoration with no functional use.

Cat! is the other, rarely used, nilble. It allows cats.age style use. It's a hack for when a variable logically can't be nil, so you want to make it look like a non-nillable, but it really could be for small periods.

var cowButton : Button!  = nil // logically can't be nil, but for real it can

// this will absolutely set cowButton to 
func init() { if (...) { ... cowButton=getButton(...) }
 ...
 // no need for the ! alert, since cowButton can now never be nil:
 cowButton.text = "cat"

Assignment-wise, Cat? and Cat! are interchangeable. The only difference is the required, or not, decorations. You can have a Cat! and use standard Cat?-returning functions.

If's and if shortcuts

If's have optional ()'s around the test, and require {}'s around the body, but are otherwise normal:

if n>=0 && n<=10 { print("\(n) is in range") }

Nil-fixing special ifs

Binary ?? is meant to give a value for a possible nil. The result is a non-nilable. The first input is something that might be nil:

var ptr : Int?
n = ptr ?? = -99

if (ptr != nil ) {n = ptr!} else n = -99 // long version

Notice how there's no required ! after ptr. The idea is ! signals a possible problem, but here we have no problem. So the syntax has you leave it off. In fact ptr! ?? -99 is useless: if ptr were nil, the first part would crash.

The second doesn't have to be a non-nillable, since you can chain them. The last should be a non-nillable, but doesn't have to be:

n = ptr1 ?? ptr2 ?? 0 // attempt assign in order, 0 is backup val

That trick checks for nil. This next trick checks for errors from using a nil. Change c!.age to c?.age and it aborts on an error, returning nil:

ptr = c1?.tail?.style?.length; // no errors, instead ptr assigned nil

This can be used together with the nil-fixing ?? trick. If anything fails here, we get -99:

n =  c1?.tail?.style?.length ?? -99

This trick only fixes/replaces in that one spot. Suppose you have c1.tail.style?.length. If c1 and tail are non-nillables, this is safe. If they were Cat! (Java style), this could still crash on nils.

Trick #3, a let (not a var, only a let) can have an implicit test for nil in front of a body. As you'd guess, the variable has scope only in the body:

if let a = c1?.age { total += a }

// multiple variables:
if let a1=c1?.age, a2=c2?.age { total += a1+a2 }

if let a = c1?.age, a>=0 { total += a } // add if age exists and is >0

That last one is just silly -- use a normal if.

Guard is an over-specialized let-like, whose job is compute a value and quit on null. The test is reversed, it has only an else-body, which is required to have return/continue/break.

guard let ca = c1?.age else { return }
// we now have a non-nil ca

// make sure we have a positive ca:
guard let ca = c1?.age,  ca>=0 else { return }

The thing is, this is an early return, and you almost always replace those with valid |= ... if(valid) General: - in a tuple means a dummy var

String's

o funny indexing: some chars are multi-chars

Reference modifiers for garbage collection

Swift uses reference counting for garbage collection. To fix cycles, it has two types of weak pointers: weak and unowned. They go in front of the variable and can only be used inside classes.

Weak pointers are automatically set to nil when the pointed-to object goes away. Unowned pointers to destroyed objects are unusable:

class Node {
  var val : Int = 0
  var next : Node? = nil
  weak prev : Node? = nil // weak, since can be nil
  
  unowned base : ListHead;  // needs to be set during init. Non-nillable
    ...
 }

Tuples

The type of a tuple is other types in parens: (Int, String). Tuple literals are anything in parens: var nn = (5,"cow).

You can convert tuples to anything: not to arrays or slices, not from longer tuples (in other words, none of the stuff that makes them so nice in Perl).

The one shortcut is you can assign to variables in tuple L-values. And can use an underscore for a dont-care:

 var a=0, b=0, c=0
 (a,b,c) = (4,9,12) // a=4 b=9 c=12
 (a,_,b) = funcReturningA3Tuple()
 
 func funcReturningA3Tuple()->(Int, Int, Int) { // signature for a tuple-return
 

Access values using dot-0m dot-1 ... . There's no other way:

 var d = (4,"yaw",6.8) // d is a tuple
 
 var s : String = d.1 // yaw
 var n : Double = d.2 // 6.8
 

Classes

public/private are file-level, NOT code-block level. this is renamed self, as in self.age.

Getter/setter properties

The constructor is is named init (instead of the class name):

class Cat {
  var age : Int
  var name : String
  
  // standard constructors, named init:
  init() { age=1; name="mittens" }
  init(_ a:Int, _ nm:String) { age=a; name=nm } 
}

Inheritance

All functions are virtual, but require a decorative override keyword. It's a required hint:

class Animal {
  public var age:Int = 1
  public func cost()->Int {return 5}
}

class Cow : Animal {
  // override is required:
  public override func cost()->Int {return age*2}
}

The dynamic downcast is as?. The ? is to show it can return nil. Formally, it returns the ?-version of the type:

var aa : Animal = Cow()
var dd = aa as? Cow // dd is type Dog?

The base class is used with super. Ex: return super.cost()*2. Funny rule: super.init() must come last (actual rule: you have to init all of your variables before calling base init.)

Formal base-class-like interfaces are called Protocols. As normal, they define only functions (and properties) which users must implement:

// straight-forward interface:
protocol heldItem {
  func handsRequired()->Int  
}

// uses the standard syntax to inherit base class + interfaces:
class hotDog : Food, heldItem {
  ...
  // required to implement this:
  func handsRequired()->Int { return bunLen > 5 ? 2 : 1 }
}

// as usual, interfaces are declarable types:
var hh : heldItem = hotDog()
var n = hh.handsRequired()

Container classes

There is NO linked-list class.

Arrays

Instead of a crude built-in array, a nicer array class, Swift's built-in array is a fully functional array class. For example arrays can use A.append(8), and x=A.popLast().

Type of an array is [Int]. Official version is Array<Int>. Array literal are written in brackets: [1,2,5,7].

var A : [Int] = [5,7,8,9] // declare&create using shortcuts
var W : Array = ["cat","dog","goat"] // declare using long type

W.append("camel"); // standard operations
var w = W.remove(at:0) // remove also returns the item. How cute

Slicing

This is standard Perl-style stuff: A[2...5] grabs that part of A. Note there are 3 dots. Expressions are legal: A[n*2...A.Count-1]

var A = [10,11,12,13,14,15,16,17,18,19.20]
var a2 = A[4...6] // {14,15,16}

a2[4] // 14 -- slices NOT 0-based
a2.index(of:15) // 5 -- this is also index in original array

for n in a2 {...} // slices use standard foreach loop

Slices use indexes from where they came, not 0-based. You can search a slice and use the same indexes in the original array.

Slices are as copies. You can't write to an array through a slice, and slices won't track changes to the array (some explanations emphasize slices are windows into the array, but that's just saving space -- they're basic copy-on-write).

Slices and tuples don't mix (tuples don't mix with anything). Slices aren't arrays, but you can cast, using [Int](a2).

Unnecessary shortcuts: use a < or a missing end: A[2..<n] (pointless since you can write n-1), or W[...2] (the system fills in the 0).

Dictionary

Swift's has a standard array-syntax hash-table. One cute trick is all look-ups return nillables. You don't need to explicitly check ContainsKey -- do the look-up and nil means not found

Informal type is [ keyType : valType]. Literal syntax is [key:val, key:val]. Formal type is Dictionary<key, val>:

var CatCute = ["siameese":9, "persian":8, "calico":3]
CatCute["tabby"]=7
var c1 = CatCute["persian"]! // 8 (we need ! to convert from Int? to Int)
var c2 = CatCute["liger"] // nil

var c2=CatCute["liger"] ?? 0 // using the nil-replacing operator

Loops

While loops work as normal (but parens around the condition are optional, and the body requires {}'s).

No C-style for(;;) loop. There is a "repeat { } while" loop. Several for shortcuts:

for a in 1...6 {print(a)} // this is called a Range
for _ in 1...6 {print("x")} //  underscore means to hide the index variable

for a in B // standard foreach
for a in ["cat","duck","owl"] // ditto

for a in stride(from:2, to:20, by:2) // range-creating function

var ss : StrideTo = stride(from: 5, to:33, by:5) // the type of stride
for a in ss {print(a)}

Functions, part 2, anonymous and lambda's and function pointers

A basic anonymous function is { signature, keyword in body}:

var adder : (Int, Int)->Int; // note the clean function syntax
adder =  { (n1:Int, n2:Int)->Int in return n1+n2 } // anonymous function

var n = adder(6,2) // 8, standard function-pointer use

The hardest part is getting used to no open-curly to start the body. It's just everything after the in and before the close-curlybrace.

Note you don't add parm names when calling them. adder(6,2) doesn't have n1: and n2:.

Lots of shortcuts: 1) omit signature if inferrable, only write parm names 2) implied return for single-line, 3) omit entire inferrable signature and use $0, $1 ... for parameter names:

adder = { (x, y) in   // omit types it inferrable
  x+y // omit return if single value-returning line
}

adder = { // omit names and #'s of types if inferrable...
  if $0>$1 { return +1 } // ...and use sh-style $0, $1 for var names
  return -1
}

adder = { $0+$1 } // using both shortcuts

Then one more insane shortcut: If a function takes a function as the last input, you can write it outside of the parens. If it's the only input, you can omit the parens:

func apply(_ x:Int,  _ f:(Int.Int)->Int) { // takes an int, and an (int,int)->int function
  let n = f(x,7) + 1000 // use x and f
  return n
}

apply(5) { $0*$1 } // super-anon-func shortcut, added AFTER parens

// if apply had one one function parm, can omit ()'s:
apply { a,b in if a<b return -1; return +1 } 

First-class, Capture

Functions can return functions, and it looks pretty nice. Here's a standard curry. This function takes 1 int, and returns a function taking 1 more int and adding both:

func curriedAdd(_ n1:Int)->(Int)->Int {
  return { (n2:Int)->Int in return n1+n2} // returns an anon function, using n1
}

If you've never seen this, notice how the answer takes a single int, n2, as the input, but also remembers n1. Ex:

var add6 = curriedAdd(6)
var add10 : (Int,Int)->Int = curriedAdd(10)

add6(4) // 10
add10(4) // 14

Classes can return functions which capture class variables and parameters:

class Frog { 
  var age : Int = 0 
 
  // returns a function capturing input n and this frog:
  func addAge(_ n:Int)->()->Int 
  { 
    return { () in self.age += n; return self.age } 
  } 
} 

Template<T> functions/classes

These use the strict "must establish operations are legal" rules. Basic template functions are only allowed to use =. Otherwise can restrict to a subtype using doStuff<T : baseClass> (similar to how C# does it).

Enums

They don't caste to int's by default, but you can force them to:

enum waterStates { case ice, water, steam } // these have no values

// add int values:
enum waterState : Int { case ice, water, steam } // also allows trick: water=10

// use dot-rawValue (casts never work):
var n = waterState.ice.rawValue

The most common surprise is being able to omit the type-name when it can be inferred: var ws : waterState = .ice.

The rest of the enum rules are silly. A class with a nested enum is better. You're allowed to use any type as the fixed value for each:

enum waterState : Double {
  case ice=-99.9
  case water=32.1
  case steam = 212.0
}
// waterState.steam.rawValue is 212

Unions

enums has old C-style unions jammed in. They aren't enums any more. Each enum variable now holds a pile of data, like a class, except with no names. Ex:

// each SS variable is an int, or 2 strings:
enum SS {
  case a(Int)
  case b(String,String) 
}

var s1 = SS.a(6) // this is how you set the values
var  s2=SS.b("cat","dog")

You can't individually change these values once, and you can only read them with a special syntax using either switch, if case .b(let w1, let w2) = s1 or the same thing with a guard. so, yeah.