nscoder swift - an introduction to swift
DESCRIPTION
TRANSCRIPT
NSCoder SwiftAn introduction to Swift
October 18th, 2014
Andreas Blick - @aquarioverde
Swift
Swift
• New programming language introduced by Apple on June 2014 at WWDC
• Builds on the best of C and Objective-C, without the constraints of C compatibility
• Development started by Chris Lattner, who also started the LLVM and Clang project
Swift
• Safe programming patterns and modern features
• Seamless access to all existing Cocoa frameworks
• Mix-and-match interoperability with Objective-C
Playgrounds
Playgrounds
• Tool / Feature for
• learning
• code development
• experimentation
Playgrounds• Strings
• Arrays & dictionaries
• Color
• Views
• Images
• … etc
var / let
Variables & Constants• Defined using var or let keyword
• No need to define type
• Swift can mostly infer the type
• Can contain any unicode character
var instrument: String = “guitar” let nrOfSongs = 42 var trackLength = 3.23
Strings• Collection of characters that can be accessed easily
• Seamlessly bridged: NSString API available
• Mutability defined by assignment (var or let)
• String interpolation for literals, variables and expressions
let instrument = “guitar" !let music = "play \(instrument)" let noise = "play " + instrument
Numbers• Integers are stored as Int or UInt
• can be written as decimal, binary, octal, hexadecimal
• have max and min properties: Int.max
• Floating point numbers are stored as Float and Double
• can be written as decimal and hexadecimal
• Can contain underscores for readability: 7_777.77
• Bool can be true or false
Arrays• Stores ordered list of multiple values
• Arrays are type specific
• Accessed using properties, methods or subscripting
var numbers: Array<Int> = Array<Int>() var numbers: [Int] = [Int]() !numbers = [1,2,3] numbers = [Int](count:7,repeatedValue:0]
Dictionaries• Containers that store multiple key-value pairs of the
same type
• Accessed using properties, methods or subscripting
• Assigning nil as a value removes it
var numbers:Dictionary<Int,String> = Dictionary<Int,String>() !numbers = [1:“One”,2:“Two”,3:”Three”]
Tuples• Group multiple values of any type into a single compound value
• Values can be of any type
• Values can be accessed by index
• Tuples can have named values
let instrument = ("guitar", “strings") println(instrument.0) !let (name, family) = instrument !let instrument = (name: "guitar", family: "strings")
Loops• for-in loop to iterate using range
• for-in loops to iterate over a collection
• while / do-while loops if unknown number of iterations
for ix in 1…5 { . . . } for var ix = 0; ix < 7; ix++ { . . . } for (key, value) in aDictionary { . . . } !while ix < 7 { . . . } do { . . . } while ix < 7
Conditionals
• if / else for simple conditions
• switch statements for complex conditions
if number == 5 { . . . } !switch number { case 5: println(“5”) default: println(“default”) }
Conditionals - Switch• do not fall through (no break needed)
• must be exhaustive (use default:)
• support ranges: case 0…7:
• support tuples: case (1, 2):
• value binding: case (let x, 2):
• can use where clause to check additional conditionscase (let x, let y) where x == y:
Control Transfer Statements• continue
• break
• return
• fallthrough
• loops and switches can be labeled to be used with break and continue
counter: for i in 1...7 { for j in 1...7 { println("\(i) - \(j)") if i == 3 { continue counter} } }
?
Optionals• Used where a value might be missing by adding a ? sign
• Similar to nil in Objective-C, but can be used on any type
• non optionals can not be nil
• Optionals can implicitly be unwrapped by adding a ! sign (forced unwrapping)
let instruments = ["guitar" : 6, "bass": 4, “ukulele”: “8”] let nrOfStrings: Int? = instruments[“guitar"] !if nrOfStrings != nil { println(nrOfStrings!) } else { . . . }
Optionals• Use optional binding to find out if an optional
contains a value
let instruments = ["guitar" : 6, "bass": 4, “ukulele”: “8”] let nrOfStrings: Int? = instruments[“guitar"] !if let nrOfStrings = instruments["guitar"] { println(nrOfStrings) } else { . . . }
• Use optional chaining when a value might be nil
let familyName = guitar.family?.name?
func
Functions• Functions are blocks of code that execute a specific task
• Functions have parameters and can return values - default to void
• Parameters can have default values
• They should always be placed at the end of the list
func play(instrument: String = "guitar") -> String { return "play \(instrument)" } !play(instrument: "drums")
Functions• Parameter names are not required when calling a function
that has no default values
• Functions can return multiple values using tuples
• Use # if external and internal parameter names are the same
func play(instrument: String) -> (String, volume: Int) { return ("play \(instrument)", 7) } play(“guitar").volume !func play(instrument instr: String) -> String { . . . } func play(#instrument: String) -> String { . . . }
Functions• Functions can take a variable number of
arguments using variadic parameters
• One function can only have one variadic parameter
func playInstruments(instruments: String...) -> String { var play = "playing " for instrument in instruments { play += instrument + " " } return play } playInstruments("guitar", "drums", "sax")
Functions• Parameters are constants by default and can not be changed
• They can be defined as variables using var
• Variables parameters can only be modified within a function
• To persist a variable outside a function the inout parameter is needed
func swapTwoInts(inout a: Int, inout b: Int) { let tmpA = a a = b b = tmpA } var x = 3, y = 7 swapTwoInts(&x, &y)
Functions• Functions have a specific function type that can be assigned to
variables
• They can be passed as parameters and returned by other functions
• Functions can be nested
func iPlay(player:(String) ->String, song:String) ->String { return player(song) } func playGuitar(song: String) -> String { return "playing \(song) on guitar" } let playing = iPlay(playGuitar, "I sat by the ocean")
{}Closures
Closures• Closures are self-contained blocks of code that
can be passed around
• Functions are named closures!
• Closures can capture and store references and constants from their surrounding context
• Trailing closures can be used for readability
• Closures start using the in keyword
Closures - Example
let playMusic = { println("make some noise”) } !!!func repeat(count:Int, song:String, player:(String)->(String)) { for i in 0..<count { println(player(song)); } } repeat(5, "Even Flow") { (song: String) -> String in return "playing \(song)" }
class
Classes• Classes are program code templates for creating
objects, consisting of properties and methods
• No need for interface/header files
• No need to inherit from any other class
• No abstract class support
• Compare using the identity operator ===
Classes - Properties• Access to properties handled automatically
• Properties do not have a corresponding instance variable
• Every property needs to have a value assigned
• Properties without a setter are read-only
• Can have lazy stored properties (lazy)
• Type properties are defined using class keyword
Classes - Example
class Instrument { let type: String var isUsed: Bool = false init(type: String) { self.type = type } var info: String { get { return isUsed ? "used \(type)" : "new \(type)" } } } !let electricGuitar = Instrument(type: "guitar")
Classes - Properties
var isUsed: Bool = false { willSet(willBeUsed) { let msg = (self.isUsed ? "was" : "was not") + " used and “ + (willBeUsed ? "is used now" : "is not used now”) println(msg) } }
• Property observers can respond to changes in a properties’ value (willSet & didSet)
Classes - Methods• Methods are functions associated with a particular type
• Subscripts are special methods (subscript) for accessing members. They can contain any number of input parameters of any type.
class Instrument { func play() { println("make some noise") } subscript(ix: Int) -> String { return "model \(ix)" } }
Initialization• The class needs to be completely initialized before
accessing any property or method
• Properties that are allowed to have no value can be declared optional using the ? sign
• Property observers are NOT called on initialization
• Every class must have at least one designated initialiser
• Every class can have multiple convenience initializers
• Deinitializers can be used for cleaning up
Initializer Chaining
• Designated initializers must call a designated initializer from their immediate superclass
• Convenience initializers must call another initializer available in the same class
• Convenience initializers must ultimately end up calling a designated initializer
Initialization - Exampleclass Instrument { let type: String var family: String? init(type: String) { self.type = type } convenience init(type: String, family: String) { self.init(type: type) self.family = family } deinit { println("instrument \(type) destroyed") } } !let guitar = Instrument(type: “guitar")
Initialization
• Closures can be used for initialising complex properties
class Some { let some: String = { return "some" }() }
Inheritance• Classes can inherit methods, properties from other
classes
• Initializers are not inherited by default
• Initialize own class properties before calling super.init
• Overridden methods need override keyword
• Methods marked as final can not be overridden
• An entire class can be marked as final
Inheritance - Example
class Guitar : Instrument { init() { super.init(type: "guitar") family = "Strings" } ! override func play() { println("make some noise") } } !let guitar = Guitar()
struct
Structures• Structures are value types, they are always
copied, not referenced
• Structures can have initializers, methods and properties
• Automatically generated memberwise initializer
• Structures do not have deinitializers
• Array and Dictionary are structures
Structures• All properties of constant structures are constant as
well
• Methods for modifying structure properties need the mutating keyword
• Structures do not support inheritance
• No type-casting
• Type properties are defined using static keyword
Enumerations• An enumeration is a complete collection of items
• Enumeration values are fully-fledged in their own
• Enumerations are first-class types!
• Enumerations can have methods & initialisers
• Enumeration can have default raw values of the same type
Enumerations - Exampleenum Instrument: Int { case Guitar = 1, Base case Drums, Bongo, Trumpet, Saxophone func description() -> String { switch self { case .Guitar, .Base: return "Strings" case .Trumpet, .Saxophone: return "Wind" default: return "Unknown" } } } let instrument = Instrument.Trumpet instrument.description() instrument.toRaw() let unknown = Instrument.fromRaw(1)?.description()
Enumerations
enum Instrument { case Guitar(nrOfStrings: Int), Base case Drums, Bongo func description() -> String { switch self { case .Guitar(let nrOfStrings): return ("\(nrOfStrings)-string guitar") default: return "Unknown" } } } let instrument = Instrument.Guitar(nrOfStrings: 6) instrument.description()
• Enumerations can have associated values
++
Extensions• Extensions add new functionality to existing classes,
structures, … etc. They are similar to Categories in Objective-C
• They can add methods, computed properties, initialisers but no stored properties
extension Instrument { func rock() { println("\(type) is rocking") } }
Protocols• A protocol defines methods and properties a class, struct
or enumeration can or must adopt
• Existing types can be extended to adopt a protocol
• A protocol can inherit from one or more other protocols
• Protocol composition can be used to combine protocols
• Any protocol can be used as type
• Define as @objc for type checking and optional support
Protocols - Example@objc protocol Instrument { func play() } !class Guitar: Instrument { func play() { println("playing guitar") } } !let items = [Guitar(), NSString()] !for item in items { if let instrument = item as? Instrument { instrument.play() } }
Generics
• Generic code enables writing flexible, reusable functions and types
func swapTwoValues<T>(inout a: T, inout b: T) { let tmpA = a a = b b = a }
Generics• You can define your own generic type
• and specify type constraints
struct Stack<T: Instrument> { var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() { items.removeLast() } } var guitarStack = Stack<Guitar>()
References• WWDC 2014 Videos
• Apple Books
• The Swift Programming Language
• Using Swift with Cocoa & Objective-C
• Web
• https://developer.apple.com/swift/
+++
ARC
• Swift handles memory management using automatic reference counting
• Think about relationship between object instead of memory
• Avoid strong reference cycles by using weak or unowned references
Assertions• Use assertions whenever a condition has the
potential to be false, but must be true
• End code execution
let age = -3 assert(age >= 0, "age can no be less than 0")
Andreas Blick - @aquarioverde
Thank you!