CocoaPods trunk is moving to be read-only. Read more on the blog, there are 18 months to go.
TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | MIT |
ReleasedLast Release | Jan 2017 |
SwiftSwift Version | 3.0 |
SPMSupports SPM | ✗ |
Maintained by Marco Muccinelli.
Ferrara
is a framework which takes two collections and calculates differences between them. It returns you inserted indexes, deleted indexes, complete matches, partial matches and element movements.
Matchable
protocolYou need your objects to conform protocol Matchable
. This protocol is used to spot changes to objects.
struct Tulip: Matchable {
let color: Color
let thriving: Bool
func match(with object: Any) -> Match {
guard let tulip = object as? Tulip else {
return .none
}
if color == tulip.color {
if thriving == tulip.thriving {
return .equal
}
else {
return .change // Same color, but not thriving
}
}
else {
return .none
}
}
}
This is enough to calculate differences.
let oldTulips: [Tulip]
let newTulips: [Tulip]
let diff = Diff(from: oldTulips, to: newTulips)
Matchable
and Equatable
togetherIf an object conforms to Equatable
, a basic implementation of match(with:)
is natural: if objects are equal, return .equal
; otherwise return .none
. You can take profit of this extension to perform simple diffs, without changes.
extension Int: Matchable {} // Use standard match(with:) implementation
let a: [Int]
let b: [Int]
let diff = Diff(from: a, to: b)
What is more you can use Equatable
to simplify your custom objects.
struct Tulip: Matchable, Equatable {
let color: Color
let thriving: Bool
static func ==(lhs: Tulip, rhs: Tulip) -> Bool {
return lhs.color == rhs.color && lhs.thriving == rhs.thriving
}
func match(with object: Any) -> Match {
guard let tulip = object as? Tulip else {
return .none
}
if self == tulip {
return .equal
}
else if color == tulip.color {
return .change
}
else {
return .none
}
}
}
Also when an object conforms to Matchable
there is a free implementation of ==
. So, if your prefer, you could write a code like this:
struct Tulip: Matchable, Equatable {
let color: Color
let thriving: Bool
func match(with object: Any) -> Match {
guard let tulip = object as? Tulip else {
return .none
}
if color == tulip.color {
if thriving == tulip.thriving {
return .equal
}
else {
return .change // Same color, but not thriving
}
}
else {
return .none
}
}
}
Identifiable
protocolIdentifiable
protocol describes the common scenario when you need an object to be identifiable with a property. If you conform also to Matchable
and Equatable
you will have a free implementation of match(with:)
.
struct Person: Identifiable, Matchable, Equatable {
let identifier: String // This is enough for Identifiable conformance
let name: String
static func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.identifier == rhs.identifier && lhs.name == rhs.name
}
}
let lastYearPeople: [Person]
let thisYearPeople: [Person]
let diff = Diff(from: lastYearPeople, to: thisYearPeople)
You can also diff mixed collections because Diff
takes heterogeneous collections as input parameters.
extension Int: Matchable {}
extension String: Matchable {}
let a = ["h", 3, "l", "l", 0] as [Any]
let b = ["s", 3, "l", "l"] as [Any]
let diff = Diff(from: a, to: b) // is legal
Marco Muccinelli, [email protected]
Ferrara
is available under the MIT license. See the LICENSE file for more info.
Ferrara is a city and comune in Emilia-Romagna, northern Italy, capital city of the Province of Ferrara. For its beauty and cultural importance it has been qualified by UNESCO as a World Heritage Site. It is the historic location of River Po delta. The river flows through many important Italian cities and, near the end of its course, it creates a wide delta. So, River Po delta inspired the name :)