{"id":15038695,"url":"https://github.com/tonystone/coherence","last_synced_at":"2025-04-10T00:00:33.250Z","repository":{"id":56906224,"uuid":"45281440","full_name":"tonystone/coherence","owner":"tonystone","description":"Coherence: a comprehensive resource management framework.","archived":false,"fork":false,"pushed_at":"2020-01-29T23:41:47.000Z","size":5855,"stargazers_count":3,"open_issues_count":2,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-07T22:03:28.948Z","etag":null,"topics":["coherence","core-data-stack","coredata","coredatastack","framework","ios","macos","osx","persistent-container","persistentcontainer","swift","swift3","tvos","watchos"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tonystone.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-10-30T23:26:23.000Z","updated_at":"2020-12-24T10:22:50.000Z","dependencies_parsed_at":"2022-08-21T02:20:57.419Z","dependency_job_id":null,"html_url":"https://github.com/tonystone/coherence","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonystone%2Fcoherence","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonystone%2Fcoherence/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonystone%2Fcoherence/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonystone%2Fcoherence/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tonystone","download_url":"https://codeload.github.com/tonystone/coherence/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248131328,"owners_count":21052819,"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":["coherence","core-data-stack","coredata","coredatastack","framework","ios","macos","osx","persistent-container","persistentcontainer","swift","swift3","tvos","watchos"],"created_at":"2024-09-24T20:39:42.737Z","updated_at":"2025-04-10T00:00:33.216Z","avatar_url":"https://github.com/tonystone.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Coherence ![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-lightgray.svg?style=flat)\n\n\u003ca href=\"https://github.com/tonystone/coherence/\" target=\"_blank\"\u003e\n   \u003cimg src=\"https://img.shields.io/badge/platforms-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS-lightgray.svg?style=flat\" alt=\"platforms: iOS | macOS | tvOS | watchOS\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/tonystone/coherence/\" target=\"_blank\"\u003e\n   \u003cimg src=\"https://img.shields.io/badge/Swift-4.2-orange.svg?style=flat\" alt=\"Swift 4.2\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://travis-ci.org/tonystone/coherence\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://travis-ci.org/tonystone/coherence.svg?branch=master\" alt=\"travis-ci.org\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/tonystone/coherence\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://codecov.io/gh/tonystone/coherence/branch/master/graph/badge.svg\" alt=\"Codecov\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/tonystone/coherence/\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/cocoapods/v/Coherence.svg?style=flat\" alt=\"Pod version\"\u003e\n\u003c/a\u003e\n\n---\n**Coherence**, the configurable CoreData extension for the Web Service era.  Coherence helps you build apps that require persistence, offline storage, store and forward and web connectivity. It provides you with a comprehensive framework for managing low level resources intelligently and efficiently.\n\n# Overview\n\n### Connect\nAt the heart of Coherence are it's resource management services which are expose via the `Connect` protocol. Coherence provides a generic concrete implementation of the `Connect` protocol with the `GenericConnect` class. Coherence takes on the role of a persistent container (implementing the `PersistentStack` protocol) creating and managing a core data stack for you.  All services it offers outside this are built on top of the `GenericConnect` instance.\n\n### Container\n\nConnect's container is built on top of the Coherence Container which is an implementation of the `PersistentStack` protocol. Connect creates an instance\n\n### Context Strategy\n\nIn general usage a CoreData stack can be configured in several configurations base on your use case.  To facilitate this,\nCoherence gives you a choice of strategies used by a PersistentStack instance.  The `ContextStrategy` classes encapsulate the layout and behavior of the ManagedObjectContexts of the CoreData stack. `Connect` currently has 4 built in strategies `Direct`, `DirectIndependent`, `IndirectNested` and `Mixed`.  If one of these strategies doesn't give you what you require for your application, you can also create your own by implementing the `ContextStrategyType` protocol.\n\n###### Direct\n\nThe `ContextStrategy.Direct`  manages the viewContext and BackgroundContext connected directly to the `NSPersistentStoreCoordinator`.  Changes made to `BackgroundContext`s are propagated directly to the persistentStore allowing merge policies to be set and respected.\n\n![Context Strategy - Direct Diagram](Documentation/ContextStrategy-Direct.png)\n\n- Note: The view context will be kept up to date and persisted to the store when a background context is saved.\n\n###### Direct Independent\n\nThe `ContextStrategy.Independent` manages independent contexts (for view and background) connected directly to the `NSPersistentStoreCoordinator`.\n\n![Context Strategy - Direct Independent Diagram](Documentation/ContextStrategy-DirectIndependent.png)\n\n- Note: The view context will not be kept up to date with this strategy.\n\n###### Indirect Nested\n\nThe `ContextStrategy.Nested` manages nested (parent/child) contexts (for view and background) connected indirectly through a root context to the `NSPersistentStoreCoordinator`.\n\nPropagation of changes to the persistent store are done indirectly in the background through a root context.\n\n![Context Strategy - Indirect Nested Diagram](Documentation/ContextStrategy-IndirectNested.png)\n\n- Note: The view context will be kept up to date and persisted to the store when a background context is saved.\n\n###### Mixed\n\nThe `ContextStrategy.Mixed` manages a nested (parent/child) viewContexts connected indirectly\nthrough a root context to the `NSPersistentStoreCoordinator` and background contexts\nthat are connected directly to the `NSPersistentStoreCoordinator`.\n\nChanges made to BackgroundContexts are propagated directly to the persistentStore\nallowing merge policies to be set and respected. `viewContext` updates are done purely\nin memory and propagated to the persistentStore indirectly in a background thread\nthrough the rootContext.\n\n![Context Strategy - Mixed Diagram](Documentation/ContextStrategy-Mixed.png)\n\n- Note: The view context will be kept up to date and persisted to the store when a background context is saved.\n\n# Usage \n\n## Initialization \u0026 Startup\n\nDuring initialization and startup of **Coherence** you can rely on its default behavior, or you can take complete control over these aspects.\n\nBoth `GenericConnect` and `GenericPersistentContainer` implement the `PersistentStack` protocol.  This means that the initialization and startup sequence is the same except that `GenericConnect` has one additional requirement which is to `start` the instance.  Other than that, they are completely interchangeable as a `PersistentStack`.\n\nPersistent stores can be attached and detached in steps to facilitate various requirements and use cases.  For instance, you may want to bring a global store online that stores your user tables so you can determine the logged in user.  The user information can be used to decide the proper stores to attach for that user.   This can be accomplished through the methods `attachPersistentStore(for:)` and `detach(persistentStore:)`.\n\n\u003e Note: Both `attachPersistentStore(for:)` and `detach(persistentStore:)` have plural equivalents (`attachPersistentStores(for:)` and `detach(persistentStores:)`) that allow you to attach and detach multiple stores at a time.  In the case of `attach`, you attach multiple stores at the same location (`url`).  Both versions can be intermixed if required.\n\nStores can be attached at any time after you create the `Connect` or `PersistentStack` instance.  The \"attach\" methods all take a `StoreConfiguration` which can be used to specify various aspects of the stores as well as an `url` that specifies the location to store the file.\n\nBelow we've created startup examples for various use cases including the case described above.\n\n### No Configuration Required\n\nIn this scenario, the developer wants a no hassle, simple configuration, so all that is needed are the default values that **Coherence**.  This scenario is as simple as starting the instance after it is instantiated.\n\n```swift\nlet connect: Connect = GenericConnect\u003cContextStrategy.Mixed\u003e(name: \"MyModelName\")\n\ntry connect.start()\n```\n\n### Custom Configuration Required\n\nIn this scenario, the developer has a custom configuration setup for the persistent stores that he wants to maintain.\n```swift\nlet connect: Connect = GenericConnect\u003cContextStrategy.Mixed\u003e(name: \"MyModelName\")\n\ntry connect.attachPersistentStores(for: [StoreConfiguration(name: \"TransientData\",  type: NSInMemoryStoreType),\n                                         StoreConfiguration(name: \"PersistentData\", type: NSSQLiteStoreType)])\ntry connect.start()\n```\nThere are two configurations defined in the users model `TransientData` and `PersistentData` which will be stored in different types of PersistentStores.  In the case of `TransientData`, it will be stored in a `NSInMemoryStoreType` since it is not required to persist between application starts.  `PersistentData` on the other hand, will be stored persistently in a `NSSQLiteStoreType` store at the `defaultStoreLocation`.\n\n### User per configuration\n\nIn this scenario, the developer wants a Configuration/PersistentStore per user that logs into the application.\n```swift\nlet connect: Connect = GenericConnect\u003cContextStrategy.Mixed\u003e(name: \"MyModelName\")\n\nlet userName = loggedInUserName() /* Determine user that is logged */\n\ntry connect.attachPersistentStores(at:  URL(fileURLWithPath: \"/persistentStores/location/\\(userName)\"),\n                                   for: [StoreConfiguration(name: \"TransientData\",  type: NSInMemoryStoreType),\n                                         StoreConfiguration(name: \"PersistentData\", type: NSSQLiteStoreType)])\n\ntry connect.start()\n```\nThe first step would be to figure out what user is logged in.  Once that information is obtained, `attachPersistentStores(at:for:) can be called with an `url` that points to the location of the stores for the logged in user.\n\n\n### Global Store, User per configuration\n\nIn this scenario, the developer starts a global PersistentStore(s) before starting to connect with a custom Configuration/StoreConfigurations per user that logs into the application.\n```swift\nlet connect: Connect = GenericConnect\u003cContextStrategy.Mixed\u003e(name: \"MyModelName\")\n\ntry connect.attachPersistentStore(at: URL(fileURLWithPath: \"/persistentStores/location\"), for: StoreConfiguration(name: \"GlobalData\", type: NSSQLiteStoreType))\n\nlet userName = loggedInUserName() /* Determine user that is logged */\n\ntry connect.attachPersistentStores(at:  URL(fileURLWithPath: \"/persistentStores/location/\\(userName)\"),\n                                   for: [StoreConfiguration(name: \"TransientData\",  type: NSInMemoryStoreType),\n                                         StoreConfiguration(name: \"PersistentData\", type: NSSQLiteStoreType)])\n\ntry connect.start()\n```\nThis can be achieved by first opening a persistent user database to locate information about the logged in user.  Once determined, PersistentStores in a user specific directory can be opened.  This scenario is very similar to above with the exception that an `url` is specified for the global store in addition to the `url` for the user specific stores.\n\n\n## Where to initialize and start\n\nThe `Connect` instance can be stored in your `AppDelegate` and injected throughout the application to gain access to the services that Coherence offers.  In the following code block, we do just that and also start the instance within the `application(_:didFinishLaunchingWithOptions:)`.  This is a common place to start the instance, but it can be started in any location you desire. \n\n```swift\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    var window: UIWindow?\n\n    let connect: Connect = GenericConnect\u003cContextStrategy.Mixed\u003e(name: \"ModelName\")\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -\u003e Bool {\n\n        do {\n            try self.connect.start()\n\n        } catch {\n            fatalError(\"\\(error)\")\n        }\n        return true\n    }\n}\n```\n\nCoherence does not enforce any constraints as to where you initialize or start its instances but understand that until the `Connect` instance has been started, database operation and other services `Connect` offers are not available (this includes your CoreData stack.)\n\n## Actions\n\n**Coherenc.Connect** contains an extensive *process management and monitoring system* based around its concept of the `Action`. Actions encapsulate work related to keeping the data cache in sync\nwith remote systems through web services as well as offering an environment for running generic tasks within the system.\n\n\n**Benefits of Actions:**\n- [x] Encapsulates work in a manageable class.\n- [x] Executes in an managed container.\n- [x] Provides managed services by the container.\n- [x] Is cancelable.\n- [x] Can be monitored during execution.\n- [x] Captures run time statistics while executing.\n- [x] Can be automatically executed by Coherence to keep the cache in sync (Future)\n\nActions are executed by Connect via the `execute()` function and return an `ActionProxy` which allows you to control and monitor the action as they are executing.\n\nThere are 2 primary types of Actions, a `GenericAction` and an `EntityAction` which are described in more detail below.\n\n### Generic Action\n\nA `GenericAction` is an Action that allows you to encapsulate, monitor, and control the life cycle of just about any type of work including web services that do not interact with the database.  The template below represents a simple template for building you own actions.\nThe `Action` should implement the `GenericAction` protocol and has a requirement for 2 methods, `execute()` and `cancel()`.  These methods are called back during execution by the container.\n\n\n```swift\n\nclass MyGenericAction: GenericAction {\n\n    internal var canceled: Bool = false\n\n    ///\n    /// This will be executed by the container\n    ///\n    func execute() throws {\n    \n        guard !canceled else { return }\n\n        /// Perform work here\n    }\n\n    func cancel() {\n        self.canceled = true\n    }\n}\n```\n\n\n### Entity Action\n\nIn addition, there are `EntityAction`'s which encapsulate data cache synchronization functionality and offer database specific services while offering the same life cycle management functionality as a `GenericAction`.\n\n```swift\n\nclass MyEntityAction: EntityAction {\n\n    internal var canceled: Bool = false\n\n    private let webService: MyWebService\n    private let parameter1: Double\n    private let parameter2: Double\n\n    ///\n    /// Capture the parameters you require for the Web Service call in your init\n    ///\n    public init(webService: MyWebService, parameter1: Double, parameter1: Double) {\n        self.webService = webService\n        self.parameter1 = parameter1\n        self.parameter1 = parameter1\n    }\n\n    ///\n    /// This will be executed by the container passing you an `ActionContext` to use for your database work.\n    ///\n    func execute(context: ActionContext) throws {\n\n        guard !canceled else { return }\n\n        let (data, response, error) = webService.execute(request: MyWebServiceTask(parameter1: self.parameter1, parameter2: self.parameter2))\n\n        guard !canceled else { return }\n\n        if let response = response as? HTTPURLResponse {\n\n            switch response.statusCode {\n\n            case 200:\n                ///\n                /// Process the returned data\n                ///\n                break\n            default:\n                break\n            }\n        }\n\n        ///\n        ///  Errors can be thrown directly from actions.  The container will\n        ///  catch them and report them back through the notification system\n        ///  and you completion block if one was supplied.\n        ///\n        if let error = error {\n            throw error\n        }\n    }\n}\n```\n\n# Sources and Binaries\n\nYou can find the latest sources and binaries on [github](https://github.com/tonystone/coherence).\n\n# Communication and Contributions\n\n- If you **found a bug**, _and can provide steps to reliably reproduce it_, [open an issue](https://github.com/tonystone/coherence/issues).\n- If you **have a feature request**, [open an issue](https://github.com/tonystone/coherence/issues).\n- If you **want to contribute**\n   - Fork it! [Coherence repository](https://github.com/tonystone/coherence)\n   - Create your feature branch: `git checkout -b my-new-feature`\n   - Commit your changes: `git commit -am 'Add some feature'`\n   - Push to the branch: `git push origin my-new-feature`\n   - Submit a pull request :-)\n\n# Installation (CocoaPods)\n\nCoherence is available through [CocoaPods](http://cocoapods.org). Currently Swift is the default so to install it, simply add the following line to your Podfile:\n\n```ruby\npod \"Coherence\"\n```\n\nSee the [\"Using CocoaPods\"](https://guides.cocoapods.org/using/using-cocoapods.html) guide for more information.\n\n# Minimum Requirements\n\nBuild Environment\n\n| Platform | Swift | Swift Build | Xcode |\n|:--------:|:-----:|:----------:|:------:|\n| OSX      | 4.2 | \u0026#x2718; | 10.1 |\n| Linux    | Not supported | \u0026#x2718; | \u0026#x2718; |\n\nMinimum Runtime Version\n\n| iOS |  OS X | tvOS | watchOS | Linux |\n|:---:|:-----:|:----:|:-------:|:------------:|\n| 9.0 | 10.13 | 9.0  | 2.0 | Not supported |\n\n# Author\n\nTony Stone ([https://github.com/tonystone](https://github.com/tonystone))\n\n# License\n\nCoherence is released under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftonystone%2Fcoherence","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftonystone%2Fcoherence","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftonystone%2Fcoherence/lists"}