Memento🏆 Design Pattern — SwiftUI
a doc snapshots example
“..making snapshots of an object’s state and restoring it in future.. ”
Intent ♖♞
Without knowing the object’s internal state, have to capture and save the state externally, so that the object can be restored to this state later.
Participants ⛄︎☃︎
Internal State, Memento, Originator, Caretaker
Internal State 💎
Doc contain internal state, in this case only content (in other usecases, the internal state might be more complex)
struct DocCurrentState: Hashable {
var content: String
}
Memento 🎁
Memento have two interfaces,
☞ One is private (wide) interface, only the memento creator class (Originator) can access this interface.
private protocol DocSnapshot {
var state: DocCurrentState { set get }
}
☞ Other is public (narrow) interface, this can be seen by those who maintains and stores the collection of mementos.
protocol Snapshot {
var id: UUID { get }
var name: String { set get }
}
below is the memento, which is confirmed to both `public` and `private` interfaces.
private struct Memento: Snapshot, DocSnapshot {
var id: UUID
var name: String = ""
var state: DocCurrentState
}
Originator 🧞♂️
→ create memento
Creates a memento when requested.
func createSnapshot(for state: DocCurrentState) -> Snapshot {
Memento(id: UUID(), state: state)
}
← restore memento
Only this class has the access to the private (wide) interface. So, only this has to retrieve the internal state from the given memento.
func getDocState(for anyMemento: Snapshot) -> DocCurrentState? {
guard let memento = anyMemento as? DocSnapshot else { return nil }
return memento.state
}
‼️Originator is not responsible for maintaining the history of mementos.
Caretaker 🚛
- Responsible for the memento’s safe keeping.
- In our case, `DocEditor` maintains all the snapshots.
- Never operates on or examines the contents of a memento.
class DocEditorBusiness {
func saveDocCurrentState(for content: String) -> Snapshot {
let name = "Snapshot \(snapshotCount + 1)"
var snapshot = docBusiness.prepareSnapshot(for: content)
snapshot.name = name
snapshot.state //⚠️ no access
storage.saveSnapshot(snapshot)
return snapshot
}
func restoreDocToOldState(snapshot: Snapshot) -> String? {
docBusiness.restore(snapshot: snapshot)
}
func getAllSnapshots() -> [Snapshot] {
storage.getAllSnanshots()
}
}