GithubHelp home page GithubHelp logo

mpurbo / mmpcoredatahelper Goto Github PK

View Code? Open in Web Editor NEW
6.0 2.0 1.0 387 KB

Lightweight ActiveRecord and LINQ-inspired, functional wrapper for CoreData

License: MIT License

Objective-C 92.71% Ruby 7.29%

mmpcoredatahelper's Introduction

MMPCoreDataHelper

A lightweight helper library for common CoreData tasks providing data access pattern inspired by Active Record, LINQ, and functional programming. Even if the library doesn't have the function you need, you can always fallback to the standard CoreData, but with thread-safe context bonus.

Features:

  • Thread-safe singleton instance easily accessible from anywhere. No more worrying whether a MOC (NSManagedObjectContext) belongs to the thread or not. The library makes sure that the MOC is local to the whichever thread you're calling the function from.
  • Functional Active Record + LINQ-inspired wrapper for common tasks.
  • Import data directly from CSV file.
  • Automatic configuration and initialization (by convention over configuration) by default but manual configuration is still possible.
  • Get notified on errors and other CoreData events using NSNotificationCenter.

Installation

The recommended way to install is by using CocoaPods. Once you have CocoaPods installed, add the following line to your project's Podfile:

pod "MMPCoreDataHelper"

Usage

Include the header file in your code:

#import <MMPCoreDataHelper/MMPCoreDataHelper.h>

In general, there is no need for database initialization and you can go right ahead directly using your model object to create and query records. See below for cases that require you to explicitly set names before you start using the library.

Create, Update, Delete, Save

Start directly with your CoreData model object (extended from NSManagedObject) and simply use create, update, and save to create records:

// Create record
MMPArtist *artist = [[[MMPArtist create] 
                       update:@{@"id" : @"1", @"name" : @"Daft Punk"}] 
                       save];

// delete record
[[artist delete] save];

// saving several records once is more efficient, use MMPCoreDataHelper shared instance to do bulk saving.

[[MMPArtist create] update:@{@"id" : @"1", @"name" : @"Daft Punk"}];
[[MMPArtist create] update:@{@"id" : @"2", @"name" : @"Pink Floyd"}];
[[MMPArtist create] update:@{@"id" : @"3", @"name" : @"Porcupine Tree"}];

[MMPCoreDataHelper save];

Use clear to delete all records in an entity:

[MMPAlbum clear];

Fetching Data

To fetch data you need to first construct the query (defining constraints) then execute it to produce result. Call query to start building, then use where function to specify filter, order to define sort specification, limit to limit the number of result, offset to specify starting record number, and error to specify code block to be executed when error happens. For constructing NSFetchedResultsController, there are two additional functions: sectionNameKeyPath, and cacheName.

Once a query is constructed, there are several functions to actually produce result. Use all to get all records matches the specified constraints as array, first to get just the first one, count to just count the number of records without actually fetching anything, each to traverse each of the records using block, and fetchedResultsController to return NSFetchedResultsController.

Following code shows how to combine these functions to construct queries and execute it:

// fetch all artists
NSArray *artists = [[MMPArtist query] all];

// print all artists that starts with 'P' ordered by id using block
[[[[MMPArtist query]
              where:@"name LIKE %@", @"P*"]
              order:@"id"]
              each:^(MMPArtist *artist) {
                  NSLog(@"%@", artist.name);
              }];

// or just get the first record of the query
// note that you can also pass NSDictionary of key = value constraint to where function
MMPArtist *artist = [[[MMPArtist query]
                                 where:@{@"name" : @"Pink Floyd"}]
                                 first];
                                 
// many ways to construct where constraint
artist = [[[MMPArtist query]
                      where:@"name == 'Pink Floyd'"]
                      first];
                      
artists = [[[MMPArtist query]
                       where:@"name == 'Pink Floyd' OR name == 'Led Zeppelin'"]
                       all];
                       
artists = [[[MMPArtist query]
                       where:@"name == %@ OR name == %@", @"Queen", @"Gojira"]
                       all];
                       
// or even use NSPredicate if you feel like it
NSPredicate *predicate = ...
artists = [[[MMPArtist query]
                       where:predicate
                       all];
                                 
// use fetchedResultsController to create NSFetchedResultsController from a query
self.fetchedResultsController = [[[[MMPAlbum query]
                                             order:@"artist.name"]
                                             sectionNameKeyPath:@"artist.name"]
                                             fetchedResultsController];

Here's some examples on how to construct more complex ordering:

artists = [[[MMPArtist query] 
                       order:@"name ASC, members DESC"
                       all];
                       
// same ordering different way
artists = [[[MMPArtist query] 
                       order:@{@"name" : @"ASC", @"members" : @"DESC"}
                       all];

Aggregate Attribute Values

The equivalent of SQL's count, min, max, and sum is also available as can be seen in the following example:

// counting all records
NSLog(@"Database initialized, %lu artists created", [[MMPArtist query] count]);

// getting minimum, maximum, and sum of attribute "members"
NSLog(@"### unfiltered artists' members aggregate: min = %@, max = %@, sum = %@",
          [[MMPArtist query] min:@"members"],
          [[MMPArtist query] max:@"members"],
          [[MMPArtist query] sum:@"members"]);

// getting minimum, maximum, and sum of attribute "members" for artists with name that starts with "D"
MMPCoreDataQueryable *artistsD =[[MMPArtist query] where:@"name LIKE %@", @"D*"];
NSLog(@"### D artists' members aggregate: min = %@, max = %@, sum = %@",
      [artistsD min:@"members"],
      [artistsD max:@"members"],
      [artistsD sum:@"members"]);

Importing Data from CSV File

To import data from CSV file, call importer to start building the importer, use sourceURL to specify the CSV source URL, error to specify code block to be executed on errors, each to observe newly imported record, and finally call import to start executing.

// source CSV (with first line header/field names):
// id,name
// 100,Yes
[[[[[[MMPArtist importer]
                sourceType:MMPCoreDataSourceTypeCSV]
                sourceURL:[[NSBundle mainBundle] URLForResource: @"artists" withExtension:@"csv"]]
                error:^(NSError *error) {
                    NSLog(@"[ERROR] error importing from artists CSV: %@", error);
                }]
                each:^(MMPArtist *importedArtist) {
                    NSLog(@"artist %@ imported", importedArtist.name);
                }]
                import];

You can also filter unnecessary field values so that it won't be added to the record by using filter:using: method:

// CSV format:
// id,name,genre
// 100,Yes,"Progressive Rock"
// "genre" field will be ignored
[[[[[[[MMPArtist importer]
                 sourceType:MMPCoreDataSourceTypeCSV]
                 sourceURL:[[NSBundle mainBundle] URLForResource: @"artists" withExtension:@"csv"]]
                 error:^(NSError *error) {
                     NSLog(@"[ERROR] error importing from artists CSV: %@", error);
                 }]
                 filter:@"genre" using:^BOOL(id record) {
                     return NO;
                 }]
                 each:^(MMPArtist *importedArtist) {
                     NSLog(@"artist %@ imported", importedArtist.name);
                 }]
                 import];

For field-level transformation and custom data conversion (e.g. to populate relationship object) use map:using:. For record-level transformation use map as shown in the following example:

// source CSV (with first line header/field names):
// name,artist
// "The Yes Album",Yes
[[[[[[[[MMPAlbum importer]
                 sourceType:MMPCoreDataSourceTypeCSV]
                 sourceURL:[[NSBundle mainBundle] URLForResource: @"albums" withExtension:@"csv"]]
                 error:^(NSError *error) {
                     NSLog(@"[ERROR] error importing from albums CSV: %@", error);
                 }]
                 // map field "artist" from artist's name to NSManagedObject so that it can be set as relationship
                 map:@"artist" using:^MMPArtist *(NSString *value, NSUInteger index) {
                     return [[[MMPArtist query]
                                         where:@{@"name" : value}]
                                         first];
                 }]
                 // map record (populates ID)
                 map:^id(MMPAlbum *importedAlbum, NSUInteger index) {
                     // generate album ID based on artist ID and record index (line on the csv file)
                     importedAlbum.id = [NSString stringWithFormat:@"%@-%lu", importedAlbum.artist.id, (unsigned long)index];
                     return importedAlbum;
                 }]
                 each:^(MMPAlbum *importedAlbum) {
                     NSLog(@"album %@(id = %@) imported for artist %@",
                           importedAlbum.name,
                           importedAlbum.id,
                           importedAlbum.artist.name);
                 }]
                 import];

Optional Initialization

No initialization or configuration necessary assuming the data model (momd) is named exactly the same as the application name. Otherwise, the data model name has to be set before calling any other of singleton's function:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  // Set singleton's data model name
  [MMPCoreDataHelper instance].modelName = @"MyDataModel";

  // Do other application initialization...
  
  return YES;
}

CoreData Fallback

Should you need to use CoreData directly, simply use thread-safe MOC provided by the singleton instance, for example:

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity"
                                          inManagedObjectContext:[MMPCoreDataHelper instance].managedObjectContext];

Documentation

Not currently available, but I'll write documentation as I update the library. If you have any questions or requests for features, please feel free to contact me or create issues.

Contact

MMPCoreDataHelper is maintained by Mamad Purbo

Copyright and License

MMPCoreDataHelper is available under the MIT license. See the LICENSE file for more info.

This library uses code adapted from ObjectiveRecord (https://github.com/supermarin/ObjectiveRecord). Copyright (c) 2014 Marin Usalj http://supermar.in

mmpcoredatahelper's People

Contributors

mpurbo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

mmpcoredatahelper's Issues

Freezing on iOS 9

On iOS 9, when the library is first used in a background thread (before using it in the main thread), the app freezes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.