// The Swift Programming Language: Swift by Tutorials (solutions) // Author: Bill Bonney // // This is all the code in The Swift Tour section in the Apple Book on Swift. // This should allow you to play with the code and see behaviours, and help // solve any paticulars you get stuck on when doing the Experiments. // // Please feel free to comment on any part, I am by no means an expert in Swift ;-) // // NOTE: Not all code is identical (since I was tinkering, but should help) // import Cocoa // This is all 'pure' swift, no Math libs are used. Uncomment if you want to see the difference println("Hello Mum!") // // Simple Values // var myVariable = 42 myVariable = 50 let myConstant = 42 let implicit = 70 let implicitDoube = 70.0 let explicitDouble: Double = 20.3 // Experiment: Constant Float of value 4 // Note: I made it 20.3 to show the general rounding errors in floats // SIDENOTE: always use doubles on 64Bit systems ;) let explicitFloat: Float = 20.3 let label = "The width is " let width = 94 let widthLabel = label + String(width) // Experiment: Try removing the String conversion // let widthLabel = label + width // (uncomment as it will error otherwise) let apples = 3 let oranges = 5 let appleSummary = "I have \(apples) apples." let orangeSummary = "I have \(oranges) oranges." // Experiment: Use \() notation to include a floating point calculation let greetingPeople = "Hello People \(3 * 9)" var shoppingList = ["redfish", "yellowfish", "bluefish"] shoppingList[1] var occupations = [ "Malcolm": "capitan", "Kaylee": "mechanic" ] occupations["John"] = "domestic engineer" let emptyArray = [String()] let emptyDictionary = Dictionary() shoppingList = [] // // Control Flow // let individualScores = [75, 43, 103, 87, 12] var teamScore = 0 for score in individualScores { if score > 50 { teamScore += 3 } else { teamScore += 1 } } teamScore var optionalString: String? = "Hello" optionalString == nil var optionalName: String? = "Fred Shed" // Experiment: change optional name to nil // NOTE: Comment out line 66 and try.. // var optionalName: String? = nil // OR // var optionalName: String? var greeting = "Hello!" if let name = optionalName { greeting = "Hello \(name)" } else { greeting = "Who goes there?" } greeting let vegetable = "red pepper" switch vegetable { case "celery": let vegetableComment = "Add some raisins" case "cucumber","watercress": let vegetableComment = "That would make a nice cucumber sarnie." case let x where x.hasSuffix("pepper"): // Whah: Need to read up on this mystery x. let vegetableComment = "Is it spicy \(x)?" default: let vegetableComment = "Everything tastes good in soup" } //Experiment: Remove default case (see above) let interestingNumbers = [ "Pirme": [2,3,5,7,11,13], "Fibonacci": [1,1,2,3,5,8], "Square": [1, 4, 9, 16, 25], ] // Experiment: Add another variable to keep track of which kind of number was the largest. // NOTE: Also added code for the smallest var largest = 0 var largestType: String? var smallestType: String? var smallest = Int.max for (kind, numbers) in interestingNumbers { for number in numbers { if number > largest { largest = number largestType = kind } else if number < smallest { smallest = number smallestType = kind } } } largest largestType smallest smallestType var n = 2 while n < 100 { n = n * 2 } n var m = 2 do { m = m * 2 } while m < 100 m var firstForLoop = 0 for i in 0..<10 { firstForLoop += i } firstForLoop // Test to try a 1 to 10 count (sometimes useful to start at 1) var firstForLoop1 = 0 for i in 1...10 { // NOTE: a count from 1 to 10 firstForLoop1 += i } firstForLoop1 var secondForLoop = 0 for var i=0; i < 10; ++i { secondForLoop += i } secondForLoop // // Functions and Closures // // Experiment: Remove Day parameter and add lunch special greeting // NOTE: I just added lunch the param (D'oh must follow instructions) func greet(name:String, day:String, lunch:String) -> String { return "Helllo \(name), today is \(day) and lunch will be \(lunch)" } greet("Fred", "Monday", "eggs") greet("Wally", "Thursday", "surf'n'turf") func getGasPrices() -> (Double,Double,Double) { return (1.35,145,155) // Note: Prices are in (CAD$/L) } getGasPrices() func sumOf(numbers: Int...) -> Int { var sum = 0 for number in numbers { sum += number } return sum } sumOf() sumOf(42,597,12) // Experiment: Create a function that creates the average of the args func average(numbers:Double...) -> Double { var sum = 0.0 for number in numbers { sum += number } return sum/Double(numbers.count) } average(10.0,20.0,30.0) func returnFifteen() -> Int { var y = 10 func add() { y += 5 } add() return y } returnFifteen() func makeIncrementer() -> (Int -> Int) { func addOne(number: Int) -> Int { return 1+number } return addOne } var increment = makeIncrementer() increment(7) func hasAnyMatches(list: [Int], condition:Int -> Bool) -> Bool { for item in list { if condition(item){ return true; } } return false } func lessThanTen(number:Int) -> Bool { println("number is \(number)") return number < 10 } var numbers = [ 10, 19, 7, 12] hasAnyMatches(numbers, lessThanTen) numbers.map({ (number: Int) -> Int in let result = 3*number return result }) // Experiment: Show odd numbers as 0 (zero) // Only show even numbers, odd are zero'd numbers.map({ (number: Int) -> Int in if (number % 2) == 0 { return number } return 0 }) let mappedNumbers = numbers.map({ number in 3 * number }) mappedNumbers let sortedNumbers = sorted(numbers){ $0 > $1 } // Note: confused :-/ sortedNumbers // Objects and Classes class Shape { var numberOfSides = 0; func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } } var shape = Shape() shape.numberOfSides = 7 var shapeDescription = shape.simpleDescription() class NamedShape { var numberOfSides: Int = 0 var name: String init(name:String) { self.name = name } func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } } class Square: NamedShape { var sideLength: Double init(sideLength: Double, name: String){ self.sideLength = sideLength super.init(name: name) numberOfSides = 4 } func area() -> Double { return sideLength * sideLength //NOTE: Guessing ^2 is a import math function } override func simpleDescription() -> String { return "A square with sides of length \(sideLength)" } } let test = Square(sideLength: 5.2, name: "my test square") test.area() test.simpleDescription() // Experiment: Make another subclass Circle class Circle : NamedShape { var radius: Double init(radius:Double, name: String){ self.radius = radius super.init(name: name) numberOfSides = 1 } func area() -> Double { return 3.14592 * (radius*radius) // return M_PI * pow(radius, 2) // uncomment when using import cocoa } override func simpleDescription() -> String { return "A circle with a radius of \(radius)" } } var testCircle = Circle(radius: 2, name: "my circle") testCircle.area() testCircle.simpleDescription() class EquilateralTriangle : NamedShape { var sideLength: Double = 0.0 init(sideLength: Double, name: String){ self.sideLength = sideLength super.init(name: name) numberOfSides = 3 } var perimeter: Double { get { return 3.0 * sideLength } set(newPerimeter) { sideLength = newPerimeter / 3.0 } } override func simpleDescription() -> String { return "An equilateral triangle with sides of length \(sideLength)" } } var triangle = EquilateralTriangle(sideLength:3.1, name:"a Triangle") triangle.sideLength triangle.perimeter triangle.perimeter = 9.9 triangle.sideLength triangle.perimeter class TriangleAndSquare { var triangle: EquilateralTriangle { willSet { square.sideLength = newValue.sideLength } } var square: Square{ willSet { triangle.sideLength = newValue.sideLength } } init(size: Double, name: String){ square = Square(sideLength: size, name: name) triangle = EquilateralTriangle(sideLength: size, name: name) } } var triangleAndSquare = TriangleAndSquare(size: 10, name:"another test shape") triangleAndSquare.square.sideLength triangleAndSquare.triangle.sideLength triangleAndSquare.square = Square(sideLength: 50, name: "larger square") triangleAndSquare.triangle.sideLength class Counter { var count: Int = 0 func incrementBy(amount: Int, numberOfTimes times: Int){ count += amount * times } } var counter = Counter() counter.incrementBy(2, numberOfTimes: 7) let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") // NOTE: Try also //let optionalSquare: Square? = nil // OR //let optionalSquare: Square? let sideLength = optionalSquare?.sideLength sideLength // // Enumerations and Structures // enum Rank: Int { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King func simpleDescritption() -> String { switch self { case .Ace: return "ace" case .Jack: return "jack" case .Queen: return "queen" case .King: return "king" default: return String(self.toRaw()) } } } let ace = Rank.Ace let aceRawValue = ace.toRaw() // Exepriment: Write a function to compare two Rank values by comparing their raw values // NOTE: got a little carried away ;) let rightHand = Rank.King let leftHand = Rank.King var snap = 0 var handResult: String if rightHand.toRaw() < leftHand.toRaw() { handResult = "Left Hand Wins!" } else if rightHand.toRaw() > leftHand.toRaw() { handResult = "Right Hand Wins!" } else { handResult = "Draw!" } if let convertedRank = Rank.fromRaw(3){ let threeDescription = convertedRank.simpleDescritption() } // NOTE: I think choosing a picture card demonstrates this much better if let convertedRank = Rank.fromRaw(1){ let threeDescription = convertedRank.simpleDescritption() } enum Suit { case Spades, Hearts, Diamonds, Clubs func simpleDescription() -> String { switch self { case .Spades: return "spades" case .Hearts: return "hearts" case .Diamonds: return "diamonds" case .Clubs: return "clubs" } } // Experiment: Add a color method to Suit to return "red" or "black" // NOTE: new feature is to group values in one case statement func color() -> String { switch self { case .Hearts, .Diamonds: return "red" case .Clubs, .Spades: return "black" } } } let hearts = Suit.Hearts let heartsDescription = hearts.simpleDescription() let card = Suit.Spades card.color() struct Card { var rank: Rank var suit: Suit func simpleDescription() -> String { return "The \(rank.simpleDescritption()) of \(suit.simpleDescription())" } } let threeOfSpades = Card(rank: .Three, suit: .Spades) let thresOfSpadesDescritpion = threeOfSpades.simpleDescription() // Experiment: Add a method to create a full deck to Card (Why to card and not separate) // NOTE: This is a method to create a pack, but it's "not added to card" (?) func newPack() -> [Card] { var pack:[Card] = [] var suits = [Suit.Spades, Suit.Hearts, Suit.Diamonds, Suit.Spades] for suit in suits{ for i in 1...12 { pack += Card(rank: Rank.fromRaw(i)!, suit: suit) } } return pack } let pack = newPack() // Experiment: Add third case to ServerResponse and to the switch enum ServerResponse { case Result(String,String) case Error(String) case DaylightSavingsInEffect(Bool) } let success = ServerResponse.Result("6:00am", "8:09pm") let failure = ServerResponse.Error("Out of cheese.") let daylightsavings = ServerResponse.DaylightSavingsInEffect(true) var servResponse = success // NOTE: uncomment the lines below to see how the switch works. //var servResponse = failure //var servResponse = daylightsavings switch servResponse { case let .Result(sunrise, sunset): let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)." case let .Error(error): let serverResponse = "Failure... \(error)" case let .DaylightSavingsInEffect(state): var serverRespsonse: String if (state){ serverRespsonse = "Daylight Savings in effect." } else { serverRespsonse = "Standard Time" } } // // Protocols and Extensions // protocol ExampleProtocol { var simpleDescription: String { get } mutating func adjust() } class SimpleClass: ExampleProtocol { var simpleDescription: String = "A very simple class." var anotherProperty: Int = 69105 func adjust() { simpleDescription += " now 100% adjusted." } } var a = SimpleClass() a.adjust() let aDescription = a.simpleDescription struct SimpleStructure: ExampleProtocol { var simpleDescription: String = "A simple structure" mutating func adjust() { simpleDescription += "(adjusted)" } } var b = SimpleStructure() b.adjust() let bDescription = b.simpleDescription //Experiment: Add ExampleProtocol to an enummeration enum SimpleEnum : ExampleProtocol { case One, Two, OneOne, OneTwo, TwoOne, TwoTwo var simpleDescription: String { get { switch self { case .One: return "one" case .Two: return "two" case .OneOne: return "oneone" case .OneTwo: return "onetwo" case .TwoOne: return "twoone" case .TwoTwo: return "twotwo" } } } mutating func adjust() { switch self { case .One: self = OneOne case .Two: self = TwoTwo case .OneOne, .OneTwo: self = One case .TwoOne, .TwoTwo: self = Two } } } // Below is some examples of using the enum conforming to a protocol // and it looks quite confuing, but managing a statemachine type sequence // it could become very powerful in some applications. var c = SimpleEnum.One var enumState = c.simpleDescription c.adjust() var enumNextDescritption = c.simpleDescription c.adjust() enumNextDescritption = c.simpleDescription c = SimpleEnum.TwoOne enumState = c.simpleDescription c.adjust() enumNextDescritption = c.simpleDescription c.adjust() enumNextDescritption = c.simpleDescription // Extensions extension Int: ExampleProtocol { var simpleDescription: String { return "The number \(self)" } mutating func adjust() { // NOTE: Have no idea how this works see below. self += 42 } } 7.simpleDescription var newInt = 8 var newNumber = newInt.adjust // It just shows function in the sidebar??? extension Double { var abs: Double { get{ if (self.isSignMinus){ // demonstrates that doubles are not POT in swift. return self * -1 } else { return self } // return fabs(self) // uncomment if using import cocoa } } } var myDouble = -20.7 myDouble.abs let protocolValue: ExampleProtocol = a protocolValue.simpleDescription //protocolValue.anotherProperty // Uncomment to see the error // // Generics // func repeat(item: ItemType, times: Int) -> [ItemType] { var result = [ItemType]() for i in 0 ..< times { result += item } return result } repeat("knock", 4)