Swift notes from iOS dev learning - assumes Java knowledge. Test apps are here: https://github.com/IngmarBoddington/XcodeProjects
General
======
- Strongly typed, object oriented
- Semi-colons are optional to end statements
- // or /* */ for comments
- Used across Apple platforms
- Execution starts in main.swift
- Libraries imported using import statements
- Can use any unicode character in names
- Short circuits evaluations of conditional statements
- Functions and tuples are unnamed compound types (all others are named types)
- Any names type can have methods (classes, structs, enums)
Variables + Simple Types / Operators
=====
All variables are value types, except objects which are reference types
Standard Types
String (Double quoted)
Int (Also Int8, Int16... and UInt8, UInt16...)
Double (inferred by default over Float)
Float
Boolean
Integer literals
Decimal, e.g. 234
Binary, e.g. 0b101
Octal, e.g. 0o76
Hexadecimal, e.g. 0xA3
Floating-point literals
Decimal, e.g. 32.4
Exponent, e.g. 1.5e2
Hexadecimal, e.g. 0x1Fp2
Use var for variables or let for constants
var <identifier> = <value>
let <identifier> = <value>
//define explicit type using ':' - This is generally not required for simple types
var string: String = "I am a string"
//Can mix on same line
var varOne = 1, varTwo = 2, varThree: Int
Can define without initialising (but must be initialised before use)
Variables can generally be cast by using initialisers for the various types:
Int(<value>) //Will truncate floating point numbers
String(<value>)
etc
Standard Maths Operators / logic
= //Does not return any value on assignment / fail
+,-,*,/,%
<,>,<=,>=,==
||,&&,!
===, !==
( condition ? A : B )
x...y //Range, Ints only
x..<y //Range (not including value of y), Ints only
Can use _ to represent a temp variable which is not used (e.g iterator in for loop)
Strings
=====
var str = "Hello";
//Iterate over characters and print
for character in newString.characters {
print(character, "Can", "add", "more (spaces are added automatically)");
}
//Optionally add separator / terminator
print("hello", "this is", "a", "test", separator: "||", terminator: "***")
//Create a string with manipulation methods
var newTypeString = NSString(string: newString);
//Substring
NSString(string: newTypeString.substring(from: 6)).substring(to: 4);
//Substring
newTypeString.substring(with: NSRange(location: 6, length: 4));
//Matching
if newTypeString.contains("Dave") {
print("String contains Dave");
}
//Split into array
newTypeString.components(separatedBy: " ");
//Case
newTypeString.uppercased
newTypeString.lowercased
//Interpolate in Strings, concatenate strings
var string = "My age is \(age)" + ", more"
Collections
=====
//Arrays - stupid number of ways to create
//Value type (but only copied when needed as an optimisation)
var a1:[Int] = [Int]()
var a2:[Int] = [Int](repeating: 10, count: 4)
var myArray:Array<Int> = Array<Int>()
vara3:Array<Int> = Array<Int>(repeating: 0, count: 5)
var myArray = [1, 2, 3, 4]
let myArray: [Int] = Array(1...50);
myArray[1] === 2
myArray[1...3] = [3,4,5,6,7] //Can do daft range expansion
array.count
array.isEmpty
array.removeLast()
array.append(<value>)
array.remove(at: <index>)
//Dictionaries
varenglishFrench: Dictionary<String,String>
var myDic = ["key" : "value", "key2" : "value"]
myDic["newKey"] = <value>
myDic.removeValue(forkey: "key")
myDic.updateValue(forkey: "key") //Will add and return null if didn't exist
myDic.keys
myDic.values
//Sets
//A set can only store types that conform to the Hashableprotocol and an implementation of ==(the “is equal to” operator) (Most types are hashableby default)
var myCustomers= Set<String>()
var myFriends: Set<String> = Set<String>()
myFriends.insert("John"); myFriends.insert("Duncan"); myFriends.insert("Pam")
var yourFriends: Set = ["Pam", "Paul", "Keith"]
var numbers: Set<Int> = [10,20,30]
numbers = [] // numbers is now an empty set
let setA: Set = [1,2,3,4]
let setB: Set = [3,4,5,6]
var res = setA.union(setB) // [1,2,3,4,5,6]
res = setA.subtracting(setB) // [1,2]
res = setA.intersection(setB) // [3,4]
res = setA.symmetricDifference(setB) // [1,2,5,6]
//All collections must have types, var / let determine if mutable
var myArray : [<type>]
var myDic : [<keyType> : <valueType>]
QOWDJIOQWJDOIW
Control Structures
==================
Use break to exit a loop
Use continue to skip to next loop
//If
if <condition> {
//If true
}
//If / else
if <condition> {
//If true
} else {
//if False
}
//If / else-if
if <condition> {
//If true
} else if <condition> {
//if true
} else {
//if False
}
//While loop
while <condition> {
//Stuff
}
//Do-While loop
do {
//Stuff
} while <condition>
//Iterate over array or a range
for <identifier> in <arrayIdentifier|range> {
//Stuff with <identifier>
}
//Iterate over dictionary tuples
for (<indexIdentifier>, <valueIdentifier>) in <arrayIdentifier>.enumerate() {
//Stuff with <indexIdentifier> / <valueIdentifier>
}
//Switch statement - must be exhaustive (e.g. Int switch will have to have a default)
//Skips rest of case statements on match
switch variable {
case value1:
//Stuff
case value2:
//Stuff
case x...y: //Can use ranges
//Stuff
case x, y, z:
//Stuff
case var m where m < 0 || m > 100: //Can use a where clause with temp variable
//Stuff
default:
//Stuff
}
//Loops can be labelled to allow for use of break at parent levels
outerLoop: for i in 1...5 {
for j in 1...5 {
if i == 3 && j == 2 {
break outerLoop
} else {
print("i is \(i) and j is \(j)")
}
}
}
Functions
=====
Return definition required if the function has a return statement
//Func with return and two params (one with a defined external naming which overrides the internal name when invoking function)
//Can use _ as external name which means that no parameter naming is required when invoking function
func funcName (varInternalName: Int, varExternalName varInternalNameTwo: String) -> resultType {
statements
return value:resultType
}
//With a default value
func funcName (varInternalName: Int = 8) -> resultType {
statements
return value:resultType
}
//Variadic parameters
func myFunc(something: Int...) {
//Stuff
}
myFunc(1,2,3,4)
//Pass by reference
func swapNums(num1:inout Int, num2:inout Int) {
let temp = num1
num1 = num2
num2 = temp
}
swap(&x, &y)
//Type description
(Type, Type, Etc) ()
(Type, Type, Etc) -> Type
(Type, Type, Etc) -> (Type, Etc)
//Can set to var
var sillyFunc = { (a: Int, b: Int) -> Bool in a > b) }
//SHhrtest closure form example - params are zero index, types and return have been inferred
sortedNumList= numList.sorted(by: { $1 < $0 })
sortedNumList= numList.sorted(){ $1 < $0 }
Optionals
=====
- Values which can be nil / not set
- To force unwrap use exclamation mark after variable - reverts to type from optional
//Define:
var variable: Type?
let variable: Type?
//Can test with standard nil checks or:
if let variable = optional {
/...
} else {
/...
}
Tuples
======
Simple unnamed strongly typed data structure
Can be nested
e.g. var myTuple = ("I Am A Tuple", (10, 5))
Access using zero index
myTuple.0 //I Am A Tuple
Can use in switch statement
switch myTuple {
case ("Meh", (10, 5):
//No match
case (_, let(first, second)):
//Hit and disregards first value in tuple and inits first and second vars
case (_, let(first, second)) where <logic>
//etc
default <etc>
//etc
}
Access Modifiers
=====
Access (to variables and types) can be controlled with the keywords open, public, private, fileprivate, and internal
Default is internal
Open access and public access enable entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use open or public access when specifying the public interface to a framework.
Internal access enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure.
File-private access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
Classes
====================
All classes must have a designated initialiser (a constructor)
Any other (optional) initialisers must be marked as "convenience" and call the designated initialiser
One is provided if no parameters or all parameters have default
All parameters must either have a default or be set by the designated initialiser
Can declare properties or method as static
class ClassName {
var property
//"Designated" initialiser
init(property: Int){
//
}
//Optional "Convenience" initiliser
convenience init()
self.init(property: 5)
}
func method(vars) {
//....
}
}
var class = ClassName(5)
class.method(vars)
Methods can have same name, different parameter types / names / defaults
Classes can have a deinit destructor method with no args too
Properties can be marked as lazy (only calculated when needed)
lazy var value = Expensive()
Properties can be normal "stored" properties or "computed" properties
//Computed property (get / set)
var value: Int {
get {
//
}
set (valueName) {
//will get newValue if no valueName given
}
//
//Get only, do not need to include block:
var value: Int {
//
}
Can "adopt" protocols or inherit from parent classes
Methods which override an existing parent method must start with 'override' keyword
class myClass: parentClassOrProtocol {
init() {
self.stuff()
super.stuff()
}
}
Protocols
=====
Interfaces, basically. Classes can adopt as many as they like
protocol {
func funcName() -> Type
var variable: Int
}
Extensions
=====
Add to source without altering original code
Cannot use stored properties
Can adopt a protocol to make a class adopt it
extension classBeingExtended {
//Code
}
Structures
======
Extremely similar to classes in definition (includes all of the above syntax)
Value type, not reference like object
Cannot use inheritance
A default initializer and a memberwiseinitializer are automatically generated (if none defined)
Memberwise is simply ordered constructor with all params of the struct
Methods which change state must be marked as mutating (as value types should be immutable)
mutating method(vars) {
//can replace entire instance
self = ckass(changesVars)
}
Can aggregate further structs (can access using chained dot operators)
parent.child.parameter
Enumerations
=====
Extremely similar to classes in definition (includes all of the above syntax) - but defines a set
//Example with days of week and 1 -> 7 as rawValues (access Key)
enum WeekDay:Int {
case Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}
var day = enum.Monday
let todayNum= day.rawValue
varday4 = WeekDay(rawValue: 4) //Returns an optional
//Can shortcut when used in switch statements:
switch day {
case .Monday:
//Stuff
case .Tuesday:
//Stuff
...
default:
//Stuff
}
//Can also have different states as tuples
enum State {
case Start(String)
case DoWork(Int, Int, Int)
case End(String, Int)
}
vartheState: State = State.Start("about to start the work")
theState = State.DoWork(3,4,5)
theState = State.End("Finished", result)
Error Handling
=====
Enums which implement "Error" can be thrown and caught
Code must state (using try) when calling a method which may throw
enum myError: Error {
case IllegalArgs(args: [String])
case ...
}
func funcWhichCanThrow() {
throw myError.IllegalArgs(["Meh"])
}
do {
try funcWhichCanThrow
} catch myError.IllegalArgs {
//
}
//Will always be executed even if error in surrounding block
defer {
//
}}
//Converts result to null on exception throw
let var = try? func()
//Error suppression! :(
try! func()
//Guard statements (inverse if for error catching generally)
guard <condition> else {
//code, throw maybe
}
Generics
=====
Generally use T symbol but can use anything
class sillyClass<T> {
init() {
//Do stuff with type T
}
...
}
//Forcing adherence to type
class sillyClass<T: Type> {
init() {
//Do stuff with type T
}
...
}
func genericSwap<T>(_ item1 : inoutT, _ item2 : inoutT) {
let temp = item1
item1 = item2
item2 = temp
}
//Adding further constraints
func myFunc<T1: Sequence where T1.Generator.Element : Person>(theList: T1) -> T1.Generator.Element? {
//Stuff
}
Other Stuff
-----
arc4random_uniform(<limit>) //Random nums
--------------------------------------------------------------
iOS APIs / XCode
View Controllers
=====
Common methods:
viewDidLoad - Initial load delegate method
viewDidAppear - User navigated to view delegate method
didReceiveMemoryWarning -
Actions
=====
A method which is invoked by an event (managed / brough in through the storyboard
@IBAction <definition>
Outlets
=======
A property representing a view item which can be manipulated
@IBOutlet <definition>
Table Views
=====
Need to extend UITableViewDelegate, UITableViewDataSource and implement:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
-> Return number of rows in table view
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
-> Return content of a cell
Use <tableView>.reloadData(); to update the table with any changes
An example delete swipe:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
items.remove(at: indexPath.row);
UserDefaults.standard.set(items, forKey: "list")
self.tableList.reloadData();
}
}
Web View
=====
Example load URL in webview:
let url = URL(string: "http://www.stackoverflow.com")!;
<webviewOutlet>.loadRequest(URLRequest(url: url));
Example load HTML:
<webviewOutlet>.loadHTMLString("<h1>Hello World!</h1>", baseURL: nil);
Note (if insecure content error, can add exception):
https://stackoverflow.com/questions/31254725/transport-security-has-blocked-a-cleartext-http
Example scrape (asynchonous):
if let url = URL(string: "http://www.stackoverflow.com") {
let request = URLRequest(url: url);
let task = URLSession.shared.dataTask(with: request) {
data, response, error in
if error != nil {
print(error!);
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue);
print(dataString!);
}
}
}
task.resume();
}
Persistant Storage
=====
UserDefaults
UserDefaults.standard.set(<value>, forKey: <key>);
- Save a value
let <object> = UserDefaults.standard.object(forKey: <key>);
- Fetch a value object
Use a let check to ensure a value is set before use when fetching from store
Keyboard Control
=====
To close keyboard when user taps off the keyboard:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true);
}
To close keyboard on return tap, need to extend UITextFieldDelegate and
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder();
return true;
}
Timers
======
Set a timer using normally
Timer.scheduledTimer(timeInterval: <#T##TimeInterval#>, target: <#T##Any#>, selector: <#T##Selector#>, userInfo: <#T##Any?#>, repeats: <#T##Bool#>)
Stop a timer using:
<Timer>.invalidate();
e.g.
if (self.started == false) {
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.tickerFunction), userInfo: nil, repeats: true)
self.buttonText.setTitle("Stop!", for: .normal);
} else {
self.timer.invalidate();
self.buttonText.setTitle("Start!", for: .normal);
}
Animation
=====
(Use above timer methods)
Set a new image for an UIImageView:
self.image.image = UIImage(named: <string>);
Simple fade in example:
@IBAction func fadeIn(_ sender: Any) {
<image>.alpha = 0;
UIView.animate(withDuration: <time>, animations: {
<image>.alpha = 1;
})
}
Simple slide in example (from left):
@IBAction func slideIn(_ sender: Any) {
<image>.center = CGPoint(x: image.center.x - <offscreenValue>, y: image.center.y);
UIView.animate(withDuration: <time>, animations: {
<image>.center = CGPoint(x: self.image.center.x + <offscreenValue>, y: self.image.center.y)
})
}
Simple grow example (from top left)
@IBAction func grow(_ sender: Any) {
<image>.frame = CGRect(x: 0, y: 0, width: 0, height: 0);
UIView.animate(withDuration: 5, animations: {
<image>.frame = CGRect(x: 0, y: 0, width: 200, height: 200);
})
}