├── .gitignore ├── .travis.yml ├── DocumentAssets └── YTXRestfulModel01_1_X_X.png ├── Example ├── Podfile ├── Podfile.lock ├── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── YTXTestAFNetworkingRemoteCollection.h │ ├── YTXTestAFNetworkingRemoteCollection.m │ ├── YTXTestAFNetworkingRemoteModel.h │ ├── YTXTestAFNetworkingRemoteModel.m │ ├── YTXTestHumanModel.h │ ├── YTXTestHumanModel.m │ ├── YTXTestStudentCollection.h │ ├── YTXTestStudentCollection.m │ ├── YTXTestStudentModel.h │ ├── YTXTestStudentModel.m │ ├── YTXTestTeacherModel.h │ ├── YTXTestTeacherModel.m │ ├── db.json │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── service.json │ ├── test_YTXRestfulModelAFNetworkingRemote.m │ ├── test_YTXRestfulModelDB.m │ └── test_YTXRestfulModelUserDefaultSpec.m ├── YTXRestfulModel.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── YTXRestfulModel-Example.xcscheme └── YTXRestfulModel.xcworkspace │ └── contents.xcworkspacedata ├── LICENSE ├── Pod ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── Model │ ├── YTXRestfulCollection.h │ ├── YTXRestfulCollection.m │ ├── YTXRestfulModel.h │ └── YTXRestfulModel.m │ ├── Protocol │ ├── YTXRestfulModelDBProtocol.h │ ├── YTXRestfulModelProtocol.h │ ├── YTXRestfulModelRemoteProtocol.h │ └── YTXRestfulModelStorageProtocol.h │ ├── RACSupport │ ├── YTXRestfulCollection+RACSupport.h │ ├── YTXRestfulCollection+RACSupport.m │ ├── YTXRestfulModel+RACSupport.h │ ├── YTXRestfulModel+RACSupport.m │ └── YTXRestfulModelRACSupport.h │ └── Sync │ ├── AFNetworkingRemoteSync │ ├── AFNetworkingRemoteSync.h │ └── AFNetworkingRemoteSync.m │ ├── FMDBSync │ ├── Category │ │ ├── NSArray+YTXRestfulModelFMDBSync.h │ │ ├── NSArray+YTXRestfulModelFMDBSync.m │ │ ├── NSDate+YTXRestfulModelFMDBSync.h │ │ ├── NSDate+YTXRestfulModelFMDBSync.m │ │ ├── NSDictionary+YTXRestfulModelFMDBSync.h │ │ ├── NSDictionary+YTXRestfulModelFMDBSync.m │ │ ├── NSNumber+YTXRestfulModelFMDBSync.h │ │ ├── NSNumber+YTXRestfulModelFMDBSync.m │ │ ├── NSObject+YTXRestfulModelFMDBSync.h │ │ ├── NSObject+YTXRestfulModelFMDBSync.m │ │ ├── NSString+YTXRestfulModelFMDBSync.h │ │ ├── NSString+YTXRestfulModelFMDBSync.m │ │ ├── NSValue+YTXRestfulModelFMDBSync.h │ │ └── NSValue+YTXRestfulModelFMDBSync.m │ ├── YTXRestfulModelFMDBSync.h │ └── YTXRestfulModelFMDBSync.m │ └── UserDefaultStorageSync │ ├── YTXRestfulModelUserDefaultStorageSync.h │ └── YTXRestfulModelUserDefaultStorageSync.m ├── README.md ├── YTXRestfulModel.podspec ├── _Pods.xcodeproj ├── buildpod.sh └── lint.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | Pods/ 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | language: objective-c 6 | osx_image: xcode7.2 7 | cache: cocoapods 8 | podfile: Example/Podfile 9 | before_install: 10 | - npm install -g json-server 11 | before_script: 12 | - gem install cocoapods --no-document -v 0.39.0 # Since Travis is not always on latest version 13 | - pod install --project-directory=Example 14 | script: 15 | - cd Example/Tests; json-server db.json & cd ../.. 16 | - pwd 17 | - set -o pipefail && xcodebuild test -workspace Example/YTXRestfulModel.xcworkspace -scheme YTXRestfulModel-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,name=iPhone 6s,OS=9.2'| xcpretty 18 | - pod lib lint -------------------------------------------------------------------------------- /DocumentAssets/YTXRestfulModel01_1_X_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidao/YTXRestfulModel/1272d03b9d6675c1e2f14858bf0dc752b12f2a14/DocumentAssets/YTXRestfulModel01_1_X_X.png -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | target 'YTXRestfulModel_Tests' do 4 | pod "YTXRestfulModel", :path => "../", :subspecs => ["RACSupport", "AFNetworkingRemoteSync", "FMDBSync", "UserDefaultStorageSync"] 5 | pod 'Kiwi' 6 | end 7 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (2.6.3): 3 | - AFNetworking/NSURLConnection (= 2.6.3) 4 | - AFNetworking/NSURLSession (= 2.6.3) 5 | - AFNetworking/Reachability (= 2.6.3) 6 | - AFNetworking/Security (= 2.6.3) 7 | - AFNetworking/Serialization (= 2.6.3) 8 | - AFNetworking/UIKit (= 2.6.3) 9 | - AFNetworking/NSURLConnection (2.6.3): 10 | - AFNetworking/Reachability 11 | - AFNetworking/Security 12 | - AFNetworking/Serialization 13 | - AFNetworking/NSURLSession (2.6.3): 14 | - AFNetworking/Reachability 15 | - AFNetworking/Security 16 | - AFNetworking/Serialization 17 | - AFNetworking/Reachability (2.6.3) 18 | - AFNetworking/Security (2.6.3) 19 | - AFNetworking/Serialization (2.6.3) 20 | - AFNetworking/UIKit (2.6.3): 21 | - AFNetworking/NSURLConnection 22 | - AFNetworking/NSURLSession 23 | - FMDB (2.6.2): 24 | - FMDB/standard (= 2.6.2) 25 | - FMDB/standard (2.6.2) 26 | - Kiwi (2.4.0) 27 | - Mantle (1.5.7): 28 | - Mantle/extobjc (= 1.5.7) 29 | - Mantle/extobjc (1.5.7) 30 | - ReactiveCocoa (2.3.1): 31 | - ReactiveCocoa/UI (= 2.3.1) 32 | - ReactiveCocoa/Core (2.3.1): 33 | - ReactiveCocoa/no-arc 34 | - ReactiveCocoa/no-arc (2.3.1) 35 | - ReactiveCocoa/UI (2.3.1): 36 | - ReactiveCocoa/Core 37 | - YTXRestfulModel/AFNetworkingRemoteSync (1.2.1): 38 | - AFNetworking (~> 2.6.3) 39 | - YTXRestfulModel/Default 40 | - YTXRestfulModel/Default (1.2.1): 41 | - Mantle (~> 1.5.7) 42 | - YTXRestfulModel/FMDBSync (1.2.1): 43 | - FMDB (~> 2.6) 44 | - YTXRestfulModel/Default 45 | - YTXRestfulModel/RACSupport (1.2.1): 46 | - ReactiveCocoa (~> 2.3.1) 47 | - YTXRestfulModel/Default 48 | - YTXRestfulModel/UserDefaultStorageSync (1.2.1): 49 | - YTXRestfulModel/Default 50 | 51 | DEPENDENCIES: 52 | - Kiwi 53 | - YTXRestfulModel/AFNetworkingRemoteSync (from `../`) 54 | - YTXRestfulModel/FMDBSync (from `../`) 55 | - YTXRestfulModel/RACSupport (from `../`) 56 | - YTXRestfulModel/UserDefaultStorageSync (from `../`) 57 | 58 | EXTERNAL SOURCES: 59 | YTXRestfulModel: 60 | :path: ../ 61 | 62 | SPEC CHECKSUMS: 63 | AFNetworking: cb8d14a848e831097108418f5d49217339d4eb60 64 | FMDB: 854a0341b4726e53276f2a8996f06f1b80f9259a 65 | Kiwi: f49c9d54b28917df5928fe44968a39ed198cb8a8 66 | Mantle: 4af47cf9547a4d0ad145a48cd0be3b411a5ae2b8 67 | ReactiveCocoa: 3fe9b233ca6db810e86681b5749488564c1179a1 68 | YTXRestfulModel: 8c8b7a4c43af31c1ba6352e5782f4b122e531e51 69 | 70 | PODFILE CHECKSUM: b5cdcff3bd5d02bd4ef6d6ada4c385f3358ba536 71 | 72 | COCOAPODS: 1.0.1 73 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | 6 | #endif 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestAFNetworkingRemoteCollection.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestCollection.h 3 | // YTXRestfulModel 4 | // 5 | // Created by Chuan on 1/25/16. 6 | // Copyright © 2016 caojun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "YTXTestAFNetworkingRemoteModel.h" 12 | 13 | @interface YTXTestAFNetworkingRemoteCollection : YTXRestfulCollection 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestAFNetworkingRemoteCollection.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestCollection.m 3 | // YTXRestfulModel 4 | // 5 | // Created by Chuan on 1/25/16. 6 | // Copyright © 2016 caojun. All rights reserved. 7 | // 8 | 9 | #import "YTXTestAFNetworkingRemoteCollection.h" 10 | 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | 17 | @implementation YTXTestAFNetworkingRemoteCollection 18 | 19 | + (instancetype) shared 20 | { 21 | static YTXTestAFNetworkingRemoteCollection * collection; 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | collection = [[[self class] alloc] init]; 25 | }); 26 | return collection; 27 | } 28 | 29 | - (instancetype)init 30 | { 31 | if (self = [super initWithModelClass: [YTXTestAFNetworkingRemoteModel class]]) { 32 | self.remoteSync = [AFNetworkingRemoteSync new]; 33 | self.remoteSync.url = [NSURL URLWithString:@"http://localhost:3000/users"]; 34 | } 35 | return self; 36 | } 37 | 38 | - (NSArray *)transformerProxyOfResponse:(id)response { 39 | return [MTLJSONAdapter modelsOfClass:[self modelClass] fromJSONArray:response error:nil]; 40 | } 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestAFNetworkingRemoteModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestModel.h 3 | // YTXRestfulModel 4 | // 5 | // Created by Chuan on 1/25/16. 6 | // Copyright © 2016 caojun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YTXTestAFNetworkingRemoteModel : YTXRestfulModel 12 | 13 | @property (nonnull, nonatomic, strong) NSNumber *keyId; 14 | @property (nonnull, nonatomic, strong) NSNumber *userId; //可选属性为nullable 不可选为nonnull 15 | @property (nonnull, nonatomic, strong) NSString *title; 16 | @property (nonnull, nonatomic, strong) NSString *body; 17 | 18 | @end 19 | 20 | @interface YTXTestAFNetworkingRemoteToDoModel : YTXRestfulModel 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestAFNetworkingRemoteModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestModel.m 3 | // YTXRestfulModel 4 | // 5 | // Created by Chuan on 1/25/16. 6 | // Copyright © 2016 caojun. All rights reserved. 7 | // 8 | 9 | #import "YTXTestAFNetworkingRemoteModel.h" 10 | 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | @implementation YTXTestAFNetworkingRemoteModel 17 | 18 | //可以重写init方法 改变sync的初始值 19 | 20 | + (instancetype) shared 21 | { 22 | static YTXTestAFNetworkingRemoteModel * model; 23 | static dispatch_once_t onceToken; 24 | dispatch_once(&onceToken, ^{ 25 | model = [[[self class] alloc] init]; 26 | }); 27 | return model; 28 | } 29 | 30 | + (NSDictionary *)JSONKeyPathsByPropertyKey 31 | { 32 | return @{@"keyId": @"id"}; 33 | } 34 | 35 | + (NSString *)primaryKey 36 | { 37 | return @"keyId"; 38 | } 39 | 40 | - (instancetype)init { 41 | if (self = [super init]) { 42 | self.remoteSync = [AFNetworkingRemoteSync syncWithPrimaryKey:[[self class] syncPrimaryKey]]; 43 | self.remoteSync.urlHookBlock = ^NSURL * _Nonnull{ 44 | return [NSURL URLWithString:@"http://localhost:3000/users"]; 45 | }; 46 | } 47 | return self; 48 | } 49 | 50 | //mantle Transoformer 51 | + (MTLValueTransformer *) bodyJSONTransformer 52 | { 53 | return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString * body) { 54 | return body; 55 | } reverseBlock:^(NSString * body) { 56 | return body; 57 | }]; 58 | } 59 | 60 | 61 | @end 62 | 63 | @implementation YTXTestAFNetworkingRemoteToDoModel 64 | 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestHumanModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestHumanModel.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/2/25. 6 | // Copyright © 2016年 caojun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YTXTestHumanModel : YTXRestfulModel 12 | 13 | @property (nonnull, nonatomic, copy) NSNumber * identify; 14 | @property (nonnull, nonatomic, copy) NSString * name; 15 | @property (nonnull, nonatomic, strong) NSDate * birthday; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestHumanModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestHumanModel.m 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/2/25. 6 | // Copyright © 2016年 caojun. All rights reserved. 7 | // 8 | 9 | #import "YTXTestHumanModel.h" 10 | 11 | @implementation YTXTestHumanModel 12 | 13 | + (BOOL) autoCreateTable 14 | { 15 | return YES; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestStudentCollection.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestStudentCollection.h 3 | // YTXRestfulModel 4 | // 5 | // Created by Chuan on 3/3/16. 6 | // Copyright © 2016 caojun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YTXTestStudentCollection : YTXRestfulCollection 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestStudentCollection.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestStudentCollection.m 3 | // YTXRestfulModel 4 | // 5 | // Created by Chuan on 3/3/16. 6 | // Copyright © 2016 caojun. All rights reserved. 7 | // 8 | 9 | #import "YTXTestStudentCollection.h" 10 | #import "YTXTestStudentModel.h" 11 | 12 | #import 13 | #import 14 | 15 | @implementation YTXTestStudentCollection 16 | 17 | + (instancetype) shared 18 | { 19 | static YTXTestStudentCollection * collection; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | collection = [[[self class] alloc] init]; 23 | }); 24 | return collection; 25 | } 26 | 27 | - (instancetype)init 28 | { 29 | if (self = [super initWithModelClass: [YTXTestStudentModel class]]) { 30 | } 31 | return self; 32 | } 33 | 34 | - (NSArray *)transformerProxyOfResponse:(id)response { 35 | return [MTLJSONAdapter modelsOfClass:[self modelClass] fromJSONArray:response error:nil]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestStudentModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestSubModel.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/2/25. 6 | // Copyright © 2016年 caojun. All rights reserved. 7 | // 8 | 9 | #import "YTXTestHumanModel.h" 10 | 11 | typedef NS_ENUM(NSUInteger, Gender) { 12 | GenderMale, 13 | GenderFemale 14 | }; 15 | 16 | @interface YTXTestStudentModel : YTXTestHumanModel 17 | 18 | @property (nonnull, nonatomic, strong ,readonly) NSString *IQ; 19 | @property (nonnull, nonatomic, strong) NSArray * friendNames; 20 | @property (nonnull, nonatomic, strong) NSDictionary * properties; 21 | @property (nonnull, nonatomic, strong) NSDate * startSchoolDate; 22 | @property (nonatomic, assign) NSUInteger age; 23 | @property (nonatomic, assign) BOOL stupid; 24 | @property (nonatomic, assign) float floatNumber; 25 | @property (nonatomic, assign) Gender gender; 26 | @property (nonatomic, assign) double score; 27 | @property (nonatomic, assign) CGPoint point; 28 | 29 | @property (nonnull, nonatomic, strong) NSNumber *teacherId; 30 | 31 | + (nullable NSNumber *) newMigrationVersion; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestStudentModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestStudentModel.m 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/2/25. 6 | // Copyright © 2016年 caojun. All rights reserved. 7 | // 8 | 9 | #import "YTXTestStudentModel.h" 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | @implementation YTXTestStudentModel 17 | 18 | + (instancetype) shared 19 | { 20 | static YTXTestStudentModel * model; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | model = [[[self class] alloc] init]; 24 | }); 25 | return model; 26 | } 27 | 28 | - (instancetype)init 29 | { 30 | if (self = [super init]) { 31 | _IQ = @"100"; 32 | } 33 | return self; 34 | } 35 | 36 | + (NSDictionary *)JSONKeyPathsByPropertyKey 37 | { 38 | return @{@"identify": @"id"}; 39 | } 40 | 41 | + (NSString *)primaryKey 42 | { 43 | return @"identify"; 44 | } 45 | 46 | + (nullable NSMutableDictionary *) tableKeyPathsByPropertyKey 47 | { 48 | NSMutableDictionary * tmpDictionary = [super tableKeyPathsByPropertyKey]; 49 | 50 | YTXRestfulModelDBSerializingModel * genderStruct = tmpDictionary[@"gender"]; 51 | genderStruct.defaultValue = [@(GenderFemale) sqliteValue]; 52 | tmpDictionary[@"gender"] = genderStruct; 53 | 54 | YTXRestfulModelDBSerializingModel * scoreStruct = tmpDictionary[@"score"]; 55 | scoreStruct.unique = YES; 56 | tmpDictionary[@"score"] = scoreStruct; 57 | 58 | YTXRestfulModelDBSerializingModel * teacherIdStruct = tmpDictionary[@"teacherId"]; 59 | teacherIdStruct.defaultValue = [@1 sqliteValue]; 60 | teacherIdStruct.foreignClassName = @"YTXTestTeacherModel"; 61 | tmpDictionary[@"teacherId"] = teacherIdStruct; 62 | 63 | return tmpDictionary; 64 | } 65 | 66 | + (nullable NSNumber *) currentMigrationVersion 67 | { 68 | return @0; 69 | } 70 | 71 | + (BOOL) autoAlterTable 72 | { 73 | return NO; 74 | } 75 | 76 | + (BOOL) autoCreateTable 77 | { 78 | return YES; 79 | } 80 | 81 | + (void) migrationsMethodWithSync:(nonnull id)sync; 82 | { 83 | YTXRestfulModelDBMigrationEntity *migration = [YTXRestfulModelDBMigrationEntity new]; 84 | migration.version = @1; 85 | migration.block = ^(_Nonnull id db, NSError * _Nullable * _Nullable error) { 86 | YTXRestfulModelDBSerializingModel *runtimePStruct = [YTXRestfulModelDBSerializingModel new]; 87 | runtimePStruct.objectClass = @"NSString"; 88 | runtimePStruct.columnName = @"runtimeP"; 89 | runtimePStruct.modelName = @"runtimeP"; 90 | runtimePStruct.isPrimaryKey = NO; 91 | runtimePStruct.autoincrement = NO; 92 | runtimePStruct.unique = NO; 93 | [sync createColumnWithDB:db structSync:runtimePStruct error:error]; 94 | }; 95 | [sync migrate:migration]; 96 | } 97 | 98 | + (MTLValueTransformer *)startSchoolDateJSONTransformer { 99 | return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSNumber *timestamp) { 100 | return [NSDate dateWithTimeIntervalSince1970: timestamp.longLongValue / 1000]; 101 | } reverseBlock:^(NSDate *date) { 102 | return @((SInt64)(date.timeIntervalSince1970 * 1000)); 103 | }]; 104 | } 105 | 106 | + (MTLValueTransformer *)birthdayJSONTransformer { 107 | return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSNumber *timestamp) { 108 | return [NSDate dateWithTimeIntervalSince1970: timestamp.longLongValue / 1000]; 109 | } reverseBlock:^(NSDate *date) { 110 | return @((SInt64)(date.timeIntervalSince1970 * 1000)); 111 | }]; 112 | } 113 | 114 | + (nullable NSNumber *) newMigrationVersion 115 | { 116 | return @1; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestTeacherModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestTeacherModel.h 3 | // YTXRestfulModel 4 | // 5 | // Created by Chuan on 3/2/16. 6 | // Copyright © 2016 caojun. All rights reserved. 7 | // 8 | 9 | #import "YTXTestHumanModel.h" 10 | 11 | @interface YTXTestTeacherModel : YTXTestHumanModel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Tests/YTXTestTeacherModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXTestTeacherModel.m 3 | // YTXRestfulModel 4 | // 5 | // Created by Chuan on 3/2/16. 6 | // Copyright © 2016 caojun. All rights reserved. 7 | // 8 | 9 | #import "YTXTestTeacherModel.h" 10 | #import 11 | 12 | @implementation YTXTestTeacherModel 13 | 14 | + (instancetype) shared 15 | { 16 | static YTXTestTeacherModel * model; 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | model = [[[self class] alloc] init]; 20 | }); 21 | return model; 22 | } 23 | 24 | + (NSDictionary *)JSONKeyPathsByPropertyKey 25 | { 26 | return @{@"identify": @"id"}; 27 | } 28 | 29 | + (NSString *)primaryKey 30 | { 31 | return @"identify"; 32 | } 33 | 34 | + (nullable NSMutableDictionary *) tableKeyPathsByPropertyKey 35 | { 36 | NSMutableDictionary * tmpDictionary = [super tableKeyPathsByPropertyKey]; 37 | 38 | YTXRestfulModelDBSerializingModel * nameStruct = tmpDictionary[@"name"]; 39 | 40 | nameStruct.defaultValue = [@"Edward" sqliteValue]; 41 | 42 | tmpDictionary[@"name"] = nameStruct; 43 | 44 | return tmpDictionary; 45 | } 46 | 47 | + (nullable NSNumber *) currentMigrationVersion 48 | { 49 | return @0; 50 | } 51 | 52 | + (nullable NSNumber *) newMigrationVersion 53 | { 54 | return @1; 55 | } 56 | 57 | + (BOOL) autoAlterTable 58 | { 59 | return YES; 60 | } 61 | 62 | + (BOOL) autoCreateTable 63 | { 64 | return YES; 65 | } 66 | 67 | + (BOOL) isPrimaryKeyAutoincrement 68 | { 69 | return NO; 70 | } 71 | 72 | + (nonnull NSMutableDictionary *> *) _tableKeyPathsByPropertyKeyMapRuntime 73 | { 74 | static NSMutableDictionary *> * map; 75 | 76 | if (map == nil) { 77 | map = [NSMutableDictionary dictionary]; 78 | } 79 | 80 | return map; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /Example/Tests/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "posts": [ 3 | { 4 | "body": "改过了", 5 | "id": 1, 6 | "title": "ytx test hahahaha", 7 | "userId": 1 8 | }, 9 | { 10 | "body": "teststeststesettsetsetttsetttest", 11 | "title": "ytx test hahahaha", 12 | "userId": 1, 13 | "id": 2 14 | }, 15 | { 16 | "body": "teststeststesettsetsetttsetttest", 17 | "title": "ytx test hahahaha", 18 | "userId": 1, 19 | "id": 3 20 | }, 21 | { 22 | "body": "I Love You", 23 | "title": "同一个", 24 | "userId": 1, 25 | "id": 4 26 | }, 27 | { 28 | "body": "I Love You", 29 | "title": "同一个", 30 | "userId": 1, 31 | "id": 5 32 | }, 33 | { 34 | "body": "I Love You", 35 | "title": "同一个", 36 | "userId": 1, 37 | "id": 6 38 | }, 39 | { 40 | "body": "I Love You", 41 | "title": "同一个", 42 | "userId": 1, 43 | "id": 7 44 | }, 45 | { 46 | "body": "I Love You", 47 | "dbSync": "", 48 | "title": "同一个", 49 | "userId": 1, 50 | "id": 8 51 | }, 52 | { 53 | "body": "I Love You", 54 | "dbSync": "", 55 | "title": "同一个", 56 | "userId": 1, 57 | "id": 9 58 | }, 59 | { 60 | "body": "I Love You", 61 | "dbSync": "", 62 | "title": "同一个", 63 | "userId": 1, 64 | "id": 10 65 | }, 66 | { 67 | "body": "I Love You", 68 | "title": "Hello World", 69 | "userId": 1, 70 | "id": 11 71 | }, 72 | { 73 | "id": 12 74 | }, 75 | { 76 | "title": "更新了", 77 | "id": 13 78 | }, 79 | { 80 | "id": 14 81 | }, 82 | { 83 | "body": "I Love You", 84 | "id": 15 85 | }, 86 | { 87 | "title": "同一个", 88 | "id": 16 89 | }, 90 | { 91 | "title": "通过hook改了", 92 | "id": 17 93 | }, 94 | { 95 | "title": "通过hook改了", 96 | "id": 18 97 | }, 98 | { 99 | "title": "hook的优先级低于方法传入参数和model属性", 100 | "id": 19 101 | }, 102 | { 103 | "id": 20 104 | }, 105 | { 106 | "title": "更新了", 107 | "id": 21 108 | }, 109 | { 110 | "id": 22 111 | }, 112 | { 113 | "body": "I Love You", 114 | "id": 23 115 | }, 116 | { 117 | "title": "同一个", 118 | "id": 24 119 | }, 120 | { 121 | "title": "通过hook改了", 122 | "id": 25 123 | }, 124 | { 125 | "title": "通过hook改了", 126 | "id": 26 127 | }, 128 | { 129 | "title": "hook的优先级低于方法传入参数和model属性", 130 | "id": 27 131 | }, 132 | { 133 | "title": "通过hook改了", 134 | "id": 28 135 | }, 136 | { 137 | "title": "通过hook改了", 138 | "id": 29 139 | }, 140 | { 141 | "title": "hook的优先级低于方法传入参数和model属性", 142 | "id": 30 143 | }, 144 | { 145 | "title": "通过hook改了", 146 | "id": 31 147 | }, 148 | { 149 | "title": "通过hook改了", 150 | "id": 32 151 | }, 152 | { 153 | "title": "hook的优先级低于方法传入参数和model属性", 154 | "id": 33 155 | }, 156 | { 157 | "title": "通过hook改了", 158 | "id": 34 159 | }, 160 | { 161 | "title": "通过hook改了", 162 | "id": 35 163 | }, 164 | { 165 | "title": "hook的优先级低于方法传入参数和model属性", 166 | "id": 36 167 | }, 168 | { 169 | "title": "通过hook改了", 170 | "id": 37 171 | }, 172 | { 173 | "title": "通过hook改了", 174 | "id": 38 175 | }, 176 | { 177 | "title": "hook的优先级低于方法传入参数和model属性", 178 | "id": 39 179 | }, 180 | { 181 | "title": "通过hook改了", 182 | "id": 40 183 | }, 184 | { 185 | "title": "通过hook改了", 186 | "id": 41 187 | }, 188 | { 189 | "title": "hook的优先级低于方法传入参数和model属性", 190 | "id": 42 191 | }, 192 | { 193 | "title": "通过hook改了", 194 | "id": 43 195 | }, 196 | { 197 | "title": "通过hook改了", 198 | "id": 44 199 | }, 200 | { 201 | "title": "hook的优先级低于方法传入参数和model属性", 202 | "id": 45 203 | }, 204 | { 205 | "title": "通过hook改了", 206 | "id": 46 207 | }, 208 | { 209 | "title": "通过hook改了", 210 | "id": 47 211 | }, 212 | { 213 | "title": "hook的优先级低于方法传入参数和model属性", 214 | "id": 48 215 | }, 216 | { 217 | "title": "通过hook改了", 218 | "id": 49 219 | }, 220 | { 221 | "title": "通过hook改了", 222 | "id": 50 223 | }, 224 | { 225 | "title": "hook的优先级低于方法传入参数和model属性", 226 | "id": 51 227 | }, 228 | { 229 | "title": "通过hook改了", 230 | "id": 52 231 | }, 232 | { 233 | "title": "hook的优先级低于方法传入参数和model属性", 234 | "id": 53 235 | }, 236 | { 237 | "title": "通过hook改了", 238 | "id": 54 239 | }, 240 | { 241 | "title": "通过hook改了", 242 | "id": 55 243 | }, 244 | { 245 | "title": "通过hook改了", 246 | "id": 56 247 | }, 248 | { 249 | "title": "hook的优先级低于方法传入参数和model属性", 250 | "id": 57 251 | }, 252 | { 253 | "title": "通过hook改了", 254 | "id": 58 255 | }, 256 | { 257 | "title": "通过hook改了", 258 | "id": 59 259 | }, 260 | { 261 | "title": "hook的优先级低于方法传入参数和model属性", 262 | "id": 60 263 | }, 264 | { 265 | "title": "通过hook改了", 266 | "id": 61 267 | }, 268 | { 269 | "title": "通过hook改了", 270 | "id": 62 271 | }, 272 | { 273 | "title": "hook的优先级低于方法传入参数和model属性", 274 | "id": 63 275 | }, 276 | { 277 | "title": "通过hook改了", 278 | "id": 64 279 | }, 280 | { 281 | "title": "通过hook改了", 282 | "id": 65 283 | }, 284 | { 285 | "title": "hook的优先级低于方法传入参数和model属性", 286 | "id": 66 287 | }, 288 | { 289 | "title": "通过hook改了", 290 | "id": 67 291 | }, 292 | { 293 | "title": "通过hook改了", 294 | "id": 68 295 | }, 296 | { 297 | "title": "hook的优先级低于方法传入参数和model属性", 298 | "id": 69 299 | }, 300 | { 301 | "title": "通过hook改了", 302 | "id": 70 303 | }, 304 | { 305 | "title": "通过hook改了", 306 | "id": 71 307 | }, 308 | { 309 | "title": "hook的优先级低于方法传入参数和model属性", 310 | "id": 72 311 | }, 312 | { 313 | "title": "通过hook改了", 314 | "id": 73 315 | }, 316 | { 317 | "title": "通过hook改了", 318 | "id": 74 319 | }, 320 | { 321 | "title": "hook的优先级低于方法传入参数和model属性", 322 | "id": 75 323 | }, 324 | { 325 | "title": "通过hook改了", 326 | "id": 76 327 | }, 328 | { 329 | "title": "通过hook改了", 330 | "id": 77 331 | }, 332 | { 333 | "title": "hook的优先级低于方法传入参数和model属性", 334 | "id": 78 335 | }, 336 | { 337 | "title": "通过hook改了", 338 | "id": 79 339 | }, 340 | { 341 | "title": "通过hook改了", 342 | "id": 80 343 | }, 344 | { 345 | "title": "hook的优先级低于方法传入参数和model属性", 346 | "id": 81 347 | }, 348 | { 349 | "title": "通过hook改了", 350 | "id": 82 351 | }, 352 | { 353 | "title": "通过hook改了", 354 | "id": 83 355 | }, 356 | { 357 | "title": "hook的优先级低于方法传入参数和model属性", 358 | "id": 84 359 | }, 360 | { 361 | "title": "通过hook改了", 362 | "id": 85 363 | }, 364 | { 365 | "title": "通过hook改了", 366 | "id": 86 367 | }, 368 | { 369 | "title": "hook的优先级低于方法传入参数和model属性", 370 | "id": 87 371 | }, 372 | { 373 | "title": "CaoJun", 374 | "id": 88 375 | }, 376 | { 377 | "title": "CaoJun", 378 | "id": 89 379 | }, 380 | { 381 | "title": "CaoJun", 382 | "id": 90 383 | }, 384 | { 385 | "title": "通过hook改了", 386 | "id": 91 387 | }, 388 | { 389 | "title": "通过hook改了", 390 | "id": 92 391 | }, 392 | { 393 | "title": "hook的优先级低于方法传入参数和model属性", 394 | "id": 93 395 | }, 396 | { 397 | "title": "CaoJun", 398 | "id": 94 399 | }, 400 | { 401 | "title": "CaoJun", 402 | "id": 95 403 | }, 404 | { 405 | "title": "CaoJun", 406 | "id": 96 407 | }, 408 | { 409 | "title": "通过hook改了", 410 | "id": 97 411 | }, 412 | { 413 | "title": "通过hook改了", 414 | "id": 98 415 | }, 416 | { 417 | "title": "hook的优先级低于方法传入参数和model属性", 418 | "id": 99 419 | }, 420 | { 421 | "title": "CaoJun", 422 | "id": 100 423 | }, 424 | { 425 | "title": "CaoJun", 426 | "id": 101 427 | }, 428 | { 429 | "title": "CaoJun", 430 | "id": 102 431 | }, 432 | { 433 | "title": "通过hook改了", 434 | "id": 103 435 | }, 436 | { 437 | "title": "通过hook改了", 438 | "id": 104 439 | }, 440 | { 441 | "title": "hook的优先级低于方法传入参数和model属性", 442 | "id": 105 443 | }, 444 | { 445 | "title": "CaoJun", 446 | "id": 106 447 | }, 448 | { 449 | "title": "CaoJun", 450 | "id": 107 451 | }, 452 | { 453 | "title": "CaoJun", 454 | "id": 108 455 | }, 456 | { 457 | "title": "通过hook改了", 458 | "id": 109 459 | }, 460 | { 461 | "title": "通过hook改了", 462 | "id": 110 463 | }, 464 | { 465 | "title": "hook的优先级低于方法传入参数和model属性", 466 | "id": 111 467 | }, 468 | { 469 | "title": "通过hook改了", 470 | "id": 112 471 | }, 472 | { 473 | "title": "通过hook改了", 474 | "id": 113 475 | }, 476 | { 477 | "title": "hook的优先级低于方法传入参数和model属性", 478 | "id": 114 479 | }, 480 | { 481 | "title": "CaoJun", 482 | "id": 115 483 | }, 484 | { 485 | "title": "CaoJun", 486 | "id": 116 487 | }, 488 | { 489 | "title": "通过hook改了", 490 | "id": 117 491 | }, 492 | { 493 | "title": "通过hook改了", 494 | "id": 118 495 | }, 496 | { 497 | "title": "hook的优先级低于方法传入参数和model属性", 498 | "id": 119 499 | }, 500 | { 501 | "title": "CaoJun", 502 | "id": 120 503 | }, 504 | { 505 | "title": "CaoJun", 506 | "id": 121 507 | }, 508 | { 509 | "title": "CaoJun", 510 | "id": 122 511 | }, 512 | { 513 | "title": "CaoJun", 514 | "id": 123 515 | }, 516 | { 517 | "title": "CaoJun", 518 | "id": 124 519 | } 520 | ], 521 | "comments": [ 522 | { 523 | "id": 1, 524 | "body": "some comment", 525 | "postId": 1 526 | } 527 | ], 528 | "users": [ 529 | { 530 | "body": "改过了", 531 | "id": 1, 532 | "title": "ytx test hahahaha", 533 | "userId": 1 534 | }, 535 | { 536 | "body": "teststeststesettsetsetttsetttest", 537 | "title": "ytx test hahahaha", 538 | "userId": 1, 539 | "id": 2 540 | }, 541 | { 542 | "body": "teststeststesettsetsetttsetttest", 543 | "title": "ytx test hahahaha", 544 | "userId": 1, 545 | "id": 3 546 | }, 547 | { 548 | "body": "I Love You", 549 | "title": "同一个", 550 | "userId": 1, 551 | "id": 4 552 | }, 553 | { 554 | "body": "I Love You", 555 | "title": "同一个", 556 | "userId": 1, 557 | "id": 5 558 | }, 559 | { 560 | "body": "I Love You", 561 | "title": "同一个", 562 | "userId": 1, 563 | "id": 6 564 | }, 565 | { 566 | "body": "I Love You", 567 | "title": "同一个", 568 | "userId": 1, 569 | "id": 7 570 | }, 571 | { 572 | "body": "I Love You", 573 | "dbSync": "", 574 | "title": "同一个", 575 | "userId": 1, 576 | "id": 8 577 | }, 578 | { 579 | "body": "I Love You", 580 | "dbSync": "", 581 | "title": "同一个", 582 | "userId": 1, 583 | "id": 9 584 | }, 585 | { 586 | "body": "I Love You", 587 | "dbSync": "", 588 | "title": "同一个", 589 | "userId": 1, 590 | "id": 10 591 | }, 592 | { 593 | "body": "I Love You", 594 | "title": "Hello World", 595 | "userId": 1, 596 | "id": 11 597 | }, 598 | { 599 | "id": 12 600 | }, 601 | { 602 | "title": "更新了", 603 | "id": 13 604 | }, 605 | { 606 | "id": 14 607 | }, 608 | { 609 | "body": "I Love You", 610 | "id": 15 611 | }, 612 | { 613 | "title": "同一个", 614 | "id": 16 615 | }, 616 | { 617 | "title": "通过hook改了", 618 | "id": 17 619 | }, 620 | { 621 | "title": "通过hook改了", 622 | "id": 18 623 | }, 624 | { 625 | "title": "hook的优先级低于方法传入参数和model属性", 626 | "id": 19 627 | }, 628 | { 629 | "id": 20 630 | }, 631 | { 632 | "title": "更新了", 633 | "id": 21 634 | }, 635 | { 636 | "id": 22 637 | }, 638 | { 639 | "body": "I Love You", 640 | "id": 23 641 | }, 642 | { 643 | "title": "同一个", 644 | "id": 24 645 | }, 646 | { 647 | "title": "通过hook改了", 648 | "id": 25 649 | }, 650 | { 651 | "title": "通过hook改了", 652 | "id": 26 653 | }, 654 | { 655 | "title": "hook的优先级低于方法传入参数和model属性", 656 | "id": 27 657 | }, 658 | { 659 | "id": 28 660 | }, 661 | { 662 | "title": "通过hook改了", 663 | "id": 29 664 | }, 665 | { 666 | "title": "hook的优先级低于方法传入参数和model属性", 667 | "id": 30 668 | }, 669 | { 670 | "title": "通过hook改了", 671 | "id": 31 672 | }, 673 | { 674 | "title": "通过hook改了", 675 | "id": 32 676 | }, 677 | { 678 | "title": "通过hook改了", 679 | "id": 33 680 | }, 681 | { 682 | "title": "hook的优先级低于方法传入参数和model属性", 683 | "id": 34 684 | }, 685 | { 686 | "title": "通过hook改了", 687 | "id": 35 688 | }, 689 | { 690 | "title": "hook的优先级低于方法传入参数和model属性", 691 | "id": 36 692 | }, 693 | { 694 | "title": "通过hook改了", 695 | "id": 37 696 | }, 697 | { 698 | "title": "通过hook改了", 699 | "id": 38 700 | }, 701 | { 702 | "title": "通过hook改了", 703 | "id": 39 704 | }, 705 | { 706 | "title": "hook的优先级低于方法传入参数和model属性", 707 | "id": 40 708 | }, 709 | { 710 | "title": "通过hook改了", 711 | "id": 41 712 | }, 713 | { 714 | "title": "通过hook改了", 715 | "id": 42 716 | }, 717 | { 718 | "title": "hook的优先级低于方法传入参数和model属性", 719 | "id": 43 720 | }, 721 | { 722 | "title": "通过hook改了", 723 | "id": 44 724 | }, 725 | { 726 | "title": "hook的优先级低于方法传入参数和model属性", 727 | "id": 45 728 | }, 729 | { 730 | "title": "通过hook改了", 731 | "id": 46 732 | }, 733 | { 734 | "title": "通过hook改了", 735 | "id": 47 736 | }, 737 | { 738 | "title": "通过hook改了", 739 | "id": 48 740 | }, 741 | { 742 | "title": "hook的优先级低于方法传入参数和model属性", 743 | "id": 49 744 | }, 745 | { 746 | "title": "通过hook改了", 747 | "id": 50 748 | }, 749 | { 750 | "title": "hook的优先级低于方法传入参数和model属性", 751 | "id": 51 752 | }, 753 | { 754 | "title": "通过hook改了", 755 | "id": 52 756 | }, 757 | { 758 | "title": "hook的优先级低于方法传入参数和model属性", 759 | "id": 53 760 | }, 761 | { 762 | "title": "通过hook改了", 763 | "id": 54 764 | }, 765 | { 766 | "title": "通过hook改了", 767 | "id": 55 768 | }, 769 | { 770 | "title": "通过hook改了", 771 | "id": 56 772 | }, 773 | { 774 | "title": "通过hook改了", 775 | "id": 57 776 | }, 777 | { 778 | "title": "hook的优先级低于方法传入参数和model属性", 779 | "id": 58 780 | }, 781 | { 782 | "title": "通过hook改了", 783 | "id": 59 784 | }, 785 | { 786 | "title": "通过hook改了", 787 | "id": 60 788 | }, 789 | { 790 | "title": "hook的优先级低于方法传入参数和model属性", 791 | "id": 61 792 | }, 793 | { 794 | "title": "通过hook改了", 795 | "id": 62 796 | }, 797 | { 798 | "title": "hook的优先级低于方法传入参数和model属性", 799 | "id": 63 800 | }, 801 | { 802 | "title": "通过hook改了", 803 | "id": 64 804 | }, 805 | { 806 | "title": "通过hook改了", 807 | "id": 65 808 | }, 809 | { 810 | "title": "通过hook改了", 811 | "id": 66 812 | }, 813 | { 814 | "title": "hook的优先级低于方法传入参数和model属性", 815 | "id": 67 816 | }, 817 | { 818 | "title": "通过hook改了", 819 | "id": 68 820 | }, 821 | { 822 | "title": "hook的优先级低于方法传入参数和model属性", 823 | "id": 69 824 | }, 825 | { 826 | "title": "通过hook改了", 827 | "id": 70 828 | }, 829 | { 830 | "title": "通过hook改了", 831 | "id": 71 832 | }, 833 | { 834 | "title": "通过hook改了", 835 | "id": 72 836 | }, 837 | { 838 | "title": "hook的优先级低于方法传入参数和model属性", 839 | "id": 73 840 | }, 841 | { 842 | "title": "通过hook改了", 843 | "id": 74 844 | }, 845 | { 846 | "title": "hook的优先级低于方法传入参数和model属性", 847 | "id": 75 848 | }, 849 | { 850 | "title": "通过hook改了", 851 | "id": 76 852 | }, 853 | { 854 | "title": "通过hook改了", 855 | "id": 77 856 | }, 857 | { 858 | "title": "通过hook改了", 859 | "id": 78 860 | }, 861 | { 862 | "title": "hook的优先级低于方法传入参数和model属性", 863 | "id": 79 864 | }, 865 | { 866 | "title": "通过hook改了", 867 | "id": 80 868 | }, 869 | { 870 | "title": "hook的优先级低于方法传入参数和model属性", 871 | "id": 81 872 | }, 873 | { 874 | "title": "通过hook改了", 875 | "id": 82 876 | }, 877 | { 878 | "title": "通过hook改了", 879 | "id": 83 880 | }, 881 | { 882 | "title": "通过hook改了", 883 | "id": 84 884 | }, 885 | { 886 | "title": "hook的优先级低于方法传入参数和model属性", 887 | "id": 85 888 | }, 889 | { 890 | "title": "通过hook改了", 891 | "id": 86 892 | }, 893 | { 894 | "title": "hook的优先级低于方法传入参数和model属性", 895 | "id": 87 896 | }, 897 | { 898 | "title": "通过hook改了", 899 | "id": 88 900 | }, 901 | { 902 | "title": "通过hook改了", 903 | "id": 89 904 | }, 905 | { 906 | "title": "通过hook改了", 907 | "id": 90 908 | }, 909 | { 910 | "title": "hook的优先级低于方法传入参数和model属性", 911 | "id": 91 912 | }, 913 | { 914 | "title": "通过hook改了", 915 | "id": 92 916 | }, 917 | { 918 | "title": "通过hook改了", 919 | "id": 93 920 | }, 921 | { 922 | "title": "hook的优先级低于方法传入参数和model属性", 923 | "id": 94 924 | }, 925 | { 926 | "title": "通过hook改了", 927 | "id": 95 928 | }, 929 | { 930 | "title": "hook的优先级低于方法传入参数和model属性", 931 | "id": 96 932 | }, 933 | { 934 | "title": "通过hook改了", 935 | "id": 97 936 | }, 937 | { 938 | "title": "通过hook改了", 939 | "id": 98 940 | }, 941 | { 942 | "title": "hook的优先级低于方法传入参数和model属性", 943 | "id": 99 944 | }, 945 | { 946 | "title": "通过hook改了", 947 | "id": 100 948 | }, 949 | { 950 | "title": "通过hook改了", 951 | "id": 101 952 | }, 953 | { 954 | "title": "通过hook改了", 955 | "id": 102 956 | }, 957 | { 958 | "title": "hook的优先级低于方法传入参数和model属性", 959 | "id": 103 960 | }, 961 | { 962 | "title": "通过hook改了", 963 | "id": 104 964 | }, 965 | { 966 | "title": "通过hook改了", 967 | "id": 105 968 | }, 969 | { 970 | "title": "hook的优先级低于方法传入参数和model属性", 971 | "id": 106 972 | }, 973 | { 974 | "title": "通过hook改了", 975 | "id": 107 976 | }, 977 | { 978 | "title": "通过hook改了", 979 | "id": 108 980 | }, 981 | { 982 | "title": "hook的优先级低于方法传入参数和model属性", 983 | "id": 109 984 | }, 985 | { 986 | "title": "通过hook改了", 987 | "id": 110 988 | }, 989 | { 990 | "title": "通过hook改了", 991 | "id": 111 992 | }, 993 | { 994 | "title": "hook的优先级低于方法传入参数和model属性", 995 | "id": 112 996 | }, 997 | { 998 | "title": "通过hook改了", 999 | "id": 113 1000 | }, 1001 | { 1002 | "title": "hook的优先级低于方法传入参数和model属性", 1003 | "id": 114 1004 | }, 1005 | { 1006 | "title": "通过hook改了", 1007 | "id": 115 1008 | }, 1009 | { 1010 | "title": "CaoJun", 1011 | "id": 116 1012 | }, 1013 | { 1014 | "title": "CaoJun", 1015 | "id": 117 1016 | }, 1017 | { 1018 | "title": "通过hook改了", 1019 | "id": 118 1020 | }, 1021 | { 1022 | "title": "hook的优先级低于方法传入参数和model属性", 1023 | "id": 119 1024 | }, 1025 | { 1026 | "title": "通过hook改了", 1027 | "id": 120 1028 | }, 1029 | { 1030 | "title": "CaoJun", 1031 | "id": 121 1032 | }, 1033 | { 1034 | "title": "CaoJun", 1035 | "id": 122 1036 | }, 1037 | { 1038 | "title": "通过hook改了", 1039 | "id": 123 1040 | }, 1041 | { 1042 | "title": "通过hook改了", 1043 | "id": 124 1044 | }, 1045 | { 1046 | "title": "hook的优先级低于方法传入参数和model属性", 1047 | "id": 125 1048 | }, 1049 | { 1050 | "title": "CaoJun", 1051 | "id": 126 1052 | }, 1053 | { 1054 | "title": "CaoJun", 1055 | "id": 127 1056 | }, 1057 | { 1058 | "title": "通过hook改了", 1059 | "id": 128 1060 | }, 1061 | { 1062 | "title": "通过hook改了", 1063 | "id": 129 1064 | }, 1065 | { 1066 | "title": "hook的优先级低于方法传入参数和model属性", 1067 | "id": 130 1068 | }, 1069 | { 1070 | "title": "CaoJun", 1071 | "id": 131 1072 | }, 1073 | { 1074 | "title": "CaoJun", 1075 | "id": 132 1076 | }, 1077 | { 1078 | "title": "通过hook改了", 1079 | "id": 133 1080 | }, 1081 | { 1082 | "title": "通过hook改了", 1083 | "id": 134 1084 | }, 1085 | { 1086 | "title": "hook的优先级低于方法传入参数和model属性", 1087 | "id": 135 1088 | }, 1089 | { 1090 | "title": "CaoJun", 1091 | "id": 136 1092 | }, 1093 | { 1094 | "title": "CaoJun", 1095 | "id": 137 1096 | }, 1097 | { 1098 | "title": "通过hook改了", 1099 | "id": 138 1100 | }, 1101 | { 1102 | "title": "hook的优先级低于方法传入参数和model属性", 1103 | "id": 139 1104 | }, 1105 | { 1106 | "title": "通过hook改了", 1107 | "id": 140 1108 | }, 1109 | { 1110 | "title": "hook的优先级低于方法传入参数和model属性", 1111 | "id": 141 1112 | }, 1113 | { 1114 | "title": "通过hook改了", 1115 | "id": 142 1116 | }, 1117 | { 1118 | "title": "通过hook改了", 1119 | "id": 143 1120 | }, 1121 | { 1122 | "title": "通过hook改了", 1123 | "id": 144 1124 | }, 1125 | { 1126 | "title": "通过hook改了", 1127 | "id": 145 1128 | }, 1129 | { 1130 | "title": "hook的优先级低于方法传入参数和model属性", 1131 | "id": 146 1132 | }, 1133 | { 1134 | "title": "通过hook改了", 1135 | "id": 147 1136 | }, 1137 | { 1138 | "title": "hook的优先级低于方法传入参数和model属性", 1139 | "id": 148 1140 | }, 1141 | { 1142 | "title": "通过hook改了", 1143 | "id": 149 1144 | } 1145 | ], 1146 | "todos": [ 1147 | { 1148 | "id": 1, 1149 | "body": "some comment", 1150 | "userId": 1 1151 | } 1152 | ], 1153 | "profile": { 1154 | "name": "typicode" 1155 | } 1156 | } -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Tests/service.json: -------------------------------------------------------------------------------- 1 | { 2 | "domains": { 3 | "test": { 4 | "jsonPlaceholder": "http://jsonplaceholder.typicode.com/" 5 | }, 6 | "localhost": { 7 | "jsonPlaceholder": "http://localhost:3000/" 8 | } 9 | }, 10 | "service": { 11 | "restful": { 12 | "posts": { 13 | "domain": "jsonPlaceholder", 14 | "path": "posts" 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Example/YTXRestfulModel.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0CB946391C716F0E009B83A7 /* test_YTXRestfulModelUserDefaultSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CB946381C716F0E009B83A7 /* test_YTXRestfulModelUserDefaultSpec.m */; }; 11 | 1867C4C81C7EEDBF00D900E9 /* test_YTXRestfulModelDB.m in Sources */ = {isa = PBXBuildFile; fileRef = 1867C4C71C7EEDBF00D900E9 /* test_YTXRestfulModelDB.m */; }; 12 | 1867C4CB1C7EF01000D900E9 /* YTXTestStudentModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1867C4CA1C7EF01000D900E9 /* YTXTestStudentModel.m */; }; 13 | 1867C4CE1C7EF62A00D900E9 /* YTXTestHumanModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1867C4CD1C7EF62A00D900E9 /* YTXTestHumanModel.m */; }; 14 | 18E9FFF61C702CA500143B68 /* db.json in Resources */ = {isa = PBXBuildFile; fileRef = 18E9FFF51C702CA500143B68 /* db.json */; }; 15 | 4A9D60319D9EC81A8EF6A839 /* libPods-YTXRestfulModel_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF3994A9A34C6C481609B709 /* libPods-YTXRestfulModel_Tests.a */; }; 16 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 17 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 18 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 19 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 20 | 621EE9721C88013C0033A211 /* YTXTestStudentCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 621EE9711C88013C0033A211 /* YTXTestStudentCollection.m */; }; 21 | 624C05911C55EDC200C40CE6 /* service.json in Resources */ = {isa = PBXBuildFile; fileRef = 624C05901C55EDC200C40CE6 /* service.json */; }; 22 | 625D8E311C868DD8003A6F81 /* YTXTestTeacherModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 625D8E301C868DD8003A6F81 /* YTXTestTeacherModel.m */; }; 23 | 62CD75A81C89814400AA6A16 /* test_YTXRestfulModelAFNetworkingRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 62CD75A71C89814400AA6A16 /* test_YTXRestfulModelAFNetworkingRemote.m */; }; 24 | 62CD75AD1C89828300AA6A16 /* YTXTestAFNetworkingRemoteCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 62CD75AA1C89828300AA6A16 /* YTXTestAFNetworkingRemoteCollection.m */; }; 25 | 62CD75B11C8985C000AA6A16 /* YTXTestAFNetworkingRemoteModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 62CD75B01C8985C000AA6A16 /* YTXTestAFNetworkingRemoteModel.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 0888F830D92D08408493FB13 /* Pods-YTXRestfulModel_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YTXRestfulModel_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-YTXRestfulModel_Tests/Pods-YTXRestfulModel_Tests.release.xcconfig"; sourceTree = ""; }; 30 | 0CB946381C716F0E009B83A7 /* test_YTXRestfulModelUserDefaultSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = test_YTXRestfulModelUserDefaultSpec.m; sourceTree = ""; }; 31 | 181817C21C843B1D00745529 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; 32 | 1867C4C71C7EEDBF00D900E9 /* test_YTXRestfulModelDB.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = test_YTXRestfulModelDB.m; sourceTree = ""; }; 33 | 1867C4C91C7EF01000D900E9 /* YTXTestStudentModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YTXTestStudentModel.h; sourceTree = ""; }; 34 | 1867C4CA1C7EF01000D900E9 /* YTXTestStudentModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YTXTestStudentModel.m; sourceTree = ""; }; 35 | 1867C4CC1C7EF62A00D900E9 /* YTXTestHumanModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YTXTestHumanModel.h; sourceTree = ""; }; 36 | 1867C4CD1C7EF62A00D900E9 /* YTXTestHumanModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YTXTestHumanModel.m; sourceTree = ""; }; 37 | 18E9FFF51C702CA500143B68 /* db.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = db.json; sourceTree = ""; }; 38 | 44BA7F8968B8D7FBB1291584 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 39 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 40 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 41 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 42 | 6003F5AE195388D20070C39A /* YTXRestfulModel_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YTXRestfulModel_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 44 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 45 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 46 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 47 | 621EE9701C88013C0033A211 /* YTXTestStudentCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YTXTestStudentCollection.h; sourceTree = ""; }; 48 | 621EE9711C88013C0033A211 /* YTXTestStudentCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YTXTestStudentCollection.m; sourceTree = ""; }; 49 | 624C05901C55EDC200C40CE6 /* service.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = service.json; sourceTree = ""; }; 50 | 625D8E2F1C868DD8003A6F81 /* YTXTestTeacherModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YTXTestTeacherModel.h; sourceTree = ""; }; 51 | 625D8E301C868DD8003A6F81 /* YTXTestTeacherModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YTXTestTeacherModel.m; sourceTree = ""; }; 52 | 62CD75A71C89814400AA6A16 /* test_YTXRestfulModelAFNetworkingRemote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = test_YTXRestfulModelAFNetworkingRemote.m; sourceTree = ""; }; 53 | 62CD75A91C89828300AA6A16 /* YTXTestAFNetworkingRemoteCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YTXTestAFNetworkingRemoteCollection.h; sourceTree = ""; }; 54 | 62CD75AA1C89828300AA6A16 /* YTXTestAFNetworkingRemoteCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YTXTestAFNetworkingRemoteCollection.m; sourceTree = ""; }; 55 | 62CD75AF1C8985AD00AA6A16 /* YTXTestAFNetworkingRemoteModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YTXTestAFNetworkingRemoteModel.h; sourceTree = ""; }; 56 | 62CD75B01C8985C000AA6A16 /* YTXTestAFNetworkingRemoteModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YTXTestAFNetworkingRemoteModel.m; sourceTree = ""; }; 57 | B49B4CC15A25D49DB32DA99D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 58 | C00D7C8B97513F9E766D02BD /* YTXRestfulModel.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = YTXRestfulModel.podspec; path = ../YTXRestfulModel.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 59 | C6176AB77AD238DD4C8DD2B5 /* Pods-YTXRestfulModel_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YTXRestfulModel_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-YTXRestfulModel_Tests/Pods-YTXRestfulModel_Tests.debug.xcconfig"; sourceTree = ""; }; 60 | DF3994A9A34C6C481609B709 /* libPods-YTXRestfulModel_Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-YTXRestfulModel_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 6003F5AB195388D20070C39A /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 69 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 70 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 71 | 4A9D60319D9EC81A8EF6A839 /* libPods-YTXRestfulModel_Tests.a in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 6003F581195388D10070C39A = { 79 | isa = PBXGroup; 80 | children = ( 81 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 82 | 6003F5B5195388D20070C39A /* Tests */, 83 | 6003F58C195388D20070C39A /* Frameworks */, 84 | 6003F58B195388D20070C39A /* Products */, 85 | CB32C1DAB34DE4955450843B /* Pods */, 86 | ); 87 | sourceTree = ""; 88 | }; 89 | 6003F58B195388D20070C39A /* Products */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 6003F5AE195388D20070C39A /* YTXRestfulModel_Tests.xctest */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | 6003F58C195388D20070C39A /* Frameworks */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 181817C21C843B1D00745529 /* libsqlite3.tbd */, 101 | 6003F58D195388D20070C39A /* Foundation.framework */, 102 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 103 | 6003F591195388D20070C39A /* UIKit.framework */, 104 | 6003F5AF195388D20070C39A /* XCTest.framework */, 105 | DF3994A9A34C6C481609B709 /* libPods-YTXRestfulModel_Tests.a */, 106 | ); 107 | name = Frameworks; 108 | sourceTree = ""; 109 | }; 110 | 6003F5B5195388D20070C39A /* Tests */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 62CD75A71C89814400AA6A16 /* test_YTXRestfulModelAFNetworkingRemote.m */, 114 | 0CB946381C716F0E009B83A7 /* test_YTXRestfulModelUserDefaultSpec.m */, 115 | 1867C4C71C7EEDBF00D900E9 /* test_YTXRestfulModelDB.m */, 116 | 6003F5B6195388D20070C39A /* Supporting Files */, 117 | 62CD75A91C89828300AA6A16 /* YTXTestAFNetworkingRemoteCollection.h */, 118 | 62CD75AA1C89828300AA6A16 /* YTXTestAFNetworkingRemoteCollection.m */, 119 | 62CD75AF1C8985AD00AA6A16 /* YTXTestAFNetworkingRemoteModel.h */, 120 | 62CD75B01C8985C000AA6A16 /* YTXTestAFNetworkingRemoteModel.m */, 121 | 1867C4CC1C7EF62A00D900E9 /* YTXTestHumanModel.h */, 122 | 1867C4CD1C7EF62A00D900E9 /* YTXTestHumanModel.m */, 123 | 1867C4C91C7EF01000D900E9 /* YTXTestStudentModel.h */, 124 | 1867C4CA1C7EF01000D900E9 /* YTXTestStudentModel.m */, 125 | 625D8E2F1C868DD8003A6F81 /* YTXTestTeacherModel.h */, 126 | 625D8E301C868DD8003A6F81 /* YTXTestTeacherModel.m */, 127 | 621EE9701C88013C0033A211 /* YTXTestStudentCollection.h */, 128 | 621EE9711C88013C0033A211 /* YTXTestStudentCollection.m */, 129 | 18E9FFF51C702CA500143B68 /* db.json */, 130 | 624C05901C55EDC200C40CE6 /* service.json */, 131 | ); 132 | path = Tests; 133 | sourceTree = ""; 134 | }; 135 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 139 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 140 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 141 | ); 142 | name = "Supporting Files"; 143 | sourceTree = ""; 144 | }; 145 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | C00D7C8B97513F9E766D02BD /* YTXRestfulModel.podspec */, 149 | B49B4CC15A25D49DB32DA99D /* README.md */, 150 | 44BA7F8968B8D7FBB1291584 /* LICENSE */, 151 | ); 152 | name = "Podspec Metadata"; 153 | sourceTree = ""; 154 | }; 155 | CB32C1DAB34DE4955450843B /* Pods */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | C6176AB77AD238DD4C8DD2B5 /* Pods-YTXRestfulModel_Tests.debug.xcconfig */, 159 | 0888F830D92D08408493FB13 /* Pods-YTXRestfulModel_Tests.release.xcconfig */, 160 | ); 161 | name = Pods; 162 | sourceTree = ""; 163 | }; 164 | /* End PBXGroup section */ 165 | 166 | /* Begin PBXNativeTarget section */ 167 | 6003F5AD195388D20070C39A /* YTXRestfulModel_Tests */ = { 168 | isa = PBXNativeTarget; 169 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "YTXRestfulModel_Tests" */; 170 | buildPhases = ( 171 | FDA13838148F72BC7F3BE3AE /* [CP] Check Pods Manifest.lock */, 172 | 6003F5AA195388D20070C39A /* Sources */, 173 | 6003F5AB195388D20070C39A /* Frameworks */, 174 | 6003F5AC195388D20070C39A /* Resources */, 175 | 4AAD9A50A9F903433AFF9967 /* [CP] Embed Pods Frameworks */, 176 | 72E53629E55A53CCDB71837F /* [CP] Copy Pods Resources */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | ); 182 | name = YTXRestfulModel_Tests; 183 | productName = YTXRestfulModelTests; 184 | productReference = 6003F5AE195388D20070C39A /* YTXRestfulModel_Tests.xctest */; 185 | productType = "com.apple.product-type.bundle.unit-test"; 186 | }; 187 | /* End PBXNativeTarget section */ 188 | 189 | /* Begin PBXProject section */ 190 | 6003F582195388D10070C39A /* Project object */ = { 191 | isa = PBXProject; 192 | attributes = { 193 | CLASSPREFIX = YTX; 194 | LastUpgradeCheck = 0720; 195 | ORGANIZATIONNAME = caojun; 196 | TargetAttributes = { 197 | 6003F5AD195388D20070C39A = { 198 | TestTargetID = 6003F589195388D20070C39A; 199 | }; 200 | }; 201 | }; 202 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "YTXRestfulModel" */; 203 | compatibilityVersion = "Xcode 3.2"; 204 | developmentRegion = English; 205 | hasScannedForEncodings = 0; 206 | knownRegions = ( 207 | en, 208 | Base, 209 | ); 210 | mainGroup = 6003F581195388D10070C39A; 211 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 212 | projectDirPath = ""; 213 | projectRoot = ""; 214 | targets = ( 215 | 6003F5AD195388D20070C39A /* YTXRestfulModel_Tests */, 216 | ); 217 | }; 218 | /* End PBXProject section */ 219 | 220 | /* Begin PBXResourcesBuildPhase section */ 221 | 6003F5AC195388D20070C39A /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | 18E9FFF61C702CA500143B68 /* db.json in Resources */, 226 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 227 | 624C05911C55EDC200C40CE6 /* service.json in Resources */, 228 | ); 229 | runOnlyForDeploymentPostprocessing = 0; 230 | }; 231 | /* End PBXResourcesBuildPhase section */ 232 | 233 | /* Begin PBXShellScriptBuildPhase section */ 234 | 4AAD9A50A9F903433AFF9967 /* [CP] Embed Pods Frameworks */ = { 235 | isa = PBXShellScriptBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | ); 239 | inputPaths = ( 240 | ); 241 | name = "[CP] Embed Pods Frameworks"; 242 | outputPaths = ( 243 | ); 244 | runOnlyForDeploymentPostprocessing = 0; 245 | shellPath = /bin/sh; 246 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-YTXRestfulModel_Tests/Pods-YTXRestfulModel_Tests-frameworks.sh\"\n"; 247 | showEnvVarsInLog = 0; 248 | }; 249 | 72E53629E55A53CCDB71837F /* [CP] Copy Pods Resources */ = { 250 | isa = PBXShellScriptBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | ); 254 | inputPaths = ( 255 | ); 256 | name = "[CP] Copy Pods Resources"; 257 | outputPaths = ( 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | shellPath = /bin/sh; 261 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-YTXRestfulModel_Tests/Pods-YTXRestfulModel_Tests-resources.sh\"\n"; 262 | showEnvVarsInLog = 0; 263 | }; 264 | FDA13838148F72BC7F3BE3AE /* [CP] Check Pods Manifest.lock */ = { 265 | isa = PBXShellScriptBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | inputPaths = ( 270 | ); 271 | name = "[CP] Check Pods Manifest.lock"; 272 | outputPaths = ( 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | shellPath = /bin/sh; 276 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 277 | showEnvVarsInLog = 0; 278 | }; 279 | /* End PBXShellScriptBuildPhase section */ 280 | 281 | /* Begin PBXSourcesBuildPhase section */ 282 | 6003F5AA195388D20070C39A /* Sources */ = { 283 | isa = PBXSourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | 62CD75B11C8985C000AA6A16 /* YTXTestAFNetworkingRemoteModel.m in Sources */, 287 | 1867C4CE1C7EF62A00D900E9 /* YTXTestHumanModel.m in Sources */, 288 | 625D8E311C868DD8003A6F81 /* YTXTestTeacherModel.m in Sources */, 289 | 0CB946391C716F0E009B83A7 /* test_YTXRestfulModelUserDefaultSpec.m in Sources */, 290 | 62CD75AD1C89828300AA6A16 /* YTXTestAFNetworkingRemoteCollection.m in Sources */, 291 | 1867C4C81C7EEDBF00D900E9 /* test_YTXRestfulModelDB.m in Sources */, 292 | 621EE9721C88013C0033A211 /* YTXTestStudentCollection.m in Sources */, 293 | 1867C4CB1C7EF01000D900E9 /* YTXTestStudentModel.m in Sources */, 294 | 62CD75A81C89814400AA6A16 /* test_YTXRestfulModelAFNetworkingRemote.m in Sources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXSourcesBuildPhase section */ 299 | 300 | /* Begin PBXVariantGroup section */ 301 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 6003F5B9195388D20070C39A /* en */, 305 | ); 306 | name = InfoPlist.strings; 307 | sourceTree = ""; 308 | }; 309 | /* End PBXVariantGroup section */ 310 | 311 | /* Begin XCBuildConfiguration section */ 312 | 6003F5BD195388D20070C39A /* Debug */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ALWAYS_SEARCH_USER_PATHS = NO; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BOOL_CONVERSION = YES; 321 | CLANG_WARN_CONSTANT_CONVERSION = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_EMPTY_BODY = YES; 324 | CLANG_WARN_ENUM_CONVERSION = YES; 325 | CLANG_WARN_INT_CONVERSION = YES; 326 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 327 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 328 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 329 | COPY_PHASE_STRIP = NO; 330 | ENABLE_TESTABILITY = YES; 331 | GCC_C_LANGUAGE_STANDARD = gnu99; 332 | GCC_DYNAMIC_NO_PIC = NO; 333 | GCC_OPTIMIZATION_LEVEL = 0; 334 | GCC_PREPROCESSOR_DEFINITIONS = ( 335 | "DEBUG=1", 336 | "$(inherited)", 337 | ); 338 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 339 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 340 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 341 | GCC_WARN_UNDECLARED_SELECTOR = YES; 342 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 343 | GCC_WARN_UNUSED_FUNCTION = YES; 344 | GCC_WARN_UNUSED_VARIABLE = YES; 345 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 346 | ONLY_ACTIVE_ARCH = YES; 347 | SDKROOT = iphoneos; 348 | TARGETED_DEVICE_FAMILY = "1,2"; 349 | }; 350 | name = Debug; 351 | }; 352 | 6003F5BE195388D20070C39A /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ALWAYS_SEARCH_USER_PATHS = NO; 356 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 357 | CLANG_CXX_LIBRARY = "libc++"; 358 | CLANG_ENABLE_MODULES = YES; 359 | CLANG_ENABLE_OBJC_ARC = YES; 360 | CLANG_WARN_BOOL_CONVERSION = YES; 361 | CLANG_WARN_CONSTANT_CONVERSION = YES; 362 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 363 | CLANG_WARN_EMPTY_BODY = YES; 364 | CLANG_WARN_ENUM_CONVERSION = YES; 365 | CLANG_WARN_INT_CONVERSION = YES; 366 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 367 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 368 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 369 | COPY_PHASE_STRIP = YES; 370 | ENABLE_NS_ASSERTIONS = NO; 371 | GCC_C_LANGUAGE_STANDARD = gnu99; 372 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 373 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 374 | GCC_WARN_UNDECLARED_SELECTOR = YES; 375 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 376 | GCC_WARN_UNUSED_FUNCTION = YES; 377 | GCC_WARN_UNUSED_VARIABLE = YES; 378 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 379 | SDKROOT = iphoneos; 380 | TARGETED_DEVICE_FAMILY = "1,2"; 381 | VALIDATE_PRODUCT = YES; 382 | }; 383 | name = Release; 384 | }; 385 | 6003F5C3195388D20070C39A /* Debug */ = { 386 | isa = XCBuildConfiguration; 387 | baseConfigurationReference = C6176AB77AD238DD4C8DD2B5 /* Pods-YTXRestfulModel_Tests.debug.xcconfig */; 388 | buildSettings = { 389 | FRAMEWORK_SEARCH_PATHS = ( 390 | "$(SDKROOT)/Developer/Library/Frameworks", 391 | "$(inherited)", 392 | "$(DEVELOPER_FRAMEWORKS_DIR)", 393 | ); 394 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 395 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 396 | GCC_PREPROCESSOR_DEFINITIONS = ( 397 | "DEBUG=1", 398 | "$(inherited)", 399 | ); 400 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 401 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 402 | PRODUCT_NAME = "$(TARGET_NAME)"; 403 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YTXRestfulModel_Example.app/YTXRestfulModel_Example"; 404 | WRAPPER_EXTENSION = xctest; 405 | }; 406 | name = Debug; 407 | }; 408 | 6003F5C4195388D20070C39A /* Release */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 0888F830D92D08408493FB13 /* Pods-YTXRestfulModel_Tests.release.xcconfig */; 411 | buildSettings = { 412 | FRAMEWORK_SEARCH_PATHS = ( 413 | "$(SDKROOT)/Developer/Library/Frameworks", 414 | "$(inherited)", 415 | "$(DEVELOPER_FRAMEWORKS_DIR)", 416 | ); 417 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 418 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 419 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 420 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YTXRestfulModel_Example.app/YTXRestfulModel_Example"; 423 | WRAPPER_EXTENSION = xctest; 424 | }; 425 | name = Release; 426 | }; 427 | /* End XCBuildConfiguration section */ 428 | 429 | /* Begin XCConfigurationList section */ 430 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "YTXRestfulModel" */ = { 431 | isa = XCConfigurationList; 432 | buildConfigurations = ( 433 | 6003F5BD195388D20070C39A /* Debug */, 434 | 6003F5BE195388D20070C39A /* Release */, 435 | ); 436 | defaultConfigurationIsVisible = 0; 437 | defaultConfigurationName = Release; 438 | }; 439 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "YTXRestfulModel_Tests" */ = { 440 | isa = XCConfigurationList; 441 | buildConfigurations = ( 442 | 6003F5C3195388D20070C39A /* Debug */, 443 | 6003F5C4195388D20070C39A /* Release */, 444 | ); 445 | defaultConfigurationIsVisible = 0; 446 | defaultConfigurationName = Release; 447 | }; 448 | /* End XCConfigurationList section */ 449 | }; 450 | rootObject = 6003F582195388D10070C39A /* Project object */; 451 | } 452 | -------------------------------------------------------------------------------- /Example/YTXRestfulModel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/YTXRestfulModel.xcodeproj/xcshareddata/xcschemes/YTXRestfulModel-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example/YTXRestfulModel.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 caojun <78612846@qq.com> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pod/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidao/YTXRestfulModel/1272d03b9d6675c1e2f14858bf0dc752b12f2a14/Pod/Assets/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidao/YTXRestfulModel/1272d03b9d6675c1e2f14858bf0dc752b12f2a14/Pod/Classes/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/Model/YTXRestfulCollection.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulCollection.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/1/25. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | 10 | #import "YTXRestfulModelStorageProtocol.h" 11 | #import "YTXRestfulModelRemoteProtocol.h" 12 | #import "YTXRestfulModelDBProtocol.h" 13 | #import "YTXRestfulModel.h" 14 | 15 | #import 16 | 17 | @interface YTXRestfulCollection : NSObject 18 | 19 | @property (nonnull, nonatomic, assign) Class modelClass; 20 | @property (nonnull, nonatomic, strong, readonly) NSArray * models; 21 | 22 | @property (nonnull, nonatomic, strong) id storageSync; 23 | @property (nonnull, nonatomic, strong) id remoteSync; 24 | @property (nonnull, nonatomic, strong) id dbSync; 25 | 26 | 27 | - (nonnull instancetype) initWithModelClass:(nonnull Class)modelClass; 28 | 29 | - (nonnull instancetype) initWithModelClass:(nonnull Class)modelClass userDefaultSuiteName:(nullable NSString *) suiteName; 30 | 31 | #pragma mark storage 32 | - (nonnull NSString *) storageKey; 33 | 34 | /** GET */ 35 | - (nullable instancetype) fetchStorageSync:(nullable NSDictionary *) param; 36 | 37 | /** POST / PUT */ 38 | - (nonnull instancetype) saveStorageSync:(nullable NSDictionary *) param; 39 | 40 | /** DELETE */ 41 | - (void) destroyStorageSync:(nullable NSDictionary *) param; 42 | 43 | /** GET */ 44 | - (nullable instancetype) fetchStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 45 | 46 | /** POST / PUT */ 47 | - (nonnull instancetype) saveStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 48 | 49 | /** DELETE */ 50 | - (void) destroyStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 51 | 52 | #pragma mark remote 53 | /* RACSignal return self **/ 54 | - (void) fetchRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 55 | 56 | /* RACSignal return self **/ 57 | - (void) fetchRemoteThenAdd:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 58 | 59 | #pragma mark db 60 | - (nonnull instancetype) fetchDBSyncAllWithError:(NSError * _Nullable * _Nullable) error; 61 | // 62 | - (nonnull instancetype) fetchDBSyncAllWithError:(NSError * _Nullable * _Nullable)error soryBy:(YTXRestfulModelDBSortBy)sortBy orderByColumnNames:(nonnull NSArray * )columnNames; 63 | - (nonnull instancetype) fetchDBSyncAllWithError:(NSError * _Nullable * _Nullable)error soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )columnName, ...; 64 | // 65 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error start:(NSUInteger)start count:(NSUInteger)count soryBy:(YTXRestfulModelDBSortBy)sortBy orderByColumnNames:(nonnull NSArray * )columnNames; 66 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error start:(NSUInteger)start count:(NSUInteger)count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )columnName, ...; 67 | // 68 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetConditions:(nonnull NSArray * )conditions; 69 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMet:(nonnull NSString * )condition, ...; 70 | // 71 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditionsArray:(nonnull NSArray * )conditions; 72 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSString * )condition, ...; 73 | // 74 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditionsArray:(nonnull NSArray * )conditions; 75 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSString * )condition, ...; 76 | // 77 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetConditionsArray:(nonnull NSArray * )conditions; 78 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMet:(nonnull NSString * )condition, ...; 79 | // 80 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditionsArray:(nonnull NSArray * )conditions; 81 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSString * )condition, ...; 82 | // 83 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditionsArray:(nonnull NSArray * )conditions; 84 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSString * )condition, ...; 85 | // 86 | - (BOOL) destroyDBSyncAllWithError:(NSError * _Nullable * _Nullable)error; 87 | 88 | #pragma clang diagnostic push 89 | #pragma clang diagnostic ignored "-Wnullability-completeness" 90 | - (nonnull NSArray *) arrayWithArgs:(va_list) args firstArgument:(nullable id)firstArgument; 91 | #pragma clang diagnostic pop 92 | 93 | - (nonnull NSArray *) arrayOfMappedArgsWithOriginArray:(nonnull NSArray *)originArray; 94 | 95 | #pragma mark - function 96 | 97 | - (nullable NSArray< id > *) transformerProxyOfResponse:(nullable id) response error:(NSError * _Nullable * _Nullable) error; 98 | 99 | /** 在拉到数据转mantle的时候用 */ 100 | - (nullable NSArray *) transformerProxyOfModels:(nonnull NSArray< id > *) array; 101 | 102 | /** 注入自己时使用 */ 103 | - (nonnull instancetype) removeAllModels; 104 | 105 | - (nonnull instancetype) resetModels:(nonnull NSArray *) array; 106 | 107 | - (nonnull instancetype) addModels:(nonnull NSArray *) array; 108 | 109 | - (nonnull instancetype) insertFrontModels:(nonnull NSArray *) array; 110 | 111 | - (nonnull instancetype) sortedArrayUsingComparator:(nonnull NSComparator)cmptr; 112 | 113 | - (nullable NSArray *) arrayWithRange:(NSRange)range; 114 | 115 | - (nullable YTXRestfulCollection *) collectionWithRange:(NSRange)range; 116 | 117 | - (nullable YTXRestfulModel *) modelAtIndex:(NSInteger) index; 118 | 119 | /** 主键可能是NSNumber或NSString,统一转成NSString来判断*/ 120 | - (nullable YTXRestfulModel *) modelWithPrimaryKey:(nonnull NSString *) primaryKey; 121 | 122 | - (BOOL) addModel:(nonnull YTXRestfulModel *) model; 123 | 124 | - (BOOL) insertFrontModel:(nonnull YTXRestfulModel *) model; 125 | 126 | /** 插入到index之后*/ 127 | - (BOOL) insertModel:(nonnull YTXRestfulModel *) model afterIndex:(NSInteger) index; 128 | 129 | /** 插入到index之前*/ 130 | - (BOOL) insertModel:(nonnull YTXRestfulModel *) model beforeIndex:(NSInteger) index; 131 | 132 | - (BOOL) removeModelAtIndex:(NSInteger) index; 133 | 134 | /** 主键可能是NSNumber或NSString,统一转成NSString来判断*/ 135 | - (BOOL) removeModelWithPrimaryKey:(nonnull NSString *) primaryKey; 136 | 137 | - (BOOL) removeModelWithModel:(nonnull YTXRestfulModel *) model; 138 | 139 | /** 逆序输出Collection*/ 140 | - (void) reverseModels; 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /Pod/Classes/Model/YTXRestfulCollection.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulCollection.m 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/1/19. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import "YTXRestfulCollection.h" 10 | #import "YTXRestfulModel.h" 11 | 12 | #ifdef YTX_USERDEFAULTSTORAGESYNC_EXISTS 13 | #import "YTXRestfulModelUserDefaultStorageSync.h" 14 | #endif 15 | 16 | #ifdef YTX_AFNETWORKINGREMOTESYNC_EXISTS 17 | #import "AFNetworkingRemoteSync.h" 18 | #endif 19 | 20 | #ifdef YTX_FMDBSYNC_EXISTS 21 | #import "YTXRestfulModelFMDBSync.h" 22 | #endif 23 | 24 | #import 25 | 26 | 27 | typedef enum { 28 | RESET, 29 | ADD, 30 | INSERTFRONT, 31 | } FetchRemoteHandleScheme; 32 | 33 | @interface YTXRestfulCollection() 34 | 35 | @property (nonnull, nonatomic, strong) NSArray * models; 36 | 37 | @end 38 | 39 | @implementation YTXRestfulCollection 40 | 41 | - (instancetype)init 42 | { 43 | if(self = [super init]) 44 | { 45 | 46 | #ifdef YTX_USERDEFAULTSTORAGESYNC_EXISTS 47 | self.storageSync = [YTXRestfulModelUserDefaultStorageSync new]; 48 | #endif 49 | 50 | #ifdef YTX_AFNETWORKINGREMOTESYNC_EXISTS 51 | self.remoteSync = [AFNetworkingRemoteSync new]; 52 | #endif 53 | 54 | #ifdef YTX_FMDBSYNC_EXISTS 55 | self.dbSync = [YTXRestfulModelFMDBSync new]; 56 | #endif 57 | self.modelClass = [YTXRestfulModel class]; 58 | self.models = @[]; 59 | } 60 | return self; 61 | 62 | } 63 | 64 | - (instancetype)initWithModelClass:(Class)modelClass 65 | { 66 | return [self initWithModelClass:modelClass userDefaultSuiteName:nil]; 67 | } 68 | 69 | - (instancetype)initWithModelClass:(Class)modelClass userDefaultSuiteName:(NSString *) suiteName 70 | { 71 | if(self = [super init]) 72 | { 73 | 74 | #ifdef YTX_USERDEFAULTSTORAGESYNC_EXISTS 75 | self.storageSync = [YTXRestfulModelUserDefaultStorageSync new]; 76 | #endif 77 | 78 | #ifdef YTX_AFNETWORKINGREMOTESYNC_EXISTS 79 | self.remoteSync = [AFNetworkingRemoteSync new]; 80 | #endif 81 | 82 | #ifdef YTX_FMDBSYNC_EXISTS 83 | self.dbSync = [YTXRestfulModelFMDBSync syncWithModelOfClass:modelClass primaryKey:[modelClass syncPrimaryKey]]; 84 | #endif 85 | self.modelClass = modelClass; 86 | self.models = @[]; 87 | } 88 | return self; 89 | } 90 | 91 | #pragma mark storage 92 | /** GET */ 93 | - (nullable instancetype) fetchStorageSync:(nullable NSDictionary *) param 94 | { 95 | return [self fetchStorageSyncWithKey:[self storageKey] param:param]; 96 | } 97 | 98 | /** POST / PUT */ 99 | - (nonnull instancetype) saveStorageSync:(nullable NSDictionary *) param 100 | { 101 | return [self saveStorageSyncWithKey:[self storageKey] param:param]; 102 | } 103 | 104 | /** DELETE */ 105 | - (void) destroyStorageSync:(nullable NSDictionary *) param 106 | { 107 | return [self destroyStorageSyncWithKey:[self storageKey] param:param]; 108 | } 109 | 110 | /** GET */ 111 | - (nullable instancetype) fetchStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param 112 | { 113 | 114 | NSArray * x = [self.storageSync fetchStorageSyncWithKey:storage param:param]; 115 | if (x) { 116 | NSError * error; 117 | NSArray * ret = [self transformerProxyOfResponse:x error:nil]; 118 | if (!error) { 119 | [self resetModels:ret]; 120 | return self; 121 | } 122 | } 123 | return nil; 124 | } 125 | 126 | /** POST / PUT */ 127 | - (nonnull instancetype) saveStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param 128 | { 129 | [self.storageSync saveStorageSyncWithKey:storage withObject:[self transformerProxyOfModels:[self.models copy]] param:param]; 130 | 131 | return self; 132 | } 133 | 134 | /** DELETE */ 135 | - (void) destroyStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param 136 | { 137 | [self.storageSync destroyStorageSyncWithKey:storage param:param]; 138 | } 139 | 140 | - (nonnull NSString *) storageKey 141 | { 142 | return [NSString stringWithFormat:@"EFSCollection+%@", NSStringFromClass(self.modelClass)]; 143 | } 144 | 145 | #pragma mark remote 146 | 147 | /** 在拉到数据转mantle的时候用 */ 148 | - (nullable NSArray< id > *) transformerProxyOfResponse:(nullable id) response error:(NSError * _Nullable * _Nullable) error 149 | { 150 | return [MTLJSONAdapter modelsOfClass:[self modelClass] fromJSONArray:response error:error]; 151 | } 152 | 153 | /** 在拉到数据转mantle的时候用 */ 154 | - (nullable NSArray *) transformerProxyOfModels:(nonnull NSArray< id > *) array 155 | { 156 | return [MTLJSONAdapter JSONArrayFromModels:array]; 157 | } 158 | 159 | - (nonnull instancetype) removeAllModels 160 | { 161 | self.models = @[]; 162 | return self; 163 | } 164 | 165 | - (nonnull instancetype) resetModels:(nonnull NSArray *) array 166 | { 167 | # if DEBUG 168 | for (id item in array) { 169 | NSAssert([item isMemberOfClass:self.modelClass], @"加入的数组中的每一项都必须是当前的Model类型"); 170 | } 171 | # endif 172 | self.models = array; 173 | return self; 174 | } 175 | 176 | - (nonnull instancetype) addModels:(nonnull NSArray *) array 177 | { 178 | NSMutableArray * temp = [NSMutableArray arrayWithArray:self.models]; 179 | 180 | [temp addObjectsFromArray:array]; 181 | 182 | return [self resetModels:temp]; 183 | } 184 | 185 | - (nonnull instancetype) insertFrontModels:(nonnull NSArray *) array 186 | { 187 | NSMutableArray * temp = [NSMutableArray arrayWithArray:array]; 188 | 189 | [temp addObjectsFromArray:self.models]; 190 | 191 | return [self resetModels:temp]; 192 | } 193 | 194 | - (nonnull instancetype)sortedArrayUsingComparator:(NSComparator)cmptr 195 | { 196 | [self resetModels:[self.models sortedArrayUsingComparator:cmptr]]; 197 | return self; 198 | } 199 | 200 | 201 | /* RACSignal return self **/ 202 | - (void) fetchRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 203 | { 204 | __weak __typeof(&*self)weakSelf = self; 205 | [self.remoteSync fetchRemote:param success:^(id _Nullable response) { 206 | NSError * error = nil; 207 | NSArray * arr = [weakSelf transformerProxyOfResponse:response error:&error]; 208 | 209 | [weakSelf resetModels:arr]; 210 | if (!error) { 211 | success(weakSelf); 212 | } 213 | else { 214 | failed(error); 215 | } 216 | } failed:failed]; 217 | 218 | } 219 | 220 | /* RACSignal return self **/ 221 | - (void) fetchRemoteThenAdd:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 222 | { 223 | __weak __typeof(&*self)weakSelf = self; 224 | [self.remoteSync fetchRemote:param success:^(id _Nullable response) { 225 | NSError * error = nil; 226 | NSArray * arr = [weakSelf transformerProxyOfResponse:response error:&error]; 227 | 228 | [weakSelf addModels:arr]; 229 | if (!error) { 230 | success(weakSelf); 231 | } 232 | else { 233 | failed(error); 234 | } 235 | } failed:failed]; 236 | } 237 | 238 | 239 | #pragma mark db 240 | - (nonnull instancetype) fetchDBSyncAllWithError:(NSError * _Nullable * _Nullable)error 241 | { 242 | NSArray * x = [self.dbSync fetchAllSyncWithError:error]; 243 | 244 | if (x && *error == nil) { 245 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 246 | } 247 | 248 | return self; 249 | } 250 | 251 | - (nonnull instancetype) fetchDBSyncAllWithError:(NSError * _Nullable * _Nullable)error soryBy:(YTXRestfulModelDBSortBy)sortBy orderByColumnNames:(nonnull NSArray * )columnNames 252 | { 253 | NSArray * x = [self.dbSync fetchAllSyncWithError:error soryBy:sortBy orderBy:columnNames]; 254 | 255 | if (x && *error == nil) { 256 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 257 | } 258 | 259 | return self; 260 | } 261 | 262 | - (nonnull instancetype) fetchDBSyncAllWithError:(NSError * _Nullable * _Nullable)error soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) columnName, ... 263 | { 264 | va_list args; 265 | va_start(args, columnName); 266 | 267 | NSArray * columnNames = [self arrayWithArgs:args firstArgument:columnName]; 268 | 269 | va_end(args); 270 | 271 | columnNames = [self arrayOfMappedArgsWithOriginArray:columnNames]; 272 | 273 | return [self fetchDBSyncAllWithError:error soryBy:sortBy orderByColumnNames:columnNames]; 274 | } 275 | 276 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error start:(NSUInteger)start count:(NSUInteger)count soryBy:(YTXRestfulModelDBSortBy)sortBy orderByColumnNames:(nonnull NSArray * )columnNames 277 | { 278 | NSArray * x = [self.dbSync fetchMultipleSyncWithError:error start:start count:count soryBy:sortBy orderBy:columnNames]; 279 | 280 | if (x && *error == nil) { 281 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 282 | } 283 | 284 | return self; 285 | } 286 | 287 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error start:(NSUInteger)start count:(NSUInteger)count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )columnName, ... 288 | { 289 | va_list args; 290 | va_start(args, columnName); 291 | 292 | NSArray * columnNames = [self arrayWithArgs:args firstArgument:columnName]; 293 | 294 | va_end(args); 295 | 296 | columnNames = [self arrayOfMappedArgsWithOriginArray:columnNames]; 297 | 298 | return [self fetchDBSyncMultipleWithError:error start:start count:count soryBy:sortBy orderByColumnNames:columnNames]; 299 | } 300 | 301 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetConditions:(nonnull NSArray * )conditions 302 | { 303 | NSArray * x = [self.dbSync fetchMultipleSyncWithError:error whereAllTheConditionsAreMet:conditions]; 304 | 305 | if (x && *error == nil) { 306 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 307 | } 308 | 309 | return self; 310 | } 311 | 312 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMet:(nonnull NSString * )condition, ... 313 | { 314 | va_list args; 315 | va_start(args, condition); 316 | 317 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 318 | 319 | va_end(args); 320 | 321 | return [self fetchDBSyncMultipleWithError:error whereAllTheConditionsAreMetConditions:conditions]; 322 | } 323 | 324 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditionsArray:(nonnull NSArray * )conditions 325 | { 326 | NSArray * x = [self.dbSync fetchMultipleSyncWithError:error whereAllTheConditionsAreMetWithSoryBy:sortBy orderBy:orderBy conditions:conditions]; 327 | 328 | if (x && *error == nil) { 329 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 330 | } 331 | 332 | return self; 333 | } 334 | 335 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSString * )condition, ... 336 | { 337 | va_list args; 338 | va_start(args, condition); 339 | 340 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 341 | 342 | va_end(args); 343 | 344 | NSArray * x = [self.dbSync fetchMultipleSyncWithError:error whereAllTheConditionsAreMetWithSoryBy:sortBy orderBy:orderBy conditions:conditions]; 345 | 346 | if (x && *error == nil) { 347 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 348 | } 349 | 350 | return [self fetchDBSyncMultipleWithError:error whereAllTheConditionsAreMetWithSoryBy:sortBy orderBy:orderBy conditionsArray:conditions]; 351 | } 352 | 353 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditionsArray:(nonnull NSArray * )conditions 354 | { 355 | NSArray * x = [self.dbSync fetchMultipleSyncWithError:error whereAllTheConditionsAreMetWithStart:start count:count soryBy:sortBy orderBy:orderBy conditions:conditions]; 356 | 357 | if (x && *error == nil) { 358 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 359 | } 360 | 361 | return self; 362 | 363 | } 364 | 365 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSString * )condition, ... 366 | { 367 | va_list args; 368 | va_start(args, condition); 369 | 370 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 371 | 372 | va_end(args); 373 | 374 | return [self fetchDBSyncMultipleWithError:error whereAllTheConditionsAreMetWithStart:start count:count soryBy:sortBy orderBy:orderBy conditionsArray:conditions]; 375 | } 376 | 377 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetConditionsArray:(nonnull NSArray * )conditions 378 | { 379 | 380 | NSArray * x = [self.dbSync fetchMultipleSyncWithError:error wherePartOfTheConditionsAreMet:conditions]; 381 | 382 | if (x && *error == nil) { 383 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 384 | } 385 | 386 | return self; 387 | } 388 | 389 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMet:(nonnull NSString * )condition, ... 390 | { 391 | va_list args; 392 | va_start(args, condition); 393 | 394 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 395 | 396 | va_end(args); 397 | 398 | return [self fetchDBSyncMultipleWithError:error wherePartOfTheConditionsAreMetConditionsArray:conditions]; 399 | } 400 | 401 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditionsArray:(nonnull NSArray * )conditions 402 | { 403 | NSArray * x = [self.dbSync fetchMultipleSyncWithError:error wherePartOfTheConditionsAreMetWithSoryBy:sortBy orderBy:orderBy conditions:conditions]; 404 | 405 | if (x && *error == nil) { 406 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 407 | } 408 | 409 | return self; 410 | } 411 | 412 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSString * )condition, ... 413 | { 414 | va_list args; 415 | va_start(args, condition); 416 | 417 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 418 | 419 | va_end(args); 420 | 421 | return [self fetchDBSyncMultipleWithError:error wherePartOfTheConditionsAreMetWithSoryBy:sortBy orderBy:orderBy conditionsArray:conditions]; 422 | } 423 | 424 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditionsArray:(nonnull NSArray * )conditions 425 | { 426 | NSArray * x = [self.dbSync fetchMultipleSyncWithError:error wherePartOfTheConditionsAreMetWithStart:start count:count soryBy:sortBy orderBy:orderBy conditions:conditions]; 427 | 428 | if (x && *error == nil) { 429 | [self resetModels:[self transformerProxyOfResponse:x error:error]]; 430 | } 431 | 432 | return self; 433 | } 434 | 435 | - (nonnull instancetype) fetchDBSyncMultipleWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSString * )condition, ... 436 | { 437 | va_list args; 438 | va_start(args, condition); 439 | 440 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 441 | 442 | va_end(args); 443 | 444 | return [self fetchDBSyncMultipleWithError:error wherePartOfTheConditionsAreMetWithStart:start count:count soryBy:sortBy orderBy:orderBy conditionsArray:conditions]; 445 | } 446 | 447 | - (BOOL) destroyDBSyncAllWithError:(NSError * _Nullable * _Nullable) error 448 | { 449 | return [self.dbSync destroyAllSyncWithError:error]; 450 | } 451 | 452 | - (nullable NSArray *) arrayWithRange:(NSRange)range 453 | { 454 | if (range.location + range.length > self.models.count) { 455 | return nil; 456 | } 457 | 458 | return [self.models subarrayWithRange:range]; 459 | } 460 | 461 | - (nullable YTXRestfulCollection *) collectionWithRange:(NSRange)range 462 | { 463 | NSArray * arr = [self arrayWithRange:range]; 464 | 465 | return arr ? [[[YTXRestfulCollection alloc] initWithModelClass:self.modelClass] addModels:arr] : nil; 466 | } 467 | 468 | - (nullable YTXRestfulModel *) modelAtIndex:(NSInteger) index 469 | { 470 | if (index < 0 || index >= self.models.count) { 471 | return nil; 472 | } 473 | 474 | return self.models[index]; 475 | } 476 | 477 | - (nullable YTXRestfulModel *) modelWithPrimaryKey:(nonnull NSString *) primaryKey 478 | { 479 | for (YTXRestfulModel *model in self.models) { 480 | if ( [[[model primaryValue] description] isEqualToString:primaryKey]) { 481 | return model; 482 | } 483 | } 484 | return nil; 485 | } 486 | 487 | - (BOOL) addModel:(nonnull YTXRestfulModel *) model 488 | { 489 | NSMutableArray * temp = [NSMutableArray arrayWithArray:self.models]; 490 | [temp addObject:model]; 491 | [self resetModels:temp]; 492 | return YES; 493 | } 494 | 495 | - (BOOL) insertFrontModel:(nonnull YTXRestfulModel *) model 496 | { 497 | return [self insertModel:model beforeIndex:0]; 498 | } 499 | 500 | /** 插入到index之后*/ 501 | - (BOOL) insertModel:(nonnull YTXRestfulModel *) model afterIndex:(NSInteger) index 502 | { 503 | if (self.models.count == 0 || self.models.count == index+1) { 504 | return [self addModel:model]; 505 | } 506 | 507 | if (index < 0 || index >= self.models.count) { 508 | return NO; 509 | } 510 | NSMutableArray * temp = [NSMutableArray arrayWithArray:self.models]; 511 | [temp insertObject:model atIndex:index+1]; 512 | [self resetModels:temp]; 513 | return YES; 514 | } 515 | 516 | /** 插入到index之前*/ 517 | - (BOOL) insertModel:(nonnull YTXRestfulModel *) model beforeIndex:(NSInteger) index 518 | { 519 | if (self.models.count == 0) { 520 | return [self addModel:model]; 521 | } 522 | 523 | if (index < 0 || index >= self.models.count) { 524 | return NO; 525 | } 526 | NSMutableArray * temp = [NSMutableArray arrayWithArray:self.models]; 527 | [temp insertObject:model atIndex:index]; 528 | [self resetModels:temp]; 529 | return YES; 530 | } 531 | 532 | - (BOOL) removeModelAtIndex:(NSInteger) index 533 | { 534 | if (index < 0 || index >= self.models.count) { 535 | return NO; 536 | } 537 | NSMutableArray * temp = [NSMutableArray arrayWithArray:self.models]; 538 | [temp removeObjectAtIndex:index]; 539 | [self resetModels:temp]; 540 | return YES; 541 | } 542 | 543 | /** 主键可能是NSNumber或NSString,统一转成NSString来判断*/ 544 | - (BOOL) removeModelWithPrimaryKey:(nonnull NSString *) primaryKey 545 | { 546 | for (YTXRestfulModel *model in self.models) { 547 | if ( [[[model primaryValue] description] isEqualToString:primaryKey]) { 548 | NSMutableArray * temp = [NSMutableArray arrayWithArray:self.models]; 549 | [temp removeObject:model]; 550 | [self resetModels:temp]; 551 | return YES; 552 | } 553 | } 554 | return NO; 555 | } 556 | 557 | - (BOOL) removeModelWithModel:(nonnull YTXRestfulModel *) model 558 | { 559 | NSMutableArray * temp = [NSMutableArray arrayWithArray:self.models]; 560 | 561 | NSInteger index = [[self models] indexOfObject:model]; 562 | 563 | if (NSNotFound == index) { 564 | return NO; 565 | } 566 | 567 | [temp removeObjectAtIndex:index]; 568 | [self resetModels:temp]; 569 | return YES; 570 | } 571 | 572 | - (void)reverseModels 573 | { 574 | [self resetModels:self.models.reverseObjectEnumerator.allObjects]; 575 | } 576 | 577 | - (nonnull NSArray *) arrayWithArgs:(va_list) args firstArgument:(nullable id)firstArgument 578 | { 579 | if (firstArgument == nil) { 580 | return @[]; 581 | } 582 | 583 | NSMutableArray * array = [NSMutableArray arrayWithObject:firstArgument]; 584 | id arg = nil; 585 | while ((arg = va_arg(args,id))) { 586 | [array addObject:arg]; 587 | } 588 | return array; 589 | } 590 | 591 | - (nonnull NSArray *) arrayOfMappedArgsWithOriginArray:(nonnull NSArray *)originArray 592 | { 593 | NSDictionary * propertiesMap = [self.modelClass JSONKeyPathsByPropertyKey]; 594 | NSMutableArray *retArray = [NSMutableArray array]; 595 | for (id arg in originArray) { 596 | [retArray addObject:propertiesMap[arg] ?: arg]; 597 | } 598 | return retArray; 599 | } 600 | 601 | - (id)storageSync 602 | { 603 | YTXAssertSyncExists(_storageSync, @"StorageSync"); 604 | return _storageSync; 605 | } 606 | 607 | -(id)dbSync 608 | { 609 | YTXAssertSyncExists(_dbSync, @"DBSync"); 610 | return _dbSync; 611 | } 612 | 613 | -(id)remoteSync 614 | { 615 | YTXAssertSyncExists(_remoteSync, @"RemoteSync"); 616 | return _remoteSync; 617 | } 618 | 619 | @end 620 | -------------------------------------------------------------------------------- /Pod/Classes/Model/YTXRestfulModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXModel.h 3 | // YTXRestfulModel 4 | // 5 | // Created by cao jun on 16/01/25. 6 | // Copyright © 2015 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import "YTXRestfulModelProtocol.h" 10 | #import "YTXRestfulModelStorageProtocol.h" 11 | #import "YTXRestfulModelRemoteProtocol.h" 12 | #import "YTXRestfulModelDBProtocol.h" 13 | #import 14 | #import 15 | #import 16 | 17 | #define YTXAssertSyncExists(__SYNC__, __DESC__) \ 18 | NSAssert(__SYNC__ != nil, @"应该在pod中安装%@ 像这样:%@", __DESC__, @"pod 'YTXRestfulModel', :path => '../', :subspecs => [ 'FMDBSync', 'UserDefaultStorageSync']");\ 19 | 20 | @interface YTXRestfulModel : MTLModel 21 | 22 | @property (nonnull, nonatomic, strong) id storageSync; 23 | @property (nonnull, nonatomic, strong) id remoteSync; 24 | @property (nonnull, nonatomic, strong) id dbSync; 25 | 26 | 27 | - (nonnull instancetype) mergeWithAnother:(_Nonnull id) model; 28 | 29 | /** 要用keyId判断 */ 30 | - (BOOL) isNew; 31 | 32 | /** 需要告诉我主键是什么,子类也应当实现 */ 33 | + (nonnull NSString *) primaryKey; 34 | 35 | + (nonnull NSString *)syncPrimaryKey; 36 | 37 | /** 方便的直接取主键的值*/ 38 | - (nullable id) primaryValue; 39 | 40 | /** 在拉到数据转mantle的时候用 */ 41 | - (nonnull instancetype) transformerProxyOfResponse:(nonnull id) response error:(NSError * _Nullable * _Nullable) error; 42 | 43 | /** 在拉到数据转外部mantle对象的时候用 */ 44 | - (nonnull id) transformerProxyOfForeign:(nonnull Class)modelClass response:(nonnull id) response error:(NSError * _Nullable * _Nullable) error; 45 | 46 | /** 将自身转化为Dictionary,然后对传入参数进行和自身属性的融合。自身的属性优先级最高,不可被传入参数修改。 */ 47 | - (nonnull NSDictionary *)mergeSelfAndParameters:(nullable NSDictionary *)param; 48 | 49 | - (nonnull NSString *) storageKeyWithParam:(nullable NSDictionary *) param; 50 | 51 | /** GET */ 52 | - (nullable instancetype) fetchStorageSync:(nullable NSDictionary *) param; 53 | 54 | /** POST / PUT */ 55 | - (nonnull instancetype) saveStorageSync:(nullable NSDictionary *) param; 56 | 57 | /** DELETE */ 58 | - (void) destroyStorageSync:(nullable NSDictionary *) param; 59 | 60 | /** GET */ 61 | - (nullable instancetype) fetchStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 62 | 63 | /** POST / PUT */ 64 | - (nonnull instancetype) saveStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 65 | 66 | /** DELETE */ 67 | - (void) destroyStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 68 | 69 | 70 | /** :id/comment 这种形式的时候使用GET; modelClass is MTLModel*/ 71 | - (void) fetchRemoteForeignWithName:(nonnull NSString *)name modelClass:(nonnull Class)modelClass param:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 72 | /** GET */ 73 | - (void) fetchRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 74 | /** POST / PUT */ 75 | - (void) saveRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 76 | /** DELETE */ 77 | - (void) destroyRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 78 | 79 | /** 主键是否自增,默认为YES */ 80 | + (BOOL) isPrimaryKeyAutoincrement; 81 | 82 | /** 若主键是NSNumber 将会默认设置为自增的 */ 83 | + (nullable NSMutableDictionary *) tableKeyPathsByPropertyKey; 84 | 85 | + (nullable NSNumber *) currentMigrationVersion; 86 | 87 | + (void) migrationsMethodWithSync:(nonnull id)sync; 88 | 89 | /** GET */ 90 | - (nonnull instancetype) fetchDBSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 91 | 92 | /** 93 | * POST / PUT 94 | * 数据库不存在时创建,否则更新 95 | * 更新必须带主键 96 | */ 97 | - (nonnull instancetype) saveDBSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 98 | 99 | /** DELETE */ 100 | - (BOOL) destroyDBSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 101 | 102 | /** GET Foreign Models with primary key */ 103 | - (nonnull NSArray *) fetchDBForeignSyncWithModelClass:(nonnull Class)modelClass error:(NSError * _Nullable * _Nullable) error param:(nullable NSDictionary *)param; 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /Pod/Classes/Model/YTXRestfulModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXModel.m 3 | // YTXRestfulModel 4 | // 5 | // Created by cao jun on 16/01/25. 6 | // Copyright © 2015 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import "YTXRestfulModel.h" 10 | 11 | #ifdef YTX_USERDEFAULTSTORAGESYNC_EXISTS 12 | #import "YTXRestfulModelUserDefaultStorageSync.h" 13 | #endif 14 | 15 | #ifdef YTX_AFNETWORKINGREMOTESYNC_EXISTS 16 | #import "AFNetworkingRemoteSync.h" 17 | #endif 18 | 19 | #ifdef YTX_FMDBSYNC_EXISTS 20 | #import "YTXRestfulModelFMDBSync.h" 21 | #import "NSValue+YTXRestfulModelFMDBSync.h" 22 | #endif 23 | 24 | #import 25 | #import 26 | 27 | #import 28 | 29 | @implementation YTXRestfulModelDBSerializingModel 30 | 31 | @end 32 | 33 | @implementation YTXRestfulModelDBMigrationEntity 34 | 35 | @end 36 | 37 | @implementation YTXRestfulModel 38 | 39 | - (instancetype)init 40 | { 41 | if(self = [super init]) 42 | { 43 | #ifdef YTX_USERDEFAULTSTORAGESYNC_EXISTS 44 | self.storageSync = [YTXRestfulModelUserDefaultStorageSync new]; 45 | #endif 46 | 47 | #ifdef YTX_AFNETWORKINGREMOTESYNC_EXISTS 48 | self.remoteSync = [AFNetworkingRemoteSync syncWithPrimaryKey: [[self class] syncPrimaryKey]]; 49 | #endif 50 | 51 | #ifdef YTX_FMDBSYNC_EXISTS 52 | self.dbSync = [YTXRestfulModelFMDBSync syncWithModelOfClass:[self class] primaryKey: [[self class] syncPrimaryKey]]; 53 | #endif 54 | } 55 | return self; 56 | } 57 | 58 | #pragma mark MTL 59 | - (NSDictionary *)dictionaryValue { 60 | NSDictionary *originalDictValue = [super dictionaryValue]; 61 | NSMutableDictionary *dictValue = [originalDictValue mutableCopy]; 62 | for (NSString *key in originalDictValue) { 63 | if ([self valueForKey:key] == nil) { 64 | [dictValue removeObjectForKey:key]; 65 | } 66 | } 67 | //删除这个2个不必要的属性 可以用http://stackoverflow.com/questions/18961622/how-to-omit-null-values-in-json-dictionary-using-mantle这些方法,但是最合理的还是这个。 68 | [dictValue removeObjectForKey:@"remoteSync"]; 69 | [dictValue removeObjectForKey:@"storageSync"]; 70 | [dictValue removeObjectForKey:@"dbSync"]; 71 | return [dictValue copy]; 72 | } 73 | 74 | 75 | //Mantle的model属性和目标源属性的映射表。DBSync也会使用。 76 | + (NSDictionary *)JSONKeyPathsByPropertyKey 77 | { 78 | return @{}; 79 | } 80 | 81 | #pragma mark EFSModelProtocol 82 | 83 | - (nonnull instancetype) mergeWithAnother:(_Nonnull id) model 84 | { 85 | if ([self class] != [model class]){ 86 | return self; 87 | } 88 | NSSet * keys = [[self class] propertyKeys]; 89 | 90 | for (NSString * key in keys) { 91 | id value = [model valueForKey:key]; 92 | if (value) { 93 | [self setValue:value forKey:key]; 94 | } 95 | } 96 | return self; 97 | } 98 | 99 | //主键名。在Model上的名字 100 | + (nonnull NSString *)primaryKey 101 | { 102 | return @"keyId"; 103 | } 104 | 105 | - (nullable id) primaryValue 106 | { 107 | return [self valueForKey:[[self class] primaryKey]]; 108 | } 109 | 110 | + (nonnull NSString *)syncPrimaryKey 111 | { 112 | return [self JSONKeyPathsByPropertyKey][[self primaryKey]] ?: [self primaryKey]; 113 | } 114 | 115 | /** 要用keyId判断 */ 116 | - (BOOL) isNew 117 | { 118 | return [self primaryValue] == nil; 119 | } 120 | 121 | 122 | #pragma mark storage 123 | /** GET */ 124 | - (nullable instancetype) fetchStorageSync:(nullable NSDictionary *) param 125 | { 126 | return [self fetchStorageSyncWithKey:[self storageKeyWithParam:param] param:param]; 127 | } 128 | 129 | /** POST / PUT */ 130 | - (nonnull instancetype) saveStorageSync:(nullable NSDictionary *) param 131 | { 132 | return [self saveStorageSyncWithKey:[self storageKeyWithParam:param] param:param]; 133 | } 134 | 135 | /** DELETE */ 136 | - (void) destroyStorageSync:(nullable NSDictionary *) param 137 | { 138 | return [self destroyStorageSyncWithKey:[self storageKeyWithParam:param] param:param]; 139 | } 140 | 141 | /** GET */ 142 | - (nullable instancetype) fetchStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param 143 | { 144 | NSDictionary * dict = [self.storageSync fetchStorageSyncWithKey:storage param:param]; 145 | if (dict) { 146 | NSError * error; 147 | [self transformerProxyOfResponse:dict error:&error]; 148 | if (!error) { 149 | return self; 150 | } 151 | 152 | } 153 | return nil; 154 | } 155 | 156 | /** POST / PUT */ 157 | - (nonnull instancetype) saveStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param 158 | { 159 | [self.storageSync saveStorageSyncWithKey:storage withObject:[self mergeSelfAndParameters:param] param:param]; 160 | return self; 161 | } 162 | 163 | /** DELETE */ 164 | - (void) destroyStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param 165 | { 166 | [self.storageSync destroyStorageSyncWithKey:storage param:param]; 167 | } 168 | 169 | 170 | - (nonnull NSString *) storageKeyWithParam:(nullable NSDictionary *) param 171 | { 172 | NSDictionary * dict = [self mergeSelfAndParameters:param]; 173 | id primaryKeyValue = dict[[[self class] syncPrimaryKey]]; 174 | 175 | NSAssert(primaryKeyValue != nil, @"Stroage必须找到主键的值"); 176 | 177 | return [NSString stringWithFormat:@"EFSModel+%@+%@", NSStringFromClass([self class]), [primaryKeyValue description]]; 178 | } 179 | 180 | #pragma mark remote 181 | /** 在拉到数据转mantle的时候用 */ 182 | - (nonnull instancetype) transformerProxyOfResponse:(nonnull id) response error:(NSError * _Nullable * _Nullable) error 183 | { 184 | return [self mergeWithAnother:[MTLJSONAdapter modelOfClass:[self class] fromJSONDictionary:response error:error]]; 185 | } 186 | 187 | - (nonnull NSDictionary *)mapParameters:(nonnull NSDictionary *)param 188 | { 189 | NSMutableDictionary * retDict = [NSMutableDictionary dictionary]; 190 | 191 | NSMutableDictionary *_JSONKeyPathsByPropertyKey = [[[self class] JSONKeyPathsByPropertyKey] copy]; 192 | 193 | NSString * mappedPropertyKey = nil; 194 | 195 | for (NSString *key in param) { 196 | 197 | mappedPropertyKey = _JSONKeyPathsByPropertyKey[key] ? : key; 198 | 199 | retDict[mappedPropertyKey] = param[key]; 200 | } 201 | 202 | return retDict; 203 | } 204 | 205 | - (nonnull NSDictionary *)mergeSelfAndParameters:(nullable NSDictionary *)param 206 | { 207 | NSDictionary * mapParam = [self mapParameters:param]; 208 | 209 | NSMutableDictionary *retDic = [[MTLJSONAdapter JSONDictionaryFromModel:self] mutableCopy]; 210 | 211 | for (NSString *key in mapParam) { 212 | retDic[key] = mapParam[key]; 213 | } 214 | return retDic; 215 | } 216 | 217 | - (nonnull NSDictionary *)mergeSelfPrimaryKeyAndParameters:(nullable NSDictionary *)param 218 | { 219 | NSDictionary * mapParam = [self mapParameters:param]; 220 | 221 | NSDictionary *dic = [MTLJSONAdapter JSONDictionaryFromModel:self]; 222 | 223 | NSMutableDictionary *retDic = [NSMutableDictionary dictionary]; 224 | 225 | if (dic[[[self class] syncPrimaryKey]] != nil) { 226 | retDic[[[self class] syncPrimaryKey]] = dic[[[self class] syncPrimaryKey]]; 227 | } 228 | 229 | for (NSString *key in mapParam) { 230 | retDic[key] = mapParam[key]; 231 | } 232 | return retDic; 233 | } 234 | 235 | - (nonnull id) transformerProxyOfForeign:(nonnull Class)modelClass response:(nonnull id) response error:(NSError * _Nullable * _Nullable) error; 236 | { 237 | return [MTLJSONAdapter modelsOfClass:modelClass fromJSONArray:response error:error]; 238 | } 239 | 240 | - (void) fetchRemoteForeignWithName:(nonnull NSString *)name modelClass:(nonnull Class)modelClass param:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 241 | { 242 | __weak __typeof(&*self)weakSelf = self; 243 | [self.remoteSync fetchRemoteForeignWithName:name param:[self mergeSelfAndParameters:param] success:^(id _Nullable response) { 244 | NSError * error = nil; 245 | id model = [weakSelf transformerProxyOfForeign:modelClass response:response error:&error]; 246 | if (!error) { 247 | success(model); 248 | } 249 | else { 250 | failed(error); 251 | } 252 | } failed:failed]; 253 | } 254 | 255 | - (void) fetchRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 256 | { 257 | __weak __typeof(&*self)weakSelf = self; 258 | [self.remoteSync fetchRemote:[self mergeSelfPrimaryKeyAndParameters:param] success:^(id _Nullable response) { 259 | NSError * error = nil; 260 | id model = [weakSelf transformerProxyOfResponse:response error:&error]; 261 | if (!error) { 262 | success(model); 263 | } 264 | else { 265 | failed(error); 266 | } 267 | } failed:failed]; 268 | } 269 | 270 | - (void) saveRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 271 | { 272 | __weak __typeof(&*self)weakSelf = self; 273 | if ([self isNew]) { 274 | [self.remoteSync createRemote:[self mergeSelfAndParameters:param] success:^(id _Nullable response) { 275 | NSError * error = nil; 276 | id model = [weakSelf transformerProxyOfResponse:response error:&error]; 277 | if (!error) { 278 | success(model); 279 | } 280 | else { 281 | failed(error); 282 | } 283 | } failed:failed]; 284 | } else { 285 | [self.remoteSync updateRemote:[self mergeSelfAndParameters:param] success:^(id _Nullable response) { 286 | NSError * error = nil; 287 | id model = [weakSelf transformerProxyOfResponse:response error:&error]; 288 | if (!error) { 289 | success(model); 290 | } 291 | else { 292 | failed(error); 293 | } 294 | } failed:failed]; 295 | } 296 | } 297 | 298 | - (void) destroyRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 299 | { 300 | __weak __typeof(&*self)weakSelf = self; 301 | [self.remoteSync destroyRemote:[self mergeSelfAndParameters:param] success:^(id _Nullable response) { 302 | NSError * error = nil; 303 | id model = [weakSelf transformerProxyOfResponse:response error:&error]; 304 | if (!error) { 305 | success(model); 306 | } 307 | else { 308 | failed(error); 309 | } 310 | } failed:failed]; 311 | } 312 | 313 | + (nonnull NSMutableDictionary *> *) _tableKeyPathsByPropertyKeyMap 314 | { 315 | static NSMutableDictionary *> * map; 316 | 317 | if (map == nil) { 318 | map = [NSMutableDictionary dictionary]; 319 | } 320 | 321 | return map; 322 | } 323 | 324 | + (nonnull NSString *) _tableKeyPathsCachedKey 325 | { 326 | return [NSString stringWithFormat:@"YTX.%@", NSStringFromClass([self class])]; 327 | } 328 | 329 | #pragma mark DB 330 | + (BOOL) isPrimaryKeyAutoincrement 331 | { 332 | return YES; 333 | } 334 | 335 | + (nullable NSMutableDictionary *) tableKeyPathsByPropertyKey 336 | { 337 | NSMutableDictionary * cachedKeys = [self _tableKeyPathsByPropertyKeyMap][[self _tableKeyPathsCachedKey]]; 338 | 339 | if (cachedKeys != nil) { 340 | return cachedKeys; 341 | } 342 | 343 | __block NSMutableDictionary * properties = [NSMutableDictionary dictionary]; 344 | 345 | #pragma clang diagnostic push 346 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 347 | 348 | [[self class] performSelector:NSSelectorFromString(@"enumeratePropertiesUsingBlock:") withObject:^(objc_property_t property, BOOL *stop) { 349 | mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); 350 | char *propertyType = property_copyAttributeValue(property, "T"); 351 | @onExit { 352 | free(attributes); 353 | free(propertyType); 354 | }; 355 | 356 | if (attributes->readonly && attributes->ivar == NULL) return; 357 | 358 | NSString *modelProperyName = [NSString stringWithUTF8String:property_getName(property)]; 359 | 360 | if ([modelProperyName isEqualToString:@"dbSync"] || [modelProperyName isEqualToString:@"remoteSync"] || [modelProperyName isEqualToString:@"storageSync"]) { 361 | return; 362 | } 363 | 364 | NSString *columnName = [self JSONKeyPathsByPropertyKey][modelProperyName] ? : modelProperyName; 365 | 366 | NSString * propertyClassName = [[self class] formateObjectType:propertyType]; 367 | 368 | BOOL isPrimaryKey = [modelProperyName isEqualToString:[self primaryKey]]; 369 | 370 | BOOL isPrimaryKeyAutoincrement = NO; 371 | if (isPrimaryKey) { 372 | isPrimaryKeyAutoincrement = [self isPrimaryKeyAutoincrement]; 373 | } 374 | 375 | YTXRestfulModelDBSerializingModel * dbsm = [YTXRestfulModelDBSerializingModel new]; 376 | dbsm.objectClass = propertyClassName; 377 | dbsm.columnName = columnName; 378 | dbsm.modelName = modelProperyName; 379 | dbsm.isPrimaryKey = isPrimaryKey; 380 | dbsm.autoincrement = isPrimaryKeyAutoincrement; 381 | dbsm.unique = NO; 382 | 383 | [properties setObject:dbsm forKey:columnName]; 384 | }]; 385 | #pragma clang diagnostic pop 386 | 387 | [self _tableKeyPathsByPropertyKeyMap][[self _tableKeyPathsCachedKey]] = properties; 388 | 389 | return properties; 390 | } 391 | 392 | + (nullable NSNumber *) currentMigrationVersion 393 | { 394 | return @0; 395 | } 396 | 397 | + (BOOL) autoAlterTable 398 | { 399 | return YES; 400 | } 401 | 402 | + (BOOL) autoCreateTable 403 | { 404 | return NO; 405 | } 406 | 407 | + (void) migrationsMethodWithSync:(nonnull id)sync; 408 | { 409 | 410 | } 411 | 412 | + (void)dbWillMigrateWithSync:(nonnull id)sync 413 | { 414 | 415 | } 416 | 417 | + (void)dbDidMigrateWithSync:(nonnull id)sync 418 | { 419 | 420 | } 421 | 422 | /** GET */ 423 | - (nonnull instancetype) fetchDBSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error 424 | { 425 | NSDictionary * x = [self.dbSync fetchOneSync:[self mergeSelfAndParameters:param] error:error]; 426 | 427 | if (x && *error == nil ) { 428 | [self transformerProxyOfResponse:x error:error]; 429 | } 430 | return self; 431 | } 432 | 433 | /** 434 | * POST / PUT 435 | * 数据库不存在时创建,否则更新 436 | * 更新必须带主键 437 | */ 438 | - (nonnull instancetype) saveDBSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error 439 | { 440 | NSDictionary * x = [self.dbSync saveOneSync:[self mergeSelfAndParameters:param] error:error]; 441 | 442 | if (x && *error == nil ) { 443 | [self transformerProxyOfResponse:x error:error]; 444 | } 445 | 446 | return self; 447 | } 448 | 449 | 450 | /** DELETE */ 451 | - (BOOL) destroyDBSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error 452 | { 453 | return [self.dbSync destroyOneSync:[self mergeSelfAndParameters:param] error:error]; 454 | } 455 | 456 | /** GET Foreign Models with primary key */ 457 | - (nonnull NSArray *) fetchDBForeignSyncWithModelClass:(nonnull Class)modelClass error:(NSError * _Nullable * _Nullable) error param:(nullable NSDictionary *)param 458 | { 459 | NSDictionary * dict = [self mergeSelfAndParameters:param]; 460 | 461 | id primaryKeyValue = dict[[[self class] syncPrimaryKey]]; 462 | 463 | NSAssert(primaryKeyValue != nil, @"主键的值不能为空"); 464 | 465 | NSArray * x = [self.dbSync fetchForeignSyncWithModelClass:modelClass primaryKeyValue:primaryKeyValue error:error param:dict]; 466 | 467 | return [self transformerProxyOfForeign:modelClass response:x error:error]; 468 | } 469 | 470 | #pragma mark - tools 471 | + (nonnull NSString*) formateObjectType:(const char* _Nonnull )objcType 472 | { 473 | if (!objcType || !strlen(objcType)) return nil; 474 | NSString* type = [NSString stringWithCString:objcType encoding:NSUTF8StringEncoding]; 475 | 476 | switch (objcType[0]) { 477 | case '@': 478 | type = [type substringWithRange:NSMakeRange(2, strlen(objcType)-3)]; 479 | break; 480 | case '{': 481 | type = [type substringWithRange:NSMakeRange(1, strchr(objcType, '=')-objcType-1)]; 482 | break; 483 | default: 484 | break; 485 | } 486 | return type; 487 | } 488 | 489 | 490 | - (id)storageSync 491 | { 492 | YTXAssertSyncExists(_storageSync, @"StorageSync"); 493 | return _storageSync; 494 | } 495 | 496 | -(id)dbSync 497 | { 498 | YTXAssertSyncExists(_dbSync, @"DBSync"); 499 | return _dbSync; 500 | } 501 | 502 | -(id)remoteSync 503 | { 504 | YTXAssertSyncExists(_remoteSync, @"RemoteSync"); 505 | return _remoteSync; 506 | } 507 | 508 | @end 509 | -------------------------------------------------------------------------------- /Pod/Classes/Protocol/YTXRestfulModelDBProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModelDBProtocol.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/1/19. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger { 12 | YTXRestfulModelDBErrorCodeNotFound = 0, 13 | YTXRestfulModelDBErrorCodeNotColumns = 1, 14 | YTXRestfulModelDBErrorCodeUnkonw = 9999 15 | } YTXRestfulModelDBErrorCode; 16 | 17 | typedef enum : NSUInteger { 18 | //升序 19 | YTXRestfulModelDBSortByDESC, 20 | //降序 21 | YTXRestfulModelDBSortByASC 22 | } YTXRestfulModelDBSortBy; 23 | 24 | @interface YTXRestfulModelDBSerializingModel : NSObject 25 | 26 | /** 可以是CType @"d"这种*/ 27 | @property (nonatomic, nonnull, copy) NSString * objectClass; 28 | 29 | /** 表名 */ 30 | @property (nonatomic, nonnull, copy) NSString * columnName; 31 | 32 | /** Model原始的属性名字 */ 33 | @property (nonatomic, nonnull, copy) NSString * modelName; 34 | 35 | @property (nonatomic, assign) BOOL isPrimaryKey; 36 | 37 | @property (nonatomic, assign) BOOL autoincrement; 38 | 39 | @property (nonatomic, assign) BOOL unique; 40 | 41 | @property (nonatomic, nonnull, copy) NSString * defaultValue; 42 | 43 | /** 外键类名 可以使用fetchForeignWithName */ 44 | @property (nonatomic, nonnull, copy) NSString * foreignClassName; 45 | 46 | @end 47 | 48 | @protocol YTXRestfulModelDBProtocol; 49 | 50 | @protocol YTXRestfulModelDBSerializing 51 | 52 | /** NSDictionary */ 53 | + (nullable NSMutableDictionary *) tableKeyPathsByPropertyKey; 54 | 55 | + (nullable NSNumber *) currentMigrationVersion; 56 | 57 | + (BOOL) autoAlterTable; 58 | 59 | + (BOOL) autoCreateTable; 60 | 61 | + (BOOL) isPrimaryKeyAutoincrement; 62 | 63 | + (void) migrationsMethodWithSync:(nonnull id)sync; 64 | 65 | @optional 66 | /** 在这个方法内migrateWithVersion*/ 67 | + (void) dbWillMigrateWithSync:(nonnull id)sync; 68 | 69 | + (void) dbDidMigrateWithSync:(nonnull id)sync; 70 | 71 | @end 72 | 73 | 74 | typedef void (^YTXRestfulModelMigrationBlock)(_Nonnull id, NSError * _Nullable * _Nullable); 75 | 76 | @interface YTXRestfulModelDBMigrationEntity : NSObject 77 | 78 | @property (nonnull, nonatomic, copy) YTXRestfulModelMigrationBlock block; 79 | @property (nonnull, nonatomic, copy) NSNumber *version; 80 | 81 | @end 82 | 83 | @protocol YTXRestfulModelDBProtocol 84 | 85 | @required 86 | 87 | @property (nonatomic, assign, readonly, nonnull) Class modelClass; 88 | 89 | @property (nonnull, nonatomic, copy, readonly) NSString * primaryKey; 90 | 91 | + (nonnull instancetype) syncWithModelOfClass:(nonnull Class) modelClass primaryKey:(nonnull NSString *) key; 92 | 93 | + (nonnull NSString *) path; 94 | 95 | - (nonnull NSString *) tableName; 96 | 97 | - (nonnull instancetype) initWithModelOfClass:(nonnull Class) modelClass primaryKey:(nonnull NSString *) key; 98 | 99 | - (nonnull NSError *) createTable; 100 | 101 | - (nonnull NSError *) dropTable; 102 | 103 | //操作将会保证在migration之后进行 104 | 105 | /** GET Model with primary key */ 106 | - (nullable NSDictionary *) fetchOneSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 107 | 108 | /** POST / PUT Model with primary key */ 109 | - (nullable NSDictionary *) saveOneSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 110 | 111 | /** DELETE Model with primary key */ 112 | - (BOOL) destroyOneSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 113 | 114 | /** GET */ 115 | - (nullable NSDictionary *) fetchTopOneSyncWithError:(NSError * _Nullable * _Nullable) error; 116 | 117 | /** GET */ 118 | - (nullable NSDictionary *) fetchLatestOneSyncWithError:(NSError * _Nullable * _Nullable) error; 119 | 120 | /** DELETE All Model with primary key */ 121 | - (BOOL) destroyAllSyncWithError:(NSError * _Nullable * _Nullable) error; 122 | 123 | /** GET Foreign Models with primary key */ 124 | - (nonnull NSArray *) fetchForeignSyncWithModelClass:(nonnull Class)modelClass primaryKeyValue:(nonnull id) value error:(NSError * _Nullable * _Nullable) error param:(nullable NSDictionary *)param; 125 | 126 | /** ORDER BY primaryKey ASC*/ 127 | - (nonnull NSArray *) fetchAllSyncWithError:(NSError * _Nullable * _Nullable) error; 128 | 129 | 130 | - (nonnull NSArray *) fetchAllSyncWithError:(NSError * _Nullable * _Nullable)error soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSArray * )columnNames; 131 | 132 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error start:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSArray * )columnNames; 133 | 134 | /** 135 | * ORDER BY primaryKey ASC 136 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' AND old >= 10 137 | */ 138 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMet:(nonnull NSArray * )conditions; 139 | 140 | /** 141 | * ORDER BY primaryKey ASC 142 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' AND old >= 10 143 | */ 144 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSArray * )conditions; 145 | 146 | /** 147 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' AND old >= 10 148 | */ 149 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSArray * )conditions; 150 | 151 | /** 152 | * ORDER BY primaryKey ASC 153 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' OR old >= 10 154 | */ 155 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMet:(nonnull NSArray * )conditions; 156 | 157 | /** 158 | * ORDER BY primaryKey ASC 159 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' OR old >= 10 160 | */ 161 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSArray * )conditions; 162 | 163 | /** 164 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' OR old >= 10 165 | */ 166 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSArray * )conditions; 167 | 168 | 169 | //Migration 170 | 171 | /** 数字越大越后面执行*/ 172 | 173 | @property (nonatomic, strong, readonly, nonnull) NSMutableArray * migrationBlocks; 174 | 175 | /** 大于currentMigrationVersion将会依次执行,数字越大越后执行*/ 176 | - (void) migrate:(nonnull YTXRestfulModelDBMigrationEntity *) entity; 177 | 178 | - (BOOL) createColumnWithDB:(nonnull id)db structSync:(nonnull YTXRestfulModelDBSerializingModel *)sstruct error:(NSError * _Nullable * _Nullable)error; 179 | 180 | @optional 181 | 182 | - (BOOL) renameColumnWithDB:(nonnull id)db originName:(nonnull NSString *)originName newName:(nonnull NSString *)newName error:(NSError * _Nullable * _Nullable)error; 183 | 184 | - (BOOL) dropColumnWithDB:(nonnull id)db structSync:(nonnull YTXRestfulModelDBSerializingModel *)sstruct error:(NSError * _Nullable * _Nullable)error; 185 | 186 | - (BOOL) changeCollumnDB:(nonnull id)db oldStructSync:(nonnull YTXRestfulModelDBSerializingModel *) oldStruct toNewStruct:(nonnull YTXRestfulModelDBSerializingModel *) newStruct error:(NSError * _Nullable * _Nullable)error; 187 | 188 | @end 189 | -------------------------------------------------------------------------------- /Pod/Classes/Protocol/YTXRestfulModelProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModelProtocol.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/1/25. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //尽量想写成Restful 12 | @protocol YTXRestfulModelProtocol 13 | 14 | @required 15 | - (nonnull instancetype) mergeWithAnother:(_Nonnull id) model; 16 | 17 | /** 需要告诉我主键PrimaryKey是什么 */ 18 | + (nonnull NSString *) primaryKey; 19 | 20 | + (nonnull NSString *)syncPrimaryKey; 21 | /** 知道主键后我可以方便的直接取Value */ 22 | - (nullable id) primaryValue; 23 | /** 要用keyId判断 */ 24 | - (BOOL) isNew; 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Pod/Classes/Protocol/YTXRestfulModelRemoteProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModelRemoteProtocol.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/1/25. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NSDictionary * _Nonnull(^YTXRestfulModelRemoteHookExtraParamBlock)(); 12 | typedef void(^YTXRestfulModelRemoteHookRequestBlock)(_Nonnull id request, NSString * _Nonnull method, NSURL * _Nonnull *_Nonnull url, NSMutableDictionary * _Nonnull * _Nonnull parameters); 13 | 14 | typedef void (^YTXRestfulModelRemoteSuccessBlock)(id _Nullable response); 15 | typedef void (^YTXRestfulModelRemoteFailedBlock)(NSError * _Nullable error); 16 | 17 | @protocol YTXRestfulModelRemoteProtocol 18 | 19 | @required 20 | 21 | /** 超时时间 */ 22 | @property (nonatomic, assign) NSTimeInterval timeoutInterval; 23 | 24 | @property (nonnull, nonatomic, strong) NSURL * url; 25 | 26 | /** 设置网络请求的地址,通过Block形式,每次访问都会重新执行,以处理shared中URL会变的情况。同时使用URL和URLBlock会优先使用Block */ 27 | @property (nullable, nonatomic, strong) NSURL * _Nonnull (^urlHookBlock)(void); 28 | 29 | @property (nonnull, nonatomic, copy, readonly) NSString * primaryKey; 30 | 31 | + (nonnull instancetype) syncWithURL:(nonnull NSURL *)URL primaryKey:(nonnull NSString *) primaryKey; 32 | 33 | + (nonnull instancetype) syncWithPrimaryKey:(nonnull NSString *) primaryKey; 34 | 35 | + (nullable YTXRestfulModelRemoteHookExtraParamBlock) hookExtraParamBlock; 36 | 37 | + (void) setHookExtraParamBlock:(nullable YTXRestfulModelRemoteHookExtraParamBlock) hookExtraParamBlock; 38 | 39 | + (nullable YTXRestfulModelRemoteHookRequestBlock) hookRequestBlock; 40 | 41 | + (void) setHookRequestBlock:(nullable YTXRestfulModelRemoteHookRequestBlock) hookRequestBlock; 42 | 43 | - (nonnull instancetype) initWithURL:(nonnull NSURL *)URL primaryKey:(nonnull NSString *) primaryKey; 44 | 45 | - (nonnull instancetype) initWithPrimaryKey:(nonnull NSString *) primaryKey; 46 | 47 | /** GET :id/commont */ 48 | - (void) fetchRemoteForeignWithName:(nonnull NSString *)name param:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 49 | 50 | /** GET */ 51 | - (void) fetchRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 52 | 53 | /** POST */ 54 | - (void) createRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 55 | 56 | /** put */ 57 | - (void) updateRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 58 | 59 | /** DELETE */ 60 | - (void) destroyRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Pod/Classes/Protocol/YTXRestfulModelStorageProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModelStorageProtocol.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/1/25. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol YTXRestfulModelStorageProtocol 13 | 14 | @required 15 | 16 | /** GET */ 17 | - (nullable id) fetchStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 18 | 19 | /** POST / PUT */ 20 | - (nullable id) saveStorageSyncWithKey:(nonnull NSString *)storage withObject:(nonnull id)object param:(nullable NSDictionary *) param; 21 | 22 | /** DELETE */ 23 | - (void) destroyStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 24 | 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Pod/Classes/RACSupport/YTXRestfulCollection+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulCollection+RACSupport.h 3 | // Pods 4 | // 5 | // Created by Chuan on 4/11/16. 6 | // 7 | // 8 | 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | @interface YTXRestfulCollection (RACSupport) 15 | 16 | #pragma mark - remote 17 | /* RACSignal return self **/ 18 | - (nonnull RACSignal *) rac_fetchRemote:(nullable NSDictionary *)param; 19 | 20 | /* RACSignal return self **/ 21 | - (nonnull RACSignal *) rac_fetchRemoteThenAdd:(nullable NSDictionary *)param; 22 | 23 | #pragma mark - storage 24 | /** GET */ 25 | - (nonnull RACSignal *) rac_fetchStorage:(nullable NSDictionary *)param; 26 | 27 | /** POST / PUT */ 28 | - (nonnull RACSignal *) rac_saveStorage:(nullable NSDictionary *)param; 29 | 30 | /** DELETE */ 31 | - (nonnull RACSignal *) rac_destroyStorage:(nullable NSDictionary *)param; 32 | 33 | /** GET */ 34 | - (nonnull RACSignal *) rac_fetchStorageWithKey:(nonnull NSString *)storageKey param:(nullable NSDictionary *)param; 35 | 36 | /** POST / PUT */ 37 | - (nonnull RACSignal *) rac_saveStorageWithKey:(nonnull NSString *)storageKey param:(nullable NSDictionary *)param; 38 | 39 | /** DELETE */ 40 | - (nonnull RACSignal *) rac_destroyStorageWithKey:(nonnull NSString *)storageKey param:(nullable NSDictionary *)param; 41 | 42 | #pragma mark - db 43 | - (nonnull RACSignal *) rac_fetchDBAll; 44 | 45 | - (nonnull RACSignal *) rac_fetchDBAllSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )columnName, ...; 46 | 47 | - (nonnull RACSignal *) rac_fetchDBMultipleWith:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )columnName, ...; 48 | 49 | - (nonnull RACSignal *) rac_fetchDBMultipleWhereAllTheConditionsAreMet:(nonnull NSString * )condition, ...; 50 | 51 | - (nonnull RACSignal *) rac_fetchDBMultipleWhereAllTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSString * )condition, ...; 52 | 53 | - (nonnull RACSignal *) rac_fetchDBMultipleWhereAllTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSString * )condition, ...; 54 | 55 | - (nonnull RACSignal *) rac_fetchDBMultipleWherePartOfTheConditionsAreMet:(nonnull NSString * )condition, ...; 56 | 57 | - (nonnull RACSignal *) rac_fetchDBMultipleWherePartOfTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSString * )condition, ...; 58 | 59 | - (nonnull RACSignal *) rac_fetchDBMultipleWherePartOfTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSString * )condition, ...; 60 | 61 | /* RACSignal return BOOL **/ 62 | - (nonnull RACSignal *) rac_destroyDBAll; 63 | @end 64 | -------------------------------------------------------------------------------- /Pod/Classes/RACSupport/YTXRestfulCollection+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulCollection+RACSupport.m 3 | // Pods 4 | // 5 | // Created by Chuan on 4/11/16. 6 | // 7 | // 8 | 9 | #import "YTXRestfulCollection+RACSupport.h" 10 | 11 | @implementation YTXRestfulCollection (RACSupport) 12 | 13 | #pragma mark - remote 14 | 15 | - (nonnull RACSignal *) rac_fetchRemote:(nullable NSDictionary *)param 16 | { 17 | RACSubject * subject = [RACSubject subject]; 18 | [self fetchRemote:param success:^(id _Nullable response) { 19 | [subject sendNext:response]; 20 | [subject sendCompleted]; 21 | } failed:^(NSError * _Nullable error) { 22 | [subject sendError:error]; 23 | }]; 24 | return subject; 25 | } 26 | 27 | - (nonnull RACSignal *) rac_fetchRemoteThenAdd:(nullable NSDictionary *)param 28 | { 29 | RACSubject * subject = [RACSubject subject]; 30 | [self fetchRemoteThenAdd:param success:^(id _Nullable response) { 31 | [subject sendNext:response]; 32 | [subject sendCompleted]; 33 | } failed:^(NSError * _Nullable error) { 34 | [subject sendError:error]; 35 | }]; 36 | return subject; 37 | } 38 | 39 | #pragma mark - storage 40 | 41 | /** GET */ 42 | - (nonnull RACSignal *) rac_fetchStorage:(nullable NSDictionary *)param 43 | { 44 | return [self rac_fetchStorageWithKey:[self storageKey] param:param]; 45 | } 46 | 47 | /** POST / PUT */ 48 | - (nonnull RACSignal *) rac_saveStorage:(nullable NSDictionary *)param 49 | { 50 | return [self rac_saveStorageWithKey:[self storageKey] param:param]; 51 | } 52 | 53 | /** DELETE */ 54 | - (nonnull RACSignal *) rac_destroyStorage:(nullable NSDictionary *)param 55 | { 56 | return [self rac_destroyStorageWithKey:[self storageKey] param:param]; 57 | } 58 | 59 | - (nonnull RACSignal *) rac_fetchStorageWithKey:(NSString *)storageKey param:(NSDictionary *)param 60 | { 61 | NSArray * x = [self.storageSync fetchStorageSyncWithKey:storageKey param:param]; 62 | NSError * error = nil; 63 | if (x) { 64 | NSArray * ret = [self transformerProxyOfResponse:x error:&error]; 65 | [self resetModels:ret]; 66 | } 67 | else { 68 | error = [NSError errorWithDomain:NSStringFromClass([self class]) code:404 userInfo:nil]; 69 | } 70 | 71 | 72 | @weakify(self); 73 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 74 | @strongify(self); 75 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 76 | if (!error) { 77 | [subscriber sendNext:self]; 78 | [subscriber sendCompleted]; 79 | } 80 | else { 81 | [subscriber sendError:error]; 82 | } 83 | }); 84 | 85 | return nil; 86 | }]; 87 | } 88 | 89 | - (nonnull RACSignal *) rac_saveStorageWithKey:(NSString *)storageKey param:(NSDictionary *)param 90 | { 91 | [self.storageSync saveStorageSyncWithKey:storageKey withObject:[self transformerProxyOfModels:[self.models copy]] param:param]; 92 | @weakify(self); 93 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 94 | @strongify(self); 95 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 96 | [subscriber sendNext:self]; 97 | [subscriber sendCompleted]; 98 | }); 99 | 100 | return nil; 101 | }]; 102 | } 103 | 104 | - (nonnull RACSignal *) rac_destroyStorageWithKey:(NSString *)storageKey param:(NSDictionary *)param 105 | { 106 | [self.storageSync destroyStorageSyncWithKey:storageKey param:param]; 107 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 108 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 109 | [subscriber sendNext:nil]; 110 | [subscriber sendCompleted]; 111 | }); 112 | 113 | return nil; 114 | }]; 115 | } 116 | 117 | #pragma mark - db 118 | - (nonnull RACSignal *) rac_fetchDBAll 119 | { 120 | NSError * error = nil; 121 | return [self _createRACSingalWithNext:[self fetchDBSyncAllWithError:&error] error:error]; 122 | } 123 | 124 | - (nonnull RACSignal *) rac_fetchDBAllSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )columnName, ... 125 | { 126 | NSError * error = nil; 127 | va_list args; 128 | va_start(args, columnName); 129 | 130 | NSArray * columnNames = [self arrayWithArgs:args firstArgument:columnName]; 131 | 132 | va_end(args); 133 | 134 | columnNames = [self arrayOfMappedArgsWithOriginArray:columnNames]; 135 | 136 | return [self _createRACSingalWithNext:[self fetchDBSyncAllWithError:&error soryBy:sortBy orderByColumnNames:columnNames] error:error]; 137 | } 138 | 139 | - (nonnull RACSignal *) rac_fetchDBMultipleWith:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )columnName, ... 140 | { 141 | NSError * error = nil; 142 | va_list args; 143 | va_start(args, columnName); 144 | 145 | NSArray * columnNames = [self arrayWithArgs:args firstArgument:columnName]; 146 | 147 | va_end(args); 148 | 149 | columnNames = [self arrayOfMappedArgsWithOriginArray:columnNames]; 150 | 151 | return [self _createRACSingalWithNext:[self fetchDBSyncMultipleWithError:&error start:start count:count soryBy:sortBy orderByColumnNames:columnNames] error:error]; 152 | } 153 | 154 | - (nonnull RACSignal *) rac_fetchDBMultipleWhereAllTheConditionsAreMet:(nonnull NSString * )condition, ... 155 | { 156 | va_list args; 157 | va_start(args, condition); 158 | 159 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 160 | 161 | va_end(args); 162 | 163 | NSError * error = nil; 164 | return [self _createRACSingalWithNext:[self fetchDBSyncMultipleWithError:&error whereAllTheConditionsAreMetConditions:conditions] error:error]; 165 | } 166 | 167 | - (nonnull RACSignal *) rac_fetchDBMultipleWhereAllTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSString * )condition, ... 168 | { 169 | va_list args; 170 | va_start(args, condition); 171 | 172 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 173 | 174 | va_end(args); 175 | 176 | NSError * error = nil; 177 | return [self _createRACSingalWithNext:[self fetchDBSyncMultipleWithError:&error whereAllTheConditionsAreMetWithSoryBy:sortBy orderBy:orderBy conditionsArray:conditions] error:error]; 178 | } 179 | 180 | - (nonnull RACSignal *) rac_fetchDBMultipleWhereAllTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSString * )condition, ... 181 | { 182 | va_list args; 183 | va_start(args, condition); 184 | 185 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 186 | 187 | va_end(args); 188 | 189 | NSError * error = nil; 190 | return [self _createRACSingalWithNext:[self fetchDBSyncMultipleWithError:&error whereAllTheConditionsAreMetWithStart:start count:count soryBy:sortBy orderBy:orderBy conditionsArray:conditions] error:error]; 191 | } 192 | 193 | - (nonnull RACSignal *) rac_fetchDBMultipleWherePartOfTheConditionsAreMet:(nonnull NSString * )condition, ... 194 | { 195 | va_list args; 196 | va_start(args, condition); 197 | 198 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 199 | 200 | va_end(args); 201 | 202 | NSError * error = nil; 203 | return [self _createRACSingalWithNext:[self fetchDBSyncMultipleWithError:&error wherePartOfTheConditionsAreMetConditionsArray:conditions] error:error]; 204 | } 205 | 206 | - (nonnull RACSignal *) rac_fetchDBMultipleWherePartOfTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSString * )condition, ... 207 | { 208 | va_list args; 209 | va_start(args, condition); 210 | 211 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 212 | 213 | va_end(args); 214 | 215 | NSError * error = nil; 216 | return [self _createRACSingalWithNext:[self fetchDBSyncMultipleWithError:&error wherePartOfTheConditionsAreMetWithSoryBy:sortBy orderBy:orderBy conditionsArray:conditions] error:error]; 217 | } 218 | 219 | - (nonnull RACSignal *) rac_fetchDBMultipleWherePartOfTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSString * )condition, ... 220 | { 221 | va_list args; 222 | va_start(args, condition); 223 | 224 | NSArray * conditions = [self arrayWithArgs:args firstArgument:condition]; 225 | 226 | va_end(args); 227 | 228 | NSError * error = nil; 229 | return [self _createRACSingalWithNext:[self fetchDBSyncMultipleWithError:&error wherePartOfTheConditionsAreMetWithStart:start count:count soryBy:sortBy orderBy:orderBy conditionsArray:conditions] error:error]; 230 | } 231 | 232 | /* RACSignal return BOOL **/ 233 | - (nonnull RACSignal *) rac_destroyDBAll 234 | { 235 | NSError * error = nil; 236 | return [self _createRACSingalWithNext:@([self destroyDBSyncAllWithError:&error]) error:error]; 237 | } 238 | 239 | - (nonnull RACSignal *) _createRACSingalWithNext:(id) ret error:(nullable NSError *) error 240 | { 241 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 242 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 243 | if (error) { 244 | [subscriber sendError:error]; 245 | return; 246 | } 247 | [subscriber sendNext:ret]; 248 | [subscriber sendCompleted]; 249 | }); 250 | return nil; 251 | }]; 252 | } 253 | @end 254 | -------------------------------------------------------------------------------- /Pod/Classes/RACSupport/YTXRestfulModel+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModel+RACSupport.h 3 | // Pods 4 | // 5 | // Created by Chuan on 4/11/16. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface YTXRestfulModel (RACSupport) 14 | 15 | #pragma mark - remote 16 | /** :id/comment 这种形式的时候使用GET; modelClass is MTLModel*/ 17 | - (nonnull RACSignal *) rac_fetchRemoteForeignWithName:(nonnull NSString *)name modelClass:(nonnull Class)modelClass param:(nullable NSDictionary *)param; 18 | /** GET */ 19 | - (nonnull RACSignal *) rac_fetchRemote:(nullable NSDictionary *)param; 20 | /** POST / PUT */ 21 | - (nonnull RACSignal *) rac_saveRemote:(nullable NSDictionary *)param; 22 | /** DELETE */ 23 | - (nonnull RACSignal *) rac_destroyRemote:(nullable NSDictionary *)param; 24 | 25 | #pragma mark - storage 26 | 27 | - (nonnull RACSignal *) rac_fetchStorage:(nullable NSDictionary *)param; 28 | 29 | - (nonnull RACSignal *) rac_saveStorage:(nullable NSDictionary *)param; 30 | /** DELETE */ 31 | - (nonnull RACSignal *) rac_destroyStorage:(nullable NSDictionary *)param; 32 | 33 | - (nonnull RACSignal *) rac_fetchStorageWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *)param; 34 | 35 | - (nonnull RACSignal *) rac_saveStorageWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *)param; 36 | /** DELETE */ 37 | - (nonnull RACSignal *) rac_destroyStorageWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *)param; 38 | 39 | #pragma mark - db 40 | 41 | /** GET */ 42 | - (nonnull RACSignal *) rac_fetchDB:(nullable NSDictionary *)param; 43 | /** 44 | * POST / PUT 45 | * 数据库不存在时创建,否则更新 46 | * 更新必须带主键 47 | */ 48 | - (nonnull RACSignal *) rac_saveDB:(nullable NSDictionary *)param; 49 | /** DELETE */ 50 | - (nonnull RACSignal *) rac_destroyDB:(nullable NSDictionary *)param; 51 | /** GET Foreign Models with primary key */ 52 | - (nonnull RACSignal *) rac_fetchDBForeignWithModelClass:(nonnull Class)modelClass param:(nullable NSDictionary *)param; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Pod/Classes/RACSupport/YTXRestfulModel+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModel+RACSupport.m 3 | // Pods 4 | // 5 | // Created by Chuan on 4/11/16. 6 | // 7 | // 8 | 9 | #import "YTXRestfulModel+RACSupport.h" 10 | 11 | @implementation YTXRestfulModel (RACSupport) 12 | 13 | # pragma mark - remote 14 | 15 | - (nonnull RACSignal *) rac_fetchRemoteForeignWithName:(nonnull NSString *)name modelClass:(nonnull Class)modelClass param:(nullable NSDictionary *)param; 16 | { 17 | NSAssert([modelClass isSubclassOfClass: [MTLModel class] ], @"希望传入的class是MTLModel的子类,这样才能使用mantle转换"); 18 | RACSubject * subject = [RACSubject subject]; 19 | [self fetchRemoteForeignWithName:name modelClass:modelClass param:param success:^(id _Nullable response) { 20 | [subject sendNext:response]; 21 | [subject sendCompleted]; 22 | } failed:^(NSError * _Nullable error) { 23 | [subject sendError:error]; 24 | }]; 25 | return subject; 26 | } 27 | 28 | - (nonnull RACSignal *) rac_fetchRemote:(nullable NSDictionary *)param 29 | { 30 | RACSubject * subject = [RACSubject subject]; 31 | [self fetchRemote:param success:^(id _Nullable response) { 32 | [subject sendNext:response]; 33 | [subject sendCompleted]; 34 | } failed:^(NSError * _Nullable error) { 35 | [subject sendError:error]; 36 | }]; 37 | return subject; 38 | } 39 | 40 | - (nonnull RACSignal *) rac_saveRemote:(nullable NSDictionary *)param 41 | { 42 | RACSubject * subject = [RACSubject subject]; 43 | [self saveRemote:param success:^(id _Nullable response) { 44 | [subject sendNext:response]; 45 | [subject sendCompleted]; 46 | } failed:^(NSError * _Nullable error) { 47 | [subject sendError:error]; 48 | }]; 49 | return subject; 50 | } 51 | 52 | - (nonnull RACSignal *) rac_destroyRemote:(nullable NSDictionary *)param 53 | { 54 | RACSubject * subject = [RACSubject subject]; 55 | [self destroyRemote:param success:^(id _Nullable response) { 56 | [subject sendNext:response]; 57 | [subject sendCompleted]; 58 | } failed:^(NSError * _Nullable error) { 59 | [subject sendError:error]; 60 | }]; 61 | return subject; 62 | } 63 | 64 | # pragma mark - storage 65 | 66 | - (nonnull RACSignal *) rac_fetchStorage:(nullable NSDictionary *)param 67 | { 68 | return [self rac_fetchStorageWithKey:[self storageKeyWithParam:param] param:param]; 69 | } 70 | 71 | - (nonnull RACSignal *) rac_saveStorage:(nullable NSDictionary *)param 72 | { 73 | return [self rac_saveStorageWithKey:[self storageKeyWithParam:param] param:param]; 74 | } 75 | 76 | /** DELETE */ 77 | - (nonnull RACSignal *) rac_destroyStorage:(nullable NSDictionary *)param 78 | { 79 | return [self rac_destroyStorageWithKey:[self storageKeyWithParam:param] param:param]; 80 | } 81 | 82 | - (nonnull RACSignal *) rac_fetchStorageWithKey:(NSString *)storage param:(NSDictionary *)param 83 | { 84 | NSDictionary * dict = [self.storageSync fetchStorageSyncWithKey:storage param:param]; 85 | NSError * error = nil; 86 | if (dict) { 87 | [self transformerProxyOfResponse:dict error:&error]; 88 | } 89 | else { 90 | error = [NSError errorWithDomain:NSStringFromClass([self class]) code:404 userInfo:nil]; 91 | } 92 | @weakify(self); 93 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 94 | @strongify(self); 95 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 96 | if (!error) { 97 | [subscriber sendNext:self]; 98 | [subscriber sendCompleted]; 99 | } 100 | else { 101 | [subscriber sendError:error]; 102 | } 103 | }); 104 | 105 | return nil; 106 | }]; 107 | } 108 | 109 | - (nonnull RACSignal *) rac_saveStorageWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *)param 110 | { 111 | id object = [self mergeSelfAndParameters:param]; 112 | [self.storageSync saveStorageSyncWithKey:storage withObject:object param:param]; 113 | @weakify(self); 114 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 115 | @strongify(self); 116 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 117 | [subscriber sendNext:self]; 118 | [subscriber sendCompleted]; 119 | }); 120 | 121 | return nil; 122 | }]; 123 | } 124 | 125 | - (nonnull RACSignal *) rac_destroyStorageWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *)param 126 | { 127 | [self.storageSync destroyStorageSyncWithKey:storage param:param]; 128 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 129 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 130 | [subscriber sendNext:nil]; 131 | [subscriber sendCompleted]; 132 | }); 133 | 134 | return nil; 135 | }]; 136 | } 137 | 138 | #pragma mark - db 139 | 140 | /** GET */ 141 | - (nonnull RACSignal *) rac_fetchDB:(nullable NSDictionary *)param 142 | { 143 | NSError * error = nil; 144 | return [self _createRACSingalWithNext:[self fetchDBSync:param error:&error] error:error]; 145 | } 146 | 147 | /** 148 | * POST / PUT 149 | * 数据库不存在时创建,否则更新 150 | * 更新必须带主键 151 | */ 152 | - (nonnull RACSignal *) rac_saveDB:(nullable NSDictionary *)param 153 | { 154 | NSError * error = nil; 155 | return [self _createRACSingalWithNext:[self saveDBSync:param error:&error] error:error]; 156 | } 157 | 158 | /** DELETE */ 159 | - (nonnull RACSignal *) rac_destroyDB:(nullable NSDictionary *)param 160 | { 161 | NSError * error = nil; 162 | return [self _createRACSingalWithNext:@([self destroyDBSync:param error:&error]) error:error]; 163 | } 164 | 165 | /** GET Foreign Models with primary key */ 166 | - (nonnull RACSignal *) rac_fetchDBForeignWithModelClass:(nonnull Class)modelClass param:(nullable NSDictionary *)param 167 | { 168 | NSError * error; 169 | NSArray * ret = [self fetchDBForeignSyncWithModelClass:modelClass error:&error param:param]; 170 | return [self _createRACSingalWithNext:ret error:error]; 171 | } 172 | 173 | 174 | - (nonnull RACSignal *) _createRACSingalWithNext:(id) ret error:(nullable NSError *) error 175 | { 176 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 177 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 178 | if (error) { 179 | [subscriber sendError:error]; 180 | return; 181 | } 182 | [subscriber sendNext:ret]; 183 | [subscriber sendCompleted]; 184 | }); 185 | return nil; 186 | }]; 187 | } 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /Pod/Classes/RACSupport/YTXRestfulModelRACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModelRACSupport.h 3 | // Pods 4 | // 5 | // Created by Chuan on 4/11/16. 6 | // 7 | // 8 | 9 | #ifndef YTXRestfulModelRACSupport_h 10 | #define YTXRestfulModelRACSupport_h 11 | 12 | #import 13 | #import 14 | 15 | #endif /* YTXRestfulModelRACSupport_h */ 16 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/AFNetworkingRemoteSync/AFNetworkingRemoteSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // AFNetworkingRemoteSync.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/3/4. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import "YTXRestfulModelRemoteProtocol.h" 10 | 11 | #import 12 | 13 | @class AFHTTPSessionManager; 14 | 15 | @interface AFNetworkingRemoteSync : NSObject 16 | 17 | @property (nonnull, nonatomic, strong, readonly) AFHTTPSessionManager * requestSessionManager; 18 | 19 | /** 超时时间 默认60 */ 20 | @property (nonatomic, assign) NSTimeInterval timeoutInterval; 21 | 22 | @property (nonnull, nonatomic, strong) NSURL * url; 23 | 24 | /** 设置网络请求的地址,通过Block形式,每次访问都会重新执行,以处理shared中URL会变的情况。同时使用URL和URLBlock会优先使用Block */ 25 | @property (nullable, nonatomic, strong) NSURL * _Nonnull (^urlHookBlock)(void); 26 | 27 | @property (nonnull, nonatomic, copy, readonly) NSString * primaryKey; 28 | 29 | + (nonnull instancetype) syncWithURL:(nonnull NSURL *)URL primaryKey:(nonnull NSString *) primaryKey; 30 | 31 | + (nonnull instancetype) syncWithPrimaryKey:(nonnull NSString *) primaryKey; 32 | 33 | + (nullable YTXRestfulModelRemoteHookExtraParamBlock) hookExtraParamBlock; 34 | 35 | + (void) setHookExtraParamBlock:(nullable YTXRestfulModelRemoteHookExtraParamBlock) hookExtraParamBlock; 36 | 37 | + (nullable YTXRestfulModelRemoteHookRequestBlock) hookRequestBlock; 38 | 39 | + (void) setHookRequestBlock:(nullable YTXRestfulModelRemoteHookRequestBlock) hookRequestBlock; 40 | 41 | - (nonnull instancetype) initWithURL:(nonnull NSURL *)URL primaryKey:(nonnull NSString *) primaryKey; 42 | 43 | - (nonnull instancetype) initWithPrimaryKey:(nonnull NSString *) primaryKey; 44 | 45 | /** GET :id/commont */ 46 | - (void) fetchRemoteForeignWithName:(nonnull NSString *)name param:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 47 | 48 | /** GET */ 49 | - (void) fetchRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 50 | 51 | /** POST */ 52 | - (void) createRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 53 | 54 | /** put */ 55 | - (void) updateRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 56 | 57 | /** DELETE */ 58 | - (void) destroyRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/AFNetworkingRemoteSync/AFNetworkingRemoteSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // AFNetworkingRemoteSync.m 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/3/4. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import "AFNetworkingRemoteSync.h" 10 | 11 | #import 12 | 13 | static YTXRestfulModelRemoteHookExtraParamBlock __hookExtraParamBlock; 14 | 15 | static YTXRestfulModelRemoteHookRequestBlock __hookRequestBlock; 16 | 17 | static NSString *const RestFulDomain = @"AFNetworkingRemoteSync"; //error domain 18 | 19 | @interface AFNetworkingRemoteSync() 20 | 21 | @property (nonnull, nonatomic, strong) AFHTTPSessionManager * requestSessionManager; 22 | 23 | @end 24 | 25 | @implementation AFNetworkingRemoteSync 26 | 27 | - (NSURL *)url { 28 | if (self.urlHookBlock) { 29 | return self.urlHookBlock(); 30 | } 31 | return _url; 32 | } 33 | 34 | + (nonnull instancetype) syncWithURL:(nonnull NSURL *)URL primaryKey:(nonnull NSString *) primaryKey 35 | { 36 | return [[self alloc] initWithURL:URL primaryKey:primaryKey]; 37 | } 38 | 39 | + (nonnull instancetype) syncWithPrimaryKey:(nonnull NSString *) primaryKey 40 | { 41 | return [[self alloc] initWithPrimaryKey:primaryKey]; 42 | } 43 | 44 | + (nullable YTXRestfulModelRemoteHookExtraParamBlock) hookExtraParamBlock 45 | { 46 | return __hookExtraParamBlock; 47 | } 48 | 49 | + (void) setHookExtraParamBlock:(nullable YTXRestfulModelRemoteHookExtraParamBlock)hookExtraParamBlock 50 | { 51 | __hookExtraParamBlock = hookExtraParamBlock; 52 | } 53 | 54 | + (nullable YTXRestfulModelRemoteHookRequestBlock) hookRequestBlock 55 | { 56 | return __hookRequestBlock; 57 | } 58 | 59 | + (void) setHookRequestBlock:(nullable YTXRestfulModelRemoteHookRequestBlock) hookRequestBlock 60 | { 61 | __hookRequestBlock = hookRequestBlock; 62 | } 63 | 64 | - (nonnull instancetype) initWithURL:(nonnull NSURL *)URL primaryKey:(nonnull NSString *) primaryKey 65 | { 66 | if (self = [super init]) { 67 | _url = URL; 68 | _primaryKey = primaryKey; 69 | _timeoutInterval = 60; 70 | self.requestSessionManager.requestSerializer.timeoutInterval = _timeoutInterval; 71 | } 72 | return self; 73 | } 74 | 75 | - (nonnull instancetype) initWithPrimaryKey:(nonnull NSString *) primaryKey 76 | { 77 | if (self = [super init]) { 78 | _primaryKey = primaryKey; 79 | _timeoutInterval = 60; 80 | self.requestSessionManager.requestSerializer.timeoutInterval = _timeoutInterval; 81 | } 82 | return self; 83 | } 84 | 85 | /** GET :id/comment */ 86 | - (void) fetchRemoteForeignWithName:(nonnull NSString *)name param:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 87 | { 88 | NSURL * url = [[self restfulURLWithParam:param] URLByAppendingPathComponent:name]; 89 | NSMutableDictionary * newParam = [[self restfulParamWithParam:param] mutableCopy]; 90 | 91 | if (AFNetworkingRemoteSync.hookRequestBlock) { 92 | AFNetworkingRemoteSync.hookRequestBlock(self, @"GET", &url, &newParam); 93 | } 94 | 95 | [self.requestSessionManager GET:[url absoluteString] parameters:newParam success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { 96 | success(responseObject); 97 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 98 | failed(error); 99 | }]; 100 | } 101 | 102 | /** GET */ 103 | - (void) fetchRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 104 | { 105 | NSURL * url = [self restfulURLWithParam:param]; 106 | NSMutableDictionary * newParam = [[self restfulParamWithParam:param] mutableCopy]; 107 | 108 | if (AFNetworkingRemoteSync.hookRequestBlock) { 109 | AFNetworkingRemoteSync.hookRequestBlock(self, @"GET", &url, &newParam); 110 | } 111 | 112 | [self.requestSessionManager GET:[url absoluteString] parameters:newParam success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { 113 | success(responseObject); 114 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 115 | failed(error); 116 | }]; 117 | } 118 | 119 | /** POST */ 120 | - (void) createRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 121 | { 122 | NSURL * url = [self restfulURLWithParam:param]; 123 | NSMutableDictionary * newParam = [[self restfulParamWithParam:param] mutableCopy]; 124 | 125 | if (AFNetworkingRemoteSync.hookRequestBlock) { 126 | AFNetworkingRemoteSync.hookRequestBlock(self, @"POST", &url, &newParam); 127 | } 128 | 129 | [self.requestSessionManager POST:[url absoluteString] parameters:newParam success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { 130 | success(responseObject); 131 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 132 | failed(error); 133 | }]; 134 | } 135 | 136 | /** PUT */ 137 | - (void) updateRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 138 | { 139 | NSURL * url = [self restfulURLWithParam:param]; 140 | NSMutableDictionary * newParam = [[self restfulParamWithParam:param] mutableCopy]; 141 | 142 | if (AFNetworkingRemoteSync.hookRequestBlock) { 143 | AFNetworkingRemoteSync.hookRequestBlock(self, @"PUT", &url, &newParam); 144 | } 145 | 146 | [self.requestSessionManager PUT:[url absoluteString] parameters:newParam success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { 147 | success(responseObject); 148 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 149 | failed(error); 150 | }]; 151 | } 152 | 153 | /** DELETE */ 154 | - (void) destroyRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed 155 | { 156 | NSURL * url = [self restfulURLWithParam:param]; 157 | NSMutableDictionary * newParam = [[self restfulParamWithParam:param] mutableCopy]; 158 | 159 | if (AFNetworkingRemoteSync.hookRequestBlock) { 160 | AFNetworkingRemoteSync.hookRequestBlock(self, @"DELETE", &url, &newParam); 161 | } 162 | 163 | [self.requestSessionManager DELETE:[url absoluteString] parameters:newParam success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { 164 | success(responseObject); 165 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 166 | failed(error); 167 | }]; 168 | } 169 | 170 | #pragma mark - function 171 | 172 | - (NSURL *) restfulURLWithParam:(NSDictionary *)param 173 | { 174 | NSURL *url = [self.url copy]; 175 | id primaryKey = param[self.primaryKey]; 176 | if (primaryKey) { 177 | url = [url URLByAppendingPathComponent:[primaryKey description]]; 178 | } 179 | return url; 180 | } 181 | 182 | - (NSDictionary *) restfulParamWithParam:(NSDictionary *)param 183 | { 184 | NSMutableDictionary *retParam = [NSMutableDictionary dictionaryWithDictionary:AFNetworkingRemoteSync.hookExtraParamBlock != nil ? AFNetworkingRemoteSync.hookExtraParamBlock() : @{}]; 185 | 186 | for (NSString * key in param) { 187 | [retParam setObject:param[key] forKey:key]; 188 | } 189 | 190 | if (self.primaryKey) { 191 | [retParam removeObjectForKey:self.primaryKey]; 192 | } 193 | return retParam; 194 | } 195 | 196 | - (nonnull AFHTTPSessionManager *) requestSessionManager 197 | { 198 | if (!_requestSessionManager) { 199 | _requestSessionManager = [AFHTTPSessionManager manager]; 200 | } 201 | return _requestSessionManager; 202 | } 203 | 204 | - (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval 205 | { 206 | _timeoutInterval = timeoutInterval; 207 | self.requestSessionManager.requestSerializer.timeoutInterval = _timeoutInterval; 208 | } 209 | 210 | @end 211 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSArray+YTXRestfulModelFMDBSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+YTXRestfulModelFMDBSync.h 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray (YTXRestfulModelFMDBSync) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSArray+YTXRestfulModelFMDBSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+YTXRestfulModelFMDBSync.m 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import "NSArray+YTXRestfulModelFMDBSync.h" 10 | #import "NSObject+YTXRestfulModelFMDBSync.h" 11 | 12 | @implementation NSArray (YTXRestfulModelFMDBSync) 13 | 14 | - (nullable NSString *) sqliteValue 15 | { 16 | NSError *error; 17 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self 18 | options:kNilOptions 19 | error:&error]; 20 | NSString *jsonStr = [[NSString alloc] initWithData:jsonData 21 | encoding:NSUTF8StringEncoding]; 22 | if (error) { 23 | return nil; 24 | } 25 | 26 | return [jsonStr sqliteValue]; 27 | } 28 | + (nullable id) objectForSqliteString:(nonnull NSString *) sqlstring objectType:(nonnull NSString *) type 29 | { 30 | NSError *error; 31 | NSData *objectData = [sqlstring dataUsingEncoding:NSUTF8StringEncoding]; 32 | NSArray *arr = [NSJSONSerialization JSONObjectWithData:objectData 33 | options:NSJSONReadingMutableContainers 34 | error:&error]; 35 | if (error) { 36 | return nil; 37 | } 38 | 39 | return arr; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSDate+YTXRestfulModelFMDBSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+YTXRestfulModelFMDBSync.h 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSDate (YTXRestfulModelFMDBSync) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSDate+YTXRestfulModelFMDBSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+YTXRestfulModelFMDBSync.m 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import "NSDate+YTXRestfulModelFMDBSync.h" 10 | 11 | @implementation NSDate (YTXRestfulModelFMDBSync) 12 | 13 | - (nullable NSString *) sqliteValue 14 | { 15 | return [NSString stringWithFormat:@"%f", [self timeIntervalSince1970]]; 16 | } 17 | + (nullable id) objectForSqliteString:(nonnull NSString *) sqlstring objectType:(nonnull NSString *) type 18 | { 19 | return @([sqlstring doubleValue]); 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSDictionary+YTXRestfulModelFMDBSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+YTXRestfulModelFMDBSync.h 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSDictionary (YTXRestfulModelFMDBSync) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSDictionary+YTXRestfulModelFMDBSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+YTXRestfulModelFMDBSync.m 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import "NSDictionary+YTXRestfulModelFMDBSync.h" 10 | #import "NSObject+YTXRestfulModelFMDBSync.h" 11 | 12 | @implementation NSDictionary (YTXRestfulModelFMDBSync) 13 | 14 | - (nullable NSString *) sqliteValue 15 | { 16 | NSError *error; 17 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self 18 | options:kNilOptions 19 | error:&error]; 20 | NSString *jsonStr = [[NSString alloc] initWithData:jsonData 21 | encoding:NSUTF8StringEncoding]; 22 | 23 | if (error) { 24 | return nil; 25 | } 26 | 27 | return [jsonStr sqliteValue];; 28 | } 29 | + (nullable id) objectForSqliteString:(nonnull NSString *) sqlstring objectType:(nonnull NSString *) type 30 | { 31 | NSError *error; 32 | NSData *objectData = [sqlstring dataUsingEncoding:NSUTF8StringEncoding]; 33 | NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:objectData 34 | options:NSJSONReadingMutableContainers 35 | error:&error]; 36 | if (error) { 37 | return nil; 38 | } 39 | 40 | return dict; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSNumber+YTXRestfulModelFMDBSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+YTXRestfulModelFMDBSync.h 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSNumber (YTXRestfulModelFMDBSync) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSNumber+YTXRestfulModelFMDBSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+YTXRestfulModelFMDBSync.m 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import "NSNumber+YTXRestfulModelFMDBSync.h" 10 | 11 | @implementation NSNumber (YTXRestfulModelFMDBSync) 12 | 13 | - (nullable NSString *) sqliteValue 14 | { 15 | return [self stringValue]; 16 | } 17 | + (nullable id) objectForSqliteString:(nonnull NSString *) sqlstring objectType:(nonnull NSString *) type 18 | { 19 | NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init]; 20 | return [fmt numberFromString:sqlstring]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSObject+YTXRestfulModelFMDBSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YTXRestfulModelFMDBSync.h 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (YTXRestfulModelFMDBSync) 12 | 13 | - (nullable NSString *) sqliteValue; 14 | + (nullable id) objectForSqliteString:(nonnull NSString *) sqlstring objectType:(nonnull NSString *) type; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSObject+YTXRestfulModelFMDBSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YTXRestfulModelFMDBSync.m 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import "NSObject+YTXRestfulModelFMDBSync.h" 10 | 11 | @implementation NSObject (YTXRestfulModelFMDBSync) 12 | 13 | - (nullable NSString *) sqliteValue 14 | { 15 | return nil; 16 | } 17 | 18 | + (nullable id) objectForSqliteString:(nonnull NSString *) sqlstring objectType:(nonnull NSString *) type 19 | { 20 | return nil; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSString+YTXRestfulModelFMDBSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+YTXRestfulModelFMDBSync.h 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (YTXRestfulModelFMDBSync) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSString+YTXRestfulModelFMDBSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+YTXRestfulModelFMDBSync.m 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import "NSString+YTXRestfulModelFMDBSync.h" 10 | 11 | @implementation NSString (YTXRestfulModelFMDBSync) 12 | 13 | - (nullable NSString *) sqliteValue 14 | { 15 | return [NSString stringWithFormat:@"'%@'", [self stringByReplacingOccurrencesOfString:@"'" withString:@"''"]]; 16 | } 17 | + (nullable NSString *) objectForSqliteString:(nonnull NSString *) sqlstring objectType:(nonnull NSString *) type 18 | { 19 | return sqlstring; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSValue+YTXRestfulModelFMDBSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSValue+YTXRestfulModelFMDBSync.h 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSValue (YTXRestfulModelFMDBSync) 12 | 13 | + (nonnull NSString*) formateObjectType:(const char* _Nonnull )objcType; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/Category/NSValue+YTXRestfulModelFMDBSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSValue+YTXRestfulModelFMDBSync.m 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/24. 6 | // 7 | // 8 | 9 | #import "NSValue+YTXRestfulModelFMDBSync.h" 10 | #import "NSObject+YTXRestfulModelFMDBSync.h" 11 | #import 12 | 13 | @implementation NSValue (YTXRestfulModelFMDBSync) 14 | 15 | + (nonnull NSString*) formateObjectType:(const char* _Nonnull )objcType 16 | { 17 | if (!objcType || !strlen(objcType)) return nil; 18 | NSString* type = [NSString stringWithCString:objcType encoding:NSUTF8StringEncoding]; 19 | 20 | switch (objcType[0]) { 21 | case '@': 22 | type = [type substringWithRange:NSMakeRange(2, strlen(objcType)-3)]; 23 | break; 24 | case '{': 25 | type = [type substringWithRange:NSMakeRange(1, strchr(objcType, '=')-objcType-1)]; 26 | break; 27 | default: 28 | break; 29 | } 30 | return type; 31 | } 32 | 33 | - (nullable NSString *) sqliteValue 34 | { 35 | NSString* type = [NSValue formateObjectType:[self objCType]]; 36 | 37 | if ([type isEqualToString:@"CGPoint"]) { 38 | return [NSStringFromCGPoint([self CGPointValue]) sqliteValue]; 39 | } else if ([type isEqualToString:@"CGSize"]) { 40 | return [NSStringFromCGSize([self CGSizeValue]) sqliteValue]; 41 | } else if ([type isEqualToString:@"CGRect"]) { 42 | return [NSStringFromCGRect([self CGRectValue]) sqliteValue]; 43 | } else if ([type isEqualToString:@"CGVector"]) { 44 | return [NSStringFromCGVector([self CGVectorValue]) sqliteValue]; 45 | } else if ([type isEqualToString:@"CGAffineTransform"]) { 46 | return [NSStringFromCGAffineTransform([self CGAffineTransformValue]) sqliteValue]; 47 | } else if ([type isEqualToString:@"UIEdgeInsets"]) { 48 | return [NSStringFromUIEdgeInsets([self UIEdgeInsetsValue]) sqliteValue]; 49 | } else if ([type isEqualToString:@"UIOffset"]) { 50 | return [NSStringFromUIOffset([self UIOffsetValue]) sqliteValue]; 51 | } else if ([type isEqualToString:@"NSRange"]) { 52 | return [NSStringFromRange([self rangeValue]) sqliteValue]; 53 | } 54 | 55 | return nil; 56 | } 57 | + (nullable id) objectForSqliteString:(nonnull NSString *) sqlstring objectType:(nonnull NSString *) type 58 | { 59 | if ([type isEqualToString:@"CGPoint"]) { 60 | return [NSValue valueWithCGPoint:CGPointFromString(sqlstring)]; 61 | } else if ([type isEqualToString:@"CGSize"]) { 62 | return [NSValue valueWithCGSize:CGSizeFromString(sqlstring)]; 63 | } else if ([type isEqualToString:@"CGRect"]) { 64 | return [NSValue valueWithCGRect:CGRectFromString(sqlstring)]; 65 | } else if ([type isEqualToString:@"CGVector"]) { 66 | return [NSValue valueWithCGVector:CGVectorFromString(sqlstring)]; 67 | } else if ([type isEqualToString:@"CGAffineTransform"]) { 68 | return [NSValue valueWithCGAffineTransform:CGAffineTransformFromString(sqlstring)]; 69 | } else if ([type isEqualToString:@"UIEdgeInsets"]) { 70 | return [NSValue valueWithUIEdgeInsets:UIEdgeInsetsFromString(sqlstring)]; 71 | } else if ([type isEqualToString:@"UIOffset"]) { 72 | return [NSValue valueWithUIOffset:UIOffsetFromString(sqlstring)]; 73 | } else if ([type isEqualToString:@"NSRange"]) { 74 | return [NSValue valueWithRange:NSRangeFromString(sqlstring)]; 75 | } 76 | return nil; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/FMDBSync/YTXRestfulModelFMDBSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModelFMDBSync.h 3 | // Pods 4 | // 5 | // Created by CaoJun on 16/2/22. 6 | // 7 | // 8 | 9 | #import "YTXRestfulModelDBProtocol.h" 10 | 11 | #import 12 | 13 | @class FMDatabaseQueue; 14 | @class FMDatabase; 15 | 16 | @interface YTXRestfulModelFMDBSync : NSObject 17 | 18 | @property (nonatomic, strong, readonly, nonnull) FMDatabaseQueue * fmdbQueue; 19 | 20 | @property (nonatomic, assign, readonly, nonnull) Class modelClass; 21 | 22 | @property (nonnull, nonatomic, copy, readonly) NSString * primaryKey; 23 | 24 | 25 | #pragma mark db operation 26 | 27 | + (nonnull instancetype) syncWithModelOfClass:(nonnull Class) modelClass primaryKey:(nonnull NSString *) key; 28 | 29 | + (nonnull NSString *) path; 30 | 31 | - (nonnull NSString *) tableName; 32 | 33 | - (nonnull instancetype) initWithModelOfClass:(nonnull Class) modelClass primaryKey:(nonnull NSString *) key; 34 | - (nonnull NSError *) createTable; 35 | 36 | - (nonnull NSError *) dropTable; 37 | 38 | //操作将会保证在migration之后进行 39 | 40 | /** GET Model with primary key */ 41 | - (nullable NSDictionary *) fetchOneSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 42 | 43 | /** POST / PUT Model with primary key */ 44 | - (nullable NSDictionary *) saveOneSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 45 | 46 | /** DELETE Model with primary key */ 47 | - (BOOL) destroyOneSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 48 | 49 | /** GET */ 50 | - (nullable NSDictionary *) fetchTopOneSyncWithError:(NSError * _Nullable * _Nullable) error; 51 | 52 | /** GET */ 53 | - (nullable NSDictionary *) fetchLatestOneSyncWithError:(NSError * _Nullable * _Nullable) error; 54 | 55 | /** DELETE All Model with primary key */ 56 | - (BOOL) destroyAllSyncWithError:(NSError * _Nullable * _Nullable) error; 57 | 58 | /** ORDER BY primaryKey ASC*/ 59 | - (nonnull NSArray *) fetchAllSyncWithError:(NSError * _Nullable * _Nullable) error; 60 | 61 | /** GET Foreign Models with primary key */ 62 | - (nonnull NSArray *) fetchForeignSyncWithModelClass:(nonnull Class)modelClass primaryKeyValue:(nonnull id) value error:(NSError * _Nullable * _Nullable) error param:(nullable NSDictionary *)param; 63 | 64 | - (nonnull NSArray *) fetchAllSyncWithError:(NSError * _Nullable * _Nullable)error soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSArray * )columnNames; 65 | 66 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error start:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSArray * )columnNames; 67 | 68 | /** 69 | * ORDER BY primaryKey ASC 70 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' AND old >= 10 71 | */ 72 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMet:(nonnull NSArray * )conditions; 73 | 74 | /** 75 | * ORDER BY primaryKey ASC 76 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' AND old >= 10 77 | */ 78 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSArray * )conditions; 79 | 80 | /** 81 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' AND old >= 10 82 | */ 83 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error whereAllTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSArray * )conditions; 84 | 85 | /** 86 | * ORDER BY primaryKey ASC 87 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' OR old >= 10 88 | */ 89 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMet:(nonnull NSArray * )conditions; 90 | 91 | /** 92 | * ORDER BY primaryKey ASC 93 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' OR old >= 10 94 | */ 95 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithSoryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * )orderBy conditions:(nonnull NSArray * )conditions; 96 | 97 | /** 98 | * condition: @"name = 'CJ'", @"old >= 10" => name = 'CJ' OR old >= 10 99 | */ 100 | - (nonnull NSArray *) fetchMultipleSyncWithError:(NSError * _Nullable * _Nullable)error wherePartOfTheConditionsAreMetWithStart:(NSUInteger) start count:(NSUInteger) count soryBy:(YTXRestfulModelDBSortBy)sortBy orderBy:(nonnull NSString * ) orderBy conditions:(nonnull NSArray * )conditions; 101 | 102 | //Migration 103 | 104 | /** 数字越大越后面执行*/ 105 | 106 | @property (nonatomic, strong, readonly, nonnull) NSMutableArray * migrationBlocks; 107 | 108 | /** 大于currentMigrationVersion将会依次执行*/ 109 | - (void) migrate:(nonnull YTXRestfulModelDBMigrationEntity *) entity; 110 | 111 | - (BOOL) createColumnWithDB:(nonnull id)db structSync:(nonnull YTXRestfulModelDBSerializingModel *)sstruct error:(NSError * _Nullable * _Nullable)error; 112 | 113 | // Tools 114 | + (nonnull NSDictionary * > *) mapOfCTypeToSqliteType; 115 | + (nonnull NSArray *) arrayOfMappedArgsWithOriginArray:(nonnull NSArray *)originArray propertiesMap:(nonnull NSDictionary *)propertiesMap; 116 | 117 | + (nonnull NSString * ) sqliteStringWhere:(nonnull NSString *) key equal:(nonnull id) value; 118 | + (nonnull NSString * ) sqliteStringWhere:(nonnull NSString *) key greatThan:(nonnull id) value; 119 | + (nonnull NSString * ) sqliteStringWhere:(nonnull NSString *) key greatThanOrEqaul:(nonnull id) value; 120 | + (nonnull NSString * ) sqliteStringWhere:(nonnull NSString *) key lessThan:(nonnull id) value; 121 | + (nonnull NSString * ) sqliteStringWhere:(nonnull NSString *) key lessThanOrEqual:(nonnull id) value; 122 | + (nonnull NSString * ) sqliteStringWhere:(nonnull NSString *) key like:(nonnull id) value; 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/UserDefaultStorageSync/YTXRestfulModelUserDefaultStorageSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModelUserDefaultStorageSync.h 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/1/25. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import "YTXRestfulModelStorageProtocol.h" 10 | 11 | #import 12 | 13 | 14 | @interface YTXRestfulModelUserDefaultStorageSync : NSObject 15 | 16 | @property (nullable, nonatomic, copy, readonly) NSString * userDefaultSuiteName; 17 | 18 | - (nonnull instancetype) initWithUserDefaultSuiteName:(nullable NSString *) suiteName; 19 | 20 | /** GET */ 21 | - (nullable id) fetchStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 22 | 23 | /** POST / PUT */ 24 | - (nullable id) saveStorageSyncWithKey:(nonnull NSString *)storage withObject:(nonnull id)object param:(nullable NSDictionary *) param; 25 | 26 | /** DELETE */ 27 | - (void) destroyStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Pod/Classes/Sync/UserDefaultStorageSync/YTXRestfulModelUserDefaultStorageSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTXRestfulModelUserDefaultStorageSync.m 3 | // YTXRestfulModel 4 | // 5 | // Created by CaoJun on 16/1/25. 6 | // Copyright © 2016年 Elephants Financial Service. All rights reserved. 7 | // 8 | 9 | #import "YTXRestfulModelUserDefaultStorageSync.h" 10 | 11 | #import 12 | 13 | @implementation YTXRestfulModelUserDefaultStorageSync 14 | 15 | - (nonnull instancetype) initWithUserDefaultSuiteName:(nullable NSString *) suiteName; 16 | { 17 | if (self = [super init]) { 18 | _userDefaultSuiteName = suiteName; 19 | } 20 | return self; 21 | } 22 | 23 | /** GET */ 24 | - (nullable id) fetchStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param 25 | { 26 | return [[[NSUserDefaults alloc] initWithSuiteName:self.userDefaultSuiteName] objectForKey:storage]; 27 | } 28 | 29 | /** POST / PUT */ 30 | - (nullable id) saveStorageSyncWithKey:(nonnull NSString *)storage withObject:(nonnull id)object param:(nullable NSDictionary *) param 31 | { 32 | [[[NSUserDefaults alloc] initWithSuiteName:self.userDefaultSuiteName] setObject:object forKey:storage]; 33 | return object; 34 | } 35 | 36 | /** DELETE */ 37 | - (void) destroyStorageSyncWithKey:(nonnull NSString *)storage param:(nullable NSDictionary *) param 38 | { 39 | [[[NSUserDefaults alloc] initWithSuiteName:self.userDefaultSuiteName] removeObjectForKey:storage]; 40 | } 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YTXRestfulModel [![CI Status](https://img.shields.io/travis/baidao/YTXRestfulModel.svg?style=flat)](https://travis-ci.org/baidao/YTXRestfulModel) [![Version](https://img.shields.io/cocoapods/v/YTXRestfulModel.svg?style=flat)](http://cocoapods.org/pods/YTXRestfulModel) [![License](https://img.shields.io/cocoapods/l/YTXRestfulModel.svg?style=flat)](http://cocoapods.org/pods/YTXRestfulModel) [![Platform](https://img.shields.io/cocoapods/p/YTXRestfulModel.svg?style=flat)](http://cocoapods.org/pods/YTXRestfulModel) 2 | YTXRestfulModel makes it easy to fetch(GET), save(PUT/POST) or destroy(DELEGATE) data by database, remote or local storage.(YTXRestfulModel提供一种简单的方式从数据库,远程或本地存储去拉取,储存,销毁数据) 3 | 4 | ![YTXRestfulModel01_1_X_X](https://github.com/baidao/YTXRestfulModel/blob/github/DocumentAssets/YTXRestfulModel01_1_X_X.png) 5 | 6 | ## Dependency(依赖) 7 | - [FMDB ~>2.6](https://github.com/ccgus/fmdb)(FMDBSync) 8 | - [AFNetworking ~>2.6.3](https://github.com/AFNetworking/AFNetworking/tree/2.6.3) (AFNetworkingRemoteSync) 9 | - [Mantle ~>1.57.](https://github.com/Mantle/Mantle/tree/1.5.7) 10 | - Optional [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa)FRP。 11 | 12 | Support IPv6 13 | 14 | ## Integration 15 | 16 | You shouldn't just use: pod "YTXRestfulModel". Since CocoaPods 0.36+ you should do something like: 17 | 18 | ```ruby 19 | pod 'YTXRestfulModel', :subspecs => ["RACSupport", "YTXRequestRemoteSync", "FMDBSync", "UserDefaultStorageSync"] 20 | ``` 21 | 22 | ## Testing 23 | ```shell 24 | git clone https://github.com/baidao/YTXRestfulModel.git 25 | npm install -g json-server 26 | cd Example 27 | pod install 28 | cd Tests 29 | json-server db.json 30 | open YTXRestfulModel.xcworkspace 31 | ``` 32 | Chose Target: YTXRestfulModel-Example 33 | Command+U to run Test 34 | 35 | ## More useage, please check(更多用法,请查看)[Tests](https://github.com/baidao/YTXRestfulModel/tree/github/Example/Tests) 36 | 37 | ## A Model Example(定义Model示例) 38 | ```objective-c 39 | @interface YTXTestModel : YTXRestfulModel 40 | 41 | @property (nonnull, nonatomic, strong) NSNumber *keyId; 42 | @property (nonnull, nonatomic, strong) NSNumber *userId; 43 | @property (nonnull, nonatomic, strong) NSString *title; 44 | @property (nonnull, nonatomic, strong) NSString *body; 45 | 46 | @end 47 | ``` 48 | ```objective-c 49 | @implementation YTXTestModel 50 | 51 | //The dictionary returned by this method specifies how your model object's properties map to the keys in the JSON representation 52 | // Check Mantle 53 | + (NSDictionary *)JSONKeyPathsByPropertyKey 54 | { 55 | /** key 的值是 模型中的字段,value 的值是目标源上对应数据的名字。*/ 56 | return @{@"keyId": @"id"}; 57 | } 58 | 59 | + (NSString *)primaryKey 60 | { 61 | return @"keyId"; 62 | } 63 | 64 | - (instancetype)init { 65 | if (self = [super init]) { 66 | //这种方式可以在每次使用url的时候都重新获取 67 | self.remoteSync.urlHookBlock = ^NSURL * _Nonnull{ 68 | return [URLManager urlWithName:@"restful.posts"]; 69 | }; 70 | } 71 | return self; 72 | } 73 | 74 | // DB Migration(数据迁移) 75 | + (nullable NSNumber *) currentMigrationVersion 76 | { 77 | return @0; 78 | } 79 | 80 | // Default NO. If you wanna use dbsync, you should return YES; 81 | + (BOOL) autoCreateTable 82 | { 83 | return YES; 84 | } 85 | 86 | //Default YES. If auto alter table, it does not do migration. 87 | + (BOOL) autoAlterTable 88 | { 89 | return YES; 90 | } 91 | 92 | // Mantle Transformer 93 | // Implement this optional method to convert a property from a different type when deserializing from JSON 94 | + (MTLValueTransformer *)startSchoolDateJSONTransformer { 95 | return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSNumber *timestamp) { 96 | return [NSDate dateWithTimeIntervalSince1970: timestamp.longLongValue / 1000]; 97 | } reverseBlock:^(NSDate *date) { 98 | return @((SInt64)(date.timeIntervalSince1970 * 1000)); 99 | }]; 100 | } 101 | + (MTLValueTransformer *) bodyJSONTransformer 102 | { 103 | return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString * body) { 104 | return body; 105 | } reverseBlock:^(NSString * body) { 106 | return body; 107 | }]; 108 | } 109 | 110 | @end 111 | ``` 112 | 113 | ## Fetch 114 | ```objective-c 115 | - (nullable instancetype) fetchStorageSync:(nullable NSDictionary *) param; 116 | 117 | - (void) fetchRemote:(nullable NSDictionary *)param success:(nonnull YTXRestfulModelRemoteSuccessBlock)success failed:(nonnull YTXRestfulModelRemoteFailedBlock)failed; 118 | 119 | - (nonnull instancetype) saveDBSync:(nullable NSDictionary *)param error:(NSError * _Nullable * _Nullable) error; 120 | 121 | ``` 122 | 123 | ## Model transition Refrence(Model的转换参考)[Mantle](https://github.com/Mantle/Mantle/tree/1.5.7) 124 | 无论数据源来自Remote,Storage,DB都会经过MTLValueTransformer,如果你定义了该属性的Transformer。 125 | ```objective-c 126 | + (MTLValueTransformer *)birthdayJSONTransformer { 127 | return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSNumber *timestamp) { 128 | return [NSDate dateWithTimeIntervalSince1970: timestamp.longLongValue / 1000]; 129 | } reverseBlock:^(NSDate *date) { 130 | return @((SInt64)(date.timeIntervalSince1970 * 1000)); 131 | }]; 132 | } 133 | 134 | + (MTLValueTransformer *)passwordJSONTransformer { 135 | return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *value) { 136 | return [self encryption:value]; 137 | } reverseBlock:^(NSString *value) { 138 | return [self decryption:value]; 139 | }]; 140 | } 141 | ``` 142 | 143 | ### Response pretreatment 144 | ```objective-c 145 | - (nonnull instancetype) transformerProxyOfResponse:(nonnull id) response error:(NSError * _Nullable * _Nullable) error 146 | { 147 | return [super transformerProxyOfResponse:response[@"properties"] error:error]; 148 | } 149 | 150 | - (nonnull id) transformerProxyOfForeign:(nonnull Class)modelClass response:(nonnull id) response error:(NSError * _Nullable * _Nullable) error; 151 | { 152 | return [[super transformerProxyOfForeign:modelClass response:response[@"properties"] error:error]; 153 | } 154 | ``` 155 | 156 | ## RACSupport 157 | ```ruby 158 | pod "YTXRestfulModel", :path => "../", :subspecs => ["RACSupport", "AFNetworkingRemoteSync", "FMDBSync", "UserDefaultStorageSync"] 159 | ``` 160 | 161 | ```objective-c 162 | 163 | /** GET */ 164 | - (nonnull RACSignal *) rac_fetchStorage:(nullable NSDictionary *)param; 165 | 166 | /** GET */ 167 | - (nonnull RACSignal *) rac_fetchRemote:(nullable NSDictionary *)param; 168 | 169 | /** POST / PUT */ 170 | - (nonnull RACSignal *) rac_saveRemote:(nullable NSDictionary *)param; 171 | 172 | /** GET */ 173 | - (nonnull RACSignal *) rac_fetchDB:(nullable NSDictionary *)param; 174 | 175 | ``` 176 | 177 | ```objective-c 178 | #import 179 | 180 | YTXTestAFNetworkingRemoteCollection * collection = [YTXTestAFNetworkingRemoteCollection new]; 181 | [[collection rac_fetchRemote:@{@"_start": @"1", @"_limit": @"2"}] subscribeNext:^(YTXTestAFNetworkingRemoteCollection *x) { 182 | 183 | } error:^(NSError *error) { 184 | 185 | }]; 186 | ``` 187 | 188 | ## Example: 189 | ```objective-c 190 | /** 191 | 远程请求:http://jsonplaceholder.typicode.com/posts/1 192 | 返回数据: 193 | { 194 | "userId": 1, 195 | "id": 1, 196 | "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", 197 | "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" 198 | } 199 | */ 200 | /** 201 | keyId = @1,按照JSONKeyPathsByPropertyKey方法中的映射转成retDictionary @{@"id":@1}; 202 | 由于param为空,所以保留参数{@"id":@"1"}不做修改,直接执行同步操作,同步目标源中 id = 1 的数据; 203 | */ 204 | YTXTestModel * currentTestModel = [[YTXTestModel alloc] init]; 205 | currentTestModel.keyId = @1; 206 | [[currentTestModel rac_fetchRemote:nil] subscribeNext:^(YTXTestModel *responseModel) { 207 | 208 | } error:^(NSError *error) { 209 | 210 | }]; 211 | 212 | /** 213 | Model的属性会按照映射转成retDictionary @{@"title":@"ytx test", @"body":@"test content", @"userId":@1} 214 | param按照映射转成mapParam,@{@"id":@2};(param = @{@"id":@2} 时也会转成 @{@"id":@2})。 215 | mapParam的value会替换retDictionary 的value,所以执行同步的参数是@{@"title":@"ytx_test", @"body":@"test_content", @"userId":@1, @"id":@1} 216 | 由于retDictionary 中没有主键,所以不符合rest原则,最后发送的请求是: *******?title=ytx_test&body=test_content&userId=1&id=1 217 | */ 218 | 219 | YTXTestModel * testModel = [[YTXTestModel alloc] init]; 220 | testModel.title = @"ytx test"; 221 | testModel.body = @"test content"; 222 | testModel.userId = @1; 223 | [[testModel rac_saveRemote:@{@"keyId":@1}] subscribeNext:^(YTXTestModel *responseModel) { 224 | 225 | } error:^(NSError *error) { 226 | 227 | }]; 228 | 229 | YTXTestModel * currentTestModel = [[YTXTestModel alloc] init]; 230 | __block id ret; 231 | currentTestModel.keyId = @1; 232 | 233 | [[currentTestModel rac_fetchRemoteForeignWithName:@"comments" modelClass:[YTXTestCommentModel class] param:nil] subscribeNext:^(id x) { 234 | ret = x; 235 | } error:^(NSError *error) { 236 | 237 | }]; 238 | 239 | YTXTestModel * dbTestModel = [[YTXTestModel alloc] init]; 240 | dbTestModel.keyId = @1; 241 | 242 | [[dbTestModel rac_fetchDB:nil] subscribeNext:^(YTXTestModel *responseModel) { 243 | 244 | } error:^(NSError *error) { 245 | 246 | }]; 247 | 248 | YTXTestModel * storageTestModel = [[YTXTestModel alloc] init]; 249 | storageTestModel.keyId = @1; 250 | 251 | [[storageTestModel rac_fetchStorage:nil] subscribeNext:^(YTXTestModel *responseModel) { 252 | 253 | } error:^(NSError *error) { 254 | 255 | }]; 256 | ``` 257 | 258 | ## RACSupport 259 | ```objective-c 260 | #import 261 | 262 | @interface YTXXXXModel : YTXRestfulModel 263 | 264 | - (nonnull RACSignal *) rac_fetchFromRemoteAndStorage; 265 | 266 | @end 267 | 268 | @implementation YTXXXXXModel 269 | 270 | - (nonnull RACSignal *) rac_fetchFromRemoteAndStorage 271 | { 272 | RACSubject * subject = [RACSubject subject]; 273 | [self fetchRemote:param success:^(id _Nullable response) { 274 | [subject sendNext:response]; 275 | [subject sendCompleted]; 276 | } failed:^(NSError * _Nullable error) { 277 | [subject sendError:error]; 278 | 279 | }]; 280 | return subject; 281 | } 282 | 283 | @end 284 | 285 | ``` 286 | 287 | ## Use new sync 288 | 289 | ```objective-c 290 | id storageSync; 291 | id remoteSync; 292 | id dbSync; 293 | ``` 294 | 295 | ```objective-c 296 | - (instancetype)init 297 | { 298 | if(self = [super init]) 299 | { 300 | self.storageSync = [YTXRestfulModelXXXFileStorageSync new]; 301 | } 302 | return self; 303 | } 304 | 305 | YTXTestModel * model = [YTXTestModel new]; 306 | model.storageSync = [YTXRestfulModelXXXFileStorageSync new]; 307 | 308 | ``` 309 | 310 | If necessary, You can also using sync like this(如果有必要你可以直接使用sync想这样) 311 | ```objective-c 312 | #import 313 | 314 | YTXRestfulModelUserDefaultStorageSync * sync = [[YTXRestfulModelUserDefaultStorageSync alloc] initWithUserDefaultSuiteName:suitName1] 315 | ``` 316 | 317 | ## DB mapping 318 | 319 | Column Struct 320 | ```objective-c 321 | YTXRestfulModelDBSerializingModel * dbsm = [YTXRestfulModelDBSerializingModel new]; 322 | dbsm.objectClass = propertyClassName; 323 | dbsm.columnName = columnName; 324 | dbsm.modelName = modelProperyName; 325 | dbsm.isPrimaryKey = isPrimaryKey; 326 | dbsm.autoincrement = isPrimaryKeyAutoincrement; 327 | dbsm.unique = NO; 328 | ``` 329 | ```objective-c 330 | @interface YTXRestfulModelDBSerializingModel : NSObject 331 | 332 | /** 可以是CType @"d"这种*/ 333 | @property (nonatomic, nonnull, copy) NSString * objectClass; 334 | 335 | /** 表名 */ 336 | @property (nonatomic, nonnull, copy) NSString * columnName; 337 | 338 | /** Model原始的属性名字 */ 339 | @property (nonatomic, nonnull, copy) NSString * modelName; 340 | 341 | /** 是否 主键*/ 342 | @property (nonatomic, assign) BOOL isPrimaryKey; 343 | 344 | /** 是否自增*/ 345 | @property (nonatomic, assign) BOOL autoincrement; 346 | 347 | /** 是否唯一*/ 348 | @property (nonatomic, assign) BOOL unique; 349 | 350 | /** 默认值*/ 351 | @property (nonatomic, nonnull, copy) NSString * defaultValue; 352 | 353 | /** 外键类名 可以使用fetchForeignWithName */ 354 | @property (nonatomic, nonnull, copy) NSString * foreignClassName; 355 | 356 | @end 357 | ``` 358 | 359 | Modify DB mapping in child Model 360 | ```objective-c 361 | + (nullable NSMutableDictionary *) tableKeyPathsByPropertyKey 362 | { 363 | NSMutableDictionary * tmpDictionary = [super tableKeyPathsByPropertyKey]; 364 | 365 | YTXRestfulModelDBSerializingModel * genderStruct = tmpDictionary[@"gender"]; 366 | genderStruct.defaultValue = [@(GenderFemale) sqliteValue]; 367 | tmpDictionary[@"gender"] = genderStruct; 368 | 369 | YTXRestfulModelDBSerializingModel * scoreStruct = tmpDictionary[@"score"]; 370 | scoreStruct.unique = YES; 371 | tmpDictionary[@"score"] = scoreStruct; 372 | return tmpDictionary; 373 | } 374 | ``` 375 | 376 | Open auto creating table, if you use DB function.(开启自动创建数据库表。需要使用DB时才这样做) 377 | ```objective-c 378 | + (BOOL) autoCreateTable 379 | { 380 | return YES; 381 | } 382 | ``` 383 | 384 | Close auto incremen (关闭主键默认自增) 385 | ```objective-c 386 | + (BOOL) isPrimaryKeyAutoincrement 387 | { 388 | return NO; 389 | } 390 | ``` 391 | 392 | Model支持的属性类型(CType) Model Type SQLite Type 393 | ```objective-c 394 | @{ 395 | @"c":@[ @"NSNumber", @"INTEGER"], 396 | @"i":@[ @"NSNumber", @"INTEGER"], 397 | @"s":@[ @"NSNumber", @"INTEGER"], 398 | @"l":@[ @"NSNumber", @"INTEGER"], 399 | @"q":@[ @"NSNumber", @"INTEGER"], 400 | @"C":@[ @"NSNumber", @"INTEGER"], 401 | @"I":@[ @"NSNumber", @"INTEGER"], 402 | @"S":@[ @"NSNumber", @"INTEGER"], 403 | @"L":@[ @"NSNumber", @"INTEGER"], 404 | @"Q":@[ @"NSNumber", @"INTEGER"], 405 | @"f":@[ @"NSNumber", @"REAL"], 406 | @"d":@[ @"NSNumber", @"REAL"], 407 | @"B":@[ @"NSNumber", @"INTEGER"], 408 | @"NSString":@[ @"NSString", @"TEXT"], 409 | @"NSMutableString":@[ @"NSMutableString", @"TEXT"], 410 | @"NSDate":@[ @"NSDate", @"REAL"], 411 | @"NSNumber":@[ @"NSNumber", @"REAL"], 412 | @"NSDictionary":@[ @"NSDictionary", @"TEXT"], 413 | @"NSMutableDictionary":@[ @"NSDictionary", @"TEXT"], 414 | @"NSArray":@[ @"NSArray", @"TEXT"], 415 | @"NSMutableArray":@[ @"NSArray", @"TEXT"], 416 | //这里以下和Remote一般不兼容 417 | @"CGPoint":@[ @"NSValue", @"TEXT"], 418 | @"CGSize":@[ @"NSValue", @"TEXT"], 419 | @"CGRect":@[ @"NSValue", @"TEXT"], 420 | @"CGVector":@[ @"NSValue", @"TEXT"], 421 | @"CGAffineTransform":@[ @"NSValue", @"TEXT"], 422 | @"UIEdgeInsets":@[ @"NSValue", @"TEXT"], 423 | @"UIOffset":@[ @"NSValue", @"TEXT"], 424 | @"NSRange":@[ @"NSValue", @"TEXT"] 425 | } 426 | ``` 427 | ## DB Alter(自动扩展字段到表中) 428 | 429 | ```objective-c 430 | 431 | + (BOOL) autoAlterTable 432 | { 433 | return YES;//Default 434 | } 435 | 436 | + (nullable NSNumber *) currentMigrationVersion 437 | { 438 | return @0; 439 | } 440 | 441 | ``` 442 | 443 | | currentMigrationVersion | Property | DB Column | 444 | | ------------------------------ |:--------------------:| --------------------:| 445 | | 0 | Name,KeyId | name,keyid | 446 | | 1 | Name,KeyId,title | name,keyid,title | 447 | | 2 | Name,KeyId,title,age | name,keyId,title,age | 448 | | 3 | Name,KeyId,title | name,keyId,title,age | 449 | 450 | 451 | ## DB Migration(数据库迁移) 452 | ```objective-c 453 | // DB Migration(数据迁移) 454 | + (nullable NSNumber *) currentMigrationVersion 455 | { 456 | return @0; 457 | } 458 | 459 | + (BOOL) autoAlterTable 460 | { 461 | return NO;//Not Default 462 | } 463 | ``` 464 | 465 | | currentMigrationVersion | Property | DB Column | 466 | | ------------------------------ |:--------------------:| ---------------------------:| 467 | | 0 | KeyId,age | keyid,age | 468 | | 1 | KeyId,age,title | keyId,age,title | 469 | | 2 | KeyId,age,name | keyId,age,title,title=>name | 470 | 471 | sqlite does not support function rename an drop(sqlite并没有提供rename和drop column的方法) 472 | 473 | ## Migration Example 474 | 475 | ```objective-c 476 | 477 | //数据库迁移操作是从当前版本到最新版本依次进行的,所以本方法中要存储所有版本的迁移操作。 478 | + (void) migrationsMethodWithSync:(nonnull id)sync; 479 | { 480 | // 创建 数据库迁移的操作 481 | YTXRestfulModelDBMigrationEntity *migration = [YTXRestfulModelDBMigrationEntity new]; 482 | // 设置 进行本次操作时的版本号 483 | migration.version = @1; 484 | // 设置 迁移操作(增、删、改) 485 | migration.block = ^(_Nonnull id db, NSError * _Nullable * _Nullable error) { 486 | YTXRestfulModelDBSerializingModel *runtimePStruct = [YTXRestfulModelDBSerializingModel new]; 487 | runtimePStruct.objectClass = @"NSString"; 488 | runtimePStruct.columnName = @"runtimeP"; 489 | runtimePStruct.modelName = @"runtimeP"; 490 | runtimePStruct.isPrimaryKey = NO; 491 | runtimePStruct.autoincrement = NO; 492 | runtimePStruct.unique = NO; 493 | // 创建新的列 494 | [sync createColumnWithDB:db structSync:runtimePStruct error:error]; 495 | }; 496 | // 存储 迁移操作 497 | [sync migrate:migration]; 498 | } 499 | 500 | ``` 501 | 502 | 503 | ```objective-c 504 | - (BOOL) createColumnWithDB:(nonnull id)db structSync:(nonnull YTXRestfulModelDBSerializingModel *)sstruct error:(NSError * _Nullable * _Nullable)error; 505 | 506 | @optional 507 | 508 | - (BOOL) renameColumnWithDB:(nonnull id)db originName:(nonnull NSString *)originName newName:(nonnull NSString *)newName error:(NSError * _Nullable * _Nullable)error; 509 | 510 | - (BOOL) dropColumnWithDB:(nonnull id)db structSync:(nonnull YTXRestfulModelDBSerializingModel *)sstruct error:(NSError * _Nullable * _Nullable)error; 511 | 512 | - (BOOL) changeCollumnDB:(nonnull id)db oldStructSync:(nonnull YTXRestfulModelDBSerializingModel *) oldStruct toNewStruct:(nonnull YTXRestfulModelDBSerializingModel *) newStruct error:(NSError * _Nullable * _Nullable)error; 513 | ``` 514 | 515 | Migration Delegate 516 | ```objective-c 517 | + (void)dbWillMigrateWithSync:(nonnull id)sync 518 | { 519 | } 520 | 521 | + (void)dbDidMigrateWithSync:(nonnull id)sync 522 | { 523 | } 524 | ``` 525 | ## Remote Hook 526 | 所有请求都会附带这些参数,hook的优先级最低,总是会被其他同名参数覆盖 527 | ```objective-c 528 | NSDictionary *(^hook)() = ^NSDictionary *() { 529 | return @{ 530 | @"deviceToken": objectOrEmptyStr([NotificationManager sharedManager].deviceToken), 531 | @"deviceId": objectOrEmptyStr([AppInfo deviceUUID]), 532 | @"marketId": objectOrEmptyStr([AppInfo marketId]), 533 | @"appVersion": objectOrEmptyStr([AppInfo appVersion]) 534 | }; 535 | }; 536 | 537 | AFNetworkingRemoteSync.HookExtraParamBlock = hook; 538 | ``` 539 | 540 | ## Remote Hook Request 541 | 在发送请求的时候统一修改Header或者其他行为 542 | ```objective-c 543 | AFNetworkingRemoteSync.hookRequestBlock = ^(AFNetworkingRemoteSync * sync, NSString * method, NSURL ** url, NSMutableDictionary ** paramters) { 544 | retParamters = *paramters; 545 | retParamters[@"title"] = @"XXXX"; 546 | *paramters = retParamters; 547 | 548 | AFHTTPSessionManager * sessionManager = sync.requestSessionManager; 549 | [sessionManager.requestSerializer setValue:@"test" forHTTPHeaderField:@"x-auth-token"]; 550 | }; 551 | ``` 552 | 553 | 554 | ## subspec 555 | - AFNetworkingRemoteSync: 'AFNetworking', '~> 2.6.3' 556 | - FMDBSync: 'FMDB', '~> 2.6' 557 | - UserDefaultStorageSync: 558 | - RACSupport:'ReactiveCocoa', '~> 2.3.1' 559 | 560 | 561 | 562 | ## License 563 | 564 | YTXRestfulModel was built by caojun. It is licensed under the MIT License. 565 | 566 | If you use YTXRestfulModel in one of your apps, I'd love to hear about it. 567 | -------------------------------------------------------------------------------- /YTXRestfulModel.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint YTXRestfulModel.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = "YTXRestfulModel" 11 | s.version = "1.2.2" 12 | s.summary = "YTXRestfulModel 提供了restful的功能" 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.homepage = "https://github.com/baidao/YTXRestfulModel" 21 | # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 22 | s.license = 'MIT' 23 | s.author = { "caojun" => "78612846@qq.com" } 24 | s.source = { :git => "https://github.com/baidao/YTXRestfulModel.git", :tag => s.version.to_s } 25 | 26 | s.platform = :ios, '7.0' 27 | s.requires_arc = true 28 | 29 | s.subspec "Default" do |ss| 30 | ss.source_files = ["Pod/Classes/Model/**/*", "Pod/Classes/Protocol/**/*"] 31 | ss.dependency 'Mantle', '~> 1.5.7' 32 | end 33 | 34 | s.subspec "RACSupport" do |ss| 35 | ss.source_files = ["Pod/Classes/RACSupport/**/*"] 36 | ss.dependency 'ReactiveCocoa', '~> 2.3.1' 37 | ss.dependency 'YTXRestfulModel/Default' 38 | end 39 | 40 | _AFNetworkingRemoteSync = { :spec_name => "AFNetworkingRemoteSync", :dependency => [{:name => "AFNetworking", :version => "~> 2.6.3" }] } 41 | _FMDBSync = { :spec_name => "FMDBSync", :dependency => [{:name => "FMDB", :version => "~> 2.6" }] } 42 | _UserDefaultStorageSync = { :spec_name => "UserDefaultStorageSync" } 43 | 44 | _all_names = [] 45 | 46 | _all_sync = [_AFNetworkingRemoteSync, _FMDBSync, _UserDefaultStorageSync] 47 | 48 | _all_sync.each do |sync_spec| 49 | s.subspec sync_spec[:spec_name] do |ss| 50 | 51 | specname = sync_spec[:spec_name] 52 | 53 | _all_names << specname 54 | 55 | sources = ["Pod/Classes/Sync/#{specname}/**/*"] 56 | 57 | ss.prefix_header_contents = "#define YTX_#{specname.upcase}_EXISTS 1" 58 | 59 | ss.source_files = sources 60 | 61 | ss.dependency 'YTXRestfulModel/Default' 62 | 63 | if sync_spec[:dependency] 64 | sync_spec[:dependency].each do |dep| 65 | ss.dependency dep[:name], dep[:version] 66 | end 67 | end 68 | 69 | end 70 | end 71 | 72 | 73 | s.default_subspec = 'Default' 74 | 75 | spec_names = _all_names[0...-1].join(", ") + " 和 " + _all_names[-1] 76 | 77 | s.description = "提供了restful的model和collection。提供这些sync:#{spec_names}" 78 | end 79 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /buildpod.sh: -------------------------------------------------------------------------------- 1 | pushd "$(dirname "$0")" > /dev/null 2 | SCRIPT_DIR=$(pwd -L) 3 | popd > /dev/null 4 | 5 | echo "publish repo YTXRestfulModel" 6 | pod trunk push YTXRestfulModel.podspec --verbose 7 | 8 | ret=$? 9 | 10 | #exit $ret 11 | -------------------------------------------------------------------------------- /lint.sh: -------------------------------------------------------------------------------- 1 | pod lib lint --sources='http://gitlab.baidao.com/ios/ytx-pod-specs.git,master' --verbose 2 | --------------------------------------------------------------------------------