-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathSwift Notes
More file actions
1166 lines (843 loc) · 56.7 KB
/
Swift Notes
File metadata and controls
1166 lines (843 loc) · 56.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Swift Notes
Swift is Apple's new language for developing iOS and OS X apps.
It is set to replace Objective-C, which is good because Objective-C sucks hard!
You can just mess around in Swift using Xcode by opening up a new playground, a playground is
Xcode 6's new environment for seeing immediate results of each line of code, kind of like a shell
for an interpreted language. The playground is just used for testing code, you can't use it to make an app.
Swift has been built on top of existing Apple development framework, so it works with Objective-C and
Objective-C can be used with Swift if for some reason that is needed. Swift also borrows some of the
syntax style of Objective-C, but is much much cleaner thatn Obj-C and easier to program in and safer to
program in as well.
************************ BASIC SYNTAX ************************
Nothing is put at the end of a line of code to end it (i.e. no semi-colon)
Declaring variables:
var myVar = value
Declaring constants:
let myConst = value
There is also a variable called a Computed Property, which doesn't actually store a value, instead
it gets its value computed each time based on other values.
To declare a Computed Property you declare a variable but give it curly braces and use a get { }
statement inside it to compute the value. The get{} statement is a getter method for the Computed
Property. You can also make a setter if you want by doing set(params) { }. What the setter would do
is just update the values that are involved in the calculation of the Command Property, it wouldn't
actually directly change the value of the Command Property, because then it wouldn't be a Command
Property! Here is the syntax:
var myVar: dataType {
get {
return // calculation of myVar
}
set(params) {
// code
}
}
i.e. of Computed Property
var subtotal: Double {
get {
return total / (taxPct + 1)
}
}
Comparative Operators:
Swift does use the === identical and !== not identical operators to check if two operands are not only the same value but of the same type as well.
Logical Operators:
|| // or
&& // and
! // not
All Swift Operators:
Precedence Operators
-- .
-- ++ -- ! ~ + - &
160 << >>
150 * / % & &*
140 + - } ^ &^ &-
135 ..< ...
132 is as as? as!
131 ??
130 < <= > >= == != ~= === !==
120 &&
110 ||
100 ?:
90 = *= /= %= += -= <<= >>= ||= &&= ^= |= &=
You can extend Swift with operator overrloading and when doing so you have to assign a precedence level. That's why they left some numeric gaps between some precedence levels.
************************ iOS PLAYGROUND BASICS ************************
To get another pane in the playground window to display console output go to View in the top menu, then Assistant Editor, then Show Assitant Editor.
************************ iOS Basics ************************
An iOS app is split into a model, views,
A model for an iOS app is a class or set of classes that represent that class's data and operations the
app will perform on that data. So basically the app's model is all the programming logic that doesn't
have to do with the user interface itself I believe.
You create the User Interface for an iOS app in something callsed a Storyboard. Xcode
comes with a built-in tool called Interface Builder that lets you edit Storyboards in
a nice visual way. You can lay out all your UI elements (buttons, textfields, etc), which
are called Views, in the app by just dragging and dropping.
The View Controller, the file ViewController.swift, is the Swift code for the single
view controller ("screen") in the app. It is responsible for managing the communication
between the views and the model.
iOs is split up into multiple frameworks, each of which contain different sets of code.
Before you can use code from a framework in your app, you have to impport it, for example,
importing the UIKit, which is the framework that contains the base class for view
controllers, various controls like buttons and text fields, and more.
The syntax: class ViewController: UIViewController means that ViewController
is a subclass of the class UIViewController.
Unlike with Objective-C, with Swift you don't have to put a class prefix on your class
names to avoid namespace collisions because Swift has namespace support and the classes
you create in your project are in their own namespace.
In the View Controller, the viewDidLoad() method is called when the root view of the
view controller is first accessed.
In the View Controller, the didReceiveMemoryWarning() method is called when the device
is running low on memory. It is a good place to clean up any resources you can spare.
When making properties (variables) in the source code of the View Controller you have
to begin the declaration with the @IBOutlet keyword to let the Interface Builder know
that this property needs to be connected to their views.
By putting an exclamation mark at the end of a variable declaration you are indicating
that they are optional values but that they are implicitly wrapped. This basically means
that you can write code assuming that they are set but your app will not crash if they
are not set. Implicitly unwrapped optionals are a nice way to create variables that you
know for sure will be set before you use them (like user interface elements created in
the Storyboard) so you don't have to unwrap the optionals every time you want to use them.
Special Characters:
Can print out special characters by going to the top menu Edit...Special Characters... and then drag the special character or symbol you want into the code.
Comments:
Comments in Swift are the standard // for single line comments and /* */ for multi-line comments. Also swift comments will nest, unlike other languages, so you can put multi-line comments inside of other multi-line comments. Like so:
/* This is a /* nested comment */ and its beautiful */
Doc Comments:
Doc comments can be used for classes and functions/methods.
A three-slash comment is a documentation comments. Whenever you click on a function or class in Xcode the inspector panel on the right has a Quick Help Inspector that gives some information about that function. Putting a documentation comment above a function/class makes the Description in the Quick Help section for that function/class be whatever you put in the doc comment! You can use markdown for doc comments, for example surrounding text with a single asteriks makes it italic, while surrounding it with two asteriks makes it bold. Using a hyphen at the beginning of a line makes that line an item in a bulleted list.
/// This *is* **my** doc comment
/// - feed pidgeons
/// - catch pokemon
In the above example "this" would be in italics, "my" would be in bold, and there would be a bulleted list with "feed pidgeons" and "catch pokemon" in the Quick Help inspector.
Doc comments markdown can also specify what a function returns and it's parameters.
To do this use " - Returns: " and " - Parameters: ". To list the parameters you have to indent on the next line a parameter followed by a colon and a description of the parameter.
/// - Returns: some awesome value
/// - Parameters:
/// - name: name of user
/// - age: age of user
You can also document single parameters at a time like this:
/// - parameter name: name of user
/// - parameters age: age of user
In a function or class call you can see the documentation for that function/class by alt+clicking it, then you doc comments will pop up in the popup box.
Also, if you start a comment off as:
// MARK:
in iOS that is a special type of comment that’s used to organize your code and to help you (and anybody else who reads your code) navigate through it. For example, the comment:
// MARK: Properties
indicates that this is the section of your code that lists properties.
While the comment below is for the section of the View Controller that specifies functions for actions.
// MARK: Actions
#available and @available
You can use the #available statement to check if the code is running on a specified operating system and only run the code in that block if it is.
if #available(iOS 8.0, OSX 10.10, *) {
// code that only works with these OS environments
}
You can do the same thing for functions with @available:
@available(iOS 8.0, OSX 10.10, *)
func worksOnlyWithNewerOS() {
// code...
}
************************ FUNCTIONS / METHODS ************************
Whenever you override a method in Swift you need to mark it with the override keyword.
This is to help avoid a situation where you override a method by mistake.
Syntax for overriding:
override func funcName() { }
************************ SWIFT I/O ************************
Output:
print() // prints without an endline
prinln() // prints with an endline
Use a plus "+" symbol to join things in the print function.
print("This is a variable: " + myVar)
************************ VARIABLES ************************
The types of variables are strings, ints, unsigned ints, floats, doubles, booleans.
String, Int, UInt, Float, Double, Bool
To declare a variable use the var keyword: var myVar = value
To declare a constant use the let keyword: let myConst = value
For Ints, Int and UInt's size isn't specified, it depends on the operating system, but the Foundation library has a bunch of differently sized Ints and UInts for use in case you need to use a specifically size Integer, like if you need a really big Int:
Int8 Int16 Int32 Int64
UInt8 UInt16 UInt32 UInt64
UTF8 UTF16 UTF32 UTF64
Float80
Data types in Swift are actually objects, so they have properties and methods associated with them. For example, Int.min and Int.max return the min and max integer values that it can hold.
Can implicitly declare variables/constants just by using var or let keywords, fractional numbers when implicitly declared are of type Double, not float.
But you can also explicitly declare variable types by putting a colon and the data type after the identifier:
var myVar: Int = 34
let str: String = "yo man"
var someNum: Float = 23.29
var aDubbs: Double = 9.99 // Double is the default for decimal point values
var aBool: Bool = true
var myArr [String] = ["yo", "man"]
If you don't want to assign a value to a constant/variable at declaration then you have to assign a type:
let something: Int // ok
let something2 // error
let something3 = 4 // ok
Swift also has type aliasing so you can assign your own type name as an alias to a built-in type name, like:
typealias Unsigned = UInt16
Type Casting:
String(34) // String type-casting only seems to convert integers, not decimal numbers
Float(345) // = 345.0
Double(23) // = 23.0
Int(34.4) // = 34, doesn't work on strings, instead use .toInt()
Swift does no type conversion, so adding a float and an int will cause a compiler error. You need to typecast one of them with stuff like Int(), Double(), etc.
The .toInt() method for Strings, which returns an Optional since it might not be able to convert the string to a number. This means to safely unwrap what toInt() returns you need to use an if-let statement, as described in the section below on Optionals.
Incrementing/Decrementing:
++ and -- are used for incrementing and decrementing.
Equal sign and white space:
Note that Swift will throw an error is you put either a value or the identifier directly next to the equal sign. Swift requires spaces around the equal sign during assignment.
i.e. var yo = 45 // this is okay
var yo= 45 // THROWS AN ERROR
var yo =45 // THROWS AR ERROR
************************ STRINGS ************************
String vs NSString:
The String class is not built into Swift, it is part of the Foundation library. Objective-C uses the NSString type, and Swift's String type is just a subclass of NSString. So you can assign a String to an NSString variable but not the other way around since you can't assign a subclass type to a superclass type. But you can type convert with the "as" operator like so:
swiftString = objcNSString as String
String interpolation:
Can use a variable or constant identifier directly in a quotation by using a backslash and then putting the identifier in parentheses, like so:
print("This is a variable: \(myVar)")
There is no length property directly on the String type, what you have to do is access the "characters" property and then call .count() on that:
var bird = "It's a bird"
bird.characters.count()
Some other String methods:
.isEmpty
.hasPrefix()
.hasSuffix()
Operators like += + < > work on Swift strings.
To loop through a string use a for-in loop:
for character in s { // code... }
You can extract utf versions instead of using unicode:
str.utf8
stf.utf16
Can look at all the characters in a string as unicode scalars like so:
for scalar in s.unicodeScalers {
f(scalar.value)
}
Accessing character in a string is a little annoying, you can't just use [index], instead you have to do this crap:
var str = "0123456789"
s[s.startIndex] <-- "0"
s[s.startIndex.successor()] <-- "1"
s[s.endIndex.predecessor()] <-- "9"
s[s.endIndex] <-- won't work, points one index past end of string, get error
s[advance(s.startIndex, 1)] <-- 1
s[advance(s.startIndex, 3)] <-- 3
s[advance(s.endIndex, -2)] <-- -8
s.insert("q", atIndex: advance(s.startIndex, 4)) <-- s: "0123q456789"
To remove a group of characters in a string, use a range. Here, we get the range of characters "456789", the last 6 characters) and then the removedRange() method is used to remove that substring from the main string so "s" then equals "0123":
let range = s.endIndex.advancedBy(-6) ..< s.endIndex
s.removeRange(range)
************************ ARRAYS ************************
An array, like in Python, can hold values of different types. However if you explicitly declare the type of the array then the array can only hold the data type that you declared the array to be.
Create an empty array:
var myArr = []
Explicitly declare array type:
var myArr = Array<String>() <-- ugly
var myArr = [String]() <-- ok
var myArr: [String] = [] <-- better
var myArr: [String] = ["joman", "checkyo"]
var myArr: [Int] = [23, 34, 2]
var myArr = [String](count:3, repeatedValue:"yo") <-- initialize elements with same value
Adding to an array:
You can add to an array that is either explicitly declared or is composed of elements of a single data type, using " += [value]".
var myArr = [34,45]
myArr += [67] // myArr now is [34,45,67]
This won't work:
var myArr = ["hey", 34]
myArr += [56] // throws an error
Accessing array elements:
Just use simple square bracket notation as normal:
myArr[1]
myArr[1] = "changed element"
Some Properties and Methods of arrays:
myArr.count // gives number of elements in array
myArr.append(value) // appends new element to end of the array
myArr += ["a","b"] // also appends
myArr.removeAtIndex(index) // removes and returns element at specified index
myArr.removeAll() // removes all elements of array, can choose to keep capacity
// in the argument like so: myArr.removeAll(keepCapacity: true)
myArr.removeLast() // removes and returns last element of array
myArr.insert(value, atIndex: index) // inserts a value at a certain index, pushing later
// elements in the array back on index
myArr[1...2] = ["a","b"] // this replaces elements at indices 1 and 2 with "a" and "b"
myArr[1...2] = ["a","b","c"] // number of elements pushed into array doesn't have to match
// the number of elements replaced
No support for Two Dimensional arrays, but it does support arrays of arrays:
var twoD: [[String]] = [["a","b","c"], ["d","e","f"]]
twoD[0] <-- ["a","b","c"]
twoD[0] = ["A","B","C"]
twoD[0][0] = "x"
************************ SETS ************************
Sets are like arrays except that it is a unique list, in that no two elements can have the same value.
// awkward syntax
var mySet = Set<String>()
var mySet = Set<String>(["a","b","c"])
// good syntax
var mySet: Set<String> = []
var mySet: Set<String> = []
var mySet: Set<String> = ["a","b","c"]
Methods on Sets:
mySet.insert("A")
if let oldValue = a.remove("A") { /* code */ } // remove returns an optional
mySet.removeAll()
mySet.isEmpty
mySet.count
mySet.contains("A")
mySet.isDisjointWith(b)
mySet.isSupersetOf(b) // subset of b if all element of a are in b
mySet.isSubsetOf(b) // vice versa
mySet.isStrictSupersetOf(b) // strict subset of b only if they have none of same elements
mySet.isStrictSubetOf(b) // vice versa
// these return a set new set
mySet.exclusiveOr(b) // all elements except those that are shared
mySet.intersect(b) // only the shared elements
mySet.subtract(b) // all elements mySet minus those that are also of b
mySet.union(b) // all elements, including shared
// these modify the set in-place
mySet.exclusiveOrInPlace(b)
mySet.intersectInPlace(b)
mySet.subractInPlace(b)
mySet.unionInPlace(b)
************************ DICTIONARIES ************************
Create a dictionary with the constructor:
var aDict = [KeyType:ValueType]()
i.e. var aDict = [String:Int]() // keys will be strings, values will be ints
var aDict = [String:Int] = [:] // better syntax
var aDict = [String:Int] = ["age": 33, "weight": 150, "height": 183]
Create a dictionary similar to creating an array, using square brackets:
var myDict = [key: value, key: value, key: value]
Accessing and editing dictionary elements same as arrays:
myDict[key]
myDict[key] = "new value"
Can also update a value like this, checking for a value and returning an optional:
if let old = myDict.updateValue("some new value", forKey: "someKey") { /* ... */ }
Adding a pair to a dictionary is the same syntax as changing a value of a current pair:
myDict[newKey] = "value"
Removing a value and its key from the dictionary, just assign nil to it:
myDict["someKey"] = nil
If you want to remove the value and get the old value though, you can use a method that returns an optional:
if let old = myDict.removeValueForKey("someKey") { /* code... */ }
Printing a dictionary value you print as: Optional(value)
print(myDict[key]) // if value of the key is "yes" it will output: Optional("yes")
Some Properties and Methods of dictionaries:
myDict.count // gives number of key:value pairs in dictionary
myDict.removeValueForKey(key) // removes and returns value for the given key
myDict.keys // get all keys from dictionary
myDict.values // get all values from dictionary
myDict.isEmpty
Iterate over a dictionary with a for-in loop:
for (key, value) in dict {
// code...
}
************************ LOOPS ************************
Note that you don't need to put parenthesss around the conditions in loops, you can if you want though, it won't cause an error.
for loop:
for var i=0; i<10; i++ { }
for-in loop:
for item in collection { } // note that item is a constant in the loop
for num in 1...10 { } // ... gives a range inclusive on both ends
for num in 1..<10 { } // ..< gives a range non-inclusive of upper limit
while loop:
while condition { }
do-while loop:
repeat { } while condition
************************ CONDITIONAL STATEMENTS ************************
if-statement: // Note that in Swift you MUST always use curly braces!
if condition {
} else if condition {
} else {
}
Swift will let you break out of an if-statement with a 'break' statement.
switch statement:
Note that switch statements must handle all possible case statements or have a default statement, otherwise it will cause an error. Also no 'break' statement is needed because switch will only ever execute a single case block.
switch variable {
case value:
// code
case value:
// code
default:
// code
}
In Swift the case statement doesn't take a single value, but instead takes a pattern, so this makes it more powerful in that it could be a single value, or it could be multiple values, or it could be a range, etc. So it can do number ranges or multiple values of any type in a case like so to make up for the fact that every case statement has an implied break in it, so you can still have multiple values use the same case block this way:
switch variable {
case 1...5:
// code
case value,value,value:
// code
case "aardvark"..."antelope" // any string that would sort between these gets matched
// code
}
There is also a 'fallthrough' keyword which I guess is like the opposite of a 'break' statement in that since Swift implicitly has a break inside each case statement, the 'fallthrough' keyword allows the code to go to execute the next case statement, thereby not following the implicit break.
Switch statement in Swift can be a lot more complex than in JavaScript, you can check again other data types besides just strings, and you can use 'where' clauses in the cases:
let vegetable = "red pepper"
func switchCase(veggie: String) -> String {
switch veggie {
case "celery":
let veggieComment = "It's a celery"
return veggieComment
case "cucumber", "watercress":
let veggieComment = "Goes in a sandwich"
return veggieComment
case let x where x.hasSuffix("pepper"):
let veggieComment = "Is it a spicy \(x)"
return veggieComment
default:
let veggieComment = "Everything tastes good in a soup"
return veggieComment
}
}
switchCase(vegetable)
switchCase("celery")
switchCase("watercress")
switchCase("something else")
let num = 23
func switchNum(num: Int) -> String {
switch(num) {
case 23:
return "Michael Jordan"
default:
return "Some other num"
}
}
switchNum(num)
if-statements with optional binding and where and commas and other weird stuff:
You can use a single if-statement to bind multiple values. A 'where' clause can be added to add another check on the condition. And you can use commas to add another optional binding:
var optName: String? = "Jonny Appleseed"
var greeting = "Hello"
var optionalHello: String? = "Hello"
if let hello = optionalHello where hello.hasPrefix("H"), let name = optName {
greeting = "\(hello) \(name)"
}
let i = 5
if 1...10 ~= i {
// if i is 1 to 10 inclusive
}
Both if and switch statements can use commas to add multiple conditions, but an if-block will only run if all conditions are met, while a switch case will run if any of the commas-separated conditions are met.
You can label flow blocks like 'if' and 'while' like so:
whileLabel: while someCondition {
if otherCondition { continue whileLabel }
if someOtherCondition { break whileLabel }
}
outerLabel: if someCondition {
innerLabel: if otherCondition {
if someOtherCondition { break outerLabel }
if yetAnotherCondition { break innerLabel }
}
}
************************ FUNCTIONS ************************
Functions can return all data types including tuples and arrays and dictionaries.
To get some information on a function in Xcode just hold the "option" key on the keyboard and click on the function name and a bubble with information will pop up.
To create a function use the func keyword:
func funcName(parameter: dataType, parameter: dataType) -> returnType {
// code
}
i.e.
func area(height: Int, width: Int) -> Int {
// code
return someInt // use return keyword to return value
}
To call a function:
funcName()
As shown above, in function declarations you must explicitly assign types to the parameters.
Invoking functions:
When calling functions the first argument doesn't need to include a name but the rest do.
func blah(yo: Int, bowsa: Double, grillpiece: Bool) { //code... }
blah(23, bowsa: 9.23, grillpiece: true) // this works
blah(23, 9.23, grillpiece: true) // this causes an error because second argument missing label/name
Named Parameters:
You can force the argument list of a function call to include labels (named parameters) by putting the named parameters before each parameter name in the function definitions. When you do this Xcode will display the named parameters when the function is called to the programmer. But if the local parameter and the named parameter are the same name there is a shortcut in which you don't need to write the same name twice, you just put: #paramName
Named parameters are used to remove all ambiguity as to what the parameters are when calling a function.
Named parameters is an extra name for a parameter. Not really sure why you would ever want to do this, just choose a good name! But the named param comes before the local param, and the named param is the name you use in the function invocation, while the local name is what you'd refer to the param inside the function definition.
Has #namedParam been taken out of swift now?? Causes an error now. And it doesn't really seem to serve any purpose anyway as you would just use the one name anyways if the names were going to be the same instead of adding a # before it.
Function definition Syntax:
func myFunc(namedParam localParam: dataType, namedParam2 localParam: dataType) {
// code
}
or if namedParams and localParams are the same:
func myFunc(#param: dataType, #param: dataType) {
// code
}
Function call Syntax:
myFunc(namedParam: value, namedParam2: value)
i.e.
func area(squareHeight height: Int, #width: Int) {
// code
}
area(squareHeight: 20, width: 15) // function call
Returning tuples from a function:
To return a tuple from a function:
func myFunc(params) -> (dataType, dataType, dataType) { }
You can also return a tuple using named tuple values to access the values individually, but then still assign the return value to a single identifier and then instead of accessing the tuple values by their index you can access them by their names:
i.e.
func myFunc(params) -> (cat: String, age: Int, weight: Double) { // code... }
let result = myFunc()
result.cat // equivalent to result.0
result.weight // equivalent to result.2
************************ TUPLES ************************
A tuple can hold different data types in a single tuple, which is a list of things surrounded by parentheses.
Tuples are first class objects in Swift, so they can be passed into and returned from functions.
Create a tuple by assigning it to a single identifier:
var tupleName = (value, value)
Creating a tuple by assigning it to a tuple with each tuple value corresponding to a named value in the tuple it is being assigned to, so that you can directly access tuple values by name:
var (name1, name2, name2) = (value1, value2, value3)
i.e.
let (found, name) = (true, "todd")
print(found) // outputs: true
If you don't want to assign all the tuple values then use an underscore, and underscore basically says to ignore that tuple value:
let (_, name) = (true, "todd") // can't get access to the true value
print(name) // outputs: "todd"
Access elements of a tuple by using dot notation and the index:
myTups.0
myTups.1
Can also make the tuple values named values when assigning a tuple to a variable, you would then access those tuple values with the dot syntax and their name:
let tupleName = (name: 'Todd', age: 33)
tupleName.name
tupleName.age
Returning tuples from a function:
To return a tuple from a function:
func myFunc(params) -> (dataType, dataType, dataType) { }
You can also return a tuple using named tuple values to access the values individually, but then still assign the return value to a single identifier and then instead of accessing the tuple values by their index you can access them by their names:
i.e.
func myFunc(params) -> (cat: String, age: Int, weight: Double) { // code... }
let result = myFunc()
result.cat // equivalent to result.0
result.weight // equivalent to result.2
An example using a tuple for a switch statement including using 'let' and 'where':
let aPoint = (1.0,2.0)
switch aPoint {
case (let x, 2.0): // first tuple value can be anything
print("\(x)")
case (1.0, let y): // second tuple value can be anything
print("\(y)")
case let(x,y) where x>0.0 && y>0.0: // x and y must be greater than 0.0
print("\(x),\(y)")
case let(x,y): // basically a default but can use x and y in the block
print("\(x),\(y)")
}
************************ ENUMS ************************
An enum in Swift is an actual type, so you can't, for example, convert easily between a number and an enum number.
To make an enum you just set up a named block with the 'enum' keyword and list the enums for it with the 'case' statement. You can either put each listed enum on its own line with a 'case' or you can put a bunch on one line using commas.
enum CompassPoint {
case North
case South
case East, West
}
var direction = CompassPoint.North
direction = .East
In the above example, CompassPoint.North is assinged to direction, and since the compiler then knows that direction is of the enum type CompassPoint, you don't even need to explicitly state CompassPoint if you want to change that value, you can just throw a dot syntax in there operating on nothing explicit (implicitly on CompassPoint) with another CompassPoint enum value attached.
This looks similar to how you can use enums in switch statements:
switch direction {
case .East: print("East")
default: print("Not East")
}
Enums are not just simple lists of possible values, the enum can carry some data with it, called an associated value. There is some very compact syntax to do this:
enum PostalCode {
case US(Int,Int)
case UK(String)
case CA(code:String)
}
In the above code US, UK, and CA are the enum values for the PostalCode enum, and using parentheses you can specify the data types that are the associated value for each enum value. You can also give the associated value a name, such as in the CA case above. If you include a label/name for an associated value then when setting that value you have to include the name. Set these like so:
var somewhere = PostalCode.US(78741,1234)
var somewhere = PostalCode.CA(code: "V5K 0A1")
To access the associated data in an enum you have to either use a switch statement or a special version on an if-statement.
switch somewhere {
case .UK (let s):
print("\(s)")
case .US (let loc, var route):
print("\(loc)-\(route)")
case .CA break;
}
When the data associate with every element on an enum type all have the same type it is called a raw value. So say we create an enum whose raw value is a Character:
enum ASCIIControls: Character {
case Newline = "\n"
case Carriage = \r"
case Tab = "\t"
}
For enums that have a raw value, depending on the type of value, Swift will implicitly assign the rest of the elements values after you assign the first one. For example, with Ints, if you want the enum values to be 1,2,3,4,5... etc you only have to assign the 1 value and Swift takes care of the rest.
enum Planet: Int {
case Mercury = 1,
Venus, Earth, Mars, Jupiter, // Venus to Neptune are 2,3,4,5,6,7,8
Saturn, Uranus, Neptune
}
You can access the raw value of an enum element with dot notation and using the 'rawValue' property:
let x = Planet.Earth.rawValue // 3
You can also access a raw value of an enum element with a if-let statement and parentheses syntax:
if let aPlanet = Planet(rawValue: 9) {
print("\(aPlanet.rawValue) exists")
}
The above if-let statement will fail because there is no member of the Planet enum whose rawValue is 9, so aPlanet will be nil so the if-block will not run.
You can also put functions inside enum definitions, but they are restricted in what they can do. It can also have an init() constructor, which has restrictions as well, like it must assign to 'self'.
enum Dimension {
case DISTANCE(Int)
init(distance: Int) {
self = DISTANCE(height + 100) // init() function must assign to self
}
fun value() -> Int {
switch self {
case .DISTANCE (let value): return value
}
}
}
// use standard way to create an enum
let aDistance = Dimension.DISTANCE(10)
aDistance.value() // 10
// using the constructor results in a different value
let anotherDistance = Dimension(distance: 10)
anotherDistance.value() // 110
Example of using an enum to implement a simple state machine:
enum ConnectionState {
case closed, opening, open, closing
mutating func next() {
switch self {
case closed: self = opening
case opening: self = open
case open: self = closing
case closing: self = closed
}
}
}
var state = ConnectionState.closed
state.next() // opening
state.next() // open
state.next() // closing
state.next() // closed
************************ OPTIONALS ************************
Optionals handles situations where a particular variable doesn't have a legitimate value.
Swift has something called Optionals which allows you to return from a function a value or indicate the absence of a value by specifying nil. So an Optional just allows something to either be a normal data type value or nil, instead of just a normal data type.
To return an Optional from a function you just add a question mark at the end of the return type in the function definition:
func myFunc(params) -> returnType? { } // the ? makes the return type an optional
Optionals are used so that you can return two different types of values from a function: the given return type, or nil. Since nil is not a normal data type you can't return nil unless you specify the return type as an Optional.
The reason to use Optionals is when a function needs to either return a normal value or otherwise nil, like say a search function that should return a String if the argument matches a list of Strings, or if not found then it should return nil rather than some arbitrary String value to stand for an unsuccessful search.
An Optional cannot be used as a boolean though, so you can't put a function that returns an Optional straight into a conditional. Instead you need to set the return value to a constant and then unwrap the value, which you do by putting a ! after an Optional value. But you cannot unwrap a nil value, it will cause an error. So what you have to do is use an if-statement but assign the Optional return value to a constant (an if-let statement) in the conditional and if it is nil then the if-block won't execute, but if the return value is not nil then the if-block will execute. And you don't need to use the ! mark at all to unwrap the value of the Optional.
i.e.
if let result = myFunc() {
// code executes if myFunc() doesn't return nil
} else {
// code executes if myFunc() returns nil
}
Optional Chaining
When an Optional is used as a return type for a function, those function calls can be chained using dot syntax.
When you use an if-let statement to handle an Optional return value of a function, if the function returns a String let's say (or nil, of course) and you want to convert the string to an integer using the toInt() method on the string, instead of having to make another if-let statement (since .toInt() itself returns an Optional), you can instead chain the toInt() method onto the original if-let statement by putting a question mark before the dot operator.
i.e.
if let result = myFunc()?.toInt() { // result will now be an Int value
// code...
}
You can chain a bunch of optionals together. This is the equivalent of fixing the "cannot find ____ of undefined" error in Javascript. Here, if some property is nil, if you've used optional chaining on each property that could be nil then there will be no error because when the optional is nil it just exits the statement. Works on dot syntax or bracket syntax.
if let blah = obj.a?.b?.c?.d { /* ... * /}
if let blah = obj.a?["someKey"]?.c?.d
If you have an object that could be nil and want to call a method on it you can use the ! or the ?. Using ! means that if the object is nil then the program will terminate (obviously don't use the ! in this case). Using the ? on the optional object will short circuit the statement if the object is nil so the method doesn't get called, and the program keeps running. or of course you can handle things more elegantly and use an if-let statement to decide what to do if the thing fails due to a nil.
optObj!.someMethod()
optObj?.someMethod()
Another set of notes on optionals:
Optionals are used for variables/constants whose value may or may not be there. It is used as a safety procedure in order to avoid program crashes. An optional value either contains a value or nil to indicate the value is missing. The use of optionals involves using questions marks and exclamation marks.
The idea behind optionals is that Swift will throw an error if you try to use a variable that has no value (a nil value), so if you are declaring a variable that might not have a value at some point when it could be accessed then you need to make it an optional to avoid Swift throwing an error.
To specify a value as optional mark it with a question mark (?) on declaration after the type
i.e. let optionalInt: Int? = 9
To get at the underlying value of an optional you must unwrap it. The most straightforward way to do this is to use the force unwrap operator, the exclamation mark (!). Only use the unwrap operator if you are sure the underlying value isn't nil.
i.e. let actualInt: Int optionalInt!
Another way to get at the underlying value of an optional is using optional binding, which is where you use an if-statement to check if the optional value has a value or is nil. In the code below the 'name' constant can only be used inside that if-statement, and the if-block will only run if optName is not nil.
i.e. var optName: String? = "Johnny Appleseed"
var greeting = "Hello"
if let name = optName {
greeting = "\(greeting) \(name)"
}
if-statements with optional binding and where and commas:
You can use a single if-statement to bind multiple values. A 'where' clause can be added to add another check on the condition. And you can use commas to add another optional binding:
var optName: String? = "Jonny Appleseed"
var greeting = "Hello"
var optionalHello: String? = "Hello"
if let hello = optionalHello where hello.hasPrefix("H"), let name = optName {
greeting = "\(hello) \(name)"
}
An implicitly unwrapped optional is an optional that can also be used like a non-optional value without the need to unwrap it each time it is accessed because an implicitly unwrapped optional is assumed to always have a value after a value is initially set, although the value can change. To do this you use an exclamation mark (!) instead of a question mark when declaring the value. Usually the only time you use implicitly unwrapped optionals is for tracking outlets between an interface and source code.
i.e. let implicitlyUnwrappedOptionalInt: Int!
Overview of using ! and ? for optionals:
?
declaring an optional value: var opt: String?
create a failable initializer in a class: init?(args: Types) {}
!
unwrapping an normal optional value: opt!
declaring an implicitly unwrapped optional: var opt: String!
************************ Structs ************************
A Struct, unlike in C++, can have methods assigned to it. One crucial difference between a Struct and a Class is that Structs are always pass-by-value, whereas Classes are always pass-by-reference.
A struct, like a class, can have an init() method, however it can't have a deinit block.
In a struct, if a method is going to modify the value of the struct, then it needs to be declared with the 'mutating' keyword, because Structs are constant by default. Only mutating functions can change the value of the struct.
You can change the value of the entire struct like so:
mutating func reset() { self = Circle() }
Here 'self' refers to the struct we are currently inside, when the reset() method on the struct is called it assigned a new struct to be the value for this struct.
struct Point {
var x=0,
y=0
mutating func changeSomething(a: Int) {
self.x = a
}
}
var newStruct = Point()
newStruct.x = 10
func f(var a: Point) {
a.x = -1
}
Structs can have other classes and structs nested inside of them like:
class NestedClass { }
struct NestedStruct { }
enum Nested { }
But these are not inner classes, in that they don't have access to the fields of the outer class/struct. You would access the nested class/struct like any other property on it:
OuterThing.NestedThing
************************ Classes ************************
class NameOfClass {
let classConstantProperty: Bool
var classVariableProperty: Double
var blah: Int
init(blah: Int, jams: String) {
// initialize class
self.blah = blah
classVariableProperty = jams
}
func someMethod(blah: Int) {
// code...
}
}
class NameOfClass: ParentClass {
}
Constructors in Swift are done with the init() method. You can have more than one constructor in a class but they have to take different parameters. With the init() constructor no 'func' keyword is needed. Every property of a class needs to be initialized either in the class definition or in the init() method. When calling the initializer to create a new object you have to put the parameter name for all the arguments, including the first.
In Swift there are "failable initializers". Sometimes you might want a class initilizer to fail if bad data is given to it during object instantiation. For example if some property of the object must be within a given range or else the object shouldn't be created, you use a failable initializer, which will return nil and not create the object. Use init? to declare a failable initializer.
i.e. init?(prop: String, prop2: Int) {
if prop2 < 0 {
return nil
}
}
The 'self' keyword refers to the class. If there is no name conflict between the properties of the class and the parameter then you don't need to use 'self' because the compiles will do it for you.
Just as there are initializers in a class there are de-initializers, using a deinit block. These are the same as destructors from other languages.
class Blah {
init() { /* ... */ }
deinit { /* ... */ }
}
There is no garbage collection in Swift, so the deinit function is very predictable. For example, if an object is created inside a function, the deinit will be called when that function exits, instead of waiting for garbage collection to get rid of it at some point later.
When making methods in a class, if you use declare the function with the 'final' keyword, that means subclasses cannot override it.
final func aClassMethod() { /* ... */ }
There is also the 'lazy' keyword that can be put on properties of a Class. The 'lazy' keyword means that variable's assignment gets deferred until that variable is actually called as a property on an instance of the class, instead of being initialized when the object is instantiated.
class Circle {
var centerX:Float
var centerY:Float
lazy var radius = getDefault()
init() { /* ... */ }
func setCenter(x: Float, y: Float) {
self.centerX = x
self.centerY = y
}
}
var hole = Circle()
hole.setCenter(12.3, 20.99)
print(hole.radius) // getDefault() is called here to initalize radius
Swift also has class level properties and methods using the 'static' keyword for class level properties and 'class' for class level methods.