Skip to content

Classes, part 3

Informations presented on this page may be incomplete or outdated. You can find the most up-to-date version reading my book Learn Swift by examples. Beginner level

Prepared and tested with Xcode 8.3 and Swift 3.1

In this tutorial we cover the following topics


Optional chaining

Chaining queries are quite common when working with objects


8
8

Now imagine that, for some reason, one of the chain's item may returns or may has value of nil. Let's look into modified version of a previous code. We have change

note an extra exclamation mark in print

in the following full code

It should work as previously,


8

but changing increaseBy2 method (note >=)

results in error


fatal error: unexpectedly found nil while unwrapping an Optional value
2017-09-08 19:20:05.272966+0200 Classes_03[27444:6846170] fatal error: unexpecte
dly found nil while unwrapping an Optional value
Current stack trace:
0 Classes_03 0x0000000100341bc0 swift_reportError + 1
29
1 Classes_03 0x000000010035e5e0 _swift_stdlib_reportF
atalError + 60
2 Classes_03 0x00000001000bd1f0 specialized specializ
ed StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) ->
A) -> A + 342
3 Classes_03 0x00000001002c7730 partial apply for (_f
atalErrorMessage(StaticString, StaticString, file : StaticString, line : UInt, f
lags : UInt32) -> Never).(closure #2) + 109
4 Classes_03 0x00000001000bd1f0 specialized specializ
ed StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) ->
A) -> A + 342
5 Classes_03 0x000000010027dd20 specialized _fatalErr
orMessage(StaticString, StaticString, file : StaticString, line : UInt, flags :
UInt32) -> Never + 96
6 Classes_03 0x00000001000016d0 main + 204
7 libdyld.dylib 0x00007fff96dd7234 start + 1
(lldb)

As we can see, with exclamation mark code compiles but when the optional is nil triggers a runtime error. Now there is a time when optional chaining can help. Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might, at the time of executing, be nil. If the optional contains a value, the call succeeds; if the optional is nil, the call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

To use optional chaining, use a question mark in place of the exclamation mark


chainCallResult fails, do something eles

Notice that the optional chaining always returns a value of optional type even though in source code we have nonoptional; in our case Int? (optional Int) instead of Int.

Add the following lines to our code

As we can see, the result is not simple 8 but Optional(8)


chainCallResult fails, do something eles
Optional(8)

What is interesting, we can do this even if method does not define a return value. It is possible, because functions and methods with no return type have an implicit return type of Void and in consequence the return type with optional chaining will be Void?, not Void.


Type checking and casting

In Swift we have two special types for working with indefinite, or beter say: any, type

  1. Any which can represent an instance of any type at all (including function types);
  2. AnyObject which can represent an instance of any class type.

The most basic example of Any usage is an array to store items of any type


[5, 1.23, "test", (Function)]

The Any type represents values of any type, including optional types. Swift gives us a warning if we use an optional value where a value of type Any is expected. If we really do need to use an optional value as an Any value, we can use the as operator to explicitly cast the optional to Any

To check whether an instance is of a certain subclass type, use the type check operator: is. This operator returns true if the instance is of that subclass type and false if it is not.


5 is of Int type
1.23 is of Double type
test is of String type
(Function) is of (String) -> String type

A constant or variable of a certain class type may actually refer to an instance of a subclass. We can try to downcast to the subclass type with a type cast operator

  • in the conditional form as? when returns an optional value of the type we are trying to downcast to;
  • in the forced form as! to attempt the downcast and force-unwraps the result as a single compound action.


Classes_03.B is of B type
Classes_03.B is of Optional<A> type
Classes_03.C is of Optional<C> type


Nested types

Sometimes it can be convenient to define utility structure or class purely for use within the context of a more complex type simply to support this type’s functionality. Types can be nested to as many levels as we need. To use a nested type outside of its definition context, we have to prefix its name with the name of the type it is nested within.


key: d1 value: Classes_03.DataToSynchronize status: New
key: d1 value: Classes_03.DataToSynchronize status: WaitForConfirmation
key: d2 value: Classes_03.DataToSynchronize status: New
key: d2 value: Classes_03.DataToSynchronize status: WaitForConfirmation


Access control

Access control restricts access to/from parts our code. With this feature we can hide the implementation details, and enable access to it with a preferred interface through which that code can be used. Swift provides five different access levels. These access levels are relative to the source file in which an entity is defined, and also relative to the module that source file belongs to.

  • Open access (open keyword) and public access (public) enable entities to be used within any source file from their defining module, as well as in a source file from another module that imports the defining module. See below for difference between open and public access.
  • Internal access (internal) enables entities to be used within any source file from their defining module, but not in any source file outside of that module.
  • File-private access (fileprivate) restricts the use of an entity to its own defining source file.
  • Private access (private) restricts the use of an entity to the enclosing declaration.

Open access differs from public access as follows

  1. Open access applies only to classes and class members.
  2. Classes with public access, or any more restrictive access level, can be subclassed only within the module where they’re defined.
  3. Class members with public access, or any more restrictive access level, can be overridden by subclasses only within the module where they’re defined.
  4. Open classes can be subclassed within the module where they’re defined, and within any module that imports the module where they’re defined.
  5. Open class members can be overridden by subclasses within the module where they’re defined, and within any module that imports the module where they’re defined.
  6. More important rules

    1. Rule 1. Almost all entities in our code, if we do not specify an explicit access level ourself, have a default access level of internal.
    2. Rule 2. The access control level of a type also affects the default access level of that type’s members: properties, methods, initializers, and subscripts. For example, having defined type with a private access level, the default access level of its members will also be private.
    3. Rule 3. A public type defaults for its members is internal. This ensures that the open to the public API for a type is something we agree to publishing, and avoids presenting the internal workings details of a type as public API by mistake.
    4. Rule 4. A tuple type’s access level is deduced automatically when the tuple type is used, and can’t be specified explicitly.
    5. Rule 5. The access level for a function type is calculated as the most restrictive access level of the function’s parameter types and return type. You must specify the access level explicitly as part of the function’s definition if the function’s calculated access level doesn’t match the contextual default.
    6. Rule 6. Nested types defined within a private (or file-private) type have an automatic access level of private (or file-private). Nested types defined within a public type or an internal type have an automatic access level of internal.
    7. Rule 7. A subclass can’t have a less restrictive access level than its superclass. For example, we can’t write a public subclass of an internal superclass.
    8. Rule 8. An override can make an inherited class member more accessible than its superclass version.
    9. Rule 9. A constant, variable, or property can’t be more public than its type. For example it’s not valid to have a public property with a private type.

    For more details please refer to official documentation.