A Swift Tour - The Swift Programming Language - Solutions
// The Swift Programming Language: Swift by Tutorials (solutions) | |
// Author: Bill Bonney <billbonney (at) communistech.com> | |
// | |
// 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<String,Float>() | |
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<ItemType>(item: ItemType, times: Int) -> [ItemType] { | |
var result = [ItemType]() | |
for i in 0 ..< times { | |
result += item | |
} | |
return result | |
} | |
repeat("knock", 4) | |
This comment has been minimized.
This comment has been minimized.
And I did Double extension this way: extension Double {
var absoluteValue: Double {
get {
return abs(self)
}
}
}
var myDouble = -14.23
print(myDouble)
let absValue = myDouble.absoluteValue |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
var newNumber = newInt.adjust // It just shows function in the sidebar???
needs a fix:
This way a comment on the line 665 could be removed too :)