Skip to content

Latest commit

 

History

History
109 lines (70 loc) · 6.51 KB

Migration.md

File metadata and controls

109 lines (70 loc) · 6.51 KB

Bond 7

Bond 7 is all about observable collections! They are now much more powerful, it is easy to customize binders and create your own variants of observable collections. Anything that conforms to Swift.Collection can now be made observable. Bond now also supports observable trees! Check out observable collections documentation and new playgrounds in the project workspace.

Bond 7 updates only observable collections APIs. All other APIs remain unchanged. APIs for use cases like creating, mutating and binding collections remain mostly unchanged, however there are breaking changes in the collection/table view binders and the observable collection event type. Make sure to check out playgrounds in the project workspace to learn about the new stuff.

Observable Collections

In previous versions of Bond, each observable collection provided its own event type. For example, events of ObservableArray were of the type ObservableArrayEvent, events of Observable2DArray were of type Observable2DArrayEvent and so on. Those events represented the respective observable collection changes as "diff".

Bond 7 consolidates all change events under a single protocol ChangesetProtocol.

/// A type that represents a collection change description, i.e. a modification of a collection.
public protocol ChangesetProtocol {

    associatedtype Diff
    associatedtype Operation
    associatedtype Collection

    /// A description of the change represented by this changeset as a diff.
    var diff: Diff { get }

    /// A description of the change represented by this changeset as a patch.
    /// Patch is a sequence of operations applied to the collection in order.
    var patch: [Operation] { get }

    /// Collection in its final state.
    var collection: Collection { get }
}

A changeset provides the collection itself as well as the description of the change in both "diff" and "patch" variant.

Diff is a description of changes where indices of deletions, updates and moves from refer to the original collection (before the change), while insertion and move to indices refer to the final collection. Diff does not imply the order of the operations. UICollectionView and UITableView can use diff to perform batch updates.

Another way to describe changes is called patch. Patch represents a sequence of operations like insert, delete, update or move that if applied to the original collection, one after another, will produce final collection. On macOS, NSTableView can use patch to perform batch updates.

You can use changeset type to get both kinds of change descriptions.

Bond 7 provides few implementations of the changeset protocol:

  • OrderedCollectionChangeset<Collection: Swift.Collection> for ordered collections like arrays, 2D arrays, lists and other collection where order of elements matters.
  • UnorderedCollectionChangeset<Collection: Swift.Collection> for unordered collections like dictionary, set and other collection where order of elements does not matter.
  • TreeChangeset<Collection: TreeNodeProtocol> for trees.

The implementations mostly differ in how they specialize the associated Diff and (patch) Operation types. In ordered collections, position of elements matter, so moving elements within the collection makes sense. Thus the ordered collection diff is defined as:

public struct OrderedCollectionDiff<Index>: OrderedCollectionDiffProtocol {

    /// Indices of inserted elements in the final collection index space.
    public var inserts: [Index]

    /// Indices of deleted elements in the source collection index space.
    public var deletes: [Index]

    /// Indices of updated elements in the source collection index space.
    public var updates: [Index]

    /// Indices of moved elements where `from` is an index in the source collection
    /// index space, while `to` is an index in the final collection index space.
    public var moves: [(from: Index, to: Index)]
}

While unordered collection diff is missing the move operations because those make no sense in unordered collections like dictionary or set.

public struct UnorderedCollectionDiff<Index>: UnorderedCollectionDiffProtocol {

    /// Indices of inserted elements in the final collection index space.
    public var inserts: [Index]

    /// Indices of deleted elements in the source collection index space.
    public var deletes: [Index]

    /// Indices of updated elements in the source collection index space.
    public var updates: [Index]
}

Abstracting collection change in such fashion simplifies observable collections. Since the changeset contains the collection itself it has all needed state and we can define (mutable) observable collection as a property of such changeset:

public typealias MutableObservableArray<Element> = Property<OrderedCollectionChangeset<[Element]>>

Methods like append, insert or remove are then implemented as extensions on the Property type.

Other collections follow the same principle. This also makes it super easy to define you own observable collections. For example, if you have your own collection type MySuperArray that conforms to Swift.Collection protocol, you can make the observable variant just by doing:

public typealias MySuperObservableArray<Element> = Property<OrderedCollectionChangeset<MySuperArray<Element>>>

You can then bind it to the collection or table view without any additional code!

Data Sources

In previous versions of Bond, observable collections implemented DataSourceProtocol which enabled them to be bound to collection or table views. That principle is followed in Bond 7, although changes were made to make it work with the new changeset concept. DataSourceProtocol that is used for UITableView and UICollectionView bindings has been renamed to SectionedDataSourceProtocol because we now also have FlatDataSourceProtocol that is used for NSTableView on macOS.

OrderedCollectionChangeset as well as Swift.Array now conform to SectionedDataSourceChangesetConvertible which works in combination with SectionedDataSourceProtocol to make signals or properties of those types bindable to collection or table views.

You can now also bind observable dictionaries or sets to table or collection views by applying sortedCollection() operator on them to convert them into an ordered collection.

Custom Binder Data Sources

Bond 7 makes it easier to provide custom binder data sources. Check out custom data sources documentation.