Swift has these salient features.
- Object-orientation
”Everything is an object”- Clarity
Swift’s syntax is clear with few hidden shortcuts and minimal syntactic trickery.- Safety
Swift enforces strong typing to ensure that it knows, what the type of every object reference is at every moment.- Economy
Swift is a fairly small language. The rest must be provided by your code or by libraries of code that you use - Such as Cocoa.- Memory management
Swift manages memory automatically.- Cocoa compatibility
Swift is explicitly designed to interface with most of the Cocoa APIs which are written primarily in C and Objective-C.
- A complete Swift command is a statement.
- Swift is a compiled language. This means your code must build - passing through compiler.
- The Swift compiler is very strict. The strictness of the compiler is one of Swift’s greatest strengths.
- Object: It is something you can send a message to.
- Message: It is an imperative instruction.
In Swift, the syntax of message-sending is dot-notation. We start with the object; then there’s a dot; then there’s the message.
The idea of everything being an object is a way of suggesting that even “primitive” linguistic entities can be sent message.
In Swift, every noun is an object, and every verb is a message.
An object type can be extended in Swift meaning that you can define your own messages on that type.
In Swift, there are no scalars; all types are ultimately object types. That’s what “everything is an object” really means.
Swift has three kinds of object type: classes, structs, and enums.
- A variable is a name for an object. Technically it refers to an object; it is an object reference.
- The object to which the variable refers is the variable’s value.
- No variables comes implicitly into existence; all variables must be declared.
- In Swift declaration is usually accompanied by initialization.
- A variable declared with let is a constant; its value is assigned once and stays.
- Variables also have a type. This type is established when the variable is declared and can never change.
- Variables literally have a life of their own - more accurately, a lifetime of their own.
- Thus a variable can be not only a way of conveniently naming something, but also a way of preserving it.
- In general, executable code must live inside the body of function.
- A function is a batch of code that can be told, as a batch, to run.
- go() cannot live on its own either. It might live in the body of a different function
- Swift also has a special rule that a file called main.swift, exceptionally, can gave executable code at its top level, outside any function body, and this is the code that actually runs when the program runs.
- Module import statements
- A module is an even higher-level unit than a file.
- a module can consist of multiple files, and these can all see each other automatically.
- But a module can’t see another module without an import statement.
- Variable declarations
- A variable declared at the top level of a file is a global variable.
- All code in any file will be able to see and access it.
- Function declarations
- A function declared at the top level of a file is a global function.
- Object type declarations
- The declaration for a class, a struct, or an enum.
In a Swift program, things have a scope.
This refers to their ability to be seen by other things.
- The rule is that things can see things at their own level and at a higher level containing them.
- A module is a scope.
- A file is a scope.
- Curly braces are a scope.
- When something is declared, it is declared at some level within that hierarchy.
- Scope is thus a very important way of sharing information.
- Things also have a lifetime , which is effectively equivalent to their scope.
//Manny is a nameSpace
class Manny {
class Klass {}
}
A namespace is a named region of a program. Clearly, namespaces and scopes are closely related notion.
In effect, message-sending allows you to see into scopes you can’t see into otherwise.
The top-level namespaces are modules.
Your app is a module and hence a namespace.
That namespace’s name is, by default, the name of the app.
- When you import a module, all the top-level declarations of that module become visible to your code as well.
- Swift itself is defined in a module - the Swift module. But you don’t have to import it, because your code always implicitly imports the Swift module.
- print is in fact a function declared at the top level of the Swift module, and your code can see the Swift module’s top-level declarations because it imports the Swift.
- Your own app module, however , overshadows any modules you import.
- That means that if you declare a term identical to an imported term, you lose the magical ability to use the imported term without specifying the namespace.
- If you were to declare a print function of your own, it would effectively hide the Swift print function.
- You can still call the Swift print function, but now you have to use the namespaced Swift.print explicitly.
Dog.bark() //compile error
By default, properties and methods are instance properties and methods. You can’t use them as messages to the object type itself. You have to have instance to send those messages to.
- An instance is responsible not only for the values but also for the lifetimes of its properties.
- In short, an instance is both code and data.
- The code it gets from its type and in a sense is shared with all other instance of that type, but the data belong to it alone.
- An instance is a device for maintaining state. It’s a box for storage of data.
An instance is an object, and an object is the recipient of messages.
Thus, an instance needs a way of sending message to itself.
This made possible by the keyword self.
func sayString(_ array: String ...) {
for s in array { print(s) }
}
sayStrings("hey", "ho", "nonny nonny")
A parameter can be variadic.
This means that the caller can supply as many argument values of this parameter’s type as desired.
func removeCharacter(_ c: Character, from s: inout String) -> Int {
var s = s
var howMany = 0
while let ix = s.firstIndex(of:c) {
s.remove(at:ix)
howMany += 1
}
return howMany
}
let s = "hello"
let result = removeCharacter("1", from: &s) //2
- The type of the parameter we intend to modify must be declared inout keyword.
- When we call the function, the variable holding the value to be modified must be declared with var, not let.
- Instead of passing the variable as an argument, we must pass its address. This is done by preceding its name with an ampersand(&).
- When a function with an inout parameter is called, the variable whose address was passed was passed an argument to that parameter is always set.
func getRed(_ red: UnsafeMutablePointer<CGFloat>,
green: UnsafeMutablePointer<CGFloat>,
blue: UnsafeMutablePointer<CGFloat>,
alpha: UnsafeMutablePointer<CGFloat>) -> Bool
The Cocoa APIs are written in C and Objective-C.
So instead of the Swift term inout, it use such as UnsafeMutablePointer.
func popoverPresentationController(
_ popoverPresentationController: UIPopoverPresentationController,
willRepositionPopoverTo rect: UnsafeMutablePointer<CGRect>,
in view: AutoreleasingUnsafeMutablePointer<UIView>) {
view.pointee = self.button2
rect.pointee = self.button2.bounds
}
If you want to change its value in Cocoa framework, sometimes you have to declare UnsafeMutablePointer not the inout keyword.
An ititializer is a function for producing an instance of an object type. Strictly speaking, it is a static/class method, because it is called by talking to the object type.
Observe that
Dog()
calls an initializer even though our Dog vlass doesn't declare any initializers! The reason is that object types may have implicit initializers.
A stored instance proprty must be given an initial value. As I explained a moment ago, this doesn't have to happen throught assignment in the declaration.
struct Greeting {
static let friendly = "hello there"
static
}
A static/class property is accessed through the type, and is scoped to the type, which usually means that it is global and unique.
class Moi {
let first = "Matt"
let last = "Neuburg"
let whole = self.first + " " + self.last // compile error
}
A propert declaration that assigns an initial value to the property cannot fetch an instance property or call an instance method. There is no
self
yet -self
is exactly what we are in the process of initializing.
//Solution1
class Moi {
let first = "Matt"
let last = "Neuburg"
var whole : String {
self.first + " " + self.last
}
}
//Solution2
class Moi {
let first = "Matt"
let last = "Neuburg"
lazy var whole = self.first + " " + self.last
}
There are two common solutions in that situation
Make this a cimputed property:
A computed property can refer toself
in a getter or setter function because the function won't run until the property is accessed.
Declare this property lazy:
Alazy
property can refer toself
in its initializer because the initializer won't be evaluated until the property is accessed.
struct Greeting {
static let friendly = "hello there"
static let hostile = "go away"
static let ambivalent = friendly + " but " + hostile
}
Unlike Instacne properties , static properties can be initialized with reference to one another. the reason is that static property initializers are lazy.
class MyClass {
var s = ""
func store(_ s:String) {
self.s = s
}
}
let m = MyClass()
let f = MyClass.store(m) // what just happened!?
f("howdy")
print(m.s) // howdy
Instance methods are actually static/class methods. The reason is that an instance method is actually a curried static/class method composed of two functions - one function that takes an instance, and another function that takes the parameters of the instance method.
enum Filter : String {
case albums = "Albums"
case playlists = "Playlists"
case podcasts = "Podcasts"
case books = "Audiobooks"
var query : MPMediaQuery {
switch self {
case .albums:
return .albums()
case .playlists:
return .playlists()
case .podcasts:
return .podcasts()
case .books:
return .audiobooks()
}
}
}
An enum can have instance properties and static properties, but there's a limitation: An enum instance properyty can't be a stored property"
enum Silly {
case one
var sillyProperty : String {
get { "Howdy" }
set {} // do nothing
}
}
var silly = Silly.one
silly.sillyProperty = "silly"
Code's reference to the enum instance itself must be a variable(
var
), not a constant(let
). In you try to assign to an enum instance property through alet
reference to the enum, you'll get a compile error.
enum Filter : String, CaseIterable {
case albums = "Albums"
case playlists = "Playlists"
case podcasts = "Podcasts"
case books = "Audiobooks"
static subscript(ix: Int) -> Filter {
Filter.allCases[ix] // warning, no range checking
}
}
let type = Filter[2] // podcasts
A subscript can be a static method.
An enum is a switch whose states have names.
Even when there are only two states, an enum is often better than, say, a mere Bool, because the enum's states have names. Moreover, you can store extra information in an enum's associated value or raw value.
enum InterfaceMode : Int {
case timed = 0
case practice = 1
}
var interfaceMode : InterfaceMode = .timed {
willSet (mode) {
self.timedPractice?.selectedSegmentIndex = mode.rawValue
}
}
And what are my interfaceMode enum's raw value integers for? That's the really clever part. They correspond to the segment indexes of a UISegmentedController in the interface!
A struct can have instance methods and static methods, including subscripts, Inf an instance method sets a property, **it must be marked as
mutating
, and the callers reference to the struct instance must be a variable(var
), not a constant(let
).
Class are reference types. This means, among other things, that a class instance has two remarkable features that are not true of struct or enum.
Mutability
Even if your reference to an instance of a classs is a constant(let
), you can change the value of an instance property through that reference.
Multiple reference
When a given instance of a class is assigned to multiple variables or passed as argument to a function, you get multiple references to one and the same object.
class Dog : Quadruped {
func bark () {
print("woof")
}
}
class NoisyDog : Dog {
override func bark () {
for _ in 1...3 {
super.bark()
}
}
}
let fido = Dog()
fido.bark() // woof
let rover = NoisyDog()
rover.bark() // woof woof woof
It often happens that we want to override something in a subclass and yet access the thing overrideen in the superclass. This is done by sending a message to the keyword
super
.
NoisyDog really does when it barks is the same thing Dog does when it barks, but more times.
type(of:)
: Applied to an object: the polymorphoc (internal) type of the object, regardless of how a refernce is typed.
Self
: In a method body
.Type
: Appended to a type in a type declaration to specify that the type itself(or a subtype) is expected.
.self
: Sent to a type to generate a metatype, suitable for passing where a type(.Type
) is expected.
Protocol thus give us another way of expressing the notion of type and subtype - and polymorphism applies.
Note that a type cna adopt more than onr protocol.
func f(_ x: CustomStringConvertible & CustomDebugStringConvertible) {
}
protocol MyViewProtocol {
func doSomethingReallyCool()
}
class ViewController: UIViewController {
var v: (UIView & MyViewProtocol)?
func test() {
self.v?.doSomethingReallyCool() // a MyViewProtocol requirement
self.v?.backgroundColor = .red // a UIView property
}
}
If the only purpose of a protocol is to combine other protocols by adopting all of them, without adding any new requirements, you can avoid formally declaring the protocl in the first place by specifiying the protocol combination on the fly. To do so, join the protocol names with &.
protocol MyClassProtocol : AnyObject {
// ...
}
To specify that a protocol can be adopted only by some class (and not a struct or enum) without specifying what class it must be, use the protocol type
AnyObject
which every class type adopts.
protocol MyViewProtocol where Self:UIView {
func doSomethingReallyCool()
}
protocol MyClassProtocol where Self:AnyObject {
// ...
}
A valuable byproduct of declaring a class protocol is that the resulting type can take advantage of special memory management features that apply only to classes.
protocol SecondViewControllerDelegate : AnyObject {
func accept(data:Any)
}
class SecondViewController : UIViewController {
weak var delegate : SecondViewControllerDelegate?
// ...
}
Te keyword
weak
marks thedelegate
property as having special memory management that applies only to class instances.
The
delegate
property is typed as a protocol, and a protocol might be adopted by a struct or an enum type.
So to satisfy the compiler that this object will in fact be a class instance and not a struct or enum instance, the protocol is declared as a class protocol.
protocol Flier {
init()
}
class Bird : Flier {
init() {} // compile error
}
protocol Flier {
init()
}
class Bird : Flier {
required init() {}
}
Suppose that a protocol declares an initializer. And suppose that a class adopts this protocol. By the terms of this protocol, this class and any subclass it may ever have must implement this initializer. Therefore, the class not only must implement the initializer, but also must mark it as
required
.
Alternatively, if Bird were marked
final
, there would be no need to mark itsinit
asrequired
, because this would mean that Bird cannot have any subclasses-guaranteeing that the problem will never arise in the first place.
class ViewController: UIViewController {
init() {
super.init(nibName:"ViewController", bundle:nil) // compile error
}
}
That code won't compile. The compile error says "
required
initializerinit(coder:)
must be provided by subclass of UIViewController."
It turns out that UIViewController adopts a protocol called
NSCoding
. And this protocol requires an initializerinit(coder:)
, which UIViewController duly implements.
UIViewController and NSCoding are declared by Cocoa, not by you. This is the same situation I was just describing. Your UIViewController subclass must either inherit
init(coder:)
or must explicitly implement it and mark itrequired
.
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
You are being forced to write an initializer for which you can provide no meaningful functionality!. Fortunately, Xcode's fix-it feature will offer to write the initializer for you like above.
If, on the other hand, you do have functionality for this initializer, you will delete the fatalError line and insert that functionality in its place. A minimum meaningful implementation would be to call
super.init(coder:coder)
.
Not only UIViewController but lots of built-in Cocoa classes adopt NSCoding.
Literals are a case in point. The reason you can say 5 to express an Int whose value is 5, instead of formally initializing Int by saying Int(5), is not because of magic (or at least, not entirely because of magic). It’s because Int adopts a protocol, ExpressibleByIntegerLiteral.
struct Nest : ExpressibleByIntegerLiteral {
var eggCount : Int = 0
init() {}
init(integerLiteral val: Int) {
self.eggCount = val
}
}
func reportEggs(_ nest:Nest) {
print("this nest contains \(nest.eggCount) eggs")
}
reportEggs(4) // this nest contains 4 eggs
I have already said that an Optional is an enum, with two case
.none
and.some
. If an Optional's case os.some
, it has an associated value - the value that is wrapped by this Optional.
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
init(_ some: Wrapped)
// ...
}
That is the pseudocode declaration of an Optional whose Wrapped placeholder has been replaced everywhere with the String type.
func dogMakerAndNamer(_ whattype:Dog.Type) -> Dog {
let d = WhatType.init(name:"Fido")
return d
}
If we are passed a Dog subclass such as NoisyDog as the parameter, we will instantiate that type (which is good) but then return that instance typed as Dog (which is bad).
func dogMakerAndNamer<WhatType:Dog>(_:WhatType.Type) -> WhatType {
let d = WhatType.init(name:"Fido")
return d
}
let dog = dogMakerAndNamer(NoisyDog.self) // dog is typed as NoisyDog
func dogMakerAndNamer(_:NoisyDog.Type) -> NoisyDog {
let d = NoisyDog.init(name:"Fido")
return d
}
In that call, we pass
NoisyDog.self
as the parameter. That tells the compiler what WhatType is! It isNoisyDog
. In effect, the compiler now substitutes NoisyDog for WhatType throughout the generic
protocol Flier {
func flockTogetherWith(_ f:Self)
}
In a protocol body, use of the keyword Self turns the protocol into a generic. Self here is a placeholder meaning the type of the adopter.
protocol Flier {
associatedtype T
func flockTogetherWith(_ f:T)
func mateWith(_ f:T)
}
struct Bee {}
struct Bird : Flier {
func flockTogetherWith(_ f:Bee) {}
func mateWith(_ f:Bee) {}
}
A protocol can declare an associated type using an associatedtype statement. This turns the protocol into a generic; the associated type name is a placeholder.
func takeAndReturnSameThing<T> (_ t:T) -> T {
print(T.self)
return t
}
A function declaration can use a generic placeholder type for any of its parameters, for its return type, and within its body.
func takeAndReturnSameThing<T> (_ t:T) -> T {
print(T.self)
return t
}
let thing = takeAndReturnSameThing("howdy")
Here, the type of the argument "howdy" used in the call resolves T to String; therefore this call to takeAndReturnSameThing must also return a String, and the type of the variable capturing the result, thing, is inferred as String.
func takeAndReturnSameThing<T> (_ t:T) -> T {
if T.self is String.Type {
// ...
}
return t
}
If we call takeAndReturnSameThing("howdy"), the condition will be true. That sort of thing, however, is unusual; a generic whose behavior depends on interrogation of the placeholder type may need to be rewritten in some other way.
protocol Flier {
func fly()
}
protocol Flocker {
associatedtype T : Flier // *
func flockTogetherWith(f:T)
}
struct Bee : Flier {
func fly() {}
}
struct Bird : Flocker {
func flockTogetherWith(f:Bee) {}
}
In that example, Flocker’s associated type T is constrained to be an adopter of Flier. Bee is an adopter of Flier; therefore Bird can adopt Flocker by specifying that the parameter type in its flockTogetherWith implementation is Bee.
protocol Flocker {
func flockTogetherWith(f:Flier)
}
struct Bird : Flocker {
func flockTogetherWith(f:Flier) {}
}
That’s not the same thing! That requires that a Flocker adopter specify the parameter for flockTogetherWith as Flier.
For a generic function or a generic object type, the type constraint can appear in the angle brackets. For example, earlier I described a global function func dogMakerAndNamer<WhatType:Dog>; Dog is a class, so the constraint says that WhatType must be Dog or a Dog subclass.
func myMin<T>(_ things:T...) -> T {
var minimum = things.first!
for item in things.dropFirst() {
if item < minimum { // compile error
minimum = item
}
}
return minimum
}
The problem is the comparison item < minimum. How does the compiler know that the type T, the type of item and minimum, will be resolved to a type that can in fact be compared using the less-than operator in this way? It doesn’t, and that’s exactly why it rejects that code.
func myMin<T:Comparable>(_ things:T...) -> T {
Now myMin compiles, because it cannot be called except by resolving T to an object type that adopts Comparable and hence can be compared with the less-than operator. Naturally, built-in object types that you think should be comparable, such as Int, Double, String, and Character, do in fact adopt the Comparable protocol!
In the generic examples so far, the placeholder’s type has been resolved mostly through inference. But there’s another way to perform resolution: we can resolve the type manually. This is called explicit specialization.
protocol Flier {
associatedtype T
}
struct Bird : Flier {
typealias T = String
}
The adopter of a protocol can resolve an associated type manually through a type alias equating the associated type with some explicit type
class Dog<T> {
var name : T?
}
let d = Dog<String>()
The user of a generic object type can resolve a placeholder type manually using the same angle bracket syntax used to declare the generic in the first place
func dogMakerAndNamer<WhatType:Dog>(_:WhatType.Type) -> WhatType {
let d = WhatType.init(name:"Fido")
return d
}
You cannot explicitly specialize a generic function. One solution is to make your generic function take a type parameter resolving the generic.
protocol Flier {
init()
}
struct Bird : Flier {
init() {}
}
struct FlierMaker<T:Flier> {
static func makeFlier() -> T {
return T()
}
}
let f = FlierMaker<Bird>.makeFlier() // returns a Bird