7 | #import "JSONKeyMapper.coh"
8 |
9 | typedef NSString *(^JSONModelKeyMapBlock)(NSString *keyName);
10 |
11 | /**
12 | * **You won't need to create or store instances of this class yourself.** If you want your model
13 | * to have different property names than the JSON feed keys, look below on how to
14 | * make your model use a key mapper.
15 | *
16 | * For example if you consume JSON from twitter
17 | * you get back underscore_case style key names. For example:
18 | *
19 | * "profile_sidebar_border_color": "0094C2",
20 | * "profile_background_tile": false,
21 | *
22 | * To comply with Obj-C accepted camelCase property naming for your classes,
23 | * you need to provide mapping between JSON keys and ObjC property names.
24 | *
25 | * In your model overwrite the + (JSONKeyMapper *)keyMapper method and provide a JSONKeyMapper
26 | * instance to convert the key names for your model.
27 | *
28 | * If you need custom mapping it's as easy as:
29 | *
30 | * + (JSONKeyMapper *)keyMapper {
31 | * return [[JSONKeyMapper alloc] initWithDictionary:@{@"crazy_JSON_name":@"myCamelCaseName"}];
32 | * }
33 | *
34 | * In case you want to handle underscore_case, **use the predefined key mapper**, like so:
35 | *
36 | * + (JSONKeyMapper *)keyMapper {
37 | * return [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase];
38 | * }
39 | *
40 | */
41 | @interface JSONKeyMapper : NSObject
42 |
43 | // deprecated
44 | @property (readonly, nonatomic) JSONModelKeyMapBlock JSONToModelKeyBlock DEPRECATED_ATTRIBUTE;
45 | - (NSString *)convertValue:(NSString *)value isImportingToModel:(BOOL)importing DEPRECATED_MSG_ATTRIBUTE("use convertValue:");
46 | - (instancetype)initWithDictionary:(NSDictionary *)map DEPRECATED_MSG_ATTRIBUTE("use initWithModelToJSONDictionary:");
47 | - (instancetype)initWithJSONToModelBlock:(JSONModelKeyMapBlock)toModel modelToJSONBlock:(JSONModelKeyMapBlock)toJSON DEPRECATED_MSG_ATTRIBUTE("use initWithModelToJSONBlock:");
48 | + (instancetype)mapper:(JSONKeyMapper *)baseKeyMapper withExceptions:(NSDictionary *)exceptions DEPRECATED_MSG_ATTRIBUTE("use baseMapper:withModelToJSONExceptions:");
49 | + (instancetype)mapperFromUnderscoreCaseToCamelCase DEPRECATED_MSG_ATTRIBUTE("use mapperForSnakeCase:");
50 | + (instancetype)mapperFromUpperCaseToLowerCase DEPRECATED_ATTRIBUTE;
51 |
52 | /** @name Name converters */
53 | /** Block, which takes in a property name and converts it to the corresponding JSON key name */
54 | @property (readonly, nonatomic) JSONModelKeyMapBlock modelToJSONKeyBlock;
55 |
56 | /** Combined converter method
57 | * @param value the source name
58 | * @return JSONKeyMapper instance
59 | */
60 | - (NSString *)convertValue:(NSString *)value;
61 |
62 | /** @name Creating a key mapper */
63 |
64 | /**
65 | * Creates a JSONKeyMapper instance, based on the block you provide this initializer.
66 | * The parameter takes in a JSONModelKeyMapBlock block:
67 | * NSString *(^JSONModelKeyMapBlock)(NSString *keyName)
68 | * The block takes in a string and returns the transformed (if at all) string.
69 | * @param toJSON transforms your model property name to a JSON key
70 | */
71 | - (instancetype)initWithModelToJSONBlock:(JSONModelKeyMapBlock)toJSON;
72 |
73 | /**
74 | * Creates a JSONKeyMapper instance, based on the mapping you provide.
75 | * Use your JSONModel property names as keys, and the JSON key names as values.
76 | * @param toJSON map dictionary, in the format: @{@"myCamelCaseName":@"crazy_JSON_name"}
77 | * @return JSONKeyMapper instance
78 | */
79 | - (instancetype)initWithModelToJSONDictionary:(NSDictionary *)toJSON;
80 |
81 | /**
82 | * Given a camelCase model property, this mapper finds JSON keys using the snake_case equivalent.
83 | */
84 | + (instancetype)mapperForSnakeCase;
85 |
86 | /**
87 | * Given a camelCase model property, this mapper finds JSON keys using the TitleCase equivalent.
88 | */
89 | + (instancetype)mapperForTitleCase;
90 |
91 | /**
92 | * Creates a JSONKeyMapper based on a built-in JSONKeyMapper, with specific exceptions.
93 | * Use your JSONModel property names as keys, and the JSON key names as values.
94 | */
95 | + (instancetype)baseMapper:(JSONKeyMapper *)baseKeyMapper withModelToJSONExceptions:(NSDictionary *)toJSON;
96 |
97 | @end
98 |
--------------------------------------------------------------------------------
/TemplateFiles/JSONModelTransformations/JSONKeyMapper.m:
--------------------------------------------------------------------------------
1 | //
2 | // JSONKeyMapper.m
3 | // JSONModel
4 | //
5 |
6 | #import "JSONKeyMapper.h"
7 |
8 | @implementation JSONKeyMapper
9 |
10 | - (instancetype)initWithJSONToModelBlock:(JSONModelKeyMapBlock)toModel modelToJSONBlock:(JSONModelKeyMapBlock)toJSON
11 | {
12 | return [self initWithModelToJSONBlock:toJSON];
13 | }
14 |
15 | - (instancetype)initWithModelToJSONBlock:(JSONModelKeyMapBlock)toJSON
16 | {
17 | if (!(self = [self init]))
18 | return nil;
19 |
20 | _modelToJSONKeyBlock = toJSON;
21 |
22 | return self;
23 | }
24 |
25 | - (instancetype)initWithDictionary:(NSDictionary *)map
26 | {
27 | NSDictionary *toJSON = [JSONKeyMapper swapKeysAndValuesInDictionary:map];
28 |
29 | return [self initWithModelToJSONDictionary:toJSON];
30 | }
31 |
32 | - (instancetype)initWithModelToJSONDictionary:(NSDictionary *)toJSON
33 | {
34 | if (!(self = [super init]))
35 | return nil;
36 |
37 | _modelToJSONKeyBlock = ^NSString *(NSString *keyName)
38 | {
39 | return [toJSON valueForKeyPath:keyName] ?: keyName;
40 | };
41 |
42 | return self;
43 | }
44 |
45 | - (JSONModelKeyMapBlock)JSONToModelKeyBlock
46 | {
47 | return nil;
48 | }
49 |
50 | + (NSDictionary *)swapKeysAndValuesInDictionary:(NSDictionary *)dictionary
51 | {
52 | NSArray *keys = dictionary.allKeys;
53 | NSArray *values = [dictionary objectsForKeys:keys notFoundMarker:[NSNull null]];
54 |
55 | return [NSDictionary dictionaryWithObjects:keys forKeys:values];
56 | }
57 |
58 | - (NSString *)convertValue:(NSString *)value isImportingToModel:(BOOL)importing
59 | {
60 | return [self convertValue:value];
61 | }
62 |
63 | - (NSString *)convertValue:(NSString *)value
64 | {
65 | return _modelToJSONKeyBlock(value);
66 | }
67 |
68 | + (instancetype)mapperFromUnderscoreCaseToCamelCase
69 | {
70 | return [self mapperForSnakeCase];
71 | }
72 |
73 | + (instancetype)mapperForSnakeCase
74 | {
75 | return [[self alloc] initWithModelToJSONBlock:^NSString *(NSString *keyName)
76 | {
77 | NSMutableString *result = [NSMutableString stringWithString:keyName];
78 | NSRange range;
79 |
80 | // handle upper case chars
81 | range = [result rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]];
82 | while (range.location != NSNotFound)
83 | {
84 | NSString *lower = [result substringWithRange:range].lowercaseString;
85 | [result replaceCharactersInRange:range withString:[NSString stringWithFormat:@"_%@", lower]];
86 | range = [result rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]];
87 | }
88 |
89 | // handle numbers
90 | range = [result rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]];
91 | while (range.location != NSNotFound)
92 | {
93 | NSRange end = [result rangeOfString:@"\\D" options:NSRegularExpressionSearch range:NSMakeRange(range.location, result.length - range.location)];
94 |
95 | // spans to the end of the key name
96 | if (end.location == NSNotFound)
97 | end = NSMakeRange(result.length, 1);
98 |
99 | NSRange replaceRange = NSMakeRange(range.location, end.location - range.location);
100 | NSString *digits = [result substringWithRange:replaceRange];
101 | [result replaceCharactersInRange:replaceRange withString:[NSString stringWithFormat:@"_%@", digits]];
102 | range = [result rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet] options:0 range:NSMakeRange(end.location + 1, result.length - end.location - 1)];
103 | }
104 |
105 | return result;
106 | }];
107 | }
108 |
109 | + (instancetype)mapperForTitleCase
110 | {
111 | return [[self alloc] initWithModelToJSONBlock:^NSString *(NSString *keyName)
112 | {
113 | return [keyName stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[keyName substringToIndex:1].uppercaseString];
114 | }];
115 | }
116 |
117 | + (instancetype)mapperFromUpperCaseToLowerCase
118 | {
119 | return [[self alloc] initWithModelToJSONBlock:^NSString *(NSString *keyName)
120 | {
121 | return keyName.uppercaseString;
122 | }];
123 | }
124 |
125 | + (instancetype)mapper:(JSONKeyMapper *)baseKeyMapper withExceptions:(NSDictionary *)exceptions
126 | {
127 | NSDictionary *toJSON = [JSONKeyMapper swapKeysAndValuesInDictionary:exceptions];
128 |
129 | return [self baseMapper:baseKeyMapper withModelToJSONExceptions:toJSON];
130 | }
131 |
132 | + (instancetype)baseMapper:(JSONKeyMapper *)baseKeyMapper withModelToJSONExceptions:(NSDictionary *)toJSON
133 | {
134 | return [[self alloc] initWithModelToJSONBlock:^NSString *(NSString *keyName)
135 | {
136 | if (!keyName)
137 | return nil;
138 |
139 | if (toJSON[keyName])
140 | return toJSON[keyName];
141 |
142 | return baseKeyMapper.modelToJSONKeyBlock(keyName);
143 | }];
144 | }
145 |
146 | @end
147 |
--------------------------------------------------------------------------------
/TemplateFiles/JSONModelTransformations/JSONValueTransformer.coh:
--------------------------------------------------------------------------------
1 | //
2 | // COClass.coh
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/7/2.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | // DO NOT TRY TO MODIFY THIS FILE!
10 | // [self] = self
11 |
12 | #ifndef COClass_coh
13 | #define COClass_coh
14 |
15 | #ifndef CO_CONFUSION_CLASS
16 | #define CO_CONFUSION_CLASS
17 | #endif
18 |
19 | #ifndef CO_CONFUSION_CATEGORY
20 | #define CO_CONFUSION_CATEGORY
21 | #endif
22 |
23 | #ifndef CO_CONFUSION_PROPERTY
24 | #define CO_CONFUSION_PROPERTY
25 | #endif
26 |
27 | #ifndef CO_CONFUSION_METHOD
28 | #define CO_CONFUSION_METHOD
29 | #endif
30 |
31 | #endif /* COClass_coh */
32 |
--------------------------------------------------------------------------------
/TemplateFiles/JSONModelTransformations/JSONValueTransformer.h:
--------------------------------------------------------------------------------
1 | //
2 | // JSONValueTransformer.h
3 | // JSONModel
4 | //
5 |
6 | #import
7 | #import "JSONValueTransformer.coh"
8 |
9 | /////////////////////////////////////////////////////////////////////////////////////////////
10 |
11 | #pragma mark - extern definitions
12 | /**
13 | * Boolean function to check for null values. Handy when you need to both check
14 | * for nil and [NSNUll null]
15 | */
16 | extern BOOL isNull(id value);
17 |
18 | /////////////////////////////////////////////////////////////////////////////////////////////
19 |
20 | #pragma mark - JSONValueTransformer interface
21 | /**
22 | * **You don't need to call methods of this class manually.**
23 | *
24 | * Class providing methods to transform values from one class to another.
25 | * You are given a number of built-in transformers, but you are encouraged to
26 | * extend this class with your own categories to add further value transformers.
27 | * Just few examples of what can you add to JSONValueTransformer: hex colors in JSON to UIColor,
28 | * hex numbers in JSON to NSNumber model properties, base64 encoded strings in JSON to UIImage properties, and more.
29 | *
30 | * The class is invoked by JSONModel while transforming incoming
31 | * JSON types into your target class property classes, and vice versa.
32 | * One static copy is create and store in the JSONModel class scope.
33 | */
34 | @interface JSONValueTransformer : NSObject
35 |
36 | @property (strong, nonatomic, readonly) NSDictionary *primitivesNames;
37 |
38 | /** @name Resolving cluster class names */
39 | /**
40 | * This method returns the umbrella class for any standard class cluster members.
41 | * For example returns NSString when given as input NSString, NSMutableString, __CFString and __CFConstantString
42 | * The method currently looksup a pre-defined list.
43 | * @param sourceClass the class to get the umbrella class for
44 | * @return Class
45 | */
46 | + (Class)classByResolvingClusterClasses:(Class)sourceClass;
47 |
48 | #pragma mark - NSMutableString <-> NSString
49 | /** @name Transforming to Mutable copies */
50 | /**
51 | * Transforms a string value to a mutable string value
52 | * @param string incoming string
53 | * @return mutable string
54 | */
55 | - (NSMutableString *)NSMutableStringFromNSString:(NSString *)string;
56 |
57 | #pragma mark - NSMutableArray <-> NSArray
58 | /**
59 | * Transforms an array to a mutable array
60 | * @param array incoming array
61 | * @return mutable array
62 | */
63 | - (NSMutableArray *)NSMutableArrayFromNSArray:(NSArray *)array;
64 |
65 | #pragma mark - NSMutableDictionary <-> NSDictionary
66 | /**
67 | * Transforms a dictionary to a mutable dictionary
68 | * @param dict incoming dictionary
69 | * @return mutable dictionary
70 | */
71 | - (NSMutableDictionary *)NSMutableDictionaryFromNSDictionary:(NSDictionary *)dict;
72 |
73 | #pragma mark - NSSet <-> NSArray
74 | /** @name Transforming Sets */
75 | /**
76 | * Transforms an array to a set
77 | * @param array incoming array
78 | * @return set with the array's elements
79 | */
80 | - (NSSet *)NSSetFromNSArray:(NSArray *)array;
81 |
82 | /**
83 | * Transforms an array to a mutable set
84 | * @param array incoming array
85 | * @return mutable set with the array's elements
86 | */
87 | - (NSMutableSet *)NSMutableSetFromNSArray:(NSArray *)array;
88 |
89 | /**
90 | * Transforms a set to an array
91 | * @param set incoming set
92 | * @return an array with the set's elements
93 | */
94 | - (NSArray *)JSONObjectFromNSSet:(NSSet *)set;
95 |
96 | /**
97 | * Transforms a mutable set to an array
98 | * @param set incoming mutable set
99 | * @return an array with the set's elements
100 | */
101 | - (NSArray *)JSONObjectFromNSMutableSet:(NSMutableSet *)set;
102 |
103 | #pragma mark - BOOL <-> number/string
104 | /** @name Transforming JSON types */
105 | /**
106 | * Transforms a number object to a bool number object
107 | * @param number the number to convert
108 | * @return the resulting number
109 | */
110 | - (NSNumber *)BOOLFromNSNumber:(NSNumber *)number;
111 |
112 | /**
113 | * Transforms a number object to a bool number object
114 | * @param string the string value to convert, "0" converts to NO, everything else to YES
115 | * @return the resulting number
116 | */
117 | - (NSNumber *)BOOLFromNSString:(NSString *)string;
118 |
119 | /**
120 | * Transforms a BOOL value to a bool number object
121 | * @param number an NSNumber value coming from the model
122 | * @return the result number
123 | */
124 | - (NSNumber *)JSONObjectFromBOOL:(NSNumber *)number;
125 |
126 | #pragma mark - string <-> number
127 | /**
128 | * Transforms a string object to a number object
129 | * @param string the string to convert
130 | * @return the resulting number
131 | */
132 | - (NSNumber *)NSNumberFromNSString:(NSString *)string;
133 |
134 | /**
135 | * Transforms a number object to a string object
136 | * @param number the number to convert
137 | * @return the resulting string
138 | */
139 | - (NSString *)NSStringFromNSNumber:(NSNumber *)number;
140 |
141 | /**
142 | * Transforms a string object to a nsdecimalnumber object
143 | * @param string the string to convert
144 | * @return the resulting number
145 | */
146 | - (NSDecimalNumber *)NSDecimalNumberFromNSString:(NSString *)string;
147 |
148 | /**
149 | * Transforms a nsdecimalnumber object to a string object
150 | * @param number the number to convert
151 | * @return the resulting string
152 | */
153 | - (NSString *)NSStringFromNSDecimalNumber:(NSDecimalNumber *)number;
154 |
155 |
156 | #pragma mark - string <-> url
157 | /** @name Transforming URLs */
158 | /**
159 | * Transforms a string object to an NSURL object
160 | * @param string the string to convert
161 | * @return the resulting url object
162 | */
163 | - (NSURL *)NSURLFromNSString:(NSString *)string;
164 |
165 | /**
166 | * Transforms an NSURL object to a string
167 | * @param url the url object to convert
168 | * @return the resulting string
169 | */
170 | - (NSString *)JSONObjectFromNSURL:(NSURL *)url;
171 |
172 | #pragma mark - string <-> time zone
173 |
174 | /** @name Transforming NSTimeZone */
175 | /**
176 | * Transforms a string object to an NSTimeZone object
177 | * @param string the string to convert
178 | * @return the resulting NSTimeZone object
179 | */
180 | - (NSTimeZone *)NSTimeZoneFromNSString:(NSString *)string;
181 |
182 | /**
183 | * Transforms an NSTimeZone object to a string
184 | * @param timeZone the time zone object to convert
185 | * @return the resulting string
186 | */
187 | - (NSString *)JSONObjectFromNSTimeZone:(NSTimeZone *)timeZone;
188 |
189 | #pragma mark - string <-> date
190 | /** @name Transforming Dates */
191 | /**
192 | * The following two methods are not public. This way if there is a category on converting
193 | * dates it'll override them. If there isn't a category the default methods found in the .m
194 | * file will be invoked. If these are public a warning is produced at the point of overriding
195 | * them in a category, so they have to stay hidden here.
196 | */
197 |
198 | //- (NSDate *)NSDateFromNSString:(NSString *)string;
199 | //- (NSString *)JSONObjectFromNSDate:(NSDate *)date;
200 |
201 | #pragma mark - number <-> date
202 |
203 | /**
204 | * Transforms a number to an NSDate object
205 | * @param number the number to convert
206 | * @return the resulting date
207 | */
208 | - (NSDate *)NSDateFromNSNumber:(NSNumber *)number;
209 |
210 | @end
211 |
--------------------------------------------------------------------------------
/TemplateFiles/JSONModelTransformations/JSONValueTransformer.m:
--------------------------------------------------------------------------------
1 | //
2 | // JSONValueTransformer.m
3 | // JSONModel
4 | //
5 |
6 | #import "JSONValueTransformer.h"
7 |
8 | #pragma mark - functions
9 | extern BOOL isNull(id value)
10 | {
11 | if (!value) return YES;
12 | if ([value isKindOfClass:[NSNull class]]) return YES;
13 |
14 | return NO;
15 | }
16 |
17 | @implementation JSONValueTransformer
18 |
19 | -(id)init
20 | {
21 | self = [super init];
22 | if (self) {
23 | _primitivesNames = @{@"f":@"float", @"i":@"int", @"d":@"double", @"l":@"long", @"B":@"BOOL", @"s":@"short",
24 | @"I":@"unsigned int", @"L":@"usigned long", @"q":@"long long", @"Q":@"unsigned long long", @"S":@"unsigned short", @"c":@"char", @"C":@"unsigned char",
25 | //and some famous aliases of primitive types
26 | // BOOL is now "B" on iOS __LP64 builds
27 | @"I":@"NSInteger", @"Q":@"NSUInteger", @"B":@"BOOL",
28 |
29 | @"@?":@"Block"};
30 | }
31 | return self;
32 | }
33 |
34 | +(Class)classByResolvingClusterClasses:(Class)sourceClass
35 | {
36 | //check for all variations of strings
37 | if ([sourceClass isSubclassOfClass:[NSString class]]) {
38 | return [NSString class];
39 | }
40 |
41 | //check for all variations of numbers
42 | if ([sourceClass isSubclassOfClass:[NSNumber class]]) {
43 | return [NSNumber class];
44 | }
45 |
46 | //check for all variations of dictionaries
47 | if ([sourceClass isSubclassOfClass:[NSArray class]]) {
48 | return [NSArray class];
49 | }
50 |
51 | //check for all variations of arrays
52 | if ([sourceClass isSubclassOfClass:[NSDictionary class]]) {
53 | return [NSDictionary class];
54 | }
55 |
56 | //check for all variations of dates
57 | if ([sourceClass isSubclassOfClass:[NSDate class]]) {
58 | return [NSDate class];
59 | }
60 |
61 | //no cluster parent class found
62 | return sourceClass;
63 | }
64 |
65 | #pragma mark - NSMutableString <-> NSString
66 | -(NSMutableString*)NSMutableStringFromNSString:(NSString*)string
67 | {
68 | return [NSMutableString stringWithString:string];
69 | }
70 |
71 | #pragma mark - NSMutableArray <-> NSArray
72 | -(NSMutableArray*)NSMutableArrayFromNSArray:(NSArray*)array
73 | {
74 | return [NSMutableArray arrayWithArray:array];
75 | }
76 |
77 | #pragma mark - NSMutableDictionary <-> NSDictionary
78 | -(NSMutableDictionary*)NSMutableDictionaryFromNSDictionary:(NSDictionary*)dict
79 | {
80 | return [NSMutableDictionary dictionaryWithDictionary:dict];
81 | }
82 |
83 | #pragma mark - NSSet <-> NSArray
84 | -(NSSet*)NSSetFromNSArray:(NSArray*)array
85 | {
86 | return [NSSet setWithArray:array];
87 | }
88 |
89 | -(NSMutableSet*)NSMutableSetFromNSArray:(NSArray*)array
90 | {
91 | return [NSMutableSet setWithArray:array];
92 | }
93 |
94 | -(id)JSONObjectFromNSSet:(NSSet*)set
95 | {
96 | return [set allObjects];
97 | }
98 |
99 | -(id)JSONObjectFromNSMutableSet:(NSMutableSet*)set
100 | {
101 | return [set allObjects];
102 | }
103 |
104 | //
105 | // 0 converts to NO, everything else converts to YES
106 | //
107 |
108 | #pragma mark - BOOL <-> number/string
109 | -(NSNumber*)BOOLFromNSNumber:(NSNumber*)number
110 | {
111 | if (isNull(number)) return [NSNumber numberWithBool:NO];
112 | return [NSNumber numberWithBool: number.intValue==0?NO:YES];
113 | }
114 |
115 | -(NSNumber*)BOOLFromNSString:(NSString*)string
116 | {
117 | if (string != nil &&
118 | ([string caseInsensitiveCompare:@"true"] == NSOrderedSame ||
119 | [string caseInsensitiveCompare:@"yes"] == NSOrderedSame)) {
120 | return [NSNumber numberWithBool:YES];
121 | }
122 | return [NSNumber numberWithBool: ([string intValue]==0)?NO:YES];
123 | }
124 |
125 | -(NSNumber*)JSONObjectFromBOOL:(NSNumber*)number
126 | {
127 | return [NSNumber numberWithBool: number.intValue==0?NO:YES];
128 | }
129 |
130 | #pragma mark - string/number <-> float
131 | -(float)floatFromObject:(id)obj
132 | {
133 | return [obj floatValue];
134 | }
135 |
136 | -(float)floatFromNSString:(NSString*)string
137 | {
138 | return [self floatFromObject:string];
139 | }
140 |
141 | -(float)floatFromNSNumber:(NSNumber*)number
142 | {
143 | return [self floatFromObject:number];
144 | }
145 |
146 | -(NSNumber*)NSNumberFromfloat:(float)f
147 | {
148 | return [NSNumber numberWithFloat:f];
149 | }
150 |
151 | #pragma mark - string <-> number
152 | -(NSNumber*)NSNumberFromNSString:(NSString*)string
153 | {
154 | return [NSNumber numberWithDouble:[string doubleValue]];
155 | }
156 |
157 | -(NSString*)NSStringFromNSNumber:(NSNumber*)number
158 | {
159 | return [number stringValue];
160 | }
161 |
162 | -(NSDecimalNumber*)NSDecimalNumberFromNSString:(NSString*)string
163 | {
164 | return [NSDecimalNumber decimalNumberWithString:string];
165 | }
166 |
167 | -(NSString*)NSStringFromNSDecimalNumber:(NSDecimalNumber*)number
168 | {
169 | return [number stringValue];
170 | }
171 |
172 | #pragma mark - string <-> url
173 | -(NSURL*)NSURLFromNSString:(NSString*)string
174 | {
175 | // do not change this behavior - there are other ways of overriding it
176 | // see: https://github.com/jsonmodel/jsonmodel/pull/119
177 | return [NSURL URLWithString:string];
178 | }
179 |
180 | -(NSString*)JSONObjectFromNSURL:(NSURL*)url
181 | {
182 | return [url absoluteString];
183 | }
184 |
185 | #pragma mark - string <-> date
186 | -(NSDateFormatter*)importDateFormatter
187 | {
188 | static dispatch_once_t onceInput;
189 | static NSDateFormatter* inputDateFormatter;
190 | dispatch_once(&onceInput, ^{
191 | inputDateFormatter = [[NSDateFormatter alloc] init];
192 | [inputDateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
193 | [inputDateFormatter setDateFormat:@"yyyy-MM-dd'T'HHmmssZZZ"];
194 | });
195 | return inputDateFormatter;
196 | }
197 |
198 | -(NSDate*)__NSDateFromNSString:(NSString*)string
199 | {
200 | string = [string stringByReplacingOccurrencesOfString:@":" withString:@""]; // this is such an ugly code, is this the only way?
201 | return [self.importDateFormatter dateFromString: string];
202 | }
203 |
204 | -(NSString*)__JSONObjectFromNSDate:(NSDate*)date
205 | {
206 | static dispatch_once_t onceOutput;
207 | static NSDateFormatter *outputDateFormatter;
208 | dispatch_once(&onceOutput, ^{
209 | outputDateFormatter = [[NSDateFormatter alloc] init];
210 | [outputDateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
211 | [outputDateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZ"];
212 | });
213 | return [outputDateFormatter stringFromDate:date];
214 | }
215 |
216 | #pragma mark - number <-> date
217 | - (NSDate*)NSDateFromNSNumber:(NSNumber*)number
218 | {
219 | return [NSDate dateWithTimeIntervalSince1970:number.doubleValue];
220 | }
221 |
222 | #pragma mark - string <-> NSTimeZone
223 |
224 | - (NSTimeZone *)NSTimeZoneFromNSString:(NSString *)string {
225 | return [NSTimeZone timeZoneWithName:string];
226 | }
227 |
228 | - (id)JSONObjectFromNSTimeZone:(NSTimeZone *)timeZone {
229 | return [timeZone name];
230 | }
231 |
232 | #pragma mark - hidden transform for empty dictionaries
233 | //https://github.com/jsonmodel/jsonmodel/issues/163
234 | -(NSDictionary*)__NSDictionaryFromNSArray:(NSArray*)array
235 | {
236 | if (array.count==0) return @{};
237 | return (id)array;
238 | }
239 |
240 | -(NSMutableDictionary*)__NSMutableDictionaryFromNSArray:(NSArray*)array
241 | {
242 | if (array.count==0) return [[self __NSDictionaryFromNSArray:array] mutableCopy];
243 | return (id)array;
244 | }
245 |
246 | @end
247 |
--------------------------------------------------------------------------------
/md.res/database-structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CUITCHE/code-obfuscation/3cf075422e4e3748424ff6f90606f7f7f7ab2f2d/md.res/database-structure.png
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Arguments.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Arguments.swift
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/7/12.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | fileprivate struct __Arguments {
12 | let id = flag.String(name: "id", defValue: ".", usage: "The directory of info.plist. Default is current executed path.")
13 | let offset = flag.Integer(name: "offset", defValue: 0, usage: "The offset of obfuscation. Default is 0.")
14 | let db = flag.String(name: "db", defValue: ".", usage: "The directory of obfuscation database. Default is current executed path.")
15 | let root = flag.String(name: "root", defValue: ".", usage: "The directory of project file or what you want to start. Default is current executed path.")
16 | let `super` = flag.Bool(name: "super", defValue: false, usage: "Check the user-class' names which have been entranced obfuscation whether their super classes exist or not. If exists, will info a warning. For strict option, will check all of classes of iOS Kits.")
17 | let strict = flag.Bool(name: "strict", defValue: false, usage: "See -super.")
18 | let st = flag.Bool(name: "st", defValue: true, usage: "Strengthen the obfuscation. Default is true.")
19 | let version = flag.Bool(name: "version", defValue: false, usage: "Get the program supported iOS SDK version.")
20 | let query = flag.String(name: "q", defValue: "", usage: "Query the method whether exist or not.")
21 | }
22 |
23 | public struct Arguments {
24 | fileprivate let __arguments = __Arguments.init()
25 | public var infoPlistFilepath: NSString { return __arguments.id.pointee }
26 | public var obfuscationOffset: sint64 { return __arguments.offset.pointee }
27 | public var dbFilepath: NSString { return __arguments.db.pointee }
28 | public var rootpath : NSString { return __arguments.root.pointee }
29 |
30 | public var supercheck: Bool { return __arguments.`super`.pointee }
31 | public var strict: Bool { return __arguments.strict.pointee }
32 | public var st: Bool { return __arguments.st.pointee }
33 |
34 | public var executedPath: String { return flag.executedPath }
35 |
36 | public private(set) var identifier: String = ""
37 |
38 | public private(set) var appVersion: String = ""
39 |
40 | public init() {
41 | if flag.parsed() == false {
42 | flag.parse()
43 | }
44 | identifier = {
45 | if let infoBundle = Bundle.init(path: __arguments.id.pointee as String), let path = infoBundle.path(forResource: "info", ofType: "plist"), let dict = NSDictionary.init(contentsOfFile: path) {
46 | if let id = dict[kCFBundleIdentifierKey as Any] as? String {
47 | return id.replacingOccurrences(of: ".", with: "_").replacingOccurrences(of: "-", with: "_")
48 | }
49 | }
50 | return "com_placeholder_co"
51 | }()
52 |
53 | appVersion = {
54 | if let infoBundle = Bundle.init(path: __arguments.id.pointee as String), let path = infoBundle.path(forResource: "info", ofType: "plist"), let dict = NSDictionary.init(contentsOfFile: path) {
55 | if let ver = dict[kCFBundleVersionKey as Any] as? String {
56 | return ver.replacingOccurrences(of: ".", with: "_").replacingOccurrences(of: "-", with: "_")
57 | }
58 | }
59 | return "m_s_p"
60 | }()
61 |
62 | if __arguments.version.pointee {
63 | Arguments.printVersion()
64 | }
65 | if __arguments.query.pointee != "" {
66 | Arguments.query(with: __arguments.query.pointee as String)
67 | }
68 | }
69 |
70 | public static let arguments = Arguments.init()
71 | }
72 |
73 | fileprivate extension Arguments {
74 | static func printVersion() {
75 | printc.println(text: CacheImage.versionString)
76 | exit(0)
77 | }
78 |
79 | static func query(with statement: String) {
80 | let cache = CacheImage.init()
81 | var similarClass = [String]()
82 | var similarMethods = Set.init()
83 |
84 | printc.console.IORedirector = stdout
85 | printc.println(text: "🍺 searching...", marks: .yellow)
86 | var arrayMutex = pthread_mutex_t.init()
87 | var setMutext = pthread_mutex_t.init()
88 | pthread_mutex_init(&arrayMutex, nil)
89 | pthread_mutex_init(&setMutext, nil)
90 | cache.enumerateCache { (classname, methods, progress) -> Bool in
91 | if classname.contains(statement) {
92 | pthread_mutex_lock(&arrayMutex)
93 | similarClass.append(classname.replacingOccurrences(of: statement, with: printc.write(statement, .bold, .red).takeAssembleBuffer()))
94 | pthread_mutex_unlock(&arrayMutex)
95 | }
96 | for m in methods {
97 | if m.method.contains(statement) {
98 | pthread_mutex_lock(&setMutext)
99 | similarMethods.insert(m.method.replacingOccurrences(of: statement, with: printc.write(statement, .bold, .red).takeAssembleBuffer()))
100 | pthread_mutex_unlock(&setMutext)
101 | }
102 | }
103 | printc.console.drawProgressBar(with: progress, drawInMultiThread: true)
104 | if progress == 100 {
105 | printc.console.isHideCursor = false
106 | printc.println(text: "")
107 | if similarClass.count > 0 {
108 | similarClass.sort(by: <)
109 | printc.println(text: "Found similar class: ", marks: .bold)
110 | printc.println(text: "\(similarClass.joined(separator: "\n"))\n")
111 | } else {
112 | printc().write("Class: No such ").write("'\(statement)' ", .bold).writeln(" Found.")
113 | }
114 | if similarMethods.count > 0 {
115 | printc.println(text: "Found similar method: ", marks: .bold)
116 | var methods = similarMethods.sorted(by: <)
117 | if let idx = methods.index(where: { return $0 == statement }) {
118 | methods.remove(at: idx)
119 | printc.println(text: statement, marks: .underline)
120 | }
121 | printc.println(text: "\(methods.joined(separator: "\n"))\n")
122 | } else {
123 | printc().write("Method: No such ").write("'\(statement)' ", .bold).writeln(" Found.")
124 | }
125 | pthread_mutex_destroy(&arrayMutex)
126 | pthread_mutex_destroy(&setMutext)
127 | exit(0)
128 | }
129 | return false
130 | }
131 | RunLoop.main.run()
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/FileOutStream.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FileOutStream.swift
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/8/15.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | fileprivate extension String {
12 | static func encrypt_md5(content: String) -> String {
13 | return ("\(content)\(content.characters.count)" as NSString).md5
14 | }
15 |
16 | static func md5_for_self(content: String) -> String? {
17 | let text = content as NSString
18 | var range = text.range(of: "[self] = ")
19 | if range.location == NSNotFound {
20 | return nil
21 | }
22 | range.location = NSMaxRange(range)
23 | range.length = 32
24 | let `self` = text.replacingCharacters(in: range, with: "")
25 | return (`self` as NSString).md5
26 | }
27 |
28 | static let obfusedmd5 = "fileold" // [String: String]
29 | static let obfusemd5 = "files" // String
30 | static let selfmd5 = "self" // String
31 | }
32 |
33 | struct FileOutStream {
34 | var needGenerateObfuscationCode: Bool { return gen != nil }
35 |
36 | fileprivate var headerFilename = ""
37 | fileprivate var selfLocation = 0
38 |
39 | fileprivate let filepath: String
40 | fileprivate let originalFileContent: String
41 | fileprivate var attributed = [String: Any]()
42 | fileprivate var gen: String? = nil
43 |
44 | fileprivate static let fmtter: DateFormatter = {
45 | let fmtter = DateFormatter.init()
46 | fmtter.dateFormat = "yyyy/MM/dd"
47 | return fmtter
48 | }()
49 |
50 | init?(filepath: String) {
51 | do {
52 | let content = try String.init(contentsOfFile: filepath)
53 | originalFileContent = content
54 | self.filepath = filepath
55 | } catch {
56 | print(error)
57 | return nil
58 | }
59 | }
60 |
61 | mutating func read() {
62 | if attributed.count > 0 {
63 | return
64 | }
65 | var cohFileSearchEndIndex = 0
66 | var scanner = Scanner.init(string: originalFileContent)
67 | if scanner.scanUpTo("[self] =", into: nil) {
68 | cohFileSearchEndIndex = scanner.scanLocation
69 | scanner.scanString("[self] =", into: nil)
70 | var selfmd5: NSString? = nil
71 | scanner.scanUpToCharacters(from: .whitespaces, into: &selfmd5)
72 | if let selfmd5 = selfmd5 {
73 | let curCOHMd5 = String.md5_for_self(content: originalFileContent)
74 | self.attributed[.selfmd5] = curCOHMd5 ?? ""
75 | // coh文件校验
76 | if selfmd5.isEqual(to: curCOHMd5 ?? "") == false {
77 | gen = ""
78 | }
79 | } else {
80 | gen = ""
81 | }
82 | }
83 | // 扫描关联文件的md5值
84 | scanner = Scanner.init(string: originalFileContent.substring(to: originalFileContent.index(originalFileContent.startIndex, offsetBy: cohFileSearchEndIndex)))
85 | var oldmd5 = [String: String]()
86 | while scanner.scanUpTo("[", into: nil) {
87 | scanner.scanString("[", into: nil);
88 | var filename: NSString? = nil
89 | scanner.scanUpTo("]", into: &filename)
90 |
91 | scanner.scanUpTo("= ", into: nil)
92 | scanner.scanString("= ", into: nil)
93 |
94 | var md5: NSString? = nil
95 | scanner.scanUpToCharacters(from: .whitespacesAndNewlines, into: &md5)
96 | if let filename = filename, let md5 = md5 {
97 | if filename.isEqual(to: .selfmd5) {
98 | oldmd5[filename as String] = md5 as String
99 | }
100 | }
101 | }
102 | self.attributed[.obfusedmd5] = oldmd5
103 | }
104 |
105 | mutating func worth(parsing file: String, filename: String) -> Bool {
106 | let md5 = String.encrypt_md5(content: file)
107 | var md5s = self.attributed[.obfusemd5] as? [String: String]
108 | if md5s == nil {
109 | md5s = [String: String]()
110 | }
111 | md5s![filename] = md5
112 | self.attributed[.obfusemd5] = md5s!
113 | if md5 == (self.attributed[.obfusemd5] as! [String: String])[filename] {
114 | // FIXME: 由于当前设计缺陷,暂且决策每次都需要重新生成混淆数据
115 | return true
116 | }
117 | if gen == nil {
118 | gen = ""
119 | }
120 | return true
121 | }
122 |
123 | mutating func begin() {
124 | assert(gen != nil, "Logic error, you need not to generate code")
125 | assert(originalFileContent.characters.count != 0, "No original data")
126 |
127 | // 写头部注释
128 | let headerfilename = (self.filepath as NSString).lastPathComponent
129 | gen!.append("//\n// \(headerfilename)\n")
130 | gen!.append("// Code-Obfuscation Auto Generator\n\n")
131 | gen!.append("// Created by \((Arguments.arguments.executedPath as NSString).lastPathComponent) on \(FileOutStream.fmtter.string(from: Date.init())).\n")
132 | gen!.append("// Copyright © 2102 year \((Arguments.arguments.executedPath as NSString).lastPathComponent). All rights reserved.\n\n")
133 |
134 | gen!.append("// DO NOT TRY TO MODIFY THIS FILE!\n")
135 | let md5s = self.attributed[.obfusemd5]
136 | if let md5s = md5s {
137 | for (key, value) in md5s as! [String: String] {
138 | gen!.append("// [\(key)] = \(value)\n")
139 | }
140 | }
141 | gen!.append("// [self] = ")
142 | self.selfLocation = gen!.characters.count
143 | self.headerFilename = headerfilename.replacingOccurrences(of: ".coh", with: "_coh").uppercased()
144 |
145 | // 写头文件header 宏
146 | gen!.append("#ifndef \(self.headerFilename)\n")
147 | gen!.append("#define \(self.headerFilename)\n\n")
148 |
149 | // 生成COF的必用宏
150 | _write(macro: "CO_CONFUSION_CLASS")
151 | _write(macro: "CO_CONFUSION_CATEGORY")
152 | _write(macro: "CO_CONFUSION_PROPERTY")
153 | _write(macro: "CO_CONFUSION_METHOD")
154 | _write(macro: "CO_CONFUSION_PROTOCOL")
155 |
156 | // 尝试包含features头文件
157 | gen!.append("#if __has_include(\"CO-Features.h\")\n")
158 | gen!.append("# include \"CO-Features.h\"\n")
159 | gen!.append("#endif // __has_include\n\n")
160 |
161 | // debug下才生效
162 | gen!.append("#if !defined(DEBUG)\n")
163 | }
164 |
165 | mutating func write(obfuscation code: [String: String]) {
166 | assert(gen != nil, "Logic error, you need not to generate code")
167 | for (key, value) in code {
168 | _write(fake: value, realText: key)
169 | }
170 | }
171 |
172 | mutating func end() {
173 | assert(gen != nil, "Logic error, you need not to generate code")
174 | gen!.append("#endif\n\n")
175 | gen!.append("#endif /* \(self.headerFilename) */")
176 |
177 | let md5 = String.encrypt_md5(content: gen!)
178 | let text = NSMutableString.init(string: gen!)
179 | text.insert("\(md5)\n\n", at: selfLocation)
180 | var oldAttribute = try! FileManager.default.attributesOfItem(atPath: filepath)
181 | oldAttribute[.immutable] = false
182 | try! FileManager.default.setAttributes(oldAttribute, ofItemAtPath: filepath)
183 | do {
184 | try text.write(toFile: filepath, atomically: true, encoding: String.Encoding.utf8.rawValue)
185 | } catch {
186 | print(error)
187 | }
188 | oldAttribute[.immutable] = true
189 | try! FileManager.default.setAttributes(oldAttribute, ofItemAtPath: filepath)
190 | }
191 | }
192 |
193 | fileprivate extension FileOutStream {
194 | mutating func _write(fake text: String, realText: String) {
195 | gen!.append("#ifndef \(realText)\n")
196 | gen!.append("#define \(realText) \(text)\n")
197 | gen!.append("#endif\n\n")
198 | }
199 |
200 | mutating func _write(macro: String) {
201 | gen!.append("#ifndef \(macro)\n")
202 | gen!.append("# define \(macro)\n")
203 | gen!.append("#endif // !\(macro)\n\n")
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/ObfuscationDatabase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ObfuscationDatabase.swift
3 | // obfuse-code
4 | //
5 | // Created by hejunqiu on 2017/8/28.
6 | // Copyright © 2017年 hejunqiu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol AbstractDatabaseCreation {
12 | var creationSql: Array { get }
13 | }
14 |
15 | open class AbstractDatabase: AbstractDatabaseCreation {
16 | public var creationSql: Array { return [] }
17 |
18 | public let db: FMDatabase
19 | public let databsePath: String
20 |
21 | init?(filepath: String) {
22 | guard type(of: self) != AbstractDatabase.self else {
23 | return nil
24 | }
25 | let fm = FileManager.default
26 | if fm.fileExists(atPath: filepath) == false {
27 | do {
28 | try fm.createDirectory(atPath: (filepath as NSString).deletingLastPathComponent, withIntermediateDirectories: true, attributes: nil)
29 | } catch {
30 | print(error)
31 | return nil
32 | }
33 | guard fm.createFile(atPath: filepath, contents: nil, attributes: nil) else {
34 | printc.println(text: "Create db file failed!", marks: .red)
35 | return nil
36 | }
37 | }
38 | databsePath = filepath
39 | db = FMDatabase.init(path: filepath)
40 | guard db.open() else {
41 | printc.println(text: db.lastErrorMessage(), marks: .red)
42 | return nil
43 | }
44 | for sql in self.creationSql {
45 | guard db.executeStatements(sql) else {
46 | printc.println(text: "Create sql failed.", marks: .red)
47 | return nil
48 | }
49 | }
50 | printc.println(text: "Create db at \(filepath)")
51 | }
52 | }
53 |
54 | class ObfuscationDatabase: AbstractDatabase {
55 | enum ObfuscationType {
56 | case `class`, category, property, method, `protocol`
57 | }
58 | let bundleIdentifier: String
59 | let appVersion: String
60 |
61 | init?(filepath: String, bundleIdentifier: String, appVersion: String) {
62 | self.bundleIdentifier = bundleIdentifier
63 | self.appVersion = appVersion
64 | super.init(filepath: (((filepath as NSString).appendingPathComponent(bundleIdentifier) as NSString).appendingPathComponent(appVersion) as NSString).appendingPathComponent("\(Date()).db"))
65 | }
66 | }
67 |
68 | extension ObfuscationDatabase {
69 | func insert(filename: String, real: String, fake: String, type: ObfuscationType, location: String = "") {
70 | if self._createTable(with: filename) {
71 | self._insert(to: filename, with: (real, fake, location), type: "\(type)")
72 | }
73 | }
74 |
75 | fileprivate func _createTable(with tableName: String) -> Bool {
76 | let sql = "CREATE TABLE IF NOT EXISTS \(tableName)(real Text NOT NULL,fake Varchar(4096) NOT NULL,location Text NOT NULL,type Text NOT NULL);"
77 | guard db.executeStatements(sql) else {
78 | printc.println(text: db.lastErrorMessage(), marks: .yellow)
79 | return false
80 | }
81 | return true
82 | }
83 |
84 | @discardableResult
85 | fileprivate func _insert(to table: String, with obfuscation: (real: String, fake: String, location: String), type: String) -> Bool {
86 | let sql = "INSERT INTO \(table)(real, fake, location, type) VALUES(?,?,?,?);"
87 | guard db.executeUpdate(sql, withArgumentsIn: [obfuscation.real, obfuscation.fake, obfuscation.location, type]) else {
88 | printc.println(text: db.lastErrorMessage(), marks: .yellow)
89 | return false
90 | }
91 | return true
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/BuildSql/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 | Copyright (c) 2017 hejunqiu
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/BuildSql/README.md:
--------------------------------------------------------------------------------
1 | # buildSQL
2 | 基于C++和Objective-C的buildSQL,可以用代码语言来build一条SQL语句。
3 | # 简介
4 | 某天,我写了很多SQL语句,实在受不了了,就有了buildSQL。在有代码提示的情况下,写起来会更爽,阅读这样的SQL语句也更清晰明了。
5 | # 如何使用
6 | 将`BuildSql.h`和`BuildSql.mm`包含进你的工程,并在包含`BuildSql.h`的文件的后缀名改为`mm`即可。
7 | 具体用法:
8 | ```Objective-C
9 | // 不用把BuildSql对象建立在堆上,栈变量的点语法有助于阅读。
10 | BuildSql sqlBuilder; // 另外一个构造函数是BuildSql(@"@"),传入的参数起到placeholder作用
11 | // select
12 | sqlBuilder.select(@"field0", @"field1", @"field2").from(@"table").where(@"id").equalTo(@(1)).And(@"type").lessThan(@(9)).end();
13 | // sames to 'SELECT field0, field1, field2 FROM table WHERE id=1 AND type<9;'
14 |
15 | // insert into
16 | sqlBuilder.insertInto(@"table").field(@"field0", @"field1", @"field2", @"field3").values();
17 | // sames to 'INSERT INTO table(field0, field1, field2, field3) VALUES(?,?,?,?);'
18 |
19 | // update
20 | sqlBuilder.update(@"table").fieldPh(@"field0", @"field1", @"field2", @"field3").where(@"name").equalTo(@"buildSql").end();
21 | // sames to 'UPDATE table SET field0=?, field1=?, field2=?, field3=? WHERE name='buildSql';'
22 |
23 | // delete
24 | sqlBuilder.Delete(@"table").where(@"id").greaterThan(@1001).Or(@"id").lessThanOrEqualtTo(@2001);
25 | // sames to 'DELETE FROM table WHERE id>1001 OR id<=2001'
26 |
27 | // order by
28 | sqlBuilder.select(@"field0", @"field1", @"field2").from(@"table").where(@"id").equalTo(@(1)).And(@"type").lessThan(@(9)).orderBy(@"field0").end();
29 | // sames to 'SELECT field0, field1, field2 FROM table WHERE id=1 AND type<9 ORDER BY field0;'
30 |
31 | // create table
32 | sqlBuilder.create(@"table").
33 | column(@"id", SqlTypeInteger).primaryKey().
34 | column(@"name", SqlTypeVarchar, bs_max(200)).nonull().
35 | column(@"number", SqlTypeDecimal, bs_precision(20, 8)).nonull().end(); // 这儿的end()调用是必须的
36 | // sames to 'CREATE TABLE IF NOT EXISTS table(id Integer PRIMARY KEY,name Varchar(200) NOT NULL,number Decimal(20,8) NOT NULL);'
37 | ```
38 | 更多的用法,请参考我编写的[测试用例](/buildSQLTest/buildSQLTest.mm)。
39 |
40 | BuildSql可以被多次使用,只需要在使用前调用`reset()`就可以恢复到初始状态。
41 | # 使用要求
42 | * Only support [C]. 由于使用了Objective-C的`NSString`,所以暂时只支持[C],以后会考虑改成纯C++的构建。
43 | * 你需要知道必要的SQL语法,请参考[SQL 教程](http://www.w3school.com.cn/sql/)
44 |
45 | # 注意
46 | * buildSql基本上不会去检查语法错误!
47 | * buildSql只会简单提示一些可能会影响到sql build时的小错误。
48 |
49 | # 未实现
50 | * 「drop」、「alter」
51 | * 有的组合代码需要合并优化
52 |
53 | # 其它
54 | 欢迎各位对此感兴趣的社区同仁共同维护buildSQL。欢迎大家提bug issue。
55 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/FMDB/LICENSE.txt:
--------------------------------------------------------------------------------
1 | If you are using FMDB in your project, I'd love to hear about it. Let Gus know
2 | by sending an email to gus@flyingmeat.com.
3 |
4 | And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you
5 | might consider purchasing a drink of their choosing if FMDB has been useful to
6 | you.
7 |
8 | Finally, and shortly, this is the MIT License.
9 |
10 | Copyright (c) 2008-2014 Flying Meat Inc.
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in
20 | all copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | THE SOFTWARE.
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/FMDB/src/fmdb/FMDB.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | FOUNDATION_EXPORT double FMDBVersionNumber;
4 | FOUNDATION_EXPORT const unsigned char FMDBVersionString[];
5 |
6 | #import "FMDatabase.h"
7 | #import "FMResultSet.h"
8 | #import "FMDatabaseAdditions.h"
9 | #import "FMDatabaseQueue.h"
10 | #import "FMDatabasePool.h"
11 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/FMDB/src/fmdb/FMDatabaseAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // FMDatabaseAdditions.h
3 | // fmdb
4 | //
5 | // Created by August Mueller on 10/30/05.
6 | // Copyright 2005 Flying Meat Inc.. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "FMDatabase.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | /** Category of additions for `` class.
15 |
16 | ### See also
17 |
18 | - ``
19 | */
20 |
21 | @interface FMDatabase (FMDatabaseAdditions)
22 |
23 | ///----------------------------------------
24 | /// @name Return results of SQL to variable
25 | ///----------------------------------------
26 |
27 | /** Return `int` value for query
28 |
29 | @param query The SQL query to be performed.
30 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
31 |
32 | @return `int` value.
33 |
34 | @note This is not available from Swift.
35 | */
36 |
37 | - (int)intForQuery:(NSString*)query, ...;
38 |
39 | /** Return `long` value for query
40 |
41 | @param query The SQL query to be performed.
42 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
43 |
44 | @return `long` value.
45 |
46 | @note This is not available from Swift.
47 | */
48 |
49 | - (long)longForQuery:(NSString*)query, ...;
50 |
51 | /** Return `BOOL` value for query
52 |
53 | @param query The SQL query to be performed.
54 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
55 |
56 | @return `BOOL` value.
57 |
58 | @note This is not available from Swift.
59 | */
60 |
61 | - (BOOL)boolForQuery:(NSString*)query, ...;
62 |
63 | /** Return `double` value for query
64 |
65 | @param query The SQL query to be performed.
66 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
67 |
68 | @return `double` value.
69 |
70 | @note This is not available from Swift.
71 | */
72 |
73 | - (double)doubleForQuery:(NSString*)query, ...;
74 |
75 | /** Return `NSString` value for query
76 |
77 | @param query The SQL query to be performed.
78 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
79 |
80 | @return `NSString` value.
81 |
82 | @note This is not available from Swift.
83 | */
84 |
85 | - (NSString * _Nullable)stringForQuery:(NSString*)query, ...;
86 |
87 | /** Return `NSData` value for query
88 |
89 | @param query The SQL query to be performed.
90 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
91 |
92 | @return `NSData` value.
93 |
94 | @note This is not available from Swift.
95 | */
96 |
97 | - (NSData * _Nullable)dataForQuery:(NSString*)query, ...;
98 |
99 | /** Return `NSDate` value for query
100 |
101 | @param query The SQL query to be performed.
102 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
103 |
104 | @return `NSDate` value.
105 |
106 | @note This is not available from Swift.
107 | */
108 |
109 | - (NSDate * _Nullable)dateForQuery:(NSString*)query, ...;
110 |
111 |
112 | // Notice that there's no dataNoCopyForQuery:.
113 | // That would be a bad idea, because we close out the result set, and then what
114 | // happens to the data that we just didn't copy? Who knows, not I.
115 |
116 |
117 | ///--------------------------------
118 | /// @name Schema related operations
119 | ///--------------------------------
120 |
121 | /** Does table exist in database?
122 |
123 | @param tableName The name of the table being looked for.
124 |
125 | @return `YES` if table found; `NO` if not found.
126 | */
127 |
128 | - (BOOL)tableExists:(NSString*)tableName;
129 |
130 | /** The schema of the database.
131 |
132 | This will be the schema for the entire database. For each entity, each row of the result set will include the following fields:
133 |
134 | - `type` - The type of entity (e.g. table, index, view, or trigger)
135 | - `name` - The name of the object
136 | - `tbl_name` - The name of the table to which the object references
137 | - `rootpage` - The page number of the root b-tree page for tables and indices
138 | - `sql` - The SQL that created the entity
139 |
140 | @return `FMResultSet` of schema; `nil` on error.
141 |
142 | @see [SQLite File Format](http://www.sqlite.org/fileformat.html)
143 | */
144 |
145 | - (FMResultSet *)getSchema;
146 |
147 | /** The schema of the database.
148 |
149 | This will be the schema for a particular table as report by SQLite `PRAGMA`, for example:
150 |
151 | PRAGMA table_info('employees')
152 |
153 | This will report:
154 |
155 | - `cid` - The column ID number
156 | - `name` - The name of the column
157 | - `type` - The data type specified for the column
158 | - `notnull` - whether the field is defined as NOT NULL (i.e. values required)
159 | - `dflt_value` - The default value for the column
160 | - `pk` - Whether the field is part of the primary key of the table
161 |
162 | @param tableName The name of the table for whom the schema will be returned.
163 |
164 | @return `FMResultSet` of schema; `nil` on error.
165 |
166 | @see [table_info](http://www.sqlite.org/pragma.html#pragma_table_info)
167 | */
168 |
169 | - (FMResultSet*)getTableSchema:(NSString*)tableName;
170 |
171 | /** Test to see if particular column exists for particular table in database
172 |
173 | @param columnName The name of the column.
174 |
175 | @param tableName The name of the table.
176 |
177 | @return `YES` if column exists in table in question; `NO` otherwise.
178 | */
179 |
180 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName;
181 |
182 | /** Test to see if particular column exists for particular table in database
183 |
184 | @param columnName The name of the column.
185 |
186 | @param tableName The name of the table.
187 |
188 | @return `YES` if column exists in table in question; `NO` otherwise.
189 |
190 | @see columnExists:inTableWithName:
191 |
192 | @warning Deprecated - use `` instead.
193 | */
194 |
195 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __deprecated_msg("Use columnExists:inTableWithName: instead");
196 |
197 |
198 | /** Validate SQL statement
199 |
200 | This validates SQL statement by performing `sqlite3_prepare_v2`, but not returning the results, but instead immediately calling `sqlite3_finalize`.
201 |
202 | @param sql The SQL statement being validated.
203 |
204 | @param error This is a pointer to a `NSError` object that will receive the autoreleased `NSError` object if there was any error. If this is `nil`, no `NSError` result will be returned.
205 |
206 | @return `YES` if validation succeeded without incident; `NO` otherwise.
207 |
208 | */
209 |
210 | - (BOOL)validateSQL:(NSString*)sql error:(NSError * _Nullable *)error;
211 |
212 |
213 | ///-----------------------------------
214 | /// @name Application identifier tasks
215 | ///-----------------------------------
216 |
217 | /** Retrieve application ID
218 |
219 | @return The `uint32_t` numeric value of the application ID.
220 |
221 | @see setApplicationID:
222 | */
223 |
224 | @property (nonatomic) uint32_t applicationID;
225 |
226 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE
227 |
228 | /** Retrieve application ID string
229 |
230 | @see setApplicationIDString:
231 | */
232 |
233 | @property (nonatomic, retain) NSString *applicationIDString;
234 |
235 | #endif
236 |
237 | ///-----------------------------------
238 | /// @name user version identifier tasks
239 | ///-----------------------------------
240 |
241 | /** Retrieve user version
242 |
243 | @see setUserVersion:
244 | */
245 |
246 | @property (nonatomic) uint32_t userVersion;
247 |
248 | @end
249 |
250 | NS_ASSUME_NONNULL_END
251 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/FMDB/src/fmdb/FMDatabaseAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // FMDatabaseAdditions.m
3 | // fmdb
4 | //
5 | // Created by August Mueller on 10/30/05.
6 | // Copyright 2005 Flying Meat Inc.. All rights reserved.
7 | //
8 |
9 | #import "FMDatabase.h"
10 | #import "FMDatabaseAdditions.h"
11 | #import "TargetConditionals.h"
12 |
13 | #if FMDB_SQLITE_STANDALONE
14 | #import
15 | #else
16 | #import
17 | #endif
18 |
19 | @interface FMDatabase (PrivateStuff)
20 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray * _Nullable)arrayArgs orDictionary:(NSDictionary * _Nullable)dictionaryArgs orVAList:(va_list)args;
21 | @end
22 |
23 | @implementation FMDatabase (FMDatabaseAdditions)
24 |
25 | #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \
26 | va_list args; \
27 | va_start(args, query); \
28 | FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args]; \
29 | va_end(args); \
30 | if (![resultSet next]) { return (type)0; } \
31 | type ret = [resultSet sel:0]; \
32 | [resultSet close]; \
33 | [resultSet setParentDB:nil]; \
34 | return ret;
35 |
36 |
37 | - (NSString *)stringForQuery:(NSString*)query, ... {
38 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex);
39 | }
40 |
41 | - (int)intForQuery:(NSString*)query, ... {
42 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex);
43 | }
44 |
45 | - (long)longForQuery:(NSString*)query, ... {
46 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex);
47 | }
48 |
49 | - (BOOL)boolForQuery:(NSString*)query, ... {
50 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex);
51 | }
52 |
53 | - (double)doubleForQuery:(NSString*)query, ... {
54 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex);
55 | }
56 |
57 | - (NSData*)dataForQuery:(NSString*)query, ... {
58 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex);
59 | }
60 |
61 | - (NSDate*)dateForQuery:(NSString*)query, ... {
62 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex);
63 | }
64 |
65 |
66 | - (BOOL)tableExists:(NSString*)tableName {
67 |
68 | tableName = [tableName lowercaseString];
69 |
70 | FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName];
71 |
72 | //if at least one next exists, table exists
73 | BOOL returnBool = [rs next];
74 |
75 | //close and free object
76 | [rs close];
77 |
78 | return returnBool;
79 | }
80 |
81 | /*
82 | get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING]
83 | check if table exist in database (patch from OZLB)
84 | */
85 | - (FMResultSet*)getSchema {
86 |
87 | //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING]
88 | FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"];
89 |
90 | return rs;
91 | }
92 |
93 | /*
94 | get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER]
95 | */
96 | - (FMResultSet*)getTableSchema:(NSString*)tableName {
97 |
98 | //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER]
99 | FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"pragma table_info('%@')", tableName]];
100 |
101 | return rs;
102 | }
103 |
104 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName {
105 |
106 | BOOL returnBool = NO;
107 |
108 | tableName = [tableName lowercaseString];
109 | columnName = [columnName lowercaseString];
110 |
111 | FMResultSet *rs = [self getTableSchema:tableName];
112 |
113 | //check if column is present in table schema
114 | while ([rs next]) {
115 | if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) {
116 | returnBool = YES;
117 | break;
118 | }
119 | }
120 |
121 | //If this is not done FMDatabase instance stays out of pool
122 | [rs close];
123 |
124 | return returnBool;
125 | }
126 |
127 |
128 |
129 | - (uint32_t)applicationID {
130 | #if SQLITE_VERSION_NUMBER >= 3007017
131 | uint32_t r = 0;
132 |
133 | FMResultSet *rs = [self executeQuery:@"pragma application_id"];
134 |
135 | if ([rs next]) {
136 | r = (uint32_t)[rs longLongIntForColumnIndex:0];
137 | }
138 |
139 | [rs close];
140 |
141 | return r;
142 | #else
143 | NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil);
144 | if (self.logsErrors) NSLog(@"%@", errorMessage);
145 | return 0;
146 | #endif
147 | }
148 |
149 | - (void)setApplicationID:(uint32_t)appID {
150 | #if SQLITE_VERSION_NUMBER >= 3007017
151 | NSString *query = [NSString stringWithFormat:@"pragma application_id=%d", appID];
152 | FMResultSet *rs = [self executeQuery:query];
153 | [rs next];
154 | [rs close];
155 | #else
156 | NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil);
157 | if (self.logsErrors) NSLog(@"%@", errorMessage);
158 | #endif
159 | }
160 |
161 |
162 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE
163 |
164 | - (NSString*)applicationIDString {
165 | #if SQLITE_VERSION_NUMBER >= 3007017
166 | NSString *s = NSFileTypeForHFSTypeCode([self applicationID]);
167 |
168 | assert([s length] == 6);
169 |
170 | s = [s substringWithRange:NSMakeRange(1, 4)];
171 |
172 |
173 | return s;
174 | #else
175 | NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil);
176 | if (self.logsErrors) NSLog(@"%@", errorMessage);
177 | return nil;
178 | #endif
179 | }
180 |
181 | - (void)setApplicationIDString:(NSString*)s {
182 | #if SQLITE_VERSION_NUMBER >= 3007017
183 | if ([s length] != 4) {
184 | NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]);
185 | }
186 |
187 | [self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])];
188 | #else
189 | NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil);
190 | if (self.logsErrors) NSLog(@"%@", errorMessage);
191 | #endif
192 | }
193 |
194 | #endif
195 |
196 | - (uint32_t)userVersion {
197 | uint32_t r = 0;
198 |
199 | FMResultSet *rs = [self executeQuery:@"pragma user_version"];
200 |
201 | if ([rs next]) {
202 | r = (uint32_t)[rs longLongIntForColumnIndex:0];
203 | }
204 |
205 | [rs close];
206 | return r;
207 | }
208 |
209 | - (void)setUserVersion:(uint32_t)version {
210 | NSString *query = [NSString stringWithFormat:@"pragma user_version = %d", version];
211 | FMResultSet *rs = [self executeQuery:query];
212 | [rs next];
213 | [rs close];
214 | }
215 |
216 | #pragma clang diagnostic push
217 | #pragma clang diagnostic ignored "-Wdeprecated-implementations"
218 |
219 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) {
220 | return [self columnExists:columnName inTableWithName:tableName];
221 | }
222 |
223 | #pragma clang diagnostic pop
224 |
225 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error {
226 | sqlite3_stmt *pStmt = NULL;
227 | BOOL validationSucceeded = YES;
228 |
229 | int rc = sqlite3_prepare_v2([self sqliteHandle], [sql UTF8String], -1, &pStmt, 0);
230 | if (rc != SQLITE_OK) {
231 | validationSucceeded = NO;
232 | if (error) {
233 | *error = [NSError errorWithDomain:NSCocoaErrorDomain
234 | code:[self lastErrorCode]
235 | userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage]
236 | forKey:NSLocalizedDescriptionKey]];
237 | }
238 | }
239 |
240 | sqlite3_finalize(pStmt);
241 |
242 | return validationSucceeded;
243 | }
244 |
245 | @end
246 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/FMDB/src/fmdb/FMDatabasePool.h:
--------------------------------------------------------------------------------
1 | //
2 | // FMDatabasePool.h
3 | // fmdb
4 | //
5 | // Created by August Mueller on 6/22/11.
6 | // Copyright 2011 Flying Meat Inc. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @class FMDatabase;
14 |
15 | /** Pool of `` objects.
16 |
17 | ### See also
18 |
19 | - ``
20 | - ``
21 |
22 | @warning Before using `FMDatabasePool`, please consider using `` instead.
23 |
24 | If you really really really know what you're doing and `FMDatabasePool` is what
25 | you really really need (ie, you're using a read only database), OK you can use
26 | it. But just be careful not to deadlock!
27 |
28 | For an example on deadlocking, search for:
29 | `ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD`
30 | in the main.m file.
31 | */
32 |
33 | @interface FMDatabasePool : NSObject
34 |
35 | /** Database path */
36 |
37 | @property (atomic, copy, nullable) NSString *path;
38 |
39 | /** Delegate object */
40 |
41 | @property (atomic, assign, nullable) id delegate;
42 |
43 | /** Maximum number of databases to create */
44 |
45 | @property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate;
46 |
47 | /** Open flags */
48 |
49 | @property (atomic, readonly) int openFlags;
50 |
51 | /** Custom virtual file system name */
52 |
53 | @property (atomic, copy, nullable) NSString *vfsName;
54 |
55 |
56 | ///---------------------
57 | /// @name Initialization
58 | ///---------------------
59 |
60 | /** Create pool using path.
61 |
62 | @param aPath The file path of the database.
63 |
64 | @return The `FMDatabasePool` object. `nil` on error.
65 | */
66 |
67 | + (instancetype)databasePoolWithPath:(NSString * _Nullable)aPath;
68 |
69 | /** Create pool using file URL.
70 |
71 | @param url The file `NSURL` of the database.
72 |
73 | @return The `FMDatabasePool` object. `nil` on error.
74 | */
75 |
76 | + (instancetype)databasePoolWithURL:(NSURL * _Nullable)url;
77 |
78 | /** Create pool using path and specified flags
79 |
80 | @param aPath The file path of the database.
81 | @param openFlags Flags passed to the openWithFlags method of the database.
82 |
83 | @return The `FMDatabasePool` object. `nil` on error.
84 | */
85 |
86 | + (instancetype)databasePoolWithPath:(NSString * _Nullable)aPath flags:(int)openFlags;
87 |
88 | /** Create pool using file URL and specified flags
89 |
90 | @param url The file `NSURL` of the database.
91 | @param openFlags Flags passed to the openWithFlags method of the database.
92 |
93 | @return The `FMDatabasePool` object. `nil` on error.
94 | */
95 |
96 | + (instancetype)databasePoolWithURL:(NSURL * _Nullable)url flags:(int)openFlags;
97 |
98 | /** Create pool using path.
99 |
100 | @param aPath The file path of the database.
101 |
102 | @return The `FMDatabasePool` object. `nil` on error.
103 | */
104 |
105 | - (instancetype)initWithPath:(NSString * _Nullable)aPath;
106 |
107 | /** Create pool using file URL.
108 |
109 | @param url The file `NSURL of the database.
110 |
111 | @return The `FMDatabasePool` object. `nil` on error.
112 | */
113 |
114 | - (instancetype)initWithURL:(NSURL * _Nullable)url;
115 |
116 | /** Create pool using path and specified flags.
117 |
118 | @param aPath The file path of the database.
119 | @param openFlags Flags passed to the openWithFlags method of the database
120 |
121 | @return The `FMDatabasePool` object. `nil` on error.
122 | */
123 |
124 | - (instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags;
125 |
126 | /** Create pool using file URL and specified flags.
127 |
128 | @param url The file `NSURL` of the database.
129 | @param openFlags Flags passed to the openWithFlags method of the database
130 |
131 | @return The `FMDatabasePool` object. `nil` on error.
132 | */
133 |
134 | - (instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags;
135 |
136 | /** Create pool using path and specified flags.
137 |
138 | @param aPath The file path of the database.
139 | @param openFlags Flags passed to the openWithFlags method of the database
140 | @param vfsName The name of a custom virtual file system
141 |
142 | @return The `FMDatabasePool` object. `nil` on error.
143 | */
144 |
145 | - (instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags vfs:(NSString * _Nullable)vfsName;
146 |
147 | /** Create pool using file URL and specified flags.
148 |
149 | @param url The file `NSURL` of the database.
150 | @param openFlags Flags passed to the openWithFlags method of the database
151 | @param vfsName The name of a custom virtual file system
152 |
153 | @return The `FMDatabasePool` object. `nil` on error.
154 | */
155 |
156 | - (instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags vfs:(NSString * _Nullable)vfsName;
157 |
158 | /** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object.
159 |
160 | Subclasses can override this method to return specified Class of 'FMDatabase' subclass.
161 |
162 | @return The Class of 'FMDatabase' subclass, that will be used to instantiate database object.
163 | */
164 |
165 | + (Class)databaseClass;
166 |
167 | ///------------------------------------------------
168 | /// @name Keeping track of checked in/out databases
169 | ///------------------------------------------------
170 |
171 | /** Number of checked-in databases in pool
172 | */
173 |
174 | @property (nonatomic, readonly) NSUInteger countOfCheckedInDatabases;
175 |
176 | /** Number of checked-out databases in pool
177 | */
178 |
179 | @property (nonatomic, readonly) NSUInteger countOfCheckedOutDatabases;
180 |
181 | /** Total number of databases in pool
182 | */
183 |
184 | @property (nonatomic, readonly) NSUInteger countOfOpenDatabases;
185 |
186 | /** Release all databases in pool */
187 |
188 | - (void)releaseAllDatabases;
189 |
190 | ///------------------------------------------
191 | /// @name Perform database operations in pool
192 | ///------------------------------------------
193 |
194 | /** Synchronously perform database operations in pool.
195 |
196 | @param block The code to be run on the `FMDatabasePool` pool.
197 | */
198 |
199 | - (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block;
200 |
201 | /** Synchronously perform database operations in pool using transaction.
202 |
203 | @param block The code to be run on the `FMDatabasePool` pool.
204 | */
205 |
206 | - (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block;
207 |
208 | /** Synchronously perform database operations in pool using deferred transaction.
209 |
210 | @param block The code to be run on the `FMDatabasePool` pool.
211 | */
212 |
213 | - (void)inDeferredTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block;
214 |
215 | /** Synchronously perform database operations in pool using save point.
216 |
217 | @param block The code to be run on the `FMDatabasePool` pool.
218 |
219 | @return `NSError` object if error; `nil` if successful.
220 |
221 | @warning You can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. If you need to nest, use `<[FMDatabase startSavePointWithName:error:]>` instead.
222 | */
223 |
224 | - (NSError * _Nullable)inSavePoint:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block;
225 |
226 | @end
227 |
228 |
229 | /** FMDatabasePool delegate category
230 |
231 | This is a category that defines the protocol for the FMDatabasePool delegate
232 | */
233 |
234 | @interface NSObject (FMDatabasePoolDelegate)
235 |
236 | /** Asks the delegate whether database should be added to the pool.
237 |
238 | @param pool The `FMDatabasePool` object.
239 | @param database The `FMDatabase` object.
240 |
241 | @return `YES` if it should add database to pool; `NO` if not.
242 |
243 | */
244 |
245 | - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database;
246 |
247 | /** Tells the delegate that database was added to the pool.
248 |
249 | @param pool The `FMDatabasePool` object.
250 | @param database The `FMDatabase` object.
251 |
252 | */
253 |
254 | - (void)databasePool:(FMDatabasePool*)pool didAddDatabase:(FMDatabase*)database;
255 |
256 | @end
257 |
258 | NS_ASSUME_NONNULL_END
259 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/MetaData/CacheImage.h:
--------------------------------------------------------------------------------
1 | //
2 | // COCacheImage.h
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/6/18.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @class Function;
14 |
15 | @interface CacheImage : NSObject
16 |
17 | - (BOOL)searchFunction:(Function *)method withSuperName:(NSString *)supername;
18 | - (nullable NSString *)getSuperNameWithClassname:(NSString *)classname;
19 |
20 | @property (nonatomic, strong, readonly) NSString *imageVersion;
21 | @property (nonatomic, strong, readonly, class) NSString *versionString;
22 |
23 | - (void)enumerateCacheWithBlock:(BOOL(^)(NSString *clazz, NSArray *method, NSInteger progress))block;
24 |
25 | @end
26 |
27 | NS_ASSUME_NONNULL_END
28 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/MetaData/CacheImage.mm:
--------------------------------------------------------------------------------
1 | //
2 | // COCacheImage.m
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/6/18.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | #import "CacheImage.h"
10 | #import "structs.h"
11 | #import "obfuse_code-Swift.h"
12 | #include
13 |
14 | using namespace std;
15 |
16 | FOUNDATION_EXTERN struct __class__ **L_CO_LABEL_CLASS_$;
17 | FOUNDATION_EXTERN struct __image_info _CO_CLASS_IMAGE_INFO_$;
18 |
19 | struct __image_info {
20 | const char *version;
21 | unsigned long size;
22 | };
23 |
24 | NS_INLINE NSUInteger image_size()
25 | {
26 | return _CO_CLASS_IMAGE_INFO_$.size;
27 | }
28 |
29 | NS_INLINE const char * image_ver()
30 | {
31 | return _CO_CLASS_IMAGE_INFO_$.version;
32 | }
33 |
34 | @interface COCPointer : NSObject
35 | @property (nonatomic) struct __class__ *val;
36 | @end
37 |
38 | @implementation COCPointer
39 |
40 | + (instancetype)pointer:(struct __class__ *)val
41 | {
42 | COCPointer *obj = [COCPointer new];
43 | obj.val = val;
44 | return obj;
45 | }
46 |
47 | @end
48 |
49 | @interface CacheImage ()
50 |
51 | @property (nonatomic, strong) NSMutableDictionary *> *cache;
52 | @property (nonatomic, strong) NSMutableDictionary *image_hash;
53 |
54 | @end
55 |
56 | @implementation CacheImage
57 |
58 | - (instancetype)init
59 | {
60 | if (self = [super init]) {
61 | _cache = [NSMutableDictionary dictionary];
62 | _image_hash = [NSMutableDictionary dictionary];
63 | [self _read_image];
64 | }
65 | return self;
66 | }
67 |
68 | - (BOOL)searchFunction:(Function *)method withSuperName:(NSString *)supername
69 | {
70 | NSArray *methods = _cache[supername];
71 | struct __class__ *clazz = NULL;
72 | if (!methods) {
73 | methods = [self __cacheWithClassName:supername clazz:&clazz];
74 | if (!methods) {
75 | fprintf(stderr, "\033[41;37m[Error]: %s is not exists in cache image. Check your SDK Version(%s).\033[0m", supername.UTF8String, image_ver());
76 | exit(-1);
77 | }
78 | }
79 | for (Function *m in methods) {
80 | if ([method isEqual:m]) {
81 | return YES;
82 | }
83 | }
84 | if (!clazz) {
85 | return NO;
86 | }
87 | return [self searchFunction:method withSuperName:@(clazz->name)];
88 | }
89 |
90 | - (nullable NSString *)getSuperNameWithClassname:(NSString *)classname
91 | {
92 | COCPointer *obj = _image_hash[classname];
93 | return obj.val->superclass->name ? @(obj.val->superclass->name) : nil;
94 | }
95 |
96 | - (nullable NSArray *)__cacheWithClassName:(NSString *)classname clazz:(struct __class__ **)clazz
97 | {
98 | COCPointer *obj = _image_hash[classname];
99 | if (!obj) {
100 | return nil;
101 | }
102 |
103 | auto p = obj.val;
104 | NSMutableArray *methods = [NSMutableArray array];
105 | struct __method__ *p_method = (struct __method__ *)p->method_list->methods;
106 | for (unsigned int i=0; imethod_list->count; ++i) {
107 | NSString *selector = @(p_method[i].name);
108 | Function *m = [[Function alloc] initWithName:selector location:NSMakeRange(0, 0)];
109 | [methods addObject:m];
110 | NSArray *sels = [selector componentsSeparatedByString:@":"];
111 | for (NSString *sel in sels) {
112 | if (sel.length) {
113 | [m addWithSelector:[[SelectorPart alloc] initWithName:sel location:NSMakeRange(0, 0)]];
114 | }
115 | }
116 | }
117 | if (clazz) {
118 | *clazz = p;
119 | [_cache setObject:methods forKey:classname];
120 | }
121 | return methods;
122 | }
123 |
124 | - (NSString *)imageVersion
125 | {
126 | return @(image_ver());
127 | }
128 |
129 | - (void)_read_image
130 | {
131 | struct __class__ *p = L_CO_LABEL_CLASS_$[0];
132 | struct __class__ *end = p + image_size();
133 | do {
134 | _image_hash[@(p->name)] = [COCPointer pointer:p];
135 | } while (++p < end);
136 | }
137 |
138 | + (NSString *)versionString
139 | {
140 | return @(image_ver());
141 | }
142 |
143 | - (void)enumerateCacheWithBlock:(BOOL(^)(NSString *clazz, NSArray *method, NSInteger progress))block
144 | {
145 | static atomic_long idx;
146 | idx = 0;
147 | double total = self.image_hash.count;
148 | NSUInteger count = self.image_hash.count / 4;
149 | NSArray *objects = self.image_hash.allValues;
150 | NSArray *_1 = [objects subarrayWithRange:NSMakeRange(0, count)];
151 | NSArray *_2 = [objects subarrayWithRange:NSMakeRange(count, count)];
152 | NSArray *_3 = [objects subarrayWithRange:NSMakeRange(2 * count, count)];
153 | NSArray *_4 = [objects subarrayWithRange:NSMakeRange(3 * count, self.image_hash.count - 3 * count)];
154 |
155 | NSArray *pool = @[_1, _2, _3, _4];
156 |
157 | void(^threadBlock)(NSUInteger index) = ^(NSUInteger index){
158 | for (COCPointer *obj in pool[index]) {
159 | NSArray *methods = [self __cacheWithClassName:@(obj.val->name) clazz:nil];
160 | if (methods != nil) {
161 | if (block(@(obj.val->name), methods, (++idx / total) * 100)) {
162 | break;
163 | }
164 | }
165 | }
166 | };
167 |
168 | [NSThread detachNewThreadWithBlock:^{ threadBlock(0); }];
169 | [NSThread detachNewThreadWithBlock:^{ threadBlock(1); }];
170 | [NSThread detachNewThreadWithBlock:^{ threadBlock(2); }];
171 | [NSThread detachNewThreadWithBlock:^{ threadBlock(3); }];
172 | }
173 | @end
174 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/MetaData/structs.h:
--------------------------------------------------------------------------------
1 | //
2 | // structs.h
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/6/18.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | #ifndef structs_h
10 | #define structs_h
11 |
12 | struct __method__
13 | {
14 | const char *name;
15 | };
16 |
17 | struct __method__list
18 | {
19 | unsigned int reserved;
20 | unsigned int count;
21 | struct __method__ methods[0];
22 | };
23 |
24 | struct __class__
25 | {
26 | struct __class__ *superclass;
27 | const char *name;
28 | const struct __method__list *method_list;
29 | };
30 |
31 | #endif /* structs_h */
32 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/NSString+COMD5.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+COMD5.h
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/5/31.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NSString (COMD5)
14 |
15 | @property (nonatomic, strong, readonly) NSString *md5;
16 |
17 | @end
18 |
19 | NS_ASSUME_NONNULL_END
20 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/Objective-C/NSString+COMD5.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+COMD5.m
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/5/31.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | #import "NSString+COMD5.h"
10 | #import
11 |
12 | @implementation NSString (COMD5)
13 |
14 | - (NSString *)md5
15 | {
16 | const char *cStr = [self UTF8String];
17 | unsigned char result[16];
18 | CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
19 | return [NSString stringWithFormat:
20 | @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
21 | result[0], result[1], result[2], result[3],
22 | result[4], result[5], result[6], result[7],
23 | result[8], result[9], result[10], result[11],
24 | result[12], result[13], result[14], result[15]
25 | ];
26 | }
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // CodeObfuscation
4 | //
5 | // Created by hejunqiu on 2017/8/15.
6 | // Copyright © 2017年 CHE. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | var manager = ObfuscationManager.init()
12 | do {
13 | try manager.go(with: Arguments.arguments.rootpath as String)
14 | } catch {
15 | print(error)
16 | }
17 |
--------------------------------------------------------------------------------
/obfuse-code/obfuse-code/obfuse-code-Bridging-Header.h:
--------------------------------------------------------------------------------
1 |
2 | #import "NSString+COMD5.h"
3 | #import "CacheImage.h"
4 | #import "FMDB.h"
5 |
--------------------------------------------------------------------------------