{"id":18314004,"url":"https://github.com/oreillymedia/flapjack","last_synced_at":"2025-04-05T18:32:23.104Z","repository":{"id":33597668,"uuid":"141654051","full_name":"oreillymedia/flapjack","owner":"oreillymedia","description":"A Swift data persistence API with support for Core Data.","archived":false,"fork":false,"pushed_at":"2024-11-27T16:11:08.000Z","size":4194,"stargazers_count":20,"open_issues_count":2,"forks_count":2,"subscribers_count":61,"default_branch":"main","last_synced_at":"2025-03-21T08:51:19.467Z","etag":null,"topics":["cocoapods","core-data","ios","macos","swift"],"latest_commit_sha":null,"homepage":"https://github.com/oreillymedia/flapjack","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/oreillymedia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-07-20T02:35:07.000Z","updated_at":"2024-10-03T13:53:37.000Z","dependencies_parsed_at":"2022-08-07T22:15:29.826Z","dependency_job_id":"3e1947d0-2ab6-4146-92bf-df8f827d4a48","html_url":"https://github.com/oreillymedia/flapjack","commit_stats":{"total_commits":61,"total_committers":9,"mean_commits":6.777777777777778,"dds":0.4590163934426229,"last_synced_commit":"d72d2d4908af8b3236d74df7d6cff9403ac51cfd"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oreillymedia%2Fflapjack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oreillymedia%2Fflapjack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oreillymedia%2Fflapjack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oreillymedia%2Fflapjack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oreillymedia","download_url":"https://codeload.github.com/oreillymedia/flapjack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247384111,"owners_count":20930414,"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":["cocoapods","core-data","ios","macos","swift"],"created_at":"2024-11-05T16:29:29.642Z","updated_at":"2025-04-05T18:32:20.413Z","avatar_url":"https://github.com/oreillymedia.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flapjack\n\nFlapjack is an iOS/macOS/tvOS framework with 2 primary goals.\n\n1. Help you abstract your model-focused database persistence layer from the rest of your app\n2. Simplify the database layer's API into an easy-to-use, easy-to-remember, full Swift one\n\nIt lets you _skip_ the boilerplate commonly associated with database layers like Core Data and lets you introduce structured, sane data persistence in your app _sooner_, letting you spend more of your time creating the app you really want. We use it at [O'Reilly Media][orm] for our iOS apps, and if you like what you see, perhaps you will too.\n\n\n## Getting started\n\n### Swift Package Manager\n\nSwift Package Manager is the preferred way to use Flapjack. Add the following as a dependency to the `dependencies` array in your `Package.swift` file:\n\n```swift\n.package(name: \"Flapjack\", url: \"https://github.com/oreillymedia/flapjack.git\", .upToNextMajor(from: \"0.8.1\"))\n```\n\nThen you'll specify `Flapjack` as a dependency of the target in which you wish to use it. You can also import `FlapjackCoreData` and `FlapjackUIKit`.\n\n```swift\n.package(name: \"FlapjackCoreData\", url: \"https://github.com/oreillymedia/flapjack.git\", .upToNextMajor(from: \"0.8.1\"))\n.package(name: \"FlapjackUIKit\", url: \"https://github.com/oreillymedia/flapjack.git\", .upToNextMajor(from: \"0.8.1\"))\n```\n\n### CocoaPods\n\nFlapjack is also available through [CocoaPods][cpd]. To install it, simply add the following line to your Podfile:\n\n```ruby\npod 'Flapjack', '0.8.1'\n# If you're using Core Data...\npod 'Flapjack/CoreData', '0.8.1'\n# If you're targeting iOS and want some helpers...\npod 'Flapjack/UIKit', '0.8.1'\n```\n\nAnd run `pod install` at the command line.\n\n\n## Usage\n\nFull documentation is forthcoming, but here's a good thorough run-through of what Flapjack has to offer.\n\nIn your iOS project (like perhaps in your `UIApplicationDelegate`), kick things off with the following code (if you're using Core Data; support for more databases planned).\n\n```swift\nimport Flapjack\n\n// Create the DataAccess object, your main point-of-entry for persistence.\n// You can also pass in `.sql(filename: \"YourCoreDataStore.sql\")`.\nlet dataAccess = CoreDataAccess(name: \"YourCoreDataStore\", type: .memory)\n\n// Then tell the stack to configure itself.\ndataAccess.prepareStack(asynchronously: true) { error in\n    if let error = error {\n        print(error.localizedDescription)\n    }\n\n    // Make sure you retain your `dataAccess` variable, and now you're all\n    //   ready to go!\n}\n```\n\nFor your model objects to take part in the simplified API provided by Flapjack, you'll need to make sure they conform to `DataObject`. [For a class such as `Pancake`][pcm] that has the fields `identifier`, `flavor`, and `radius` defined in a Core Data model, this would look like the following.\n\n```swift\nextension Pancake: DataObject {\n    // The type of your primary key, if you have one of your own.\n    public typealias PrimaryKeyType = String\n    // The name of the entity as Core Data knows it.\n    public static var representedName: String {\n        return \"Pancake\"\n    }\n    // The key path to your model's primary key.\n    public static var primaryKeyPath: String {\n        return #keyPath(identifier)\n    }\n    // An array of sorting criteria.\n    public static var defaultSorters: [SortDescriptor] {\n        return [\n            SortDescriptor(#keyPath(flavor), ascending: true, caseInsensitive: true),\n            SortDescriptor(#keyPath(radius), ascending: false)\n        ]\n    }\n}\n```\n\nNow you're cookin'. Interacting with the data store is even easier.\n\n```swift\n// Get every pancake.\nlet pancakes = dataAccess.mainContext.objects(ofType: Pancake.self)\n// Get just the chocolate chip ones.\nlet pancakes = dataAccess.mainContext.objects(ofType: Pancake.self, attributes: [\"flavor\": \"Chocolate Chip\"])\n// Create your own.\nlet pancake = dataAccess.mainContext.create(Pancake.self, attributes: [\"flavor\": \"Rhubarb\"])\n// Save your changes.\nlet error = context.persist()\n```\n\nGranted you don't want to do expensive data operations on the main thread. Flapjack's Core Data support follows best practices for such a thing:\n\n```swift\ndataAccess.performInBackground { [weak self] context in\n    let pancake = context.create(Pancake.self, attributes: [\"flavor\": flavor, \"radius\": radius, \"height\": height])\n    let error = context.persist()\n\n    DispatchQueue.main.async {\n        guard let `self` = self else {\n            return\n        }\n        let foregroundPancake = self.dataAccess.mainContext.object(ofType: Pancake.self, objectID: pancake.objectID)\n        completion(foregroundPancake, error)\n    }\n}\n```\n\nSick of your database? There's a function for that, too.\n\n```swift\ndataAccess.deleteDatabase(rebuild: true) { error in\n    if let error = error {\n        print(error.localizedDescription)\n    }\n\n    // It's almost as if it never happened.\n}\n```\n\n\n## Data sources\n\nThis wouldn't be nearly as much fun if Flapjack didn't provide a way to automatically listen for model changes. The `DataSource` and `SingleDataSource` protocols define a way to listen for changes on a collection of persisted objects _or_ a single object, respectively. If you're targeting Core Data, the two implementations of those protocols (`CoreDataSource` and `CoreSingleDataSource`) are powered by `NSFetchResultsController` and listening to `.NSManagedObjectContextObjectsDidChange`, respectively.\n\n```swift\nimport Flapjack\n\nlet dataSourceFactory = CoreDataSourceFactory(dataAccess: dataAccess)\nlet queryAttributes = [\"radius\": 2.0, \"flavor\": \"Chocolate Chip\"]\nlet dataSource: CoreDataSource\u003cPancake\u003e = dataSourceFactory.vendObjectsDataSource(attributes: queryAttributes, sectionProperty: \"flavor\", limit: 100)\n\n// Prepare yourself for pancakes, but only chocolate chip ones bigger than a 2\" radius, and no more than 100.\n// This block fires every time the data source picks up an insert/change/deletion.\ndataSource.onChange = { itemChanges, sectionChanges in\n\t// If you've added `Flapjack/UIKit` to your Podfile, you get helper extensions!\n\tself.tableView.performBatchUpdates(itemChanges, sectionChanges: sectionChanges)\n\n\t// Get a specific pancake:\n\tprint(\"\\(String(describing: dataSource.object(at: IndexPath(item: 0, section: 0))))\")\n}\n\n// Kick off a call to start listening (and immediately fire `.onChange` with all existing results).\ndataSource.execute()\n```\n\nFor a more complete example on how to use `CoreDataSource`, see [AutomaticViewController.swift][avc]. To see the steps you'd have to go through to access stored data _without_ it, see [ManualViewController.swift][mvc].\n\n\n## Migrations\n\nSupport for \"easier\" Core Data migrations is currently evolving, but here's what you can expect right now. Flapjack has a `Migrator` class that you can conform to, and it's this object you'll use to provide your `DataAccess` class with a way to migrate your data store. It's a relatively sparse protocol right now, but if you look at the Core Data implementation of this object (`CoreDataMigrator`), you can see how this comes together. This is a pretty close adaptation of the way we handle migrations in our iOS apps at O'Reilly Media. Here's what happens, step by step.\n\n- By conforming to `DataAccessDelegate`, you'll be notified when the stack is ready for a `Migrator`.\n- In response to this delegate call, you'll initialize and return a `CoreDataMigrator` by providing the `storeURL` and `bundle` where the data store file and compiled model can be found, respectively.\n- Then the `DataAccess` object should handle the rest, which is essentially a call to `migrate()`.\n- Upon invocation of `migrate()`, a temporary folder is made to house any intermediary files.\n- Then your compiled data model is scanned for all available model versions, and then we also try and figure out which version is the _current_ version, and then we build an iterative list of versions by which to migrate (support for supplying a custom list of versions to migrate is forthcoming).\n- Then, between each version, we either process a heavyweight migration (if an explicit mapping model is found) or a lightweight migration (if an implicit mapping model can be inferred).\n\n\n## Authors\n\n- Matt Blackmon ([@mblackmon][mbl])\n- Laura Dickey ([@lj-dickey][ljd])\n- Ben Kreeger ([@kreeger][krg])\n- Scott Starr ([@awaltzforvenus][sst])\n\n\n## License\n\nFlapjack is available under the MIT license. See [LICENSE][lic] file for more info.\n\n\n[orm]:     https://oreilly.com\n[cpd]:     https://cocoapods.org\n[pcm]:     https://github.com/oreillymedia/flapjack/blob/master/Example/Flapjack/Core%20Data/Pancake.swift\n[avc]:     https://github.com/oreillymedia/flapjack/blob/master/Example/Flapjack/AutomaticViewController.swift\n[mvc]:     https://github.com/oreillymedia/flapjack/blob/master/Example/Flapjack/ManualViewController.swift\n[mbl]:     https://github.com/mblackmon\n[ljd]:     https://github.com/lj-dickey\n[krg]:     https://github.com/kreeger\n[sst]:     https://github.com/awaltzforvenus\n[lic]:     https://github.com/oreillymedia/flapjack/blob/master/LICENSE\n[spm]:     https://swift.org/package-manager/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foreillymedia%2Fflapjack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foreillymedia%2Fflapjack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foreillymedia%2Fflapjack/lists"}