Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Array fine grained observation using didSet

Tags:

ios

swift

If we provide a didSet observer on an Array property in swift class then we will be able to observe changes in the array as a whole How we can do a fine grained observation on array where we be able to track insertions , updations and deletions ? It seems merely using didSet observer alone will not help. Any idea how it can be done ? I came across a link that seems to explain the thing that I am looking for but it is somewhat complex to understand. Can anybody provide a simple example to solve this problem ? Thanks

like image 360
user2788672 Avatar asked Mar 12 '23 15:03

user2788672


2 Answers

If you want to investigate the 'old' value before the property is set (to calculate a diff), you need to use willSet, not didSet. In didSet it is too late to calculate changes (obviously ...)

Sample:

class AWrap {
  var values : [ String ] = [] {
    willSet {
      print("values: \(values)")
      print("   new: \(newValue)")

      let old = Set(values)
      let new = Set(newValue)
      let newElements  = new.subtracting(old)
      let goneElements = old.subtracting(new)
      print("     +: \(newElements)")
      print("     -: \(goneElements)")
    }
  }
}

Running that:

let a = AWrap()
a.values.append("2")
a.values.append("3")
a.values.remove(at:0)

Gives:

values: []
   new: ["2"]
     +: ["2"]
     -: []
values: ["2"]
   new: ["2", "3"]
     +: ["3"]
     -: []
values: ["2", "3"]
   new: ["3"]
     +: []
     -: ["2"]
like image 90
hnh Avatar answered Mar 19 '23 17:03

hnh


You can generalize the detection in an extension to make it easier to manage if you're going to do this in several places:

 extension Array where Element:Comparable
 {
    func changesFrom(old:[Element]) -> [(index:Int, mod:String, old:Element, new:Element, desc:String)]
    {
       var changes:[(index:Int, mod:String, old:Element, new:Element, desc:String)]

           changes  =  zip(old,self).enumerate()
                      .filter{ $1.0 != $1.1 }
                      .map{ ($0, "∆", $1.0, $1.1 , "\($1.0) -> \($1.1)") }
           changes +=  (old.count..<max(old.count,self.count))
                      .map{ ($0, "+", self[$0], self[$0], "\(self[$0])") }
           changes +=  (self.count..<max(old.count,self.count))
                      .map{ ($0, "-", old[$0], old[$0], "\(old[$0])" ) }
       return changes
    } 

    func printChangesFrom(old:[Element])
    {
       for changed in changesFrom(old)
       { 
         print( "[\(changed.index)] \(changed.mod) \(changed.desc)" )
       }
    }
 }

 class ContainsArray
 {
    var array  = [1,2,3,4]
    {
       didSet 
       { 
         array.printChangesFrom(oldValue)
       }
    }
 }


 var CA = ContainsArray()

 print("change an element")
 CA.array[2] = 7  

 //[2] ∆ 3 -> 7

 print("change multiple elements")
 CA.array.sortInPlace(<)

 //[2] ∆ 7 -> 4
 //[3] ∆ 4 -> 7

 print("add elements")
 CA.array += [9,12,14]

 //[4] + 9
 //[5] + 12
 //[6] + 14     

 print("remove Elements")
 CA.array.removeLast(3)

 //[4] - 9
 //[5] - 12
 //[6] - 14
like image 43
Alain T. Avatar answered Mar 19 '23 18:03

Alain T.