Skip to content

Instantly share code, notes, and snippets.

@griffingdm
Created June 8, 2014 19:45
Show Gist options
  • Save griffingdm/40e627efad94c0daf562 to your computer and use it in GitHub Desktop.
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.
/*
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