Swift - Improve computed properties in a class using Generics

Viewed 124

I have this block of code in a view class I have:

private var notes: [String] = [] {
    didSet {
      if notes.count > 0, notes.count == (oldValue.count + 1) {
        tableView.insertRows(at: [IndexPath.init(row: 0, section: 3)], with: .right)
      } else {
        tableView.reloadData()
      }
    }
  }

private var images: [UIImage?] = [] {
  didSet {
    if images.count > 0, images.count == (oldValue.count + 1) {
      tableView.insertRows(at: [IndexPath.init(row: 0, section: 2)], with: .right)
    } else {
      tableView.reloadData()
    }
  }
}

private var urls: [URL?] = [] {
  didSet {
    if urls.count > 0, urls.count == (oldValue.count + 1) {
      tableView.insertRows(at: [IndexPath.init(row: 0, section: 1)], with: .right)
    } else {
      tableView.reloadData()
    }
  }
}

It works fantastically, but I see this and straight away I want to improve it because it's almost exactly the same thing three times.. I've used some Generics in Java, and I've read through the documentation for generics in Swift but I can't quite get it to work. I want to eventually do something like

private var items: [T] = [] {
  didSet {
    if items.count > 0, items.count == (oldValue.count + 1) {
      tableView.insertRows(at: [IndexPath.init(row: 0, section: 1)], with: .right)
    } else {
      tableView.reloadData()
    }
  }
}

so that I could keep it all in one section. Is this possible? I'm still quite the newbie so I appreciate any help even if this is a simple question to you :)

Thanks!

2 Answers

You could solve this with a simple function

private func updateTable(count: Int, oldCount: Int, section: Int) {
    if count > 0, count == (oldCount + 1) {
        tableView.insertRows(at: [IndexPath.init(row: 0, section: section)], with: .right)
    } else {
        tableView.reloadData()
    }
}

This would simplify the code to

private var notes: [String] = [] {
    didSet {
        updateTable(count: notes.count, oldCount: oldValue.count, section: 3)
    }
}

Another option is to pass the arrays

private func updateTable(array: [Element], oldArray: [Element], section: Int) {
    if array.count > 0, array.count == (oldArray.count + 1) {
        tableView.insertRows(at: [IndexPath.init(row: 0, section: section)], with: .right)
    } else {
        tableView.reloadData()
    }
}

I think the problem you're having here is that, unlike many languages, in Swift the Array type is a Generic itself. You could put the array in a wrapper struct, generic over T, which will allow you to implement the setter.

struct MyArray<T> {
   var items: [T] = [] {
      didSet {
         if items.count > 0, items.count == (oldValue.count + 1) {
            //something
         } else {
            //something else
         }
      }
   }
}

You could then wrap all the common methods if you so wished, or write an Iterator and conform to Sequence to get all the default methods. Whether this is worth the overhead of dealing with the wrapper I'll leave up to you ;-)

Related