{"id":18389290,"url":"https://github.com/rightpoint/rzvinyl","last_synced_at":"2025-04-07T02:34:11.245Z","repository":{"id":17716718,"uuid":"20540688","full_name":"Rightpoint/RZVinyl","owner":"Rightpoint","description":"Stack management, ActiveRecord utilities, and seamless importing for Core Data","archived":false,"fork":false,"pushed_at":"2017-02-23T16:36:44.000Z","size":475,"stargazers_count":21,"open_issues_count":9,"forks_count":6,"subscribers_count":34,"default_branch":"master","last_synced_at":"2025-03-22T11:25:08.448Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Rightpoint.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}},"created_at":"2014-06-05T20:36:20.000Z","updated_at":"2021-07-30T18:45:03.000Z","dependencies_parsed_at":"2022-07-26T19:17:04.779Z","dependency_job_id":null,"html_url":"https://github.com/Rightpoint/RZVinyl","commit_stats":null,"previous_names":["raizlabs/rzvinyl"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FRZVinyl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FRZVinyl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FRZVinyl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rightpoint%2FRZVinyl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rightpoint","download_url":"https://codeload.github.com/Rightpoint/RZVinyl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247583232,"owners_count":20961993,"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":[],"created_at":"2024-11-06T01:42:25.496Z","updated_at":"2025-04-07T02:34:06.227Z","avatar_url":"https://github.com/Rightpoint.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"RZVinyl\n=======\n\n[![Build Status](https://travis-ci.org/Raizlabs/RZVinyl.svg)](https://travis-ci.org/Raizlabs/RZVinyl)\n\nStack management, ActiveRecord utilities, and seamless importing for Core Data.\n\n# Installation\n\n### CocoaPods (Recommended)\n\nAdd the following to your Podfile:\n\n```\npod RZVinyl\n```\n\nTo exclude RZImport extensions, use the `Core` subspec:\n\n```\npod RZVinyl/Core\n```\n\nRZVinyl follows semantic versioning conventions. As newer versions are released, you will need to update the version spec in your Podfile as necessary. See the [release history](https://github.com/Raizlabs/RZVinyl/releases) for version information and release notes.\n\n### Manual Installation\n\n##### Without Import Extensions\n\nSimply copy/add the contents of `Classes/` into your project and ensure that you are linking with Core Data. **Do not** copy the contents of `Extensions/`.\n\n##### With Import Extensions\n\nBecause of the optional RZImport extensions, which depend on the RZImport library, manual installation is a bit more difficult. Hence why CocoaPods is the recommended installation method.\n\nTo install manually with RZImport extensions:\n\n1. Follow the steps for installing without import extensions.\n2. Also copy the contents of `Extensions/` into your project.\n3. Install [RZImport](https://github.com/Raizlabs/RZImport) in your project.\n4. Add the following to your build configuration's compiler flags:\n```\n-DRZV_IMPORT_AVAILABLE=1\n```\n\nIf all went well, your project should build cleanly and the methods from `NSManagedObject+RZImport.h` should be available.\n\n# Demo Project\n\nA demo project is available in the `Example` directory. The demo project uses CocoaPods, and can be opened from a temporary directory by running\n\n```\npod try RZVinyl\n```\n\nAlternatively, the demo can be configured by running the following commands from the root project directory.\n\n```\ncd Example\npod install\n```\n\nThen, open `RZVinylDemo.xcworkspace` and check out the demo!\n\n**Note: The above steps assume that the CocoaPods gem is installed.**\n\nIf you do not have CocoaPods installed, follow the instructions [here](http://cocoapods.org/).\n\n# Swift Support\n\nRZVinyl is fully compatible with Swift, and makes use of nullability annotations and lightweight generics where appropriate.\n\n# Overview\n\nTo use RZVinyl, first add `#import \"RZVinyl.h\"` to any classes that will need to use it, or to your app's `.pch` file. RZVinyl can be broken down into three basic areas of functionality, as follows.\n\n## RZCoreDataStack\n\n`RZCoreDataStack` is a class for building and managing Core Data stacks, including the model, managed object context, and persistent store coordinator. It has convenience methods for performing concurrent background operations with a separate managed object context, as well as a set of class methods for making a default stack available via the singleton pattern.\n\n##### Create a new stack\n\n```objective-c\n// Default configuration, default store URL (in Library directory), no extra options\nRZCoreDataStack *myStack = [[RZCoreDataStack alloc] initWithModelName:@\"MyModel\"\n                                                        configuration:nil\n                                                            storeType:NSInMemoryStoreType\n                                                             storeURL:nil\n                                                              options:kNilOptions];\n```\n\n##### Perform a concurrent operation\n\n```objective-c\n[myStack performBlockUsingBackgroundContext:^(NSManagedObjectContext *context) {\n\t// do some stuff with the context\n\t// this block is on a background thread and the context has private-queue confinement\n} completion:^(NSError *err) {\n\t// this block is on the main thread\n}];\n```\n\n##### Purge stale objects from the store\n\nEach managed object subclass can provide a \"stale\" predicate that will be used here to delete all objects which pass the predicate. This is useful for cleaning up stale or orphaned objects. This can also be invoked every time the app enters the background if you initialize the stack with the `RZCoreDataStackOptionsEnableAutoStalePurge` option.\n\n```objective-c\n[myStack purgeStaleObjectsWithCompletion:^(NSError *err){\n\t// This is on the main thread\n\tif ( !err ) {\n\t\tNSLog(@\"Purged!\");\n\t}\n}];\n```\n\n##### Create a background (private queue) context\n\n```objective-c\n// This context is a child of the disk writer context and a sibling of the main thread context. \n// Saving it will automatically merge changes into the main context.\nNSManagedObjectContext *backgroundContext = [myStack backgroundManagedObjectContext];\n```\n\n##### Create a temporary main-thread context\n\n```objective-c\n// This context is a child of the primary main thread context. \n// It is useful for creating a \"sandbox\" for temporary edits that may or may not be saved.\n// Its objects can safely be used on the main thread (e.g. with UI elements).\n// Saving it will automatically push changes up to the primary main thread context.\nNSManagedObjectContext *scratchContext = [myStack temporaryManagedObjectContext];\n```\n\n## RZVinylRecord\n\n`RZVinylRecord` is a category on `NSManagedObject` which provides a partial implementation of the Active Record pattern. Each method in `NSManagedObject+RZVinylRecord` has two signatures - one which accepts a managed object context parameter, and one which uses the main managed object context from the default `RZCoreDataStack`. \n\n```objective-c\n// Delete all objects of receiver's type in default stack's main context\n+ (void)rzv_deleteAll;\n\n// Delete all objects of receiver's type in the provided context\n+ (void)rzv_deleteAllInContext:(NSManagedObjectContext *)context;\n```\n\nThe no-context versions may only be used from the main thread, or an exception will be thrown. Similarly, if no default stack has been set, attempting to call one of these methods without providing a context will also throw an exception.\n\n### Creating/Updating\n\n##### Create a new empty instance\n\n```objective-c\n// Inserted into the default main context\nMyManagedObject *newObject = [MyManagedObject rzv_newObject];\n\n// Inserted into the provided context\nMyManagedObject *newObject = [MyManagedObject rzv_newObjectInContext:context];\n```\n\n##### Retrieve or create an instance for a primary key value\n\nThese methods use the attribute provided by implementing `+ (NSString *)rzv_primaryKey;` in the managed object subclass to search for an existing object with the provided value for that attribute, optionally creating a new object and initializing it with the primary key value if one was not found.\n\n```objective-c\n// In the default main context\nMyManagedObject *existingObjectOrNil = [MyManagedObject rzv_objectWithPrimaryKeyValue:@(12345) createNew:NO];\n\n// In the provided context, creating a new instance if one isn't found\nMyManagedObject *existingObjectOrNil = [MyManagedObject rzv_objectWithPrimaryKeyValue:@(12345) createNew:YES inContext:context];\n\n```\n\nYou can also find/create objects based on a set of other attributes. If `createNew` is YES and a match isn't found, a new instance will be created and initialized with the provided attribute dictionary.\n\n```objective-c\n// In the default main context\nMyManagedObject *existingObjectOrNil = [MyManagedObject rzv_objectWithAttributes:@{ @\"name\" : @\"Bob Marley\" } \n                                                                       createNew:NO];\n\n// In the provided context, creating a new instance if one isn't found\nMyManagedObject *existingObjectOrNil = [MyManagedObject rzv_objectWithAttributes:@{ @\"name\" : @\"Bob Marley\" }\n                                                                       createNew:YES\n                                                                       inContext:context];\n\n```\n\n### Fetching\n\n##### Fetch all objects\n\n```objective-c\n// In the default main context\nNSArray *allMyObjects = [MyManagedObject rzv_all];\n\n// In the provided context\nNSArray *allMyObjects = [MyManagedObject rzv_allInContext:context];\n\n```\n\n##### Fetch objects matching a predicate\n\n```objective-c\n// In the default main context\nNSArray *matchingObjects = [MyManagedObject rzv_where:RZVPred(@\"someAttribute \u003e= 18\")];\n\n// In the provided context\nNSArray *matchingObjects = [MyManagedObject rzv_where:RZVPred(@\"someAttribute \u003e= 18\") inContext:context];\n```\n\nThe \"where\" methods also have versions that take sort descriptors to sort the results.\n\n##### Get the count of objects\n\n```objective-c\n// In the default main context\nNSUInteger theCount = [MyManagedObject rzv_count];\n\n// In the provided context, with a predicate\nNSUInteger theCount = [MyManagedObject rzv_countWhere:RZVPred(@\"someAttribute \u003e= %@\", minimum) \n                                            inContext:context];\n```\n\n### Deleting\n\n##### Delete a single object\n\n```objective-c\n// Uses the object's context\n[myObjectInstance rzv_delete];\n```\n\n##### Delete all objects of receiver's type\n\n```objective-c\n// In the default main context\n[MyManagedObject rzv_deleteAll];\n\n// In the provided context, with a predicate\n[MyManagedObject rzv_deleteAllWhere:RZVPred(@\"someAttribute == nil\") inContext:context];\n```\n\n### Saving\n\nThe semantics of saving an object in CoreData are rather different from what might be expected when using the Active Record pattern, particularly when dealing with a more complex context hierarchy, as in `RZCoreDataStack`. In order to persist changes to the persistent store, it is necessary to save the entire context tree all the way to its root, which also saves any other changes in any of the contexts along the way. To avoid unintended, non-obvious consequences, no \"save\" methods are provided for managed object classes via `RZVinylRecord`, and saving must be handled via managed object contexts themselves.\n\nTo facilitate this, `NSManagedObjectContext+RZVinylSave` provides two methods for saving up the context tree all the way to the persistent store, in synchronous and asynchronous flavors.\n\n##### Synchronous Save\n\n```objective-c\nNSError *saveError = nil;\nif ( ![context rzv_saveToStoreAndWait:\u0026saveError] ) {\n\tNSLog(@\"Error saving context: %@\", saveError);\n}\n```\n\n##### Asynchronous Save\n\n```objective-c\n[context rzv_saveToStoreWithCompletion:^(NSError *error){\n\t// Called on main thread\n\tif ( error ) {\n\t\tNSLog(@\"Error saving context: %@\", saveError);\n\t}\n}];\n```\n\n## RZImport Extensions\n\n[RZImport](https://github.com/Raizlabs/RZImport) can be combined with RZVinyl for powerful automatic importing of managed objects from deserialized JSON or any other plain-ol-data `NSDictionary` or `NSArray` source.\n\n`NSManagedObject+RZImport` provides a partial implementation of the `RZImportable` protocol that automatically handles object uniquing and relationship imports. For a working demo, see the example project.\n\n### Usage\n\nTo enable RZImport for your managed object subclasses, create a category and implement the following methods from `RZVinylRecord` and `RZVinylRip` informal protocols:\n\n##### `+ (NSString *)rzv_primaryKey;`\n\nImplement to return the name of the property attributes representing the \"unique ID\" of this object type\n\n##### `+ (NSString *)rzv_externalPrimaryKey;`\n\nIf the key in the dictionary representations of this object is different from the primary key property name, implement this method to return that key here. If not implemented, the same value returned by `rzv_primaryKey` will be used to find unique instances of the object.\n\nYou can also implement the methods of `RZImportable` in your managed object classes to handle validation, provide further custom key/property mappings, etc, with two important caveats:\n\n##### Do not override `+ (id)rzi_existingObjectForDict:(NSDictionary *)dict`\n\nThis is implemented by the `NSManagedObject+RZImport` category to handle Core Data concurrency, and internally calls the extended version with a context parameter:\n\n```objective-c\n+ (id)rzi_existingObjectForDict:(NSDictionary *)dict inContext:(NSManagedObjectContext *)context;\n```\n\nThe implementation provided by the category automatically manages unique objects during an import by finding an existing object matching the dictionary being imported. This default implementation is safe to override as long as you always return the value provided by `super` in the cases that your override does not handle.\n\n##### Must call super for `- (BOOL)rzi_shouldImportValue:(id)value forKey:(NSString *)key`\n\nThe category implementation handles recursive imports for keys representing relationships. You can override the method in a subclass, as long as you return the result of invoking the `super` implementation for keys that your override does not handle. See below for an example.\n\n\n### Example\n\nHere is an example of a managed object subclass that is configured for usage with `RZImport`.\n\n**RZArtist.h**\n\n```objective-c\n@interface RZArtist : NSManagedObject\n\n@property (nonatomic, retain) NSNumber *remoteID;\n@property (nonatomic, retain) NSDate *birthdate;\n@property (nonatomic, retain) NSString *name;\n@property (nonatomic, retain) NSNumber *rating;\n\n@property (nonatomic, retain) RZGenre *genre; \t// one-to-one relationship\n@property (nonatomic, retain) NSSet *songs; \t// one-to-many relationship of 'RZSong' objects\n\n@end\n```\n\n**RZArtist+RZImport.h**\n\n```objective-c\n@interface RZArtist (RZImport) \u003cRZImportable\u003e\n\n@end\n```\n\n**RZArtist+RZImport.m**\n\n```objective-c\n@implementation RZArtist (RZImport)\n\n+ (NSString *)rzv_primaryKey\n{\n    return @\"remoteID\";\n}\n\n+ (NSString *)rzv_externalPrimaryKey\n{\n\treturn @\"id\";\n}\n\n+ (NSDictionary *)rzi_customMappings\n{\n\treturn @{ @\"dob\" : @\"birthdate\" };\n}\n\n+ (NSString *)rzi_dateFormatForKey:(NSString *)key\n{\n\tif ( [key isEqualToString:@\"dob\"] ) {\n\t\treturn @\"yyyy-MM-dd\";\n\t}\n\treturn nil;\n}\n\n- (BOOL)rzi_shouldImportValue:(id)value forKey:(NSString *)key\n{\n\t// Genre string will be imported as a managed object (RZGenre)\n\tif ( [key isEqualToString:@\"genre\"] ) {\n\t\t// Could also use an NSValueTransformer if this will be done in multiple classes\n\t\tif ( [value isKindOfClass:[NSString class]] ) {\n\t\t\tself.genre = [RZGenre rzv_objectWithAttributes:@{ @\"name\" : value }\n\t\t\t\t\t\t\t                     createNew:YES\n\t\t\t\t\t\t\t                     inContext:self.managedObjectContext];\n\t\t}\n\t\treturn NO;\n\t}\n\treturn [super rzi_shouldImportValue:value forKey:key];\n}\n\n@end\n```\n\nUsing this basic implementation and assuming `RZSong` and `RZGenre` are also configured correctly, you can do the following:\n\n```objective-c\n// This could just as easily be deserialized JSON\nNSDictionary *artistDict = @{\n\t@\"id\" : @100,\n\t@\"dob\" : @\"1942-11-27\", \t    // string -\u003e date, via provided format\n\t@\"rating\" : @\"4.7\", \t\t    // string -\u003e number, automatically\n\t@\"name\" : @\"Jimi Hendrix\",\n\t@\"genre\" : @\"Psychedelic Rock\", // string -\u003e RZGenre, via protocol method\n\t@\"songs\" : @[\t\t\t        // array -\u003e RZSong to-many relationship, automatically \n\t\t@{\n\t\t\t@\"id\" : @1000,\n\t\t\t@\"title\" : @\"Hey Joe\"\n\t   \t},\n\t   \t@{\n\t   \t\t@\"id\" : @1001,\n\t   \t\t@\"title\" : @\"Spanish Castle Magic\"\n\t   \t}\n\t]\n};\n\n[[RZCoreDataStack defaultStack] performBlockUsingBackgroundContext:^(NSManagedObjectContext *context) {\n\t\n\t// Import jimi and his nested songs from the dictionary\n\tRZArtist *jimi = [RZArtist rzi_objectFromDictionary:artistDict inContext:context];\n\n} completion:^(NSError *err) {\n\tif ( !err ) {\n\t\t[myStack save:YES];\n\t}\n\t\n\t// Fetch the record from the main thread\n\tRZArtist *mainThreadJimi = [RZArtist rzv_objectWithPrimaryKeyValue:@100];\n}];\n\n```\n\n# Full Documentation\n\nFor more comprehensive documentation, see the [CococaDocs](http://cocoadocs.org/docsets/RZVinyl) page.\n\n## Maintainers\n\n[KingOfBrian](https://github.com/KingOfBrian) ([@KingOfBrian](http://twitter.com/KingOfBrian))\n\n[mgorbach](https://github.com/mgorbach) ([@mgorbach](http://twitter.com/mgorbach))\n\n[nbonatsakis](https://github.com/nbonatsakis) ([@nickbona](http://twitter.com/nickbona))\n\n[jatraiz](https://github.com/jatraiz) ([@jAtSway](http://twitter.com/jAtSway))\n\n[SpencerP](https://github.com/SpencerP)\n\n[jwatson](https://github.com/jwatson) ([@johnnystyle](http://twitter.com/johnnystyle))\n\n## Contributors\n\n[ndonald2](https://github.com/ndonald2) ([@infrasonick](http://twitter.com/infrasonick)) \n\n## License\n\nRZVinyl is licensed under the MIT license. See the `LICENSE` file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frightpoint%2Frzvinyl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frightpoint%2Frzvinyl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frightpoint%2Frzvinyl/lists"}