Development of iOS Mobile Generation Tool

Posted by the_lynx123 on Sat, 01 Jun 2019 20:49:11 +0200

Last month's article Document-Oriented Development on Mobile A long-lost article was recommended to the home page by the editor, which also attracted the attention of the hungry god. Although the final strength was not good enough to be hired, but also pointed out the direction of my progress. From April to now, from an idea to the realization step by step, I feel that I still have sufficient progress, today we continue to improve the project.

I have written a python script generation tool before. Through reading json files for code generation, some users reflect that the readability of json files is not very good (obviously not familiar with json grammar), as well as the need to generate multi-platform code, so Python is not very skilled, so I consider writing an oc project once and for all. Some knowledge points that will not be paid special attention to in normal development, as well as the code design and usage of the generating tool.

Select project type

The first idea that flashed through my mind was to use Command Line Tool command line tools. Isn't it code generation, and to turn on the simulator? It's not kidding.


But we need to read Template template file, because the template file is not compiled file, so we can not directly drag it into the project, and then I think it is through the relative path method to read the file, unfortunately, I found a circle of api that did not get the current path, only sandbox, sandbox.

I think command-line tools and sandboxes? Well, I try to get them through Bundle. I've been trying for a long time to get null and speechless. On Stack Overflow, the command-line tools don't support self-made Bundle reading. Okay, I recognize it. But I think it's just a link problem, it should be readable, but unfortunately I can't find a way to execute it, but there's no way to do it. Give up.


Forcing us to choose Single View Application is just helpless. Well, compromise on the world is not a part of life.

Select the configuration file

In fact, the choice of configuration file is relatively simple. Since it is an oc project, the most simple plist is the best choice. At the same time, it is easier to configure.




Some simple configurations have been made. Students who are not very clear about this part can go there. http://www.jianshu.com/p/47d565bf200e Understanding.

After configuring plist, we put the previous templates together in bundle s and introduced them into the project.


Knowledge Points: Files placed in bundle s are not compiled, but stored as resource files.

Choosing Design Patterns

For design patterns, it is true that I have read a lot of books of related types, and some commonly used design patterns are hand-to-hand. No, for code generation tools, I can immediately think of generator patterns, haha, factory patterns and decoration patterns.

In fact, the naming of these design patterns is quite misleading. What responsibility chain, prototype and so on, in fact, once you look at the code, you will understand the Shinto that things are blown, and you will be drunk.

After choosing the design mode, we created SQBuilder and SQFileParser, the former is factory mode, the latter is decoration mode.

@interface SQBuilder : NSObject

@property (nonatomic,copy) NSString * user;
@property (nonatomic,copy) NSString * prefix;
@property (nonatomic,copy) NSString * module;
@property (nonatomic,strong) NSDictionary * parameter;
@property (nonatomic,strong) NSDictionary * dataList;
@property (nonatomic,strong) NSArray * actionList;

+ (void)runWithFileParser:(NSDictionary *)config success:(void (^)())success failure:(void (^)())failure;

- (void)build;

@end
+ (SQBuilder *)builderWithType:(SQBuilderType)builderType {

    switch (builderType) {
        case SQBuilderTypeiOS:
            return [SQBuilder_iOS new];
            break;
        case SQBuilderTypeAndroid:
            return [SQBuilder_Android new];
            break;
        case SQBuilderTypeNone:
            return [SQBuilder new];
            break;
        default:
            break;
    }
}

SQBuilder's header file exposes the properties that need to be configured in plist and how to load SQFileParser file to parse classes. Different classes can be assigned and created through factory mode.

@interface SQFileParser : NSObject

+ (NSDictionary *)parser_plist_r;

+ (void)parser_rw:(NSString *)path code:(NSString *)code filename:(NSString *)filename header:(NSString *)header parameter:(NSMutableArray *)parameter;

@end
[SQBuilder runWithFileParser:[SQFileParser parser_plist_r] success:^{
    title = @"Build Finished"; message = @"please view the floder on the desktop";
} failure:^{
    title = @"Build Error!!!"; message = @"please enter the right builder type!!";
}];

SQFileParser's header file exposes the interface for reading different file types, and subsequent reading of Excel table types requires only adding a new type interface.

Create folder Api

NSFileManager * manager = [NSFileManager defaultManager];
NSString * path = [NSString stringWithFormat:@"/Users/%@/Desktop/%@", self.user, self.module];
[manager createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:nil];

This is the only way to create folders in oc. It can only read absolute paths, but it can't read the current file paths. The user of mac machine is different from each other. It's a pit. It's the safest way to write and read files like python.

SQFileParser Code Analysis

+ (NSDictionary *)parser_plist_r {
    
    NSBundle * bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"builder.bundle" ofType:nil]];
    return [NSDictionary dictionaryWithContentsOfFile:[bundle pathForResource:@"config/config.plist" ofType:nil]];
}

Read / config.plist configuration file through subbundles, knowledge points: when reading the deep structure of bundles, file paths need to be added.

+ (NSString *)parser_r:(NSString *)filename code:(NSString *)code {
    
    NSBundle * bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"builder.bundle" ofType:nil]];
    return [NSMutableString stringWithContentsOfFile:[bundle pathForResource:[NSString stringWithFormat:@"template/%@/%@", code, filename] ofType:nil] encoding:NSUTF8StringEncoding error:nil];
}

There is no difference between the reading of Templete files and the reading plist above.

static NSString * code;

+ (NSString *)replaceThougth:(NSString *)templete parameter:(NSMutableArray *)parameter {
    
    __block NSString * temp = templete;
    [[parameter firstObject] enumerateKeysAndObjectsUsingBlock:^(NSString *  _Nonnull key, NSString *  _Nonnull obj, BOOL * _Nonnull stop) {
        temp = [templete stringByReplacingOccurrencesOfString:key withString:obj];
    }];
    [parameter removeObjectAtIndex:0];
    if (parameter.count) {
        [SQFileParser replaceThougth:temp parameter:parameter];
    } else {
        code = temp;
    }
    return code;
}

Writing in a new file, that's all. The stringByReplacingOccurrencesOfString method of NSString returns a new changed string but does not change the original pointer. But if you need to replace many places, you need to call many times in a chain. It's ugly. One word summarizes all the points, so what can I think of to avoid this problem right away? Think of the above recursive invocation method, haha.

+ (void)parser_rw:(NSString *)path code:(NSString *)code filename:(NSString *)filename header:(NSString *)header parameter:(NSMutableArray *)parameter {

    NSString * arch = [[filename componentsSeparatedByString:@"."]firstObject];
    NSString * suffix = [[filename componentsSeparatedByString:@"."]lastObject];
    NSString * filename_r = [NSString stringWithFormat:@"%@Template.%@", arch,suffix];
    NSString * filename_w = [NSString stringWithFormat:@"%@/%@%@.%@", path,header,arch,suffix];
    NSString * template =  [SQFileParser parser_r:filename_r code:[code lowercaseString]];
    [[SQFileParser replaceThougth:template parameter:parameter] writeToFile:filename_w atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

This method integrates reading and writing. According to the configuration parameters, input and output file names are stitched together, and the files are read and written.

SQBuilder

In fact, there is nothing to say about this. You can just read the configuration parameters and generate the logical code of the method dynamically.

How to use it?




Open the terminal and enter pwd to get user information, fill in plist configuration file, and enter other parameters. Run will generate the code to the desktop. In the future development, you only need to pay attention to the View layer, the rest need not worry about it.~~

Specific description can be Download SQTemplate To discuss together.

Topics: Python JSON encoding Mobile