https://github.com/badchoice/daikiri
Json <-> Model <-> CoreData made easy
https://github.com/badchoice/daikiri
Last synced: about 1 year ago
JSON representation
Json <-> Model <-> CoreData made easy
- Host: GitHub
- URL: https://github.com/badchoice/daikiri
- Owner: BadChoice
- License: mit
- Created: 2016-04-16T14:03:26.000Z (about 10 years ago)
- Default Branch: master
- Last Pushed: 2025-01-13T16:19:05.000Z (over 1 year ago)
- Last Synced: 2025-03-23T08:51:14.541Z (over 1 year ago)
- Language: Objective-C
- Size: 235 KB
- Stars: 5
- Watchers: 2
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
- License: LICENSE
Awesome Lists containing this project
README
# Daikiri
## Description
Its a lighweight and simple library to work with models allowing the JSON to Model to Core Data both ways.
Really useful for server / app synchronization.
## Installation
Using pods:
```
pod 'daikiri'
```
## Usage
Create a model that inherts from `Daikiri` and add the properties you want to be automatically converted
Note that `submodels` will automatically be converted if they also inhert from `Daikiri`
#### JSON
```
#import "Daikiri.h"
#import "Headquarter.h"
@interface Hero : Daikiri
@property (strong,nonatomic) NSString* name;
@property (strong,nonatomic) NSNumber* age;
@property (strong,nonatomic) Headquarter* headquarter;
@end
```
Then you can do:
```
NSDictionary* d = @{
@"name" : @"Batman",
@"age" : @10,
@"headquarter":@{
@"address" : @"patata",
@"isActive" : @1,
@"vehicles" : @[
@{@"model" : @"Batmobile"},
@{@"model" : @"Batwing"},
@{@"model" : @"Tumbler"},
]
}
};
Hero * model = [Hero fromDictionary:d];
```
And convert it back
```
NSDictionary* modelToDict = [model toDictionary];
NSLog(@"Model to dict: %@",modelToDict);
```
You can also convert the arrays to its class, for doing so you need to create the method
`-(Class)property_DaikiriArray` where `property` is the name of the `NSArray` property.
In the previous case we have the model `Headquarter` like this
```
@interface Headquarter : Daikiri
@property(strong,nonatomic) NSString* address;
@property(strong,nonatomic) NSNumber* isActive;
@property(strong,nonatomic) NSArray* vehicles;
@end
```
with the following method
```
-(Class)vehicles_DaikiriArray{
return [Vehicle class];
}
```
And vehicles will be converted automatically.
#### CORE DATA
##### Setup
`Daikiri` Comes with a `CoreData` manager. It creaes the `managedObjectContents` and connects to the database named `yourprojectname.sqlite` at
`applicationDocumentsDirectory`.
You can change the project name by setting the property `databaseName` of the `DaikiriCoreData` manager
```
[DaikiriCoreData manager].databaseName = @"youdatabasename";
```
You should place this call before any other `CoreData` call so it's recomended to do it at `didFinishLaunchingWithOptions`.
The only thing you need to do is to add a call to `[[DaikiriCoreData manager] saveContex]` in your app delegate `-(void)applicationWillTerminate:(UIApplication *)application` to save the context even if there is a crash.
However, you can also use you custom `CoreData` manager by overridin the `+(NSManagedObjectContext*)managedObjectContext` function in your model.
```
+(NSManagedObjectContext*)managedObjectContext{
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
return context;
}
```
##### Testing
Daikiri offers a really easy method to setup the testing database as a full clean one in each test so you can fully do unint testing without any problem. It also uses transactions in each tests so everything is rolled back and the databse is clean in every test.
just add these on your `setUp` and `tearDown` methods in the test
```
- (void)setUp {
[super setUp];
[[DaikiriCoreData manager] useTestDatabase:YES];
[[DaikiriCoreData manager] beginTransaction];
}
- (void)tearDown {
[super tearDown];
[[DaikiriCoreData manager] rollback];
}
```
##### Core data models
With a `Daikiri` model we can work with coredata in an active recod like way. You just need to name the
model the same way it is in the database `.xcdatamodeld`
A `Daikiri` model comes with an `id` property that is the `primary key` used for all the following methods
Then you can do the following
```
[model create] //It creates a new record in the database needs to have the id
model.name = "Bruce wayne";
model.age = @10;
[model save] //Updates the record saved in the database (if it doesn't exists, it will create it)
```
We can also
```
// Get an specific hero
Hero* batman = [Hero find:@10]; //Search the model in the database
[batman delete]; //Deletes it from the database
// Get all heros
NSArray* allHeros = [Hero all];
```
If you want, there are the convenience methods to to those basic actions directly from a dictionary
```
+(bool)createWith:(NSDictionary*)dict;
+(bool)updateWith:(NSDictionary*)dict;
+(bool)deleteWith:(NSNumber*)id;
```
#### Relationships
Alongs with the `find` and `all` When your models are in the database you have diferent ways to acces their relationships
`belongsTo`, `hasMany` and `belongsToMany`.
Check the examples below to understand them
```
//Add models to database
Hero * batman = [Hero createWith:@{@"id":@1, @"name":@"Batman" ,@"age":@49}];
Hero * spiderman = [Hero createWith:@{@"id":@2, @"name":@"Spiderman" ,@"age":@19}];
Hero * superman = [Hero createWith:@{@"id":@3, @"name":@"Superman" ,@"age":@99}];
Enemy* luxor = [Enemy createWith:@{@"id":@1, @"name":@"Luxor" ,@"age":@32}];
Enemy* greenGoblin = [Enemy createWith:@{@"id":@2, @"name":@"Green Goblin" ,@"age":@56}];
Enemy* joker = [Enemy createWith:@{@"id":@4, @"name":@"Joker" ,@"age":@45}];
Friend* robin = [Friend createWith:@{@"id":@1, @"name":@"Robin" ,@"hero_id":batman.id}];
Friend* maryJane = [Friend createWith:@{@"id":@2, @"name":@"Mary Jane" ,@"hero_id":spiderman.id}];
Friend* blackCat = [Friend createWith:@{@"id":@3, @"name":@"Black cat" ,@"hero_id":spiderman.id}];
EnemyHero* luxorBatman = [EnemyHero createWith:@{@"id":@1, @"hero_id":batman.id ,@"enemy_id":luxor.id, @"level":@7}];
EnemyHero* luxorSuperman = [EnemyHero createWith:@{@"id":@2, @"hero_id":superman.id ,@"enemy_id":luxor.id, @"level":@5}];
EnemyHero* jokerBatman = [EnemyHero createWith:@{@"id":@3, @"hero_id":batman.id ,@"enemy_id":joker.id, @"level":@10}];
EnemyHero* greenGoblinSpider= [EnemyHero createWith:@{@"id":@4, @"hero_id":spiderman.id ,@"enemy_id":greenGoblin.id, @"level":@10}];
NSLog(@"Robin's hero is: %@",robin.hero.name); //Belongs to
for(Friend* friend in spiderman.friends){ //has many
NSLog(@"Spiderman friend: %@",friend.name);
}
for(Enemy* enemy in batman.enemies){ //Belongs to many
NSLog(@"Batman enemy: %@ with level: %@",enemy.name, ((EnemyHero*)enemy.pivot).level);
}
```
#### Query builder
We have a `QueryBuilder` to create custom queries, you can do things like
```
EnemyHero * enemyHero = [[EnemyHero.query
where:@"hero_id" is:batman.id]
where:@"enemy_id" is:joker.id]
.first;
NSArray * heroes = [[Hero.query
where:@"id" operator:@">" value:@2]
orderBy:@"age"]
.get;
for(Hero * hero in heroes){
NSLog(@"Hero: %@",hero.name);
}
```
If you class names use a prefix (two chars) and your entities don't, you can override the function
`usesPrefix` to return `true`. This will remove the prefix when fetching to the DB
#### Factory
`Daikiri` comes with a Laravel Like factory class for tests. You can have you factories to create tests objects for you and making each test look very neat.
#### Usage
First create a factory class with a simple `registerFactories` method and create the factories giving a simple dict
@implementation HeroFactory
```
+(void)registerFactories{
[DKFactory define:Hero.class builder:^NSDictionary *{
return @{
@"name": @"Batman",
@"age" : @"49"
};
}];
[DKFactory define:Enemy.class builder:^NSDictionary *{
return @{
@"name": @"Luxor",
@"age" : @"32"
};
}];
}
```
##### Factory states
You can have some diferent types of you class you can define with another name (it will merge the default one and the new one)
```
+(void)registerFactories{
[DKFactory define:Hero.class builder:^NSDictionary *{
return @{
@"name": @"Batman",
@"age" : @"49"
};
}];
[DKFactory define:Hero.class name:@"old" builder:^NSDictionary *{
return @{
@"age" : @"100"
};
}];
}
```
Then on your testClass setup method call the `[Yourfactory registerFactories]`.
After that on your tests you can instantiate any class with a simply call to
```
Hero* testHero = [factory(Hero.class) make];
Enemy* testEnemy = [factory(Enemy.class) create];
```
Note that `make` just creates the object without storing it to the database but `create` does store it to the database.
You can use the non macro constructor to have more options
```
NSArray* oldHerosArray = [[DKFactory factory:Hero.class name:@"old" count:4] make];
```
##### Relationships
The cool thing in the factory is that you can use callbacks to create relationships so you can do something like this:
```
[DKFactory define:Headquarter.class builder:^NSDictionary *{
return @{
@"name": @"Star tower",
@"hero_id" : ^{
return ((Daikiri*)[factory(Hero.class) create]).id;
}
};
}];
```
And the Hero will be only created when insantiating the object in the `make` or `create`, isn't it cool?