Swift Refreshing
This is the note for swift knowledge refreshing.
TYPES:
- All types are named with UpperCamelCase. All variables are named with lowerCamelCase.
- Types:
Bool
,Int
,String
,Character
… orMyType
- Variables: num: Int, isTrue: Bool…
- Types:
- Type conversion:
- type name(value):
Double(10)
as
keyword:let doubleVal = 3 as Double
- type name(value):
TUPLES:
When to use a tuple? For shortly operation, like sending multiple values to a function or return multiple values from a function. Otherwise, using struct or class.
1 | let coordinate: (Stirng, Int, Int) = ("Coordinate" , 3, 5) |
LOOP:
1 | var count = 0 |
SWITCHING:
1 | // if you want to fall through the switch case, use fallthrough keyword |
DICTIONARY:
- Dictionary return optional.
- Assign nil to delete key pair.
- Iterate the dictionary
- for (key, value) in dictionary {}
- for key in dictionary.keys {}
- for val in dictionary.values {}
STRUCTURE:
By default, methods in struct can’t mutate the internal variables, because the struct is immutable. But we can label the method to mutating to do the job.
1 | mutating func doSomethingToInternalVal() { |
Properties:
1 | struct TV { |
- properties: height and width. These can be accessed and mutated.
- computed properties: diagnonal. These are read only.
- getter and setter: get and set keywords in a computed property. For setter, we have a newValue keyword to get the setting value.
- type properties: static properties. We don’t need a instance to get this property, just call the type and reference the property.
CLASS:
When we created an object with “let”, we assigned a constant reference to that variable. You can not assign a new object to it, but you can still change its properties.
Class need initializer when at least one of its properties is not initialed or nil.
1 | // Compiler error: Class 'Address' has no initializers |
INHERITANCE:
final
keyword can prevent one class or function been inherited.
INITIALIZERS:
- Designated initializer need to make sure all none optional properties has a value.
- Convenience initializer must call designated initializer, however you can set some default values for some properties.
When calling parent’s initializer, we usesuper.init()
. In swift, we call parent’s initializer after we init all properties in child’s class. This prevents a property been used before it is ready. And more important, we can only call the parent’s designated initializer but not convenience initializer.
PROTOCOL:
Property Requirements
A protocol can require any confirming type to provide an instance property or type property. It only specifies the required property name and type and also specifies whether each property must be gettable or gettable and settable.
1 | protocol SomeProtocol { |
Always prefix type property requirements with the static
keyword when you define them in protocol.
1 | protocol AnotherProtocol { |
Method Requirements
Always prefix type method requirements with the static keyword when you define them in protocol.
1 | protocol SomeProtocol { |
Always prefix mutating method requirements with the mutating keyword when you define them in protocol to indicate that method is allowed to modify the instance it belongs and to any properties of that instance.
When a class confirmed the protocol with mutating method, we don’t need to put mutating
keyword before the func
. The mutating keyword is only used by structures and enumerations.
1 | protocol Togglable { |
Protocol can require specified initializer to be implemented by confirming types. When confirming types implement the initializer, put required
keyword before init
. If a subclass overrides a designated initializer from a superclass, and also implements a matching initializer requirement from a protocol, mark the initializer implementation with both the required and override modifiers.
1 | protocol SomeProtocal { |
Protocol as type
- As a parameter type or return type in a function, method, or initializer
- As the type of a constant, variable, or property
- As the type of items in an array, dictionary, or other container
Delegation:
Delegation is a design pattern that enable a class or structure to hand off some of its responsibilities to an instance of another type. The delegation pattern is implemented by:
- Define an protocol which contains all delegated responsibilities.
- A type confirms the protocol to guarantee to provide all delegated responsibilities.
Delegation can respond to a particular action, or retrive data from an external source without understanding the type of the source.
Protocol Inheritance:
Protocol can inherit one or more other protocols and add further requirements.
Class-only Protocol
We can limit the protocol adoption to class type by adding class keyword after colon.
Optional requirements
Both the protocol and the optional requirement must be marked with the @objc
attribute.
Note that @objc
protocols can be adopted only by classes that inherit from Objective-C classes or other @objc
classes.
1 | protocol CounterDataSource { |
CUSTOM OPERATORS:
Overloading an existing operator: To overloading an operator, define a new function for the operator symbol.
1 | // Use * to repeat a string a specified number of times |
Custom a new operator: A custom operator needs three things.
- Define its precedence
- Define its associativity: left, right or none.
- Define whether its prefix, in fix or postfix
1 | // create exponential operator ** |
GENERICS
Define generics:
To make your function or type to be generic, add placeholder type name inside angle brackets.
1 | // example |
Extending a generic type
When we extend a generic type, we don’t need to provide the type parameter in extension definition.
1 | // follow by the generic stack |
Type constraint syntax
1 | func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { |
Example: we have a function which finds the index number for a generic item in an array.
1 | func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? { |
We will see one error in the console, because not every Swift type confirmed the protocol Equatable.
1 | error: binary operator '==' cannot be applied to two 'T' operands |
To solve this problem, we can make a constraint to the generic type T by adding portable Equatable after T:
.
1 | func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? { |
If you want your custom types to use this function, you should make your custom types confirm the protocol Equatable. For example, we make a full name structure, and we have an array of names, find one in it.
1 | // make a FullName structure confirming Equatable protocol |
We can also use where
clause to define constraints for type.
1 | // The code above |
Associated Types
An associated type gives a placeholder name to a type that is used as part of the protocol. Assign actual type to that associated type when the protocol is adopted. associatedtype
keyword.
1 | // Example we made an associated type Item in protocol container |
More for the where clauses we talked above, it can do more jobs for type constraints. This is a function for checking two containers are equal.
1 | func allItemsMatch<C1: Container, C2: Container> |
In the where clauses:
C1.Item == C2.Item
makes sure C1 and C2 contains same type of item.C1.Item: Equatable
makes sure C1’s item confirming equatable protocol, also C2’s item since we checked two containers has same type of items.
Extension with generic where clause
We can use extension with generic where clause to require the element to confirm to a protocol and also require the element to be specific type.
1 | // Use stack we create above |
SUBSCRIPTS:
Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence.
1 | // Example syntax |
For usage, we use [] after the type. Take dictionary as a example,
1 | let numberOfPages = ["The Lord of the Rings" : 900, "The Man Without Qualities" : 289] |