Note: I’m going through draft posts that go back to 2014 and publishing where they still may have value. They may not be 100% upto date but better published late than never.
Introduction
Protocols can be thought of as similar to interfaces within languages such as C# and Java, hence are used to declare expectations for implementations and can be returned or accepted as params.
We declare a protocol in the following way
protocol Person {
var firstName: String { get }
var lastName: String { get }
var age: Int { get }
}
So we can create our implementation of this procotol like this
struct PersonImpl : Person {
var firstName: String
var lastName: String
var age: Int
}
Structs and Classes
Apple Swift developers seem to prefer value types over reference types, a struct is a value type and class a reference type. They both share many of the same functionality.
Structs do not require an initializer (constructor) as a default initializer is supplied automatically and will expect all non-optional properties to be supplied.
class PersonImpl : Person {
var firstName: String
var lastName: String
var age: Int
init(firstName: String, lastName: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
}
As you can see, init is the name of the initializer or what we may view as a constructor. Classes may also have deinit is called when Swift deallocates an instance (struct’s do not have deinit).
Mutations
Where we expect changes to the internal data, in other words where a function mutates data we use the mutating keyword. If, for example, we add a protocol function
func incrementAge() -> Void // same as func incrementAge()
By default struct properties cannot be changed by instance method. In such cases we need to include the mutating key word, i.e.
mutating func incrementAge() -> Void
So a struct method implementation will now look like this
mutating func incrementAge() -> Void {
age += 1
}
Within a class, we don’t need to mark the method as mutating.
Accessors
There are five access levels
- Open: This allows proprties, methods, classes etc. to be accessible by anything that imports the module
- Public: This allows proprties, methods, classes etc. to be accessible by anything that imports the module
- Internal: This is the default access level and allows properties, methods, classes etc. to be accessible from the module where they’re defined only
- Fileprivate: Properties and methods can access from code withint he same source file only
- Private: The least visible accessor meaning properties methods etc. are only visible within the same file or class etc.
is and as
Swift has the concept of is and as pretty much like C#. So we can declare a type as follows
let p: Person = PersonImp("Scooby", "Doo", "12")
Now if we needed to check if the const p is a PersonImpl we would use
if p is PersonImpl {
}
we could use as within the if statement if we wanted as well, for example
if let person = p as? PersonImpl {
}
Here we compare and assign in the same line of code using as? which basically means the person may be nil (if the type cannot be converted to PersonImpl).
Extensions
Just like extension classes in C# – Swift extensions allow us to add functionality to structures, protocols and classes (as well as enumerations).
An extension is defined using the extension key word, for example
extension String {
// add string functionality, for example
func capitalize() -> String {
return self[0].toUpper() + self.substr(1)
}
}
We can also add constraints to extensions, so for example a Collection extension that
extension Collection where Iterator.Element: Comparable {
// add functionality where elements support Comparable
}
Equatable
A type needs to conform to the Equatable protocol for a type to be comparable using == and !=.
Generics
Swift generics look much the same as generics in C++, Java and C# in that we can declare generics on a function like this
func add<T>(item: T) {
}
We can also add constraints to the genric type like this
func compare<T: Comparable>(a: T, b: T) -> Bool {
}