Daikiri
描述
这是一个轻量级且简洁的库,用于处理模型,允许 JSON 到 Model 到 Core Data 的双向转换。对于服务器/应用程序同步非常有用。
安装
使用 pods
pod 'daikiri'
使用说明
创建一个继承自Daikiri
的模型,并添加你想要自动转换的属性。注意,如果`submodels`也继承了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
然后你可以这样做
NSDictionary* d = @{
@"name" : @"Batman",
@"age" : @10,
@"headquarter":@{
@"address" : @"patata",
@"isActive" : @1,
@"vehicles" : @[
@{@"model" : @"Batmobile"},
@{@"model" : @"Batwing"},
@{@"model" : @"Tumbler"},
]
}
};
Hero * model = [Hero fromDictionary:d];
然后转换回
NSDictionary* modelToDict = [model toDictionary];
NSLog(@"Model to dict: %@",modelToDict);
你也可以将数组转换为类,为此您需要创建方法-(Class)property_DaikiriArray
,其中`property`是`NSArray`属性的名称。
在前面的例子中,我们有如下的模型Headquarter
@interface Headquarter : Daikiri
@property(strong,nonatomic) NSString* address;
@property(strong,nonatomic) NSNumber* isActive;
@property(strong,nonatomic) NSArray* vehicles;
@end
使用以下方法
-(Class)vehicles_DaikiriArray{
return [Vehicle class];
}
车辆将自动转换。
核心数据
设置
Daikiri
提供了一个CoreData
管理器。它创建`managedObjectContents`并连接到名为`yourprojectname.sqlite`的数据库,数据库位于`applicationDocumentsDirectory`。
您可以通过设置DaikiriCoreData
管理器的`databaseName`属性来更改项目名称
[DaikiriCoreData manager].databaseName = @"youdatabasename";
应该在其他的任何CoreData
调用之前执行这个调用,因此推荐在`didFinishLaunchingWithOptions`中进行设置。
您只需在您的应用代理的 -(void)applicationWillTerminate:(UIApplication *)application
方法中添加对 [[DaikiriCoreData manager] saveContex]
的调用即可,即使发生崩溃也能保存上下文。
但是,您也可以通过在您的模型中覆写 +(NSManagedObjectContext*)managedObjectContext
函数来自定义 CoreData
管理器。
+(NSManagedObjectContext*)managedObjectContext{
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
return context;
}
测试
Daikiri 提供了一种非常简单的方法,可以在每个测试中设置一个完整的清洁测试数据库,这样您就可以无故障地进行单元测试。它还在每个测试中使用事务,所以每次测试都会回滚,数据库在每次测试后都保持清洁。
只需将这些添加到您的测试的 setUp
和 tearDown
方法中即可。
- (void)setUp {
[super setUp];
[[DaikiriCoreData manager] useTestDatabase:YES];
[[DaikiriCoreData manager] beginTransaction];
}
- (void)tearDown {
[super tearDown];
[[DaikiriCoreData manager] rollback];
}
Core数据模型
使用 Daikiri
模型,我们可以在类似记录的方式中与 coredata 进行交互。您只需将模型的名称与数据库中相同 .xcdatamodeld
一个 Daikiri
模型有一个 id
属性,这是用于所有后续方法的 主键
然后您可以这样做
[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)
我们还可以
// 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];
如果您愿意,还有一些方便的快捷方式可以直接从字典中执行这些基本操作
+(bool)createWith:(NSDictionary*)dict;
+(bool)updateWith:(NSDictionary*)dict;
+(bool)deleteWith:(NSNumber*)id;
关系
除了 find
和 all
,当您的模型在数据库中时,您有不同的方式来访问它们的关系
belongsTo
、hasMany
和 belongsToMany
。
请查看下面的示例以了解它们
//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);
}
查询构建器
我们有一个 QueryBuilder
来创建自定义查询,你可以做诸如以下操作
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);
}
如果你的类名使用前缀(两个字符),而你的实体没有,你可以覆盖 usesPrefix
函数以返回 true
。这将移除在查询数据库时的前缀。
工厂
Daikiri
类库提供了一个类似 Laravel 的工厂类,用于测试。你可以拥有你的工厂来创建测试对象,使每个测试看起来非常整洁。
使用方法
首先创建一个工厂类,包含简单的 registerFactories
方法,并通过提供一个简单的字典创建工厂。
@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"
};
}];
}
工厂状态
你可以定义不同类型的类,可以用另一个名字(它会合并默认的和新定义的)。
+(void)registerFactories{
[DKFactory define:Hero.class builder:^NSDictionary *{
return @{
@"name": @"Batman",
@"age" : @"49"
};
}];
[DKFactory define:Hero.class name:@"old" builder:^NSDictionary *{
return @{
@"age" : @"100"
};
}];
}
然后在测试类的 setup
方法中调用 [Yourfactory registerFactories]
。
之后,在你的测试中,你可以通过简单的调用来实例化任何类
Hero* testHero = [factory(Hero.class) make];
Enemy* testEnemy = [factory(Enemy.class) create];
注意,make
只创建对象,而不会将其存储到数据库中,但 create
会存储到数据库。
您可以使用非宏构造函数来获取更多选项。
NSArray* oldHerosArray = [[DKFactory factory:Hero.class name:@"old" count:4] make];
关系
工厂中令人兴奋的一点是,您可以使用回调来创建关系,因此您可以像这样操作
[DKFactory define:Headquarter.class builder:^NSDictionary *{
return @{
@"name": @"Star tower",
@"hero_id" : ^{
return ((Daikiri*)[factory(Hero.class) create]).id;
}
};
}];
并且英雄(Hero)只会在实例化对象为make
或create
时被创建,不错吧?