Created
June 8, 2014 19:45
-
-
Save griffingdm/40e627efad94c0daf562 to your computer and use it in GitHub Desktop.
Apple's "A Swift Tour" walkthrough with completed "Experiment" solutions included and comments throughout that explain the code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Swift Programming Language Guide | |
//"A Swift Tour" Reference | |
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-XID_1 | |
Apple's "A Swift Tour" walkthrough with completed "Experiment" solutions included and comments throughout. | |
You should be able to copy-paste it into your own playground. Let me know of any errors I made throughout the code. Also tell me if any comments are unclear or have an incorrect explanation. | |
Written by Griffin Mullins | |
*/ | |
import Cocoa | |
var str = "Hello, playground" | |
//VARIABLES | |
//create variables and constants | |
var newVar = 42 | |
newVar = 50 | |
let myConst = 42 | |
//create implicit and explicit vars | |
var implicitInt = 70 | |
var implicitDub = 70.0 | |
var explicitDub: Double = 70 | |
//VALUES IN STRINGS: SHORTHAND | |
//including values in strings | |
let label = "The width is " | |
let width = 95 | |
let widthLabel = label + String(width) //convert to string inline | |
//use backslash instead of typing 'String' to convert to string | |
let apples = 2 | |
let oranges = 5 | |
let appleSummary = "I have \(apples) apples" | |
let fruitSummary = "I have \(apples + oranges) pieces of fruit" | |
//ARRAYS | |
//creating arrays and dictionaries using brackets | |
//array | |
var shoppingList = ["apples", "tulips", "water", "blue paint"] | |
shoppingList[2] = "bottle of water" //edit elements by writing index key | |
//dictionary | |
var occupations = [ | |
"Malcolm": "Captain", | |
"Kaylee": "Mechanic" | |
] | |
occupations ["Jessica"] = "Graphic Designer" //add to the dictionary | |
//create an empty array or dictionary using initializer statement | |
let emptyArray = String[]() | |
let emptyDictionary = Dictionary<String, Float>() | |
var places = [] //shortcut to create an empty array where type is inferred | |
var students = [:] //shortcut to create an empty dictionary | |
//OPTIONAL VALUES | |
var optionalString: String? = "Hello" //explicitly tell that its optional | |
optionalString == nil | |
//creates a statement based on if youve given a name or not | |
var optionalName: String? = "John Appleseed" | |
var greeting = "Hello!" | |
if let name = optionalName{//'name' cannot be nil, conditional is false | |
greeting = "Hello, \(name)" //its good to convert to string everywhere | |
} else { | |
greeting = "Hello, unknown person!" //return if optionalName = nil | |
} | |
//SWITCHES | |
//creating switches in order to do complex or simple comparison operations | |
let vegetable = "carrot pepper" | |
switch vegetable{ | |
case "celery": | |
let vegetableComment = "Add some raisins and make ants on a log." | |
case "cucumber", "watercress": | |
let vegetableComment = "That would make a good tea sandwich." | |
case let x where x.hasSuffix("pepper"): | |
let vegetableComment = "Is it a spicy \(x)?" | |
default://MUST HAVE A DEFAULT CASE | |
let vegetableComment = "Everything tastes good in soup." | |
} | |
//LOOPS IN SWFIT | |
//created a dictionary with different kinds filled with arrays of numbers | |
let interestingStuff = [ | |
"Prime": [2,3,5,7,11,13], | |
"Fibonacci": [1,1,2,3,5,8], | |
"Square": [1,4,9,16,25] | |
] | |
//what was the largest number out of all the kinds in the dictionary? | |
//what kind was that number? | |
var largest = 0 | |
var theKind: String? | |
for (kind, numbers) in interestingStuff{ | |
for number in numbers { | |
if number > largest { | |
theKind = kind | |
largest = number | |
} | |
} | |
} | |
theKind | |
largest | |
//standard while loop in Swift | |
var n = 2 | |
while n < 100 { | |
n = n*2 | |
} | |
n | |
//conditional at the end of the while loop! | |
var m = 2 | |
do{ | |
m = m*2 | |
} while m < 100 | |
m | |
//index a loop using .. | |
var firstForLoop = 0 | |
//.. is < second var, not <= | |
for i in 0..3 {//0..3 == "var i = 0; i<3; i++" | |
firstForLoop += 1 | |
} | |
firstForLoop | |
//the old way of doing it using Swift | |
var secondForLoop = 0 | |
for var i = 0; i < 3; ++i{ | |
secondForLoop += 1 | |
} | |
secondForLoop | |
//... includes the second number in the range you give the for loop | |
var thirdForLoop = 0 | |
for i in 0...3 {//... is <= | |
thirdForLoop += 1 | |
} | |
thirdForLoop | |
//FUNCTIONS INSIDE OF SWIFT | |
//use -> to define the return type | |
func greet(name: String, day: String, special: String) -> String{ | |
return "Hello \(name), today is \(day). Today's lunch special is \(special)." | |
} | |
greet("Bob", "Tuesday", "Pizza") | |
//using a "tuple" to return multiple values from a function | |
func getGasPrices() -> (Double,Double,Double){ | |
return (3.59, 3.69, 3.69) | |
} | |
getGasPrices() | |
//creating a function that takes in a variable amount of arguments | |
func sumOf(numbers: Int...) -> Int { | |
var sum = 0 | |
for number in numbers{ | |
sum += number | |
} | |
return sum | |
} | |
sumOf() | |
sumOf(54, 65, 80) | |
//creating a function that finds the average of any number of arguments | |
func averageOf(numbers: Int...) -> Int { | |
var sum = 0 | |
var amount = 0 | |
for number in numbers{ | |
sum += number | |
amount += 1 | |
} | |
return sum/amount | |
} | |
averageOf(1, 1, 1) | |
averageOf(65, 100, 53, 63, 2) | |
//NESTED FUNCTIOnS | |
//nested function example 1 | |
func returnFifteen() -> Int { | |
var y = 10 | |
func add(){ | |
y += 5 | |
} | |
add() | |
return y | |
} | |
returnFifteen() | |
//functions can return another function as its value | |
func makeIncrementer() -> (Int -> Int) { | |
func addOne(number: Int) -> Int{ | |
return 1 + number | |
} | |
return addOne | |
} | |
//creates a new function from a variable | |
var increment = makeIncrementer() | |
increment(7) | |
//functions can also take another function as one of its arguements | |
//checks an array of ints against a condition, if any make the condition, return true, else it returns false | |
func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool{ | |
for item in list{ | |
if condition(item){ | |
return true | |
} | |
} | |
return false | |
} | |
//condition I can pass into hasAnyMatches | |
func lessThanTen(number: Int) -> Bool { | |
return number < 10 | |
} | |
//condition if a number is divisible by three | |
func divThree(number: Int) -> Bool{ | |
if number % 3 == 0 { | |
return true | |
} | |
return false | |
} | |
var numbers = [20, 19, 7, 13] | |
//run the functions | |
hasAnyMatches(numbers, lessThanTen) | |
hasAnyMatches(numbers, divThree) | |
//WRITING CLOSURES | |
//takes the numbers in an array and multiplies each of them by three, returning a new array of ints | |
numbers.map({//write a closure without a name by surrounding it with {} | |
(number: Int) -> Int in//in seperates arguements and return type from the body | |
let result = 3 * number | |
return result | |
}) | |
//closure that returns a zero for all odd numbers | |
numbers.map({ | |
(number: Int) -> Int in | |
if number % 2 != 0 { | |
return 0 | |
} | |
return number | |
}) | |
//if you already know the closure type, you can omit the type of its parameters, its return type, or both | |
let newNumbers = numbers.map({number in 3 * number}) | |
newNumbers | |
let sorted = sort([1, 5, 2, 12, 2]) { $0 < $1 }//$ sorts numbers? <inc, >dec | |
sorted | |
//OBJECTS AND CLASSES | |
//create a simple class with Swift | |
class Shape{ | |
var numberOfSides = 0 | |
let color = "white" | |
func simpleDescription() -> String{ | |
return "A shape with \(numberOfSides) sides." | |
} | |
func divisible(number: Int) -> Bool{ | |
if numberOfSides % number == 0{ | |
return true | |
} | |
return false | |
} | |
} | |
//dot syntax to call on the properties of a class | |
var shape = Shape() | |
shape.numberOfSides = 7 | |
var shapeDescription = shape.simpleDescription() | |
var divByThree = shape.divisible(3) | |
//create a class with an Instatiate FxN using init | |
class NamedShape { | |
//every property needs to have a value assigned to it somewhere in the class, whether it be in the declaration, or in the initializer statement | |
var numberOfSides: Int = 0 | |
var name: String | |
init(name: String){//variables need addressing when the class is initiated | |
self.name = name//self is used to distinguish the name property from the name argument in the initializer statement | |
} | |
func simpleDescription() -> String{ | |
return "A shape with \(numberOfSides) sides." | |
} | |
//use a deinit statement if cleanup is needed before deallocation | |
} | |
//SUBCLASSES in SWIFT | |
//you may include or omit any superclass as needed | |
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 | |
} | |
//override fxns are methods that can override the superclasses' implementation | |
//overriding a method by accident are caught by the compiler and counted as errors | |
override func simpleDescription() -> String { | |
return "A square with sides of length \(sideLength)." | |
} | |
} | |
let testSquare = Square(sideLength: 4.5, name: "The first square") | |
testSquare.area() | |
testSquare.simpleDescription() | |
//subclass of NamedShape for a circle | |
//inputs a radius and a name for the initializer | |
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.14 * (radius * radius) | |
} | |
override func simpleDescription() -> String{ | |
return "A circle with a radius of \(radius), and an area of \(area())." | |
} | |
} | |
let testCircle = Circle(radius: 10, name: "The test circle") | |
testCircle.simpleDescription() | |
//getter and setter fxns in Swift | |
//equilateral triangle subclass | |
class EquilateralTriangle: NamedShape{ | |
var sideLength: Double = 0.0 | |
//three different steps in the initializer fxn | |
//1. set value of prop that the subclass declares | |
//2.calling the superclasses' initializer | |
//3.changing value of the prop. defined by the superclass + additional setup | |
init(sideLength: Double, name: String){ | |
self.sideLength = sideLength | |
super.init(name: name) | |
numberOfSides = 3 | |
} | |
//what variable do you want them to be able to get and set? | |
var perimeter: Double{ | |
get { | |
return 3 * sideLength | |
} | |
set {//if you want a different name than newValue put it in parenthesis after set | |
sideLength = newValue/3.0//newValue MUST be the name in the setter fxn | |
} | |
} | |
override func simpleDescription() -> String { | |
return "An equilateral triangle with sides of length \(sideLength)." | |
} | |
} | |
var testTriangle = EquilateralTriangle(sideLength: 4.7, name: "The test equilateral") | |
testTriangle.perimeter | |
testTriangle.perimeter = 9.9 | |
testTriangle.sideLength | |
//if computing the property is not necessary, but you still have to provide code, use willSet and didSet. | |
//this subclass creates to shapes at once that are subclasses of equilateralTriangle and Square | |
class TriangleAndSquare{ | |
var triangle: EquilateralTriangle{ | |
willSet{//value of the triangle's sideLengths will always equal the square's | |
square.sideLength = newValue.sideLength | |
} | |
} | |
var square: Square{ | |
willSet{//value of the square's sideLengths will always equal the triangle's | |
triangle.sideLength = newValue.sideLength | |
} | |
} | |
//using the superclasses' init fxns inside of this init fxn | |
//use one input, 'size' as the sideLength variable for both shapes | |
init(size: Double, name: String) { | |
square = Square(sideLength: size, name: name) | |
triangle = EquilateralTriangle (sideLength: size, name: name) | |
} | |
} | |
var testTriangleAndSquare = TriangleAndSquare(size: 5, name: "the test triangle and square") | |
testTriangleAndSquare.square.sideLength | |
testTriangleAndSquare.triangle.sideLength | |
testTriangleAndSquare.square = Square(sideLength: 50, name: "Larger Square") | |
//even though one perameter was set for the square, they both change | |
testTriangleAndSquare.triangle.sideLength | |
//perameter names are only used within a function, but parameter names in methods are also used when you call the method. Methods have the same name for its parameters when you call it and within the method itself | |
class Counter{ | |
var count: Int = 0 | |
func incrementBy(amount: Int, numberOfTimes times: Int){ | |
count += amount * times | |
} | |
} | |
var pizzaFlips = Counter()//pizzaFlips is now a counter | |
pizzaFlips.incrementBy(2, numberOfTimes: 7)//counters can incrementBy | |
//ENUMERATIONS AND STRUCTURES | |
//enumerations have a abunch of cases that can be called independently for different uses | |
enum Rank: Int{ | |
case Ace = 1 | |
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten | |
case Jack, Queen, King | |
func simpleDescription() -> String{ | |
switch self{ | |
case .Ace: | |
return "ace" | |
case .Jack: | |
return "jack" | |
case .Queen: | |
return "queen" | |
case .King: | |
return "king" | |
default: | |
//toRaw converts between raw values and enumeration values. i.e. 'two' was the second in the array so its raw value will be 2 by default | |
return String(self.toRaw()) | |
} | |
} | |
} | |
//function that compares two Rank values by comparing their raw values | |
func compareRank(rank1: Rank, rank2: Rank) -> String{ | |
if rank1.toRaw() == rank2.toRaw(){ | |
return "It's a tie!" | |
} else if rank1.toRaw() == 1 || rank2.toRaw() == 1{ | |
return "The Ace beats everything!" | |
} else if rank1.toRaw() > rank2.toRaw(){ | |
return "The \(rank1.simpleDescription()) wins!" | |
} else { | |
return "the \(rank2.simpleDescription()) wins!" | |
} | |
} | |
let ace = Rank.Ace | |
let aceRawValue = ace.toRaw() | |
let three = Rank.Three | |
let threeRawValue = three.toRaw() | |
compareRank(ace, three) | |
//Use the toRaw and fromRaw functions to convert between the raw value and the enumeration value. | |
if let convertedRank = Rank.fromRaw(3){ | |
let threeDescription = convertedRank.simpleDescription() | |
} | |
//you dont have to use toRaw or fromRaw when they are useless | |
enum Suit: Int{ | |
case Spades = 1, Hearts, Diamonds, Clubs | |
func simpleDescription() -> String{ | |
switch self{ | |
case .Spades: | |
return "spades" | |
case .Hearts: | |
return "hearts" | |
case .Diamonds: | |
return "diamonds" | |
case .Clubs: | |
return "clubs" | |
default: | |
return String(self.toRaw()) | |
} | |
} | |
//function that returns the correct color of the suit | |
func color() -> String{ | |
switch self{ | |
case .Spades, .Clubs://can check however many using , at a time | |
return "Black" | |
case .Hearts, .Diamonds: | |
return "red" | |
} | |
} | |
} | |
let hearts = Suit.Hearts//have to call Suit.Hearts when instantiating | |
//dont have to call suits first because you already know the type | |
let heartsDescription = hearts.simpleDescription() | |
let heartsColor = hearts.color() | |
//STRUCTURES | |
//structures are always copied when they are passed around throughout your code | |
//classes are just passed around by reference | |
struct Card{ | |
var rank: Rank//reference rank back in the enum Rank | |
var suit: Suit//reference suit back in the enum Suit | |
func simpleDescription() -> String{ | |
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" | |
} | |
} | |
let threeOfSpades = Card(rank: .Three, suit: .Spades) | |
let threeOfSpadesDescription = threeOfSpades.simpleDescription() | |
//create a method that creates a full deck of cards | |
func createDeck() -> Card[] { | |
var deck = Array (count: 52, repeatedValue: Card(rank: .Ace, suit: .Spades)) | |
var suits = [Suit.Spades, Suit.Hearts, Suit.Diamonds, Suit.Clubs]; | |
var counter = 0; | |
for i in 1...13 { | |
for suit in suits { | |
deck[counter++] = Card (rank: Rank.fromRaw(i)!, suit: suit) | |
} | |
} | |
return deck | |
} | |
//function to print simpleDescription for every card in the deck | |
func printDeck (deck: Card[]) { | |
for card in deck { | |
println(card.simpleDescription()) | |
} | |
} | |
let deck = createDeck() | |
printDeck(deck) | |
//raw value of an enumeration is the same for all of its instances | |
enum ServerResponse{ | |
case Result(String, String) | |
case Error(String) | |
case ComeBack(String) | |
} | |
//what does each case inside of the enumeration contain? | |
let success = ServerResponse.Result("6:00 am", "8:09pm") | |
let failure = ServerResponse.Error("Out of pizza") | |
let comeBack = ServerResponse.ComeBack("server too tired, come back at sunset") | |
switch comeBack{//make a switch looking for the right case in the enumeration | |
//the surise and sunset times are extracted from the ServerResponse value because its value was matches against the switch cases | |
case let .Result(sunrise, sunset): | |
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)." | |
case let .Error(error): | |
let serverError = "Failure... \(error)" | |
case let .ComeBack(comeBack): | |
let youComeBack = "Nope, \(comeBack)." | |
//do not need a default case because the switch is not a variable? I think | |
} | |
//PROTOCOLS AND EXTENSIONS | |
//can create what a class, enum etc requirements without creating a superclass. | |
//these protocols can apply to many different types seamlessly | |
protocol ExampleProtocol{ | |
var simpleDescription: String { get }//getter for simpleDescription (String) | |
//mutating functions can modify the structure or enums they lie within | |
//use this when trying to apply one func to many types (as a protocol does) | |
mutating func adjust() | |
} | |
//classes, enumerations, and structs can all adopt protocols | |
class SimpleClass: ExampleProtocol{//creating a class adopting ExampleProtocol | |
var simpleDescription: String = "This is a very simple class." | |
var anotherProperty: Int = 6235//can also add properties | |
//this function does not need 'mutating' because functions inside of a class can always modify the class itself | |
func adjust(){ | |
simpleDescription += " Now 100% adjusted." | |
} | |
} | |
var a = SimpleClass() | |
a.adjust() | |
let aDescrption = a.simpleDescription | |
//example of a structure folowwing the ExampleProtocol Protocol | |
struct SimpleStructure: ExampleProtocol{ | |
var simpleDescription: String = "A simple structure" | |
mutating func adjust() { | |
simpleDescription += " (adjusted)" | |
} | |
} | |
var b = SimpleStructure() | |
b.adjust() | |
let bDescription = b.simpleDescription | |
//example of an enumeration that conforms to the ExampleProtocol Protocol | |
enum SimpleEnum: ExampleProtocol{ | |
case A, B, AA, BB | |
//must create a variable with a getter function that returns the string based on the case instead of creating one by itself | |
var simpleDescription: String{ | |
get{ | |
switch self{ | |
case .AA: | |
return "The simplish adjusted A enum." | |
case .BB: | |
return "The simplish adjusted B enum." | |
default://if you have a default, no need to worry about covering all cases | |
return "Some simplish A or B enum." | |
} | |
} | |
} | |
//the adjust function that changes each based on what it was before, if its A or B and adjusted, it will change the simpleDescription | |
mutating func adjust(){//MUST INCLUDE MUTATING FOR ENUM | |
switch self{ | |
case .A: | |
return self = AA | |
case .B: | |
return self = BB | |
case .AA: | |
return self = AA | |
case .BB: | |
return self = BB | |
} | |
} | |
} | |
//start out with a call to create a var based on enum case A | |
var c = SimpleEnum.A | |
c.simpleDescription | |
c.adjust() | |
let cDescription = c.simpleDescription | |
//use extensions to add some functionality to existing types. Use this this to add conformity to something that is declared elsewhere or declared somewhere you dont have access to, such as a library or framework | |
//this extension adds functionality following the ExampleProtocol Protocol to Int | |
extension Int: ExampleProtocol{ | |
var simpleDescription: String{ | |
return "The number is \(self)." | |
} | |
mutating func adjust(){ | |
self += 42 | |
} | |
} | |
var testInt = 7 | |
testInt.simpleDescription | |
testInt.adjust()//adjust adds 42 to any int. this is now a func inside of any int | |
testInt.simpleDescription | |
//extension to Double that adds an absokute valoe property | |
extension Double{ | |
var absoluteValue: Double { | |
return sqrt(self*self) | |
} | |
} | |
var testDouble = -54.3 | |
testDouble.absoluteValue | |
//you can create a protocol type as a variable as well | |
let protocolValue: ExampleProtocol = a | |
protocolValue.simpleDescription | |
//.anotherProperty is not available because protocolValue only adopts the properties of ExampleProtocol and not from SimpleClass (which 'a' belongs to) | |
//protocolValue.anotherProperty | |
//GENERICS | |
//write a name inside of angle brackets to create a generic fxn or type | |
//this generic fxn will repeat whatever is put in it a certain amount of times and output that into an array | |
func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] { | |
var result = ItemType[]() | |
for i in 0..times{ | |
result += item | |
} | |
return result | |
} | |
repeat("knock", 4) | |
//generic forms of functions and method can be made, as well as classes, enumerations, and structures | |
//reimplementing the Swift standard library's optional type | |
enum OptionalValue<T>{ | |
case None | |
case Some(T) | |
} | |
var possibleInteger: OptionalValue<Int> = .None | |
possibleInteger = .Some(100) | |
//use 'where' after the type name to specify a list of requirements | |
//to require the type to implement a protocol, to require two types to be the same, or to require a class to have a particular superclass | |
//this fxn compares elements in two sequences of any size to see if there are any matching elements between them | |
//the two variables passed through MUST be: | |
// 1. Both Sequences | |
// 2. Comparable/ 'equatable' | |
// 3. of the same type | |
func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element:Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool{ | |
for lhsItem in lhs{ | |
for rhsItem in rhs{ | |
if lhsItem == rhsItem{ | |
return true | |
} | |
} | |
} | |
return false | |
} | |
anyCommonElements([1,2,3], [4,5,6]) | |
anyCommonElements(["hello", "goodbye", "bob"], ["george", "bob"]) | |
//fxn that does the same as anyCommonElements but it returns the common elements in a new array | |
//same as last func but returns an array of type whatever is put into it | |
func theCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element:Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Array<T.GeneratorType.Element>{ | |
var sameStuff = Array<T.GeneratorType.Element>()//create an empty array | |
for lhsItem in lhs{ | |
for rhsItem in rhs{ | |
if lhsItem == rhsItem{ | |
sameStuff += lhsItem//add to the empty array | |
} | |
} | |
} | |
//once everything is compared from both sequences, return the array of the elements that were determined to be the same | |
return sameStuff | |
} | |
theCommonElements([1,2,3], [4,5,6]) | |
theCommonElements(["hello", "goodbye", "bob"], ["george", "bob", "hello"]) | |
//THANKS FOR CODING | |
//Griffin Mullins |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment