{"id":18566344,"url":"https://github.com/perfaram/grdbundoredo","last_synced_at":"2025-04-28T18:24:08.795Z","repository":{"id":188812733,"uuid":"606461278","full_name":"perfaram/GRDBUndoRedo","owner":"perfaram","description":"Undo-redo manager for GRDB","archived":false,"fork":false,"pushed_at":"2024-12-12T12:11:06.000Z","size":27,"stargazers_count":5,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-30T11:51:10.875Z","etag":null,"topics":["grdb","sqlite","undo-redo"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/perfaram.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-25T15:08:47.000Z","updated_at":"2024-12-15T21:56:49.000Z","dependencies_parsed_at":"2024-12-26T12:24:44.087Z","dependency_job_id":"48fcf225-486a-43f8-bc5c-a4c9023f0ade","html_url":"https://github.com/perfaram/GRDBUndoRedo","commit_stats":null,"previous_names":["perfaram/grdbundoredo"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perfaram%2FGRDBUndoRedo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perfaram%2FGRDBUndoRedo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perfaram%2FGRDBUndoRedo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perfaram%2FGRDBUndoRedo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/perfaram","download_url":"https://codeload.github.com/perfaram/GRDBUndoRedo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251363159,"owners_count":21577591,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["grdb","sqlite","undo-redo"],"created_at":"2024-11-06T22:22:46.183Z","updated_at":"2025-04-28T18:24:08.778Z","avatar_url":"https://github.com/perfaram.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GRDBUndoRedo\n\nThis package provides undo-redo features for projects using [GRDB](https://github.com/groue/GRDB.swift). The undo-redo log is generated and managed through SQLite triggers. \n\n## Installation\nThrough Swift Package Manager.\n\n## Usage\nLet's say you have this GRDB record:\n```swift\nstruct Book: Identifiable, Equatable, Hashable {\n    var id: Int64? = nil\n    var title: String\n    var year: Int\n    var author: String\n}\n\n/// And its migration:\nmigrator.registerMigration(\"createRecords\") { db in\n    try db.create(table: \"books\") { t in\n        t.autoIncrementedPrimaryKey(\"id\")\n        t.column(\"title\", .text).notNull().unique()\n        t.column(\"year\", .integer).notNull()\n        t.column(\"author\", .text).notNull()\n    }\n}\n```\n\nThen, we advise to use the [database manager](https://github.com/groue/GRDB.swift/blob/master/Documentation/GoodPracticesForDesigningRecordTypes.md#how-to-design-database-managers) to hold the UndoRedoManager instance.\n```swift\nclass DatabaseManager {\n    private let dbQueue: DatabaseQueue\n    private let undoRedo: UndoRedoManager\n    \n    init(dbQueue: DatabaseQueue) throws {\n        self.dbQueue = dbQueue\n        self.undoRedo = try UndoRedoManager(recordTypes: Book.self, db: /*your GRDB database queue or pool*/)\n    }\n```\n\nYou can handle more than one table / record type: `self.undoRedo = try UndoRedoManager(recordTypes: Book.self, Author.self, Editor.self, db: ...)`. \n\nTogether, the tables watched by UndoRedoManager form a \"undo-redo scope\". If the tables included in the scope have foreign key relationships to each other, see \"Foreign Keys\" below. \n\n### Edit barriers\nAfter each \"step\" (an action in your application), call `try undoRedo.barrier()`. It can then be undone by calling `try undoRedo.perform(.undo)` (then, `.redo`). \n```\nextension DatabaseManager {\n    /// Saves (inserts or updates) a book. When the method returns, the\n    /// book is present in the database, and its id is not nil.\n    func saveBook(_ player: inout Book) throws {\n        /*validate that the book title, year, author*/\n        \n        // save to database\n        try dbWriter.write { db in\n            try book.save(db)\n        }\n        \n        // mark an atomic step, that can be un-done then possibly re-done\n        try self.undoRedo.barrier()\n    }\n}\n```\n\nA step can include more than one database transaction. Be careful, that all changes between two calls to `.barrier()` will be grouped in one step.\n\n### Freezing\nIf your database can receive changes without user action, e.g. through background network calls, be careful not to let the user accidentally undo these! You can tell `UndoRedoManager` to stop recording database changes via `.freeze()` / then `.unfreeze()`. However: this library has no understanding of your application logic, so be careful regarding data consistency.\n\n### Foreign keys\nIf foreign keys enforcement is enabled, `UndoRedoManager.init` will ensure that no table, that is related through foreign keys to tables included in the undo-redo scope, are omitted; and will raise in such a case. If a database operation has cascade effects, all the changes will be included in the same step, and thus will reverted together on undo. \n\n### Concurrent instances\nIt is possible to supply a prefix to `UndoRedoManager.init`, so that multiple instances can handle undo-redo for **non-overlapping scopes** in the same database. Having overlapping scopes will result in unpredictable consequences and possibly inconsistent data. \n\n## Acknowledgments\nIt is largely based on the [example code available on the SQLite website](https://www.sqlite.org/undoredo.html.).\n[This translation](https://github.com/McMartin/sqlite-undoredo) to C++ and Python proved useful in understanding and translating the code to Swift. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfaram%2Fgrdbundoredo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperfaram%2Fgrdbundoredo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfaram%2Fgrdbundoredo/lists"}