Thursday, July 18, 2019

Three dot operator in Swift

You may see three dot ... operator in swift for two different cases:

1. ... as variadic parameter in function definition
A variadic parameter accepts zero or more values of a specified type. The parameters are separated with comma. The value of a variadic parameter in the function’s body is an array with the specified element type. A function can only have at most one variadic parameter.

func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}

arithmeticMean(1, 2, 3, 4, 5)
arithmeticMean()

2. ... as a closed range operator in statement
The closed range operator a...b creates a ClosedRange<T> object that contains elements from a to b inclusive. The value of a must not be greater than b. It is most used to enumerate all elements in a for loop

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

Wednesday, July 17, 2019

Property in Swift

1.  Stored or Computed property

In Swift, both struct and class can define properties. There are two types of property, stored property and computed property.

In property definition, if it only contains property name and type, or it is followed by assignment '=', then it is a stored property, as shown below
var stored2 : Int
var stored1 : Int = 0
var stored3 : String = { return "hi"}()

If after the variable name and type,  and immediately followed with a curly bracket, then within the curly bracket, if it only contains willSet and/or didSet, then it is still a stored property.
     var m :Int  {
         willSet {
             print("The value is changed from \(m) to \(newValue)")
         }
         didSet {
            print("The value is changed from \(oldValue) to \(m)")
         }
    }

But if within the curly bracket it contains get and/or set, or it only contains a closure expression (for the shorthand get accessor), then it is a computed property.
   var computed :Int {
        get {
             return m
        }
        set (newValue) {
            m = newValue
       }
  }

  var computed2: Int {
      return m
 }

2. Initialize stored property with closure
Usually, stored property can be initialized by assigning it to a new object as shown below
var myComplex : UIViewController = UIViewController()

But it can also be initialized by using a closure expression with parameters in the ending parenthesis as shown below 
var myComplex2 : [Int] = { return Array(repeating: $0, count: $1)}( 5, 6)

Note this is quite similar to readonly computed property, although the assignment operator indicates it is a stored property

3. Stored property only attribute
willSet, willGet accessor can only applied to stored properties, and cannot apply to computed property, as for computed property, developer can add any logic in the get/set accessors, and there is no need to use willSet/willGet accessors.

For the similar reason, lazy property can only apply to stored properties. In addition, lazy property should be defined as no-optional type properties, instead of optional type, and it must be defined as var instead of let.

The reason to use lazy property is, usually in the init method, all no-option stored properties should be initialized. However, if a property is defined as lazy, it is not required to be initialize in init method. The lazy property's value will be set when it is used first time.
lazy var lazyBoy : ComplexData = ComplexData()

In multiple thread situation, if multi threads access the lazy property at the same time, the property may be initialized more than once.

Lazy property should not be applied to struct, as it will requires the struct can only be used by var instead of let.
 

Sunday, July 14, 2019

Default compare operator on swift tuple type

Swift standard library defineds tuple comparison operators for tuple with fewer than seven elements.
In order to compare two tuples, they must have the same type and the same number of values. Tuples are compared from left to right, one value at a time. If the comparison finds two values are equal, then it move to the next element to decide the overall compare result. If all elements are equal, then the two tuples are equal.

  1. (1, "zebra") < (2, "apple")
  2. // true because 1 is less than 2; "zebra" and "apple" are not compared

  3. (3, "apple") < (3, "bird")
  4. // true because 3 is equal to 3, and "apple" is less than "bird"

  5. (4, "dog") == (4, "dog")
  6. // true because 4 is equal to 4, and "dog" is equal to "dog"


As a result, the common comparison operators can be directly applied to tuple array as shown below

var numberStrings = [(2, "two"), (1, "one"), (3, "three")]
numberStrings.sort(by: <)
numberStrings // [(1, "one"), (2, "two"), (3, "three")]

Sunday, July 7, 2019

Difference between swift map and flatMap

In swift, Array's map method has below signature
func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

The flapMap method has the below signature
func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence

Both map and flatMap methods have a transform method to convert the original array element to a different array. In map, the transform takes a single element as input parameter and generate a single output element, then all output elements are appended in an array.

In flatMap, the transform method takes a single element as input parameter, however, the output is an array of ouput element type, and then all return arrays from the transform method are appended together into a single array and returns to caller.

Samples from Swift Array document
let numbers = [1, 2, 3, 4]

let mapped = numbers.map { Array(repeating: $0, count: $0) }
// [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]

let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
// [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]