PJ’s iOS development routine

preface

Vary is one of my favorite tools. Its official slogan is “Share All Things One” and is “lightweight social network, heavyweight creative tool.” It has been six months since joining the Vary team on August 9 last year…… In early 2017, I read about Dandy Weng’s promotion for Vary on Zhihu. If I remember correctly, I was in a software engineering class when I came across this article. After I finished, I reviewed all the author’s information and applied for the TestFlight test. Luckily, I got it.

When I open the Vary app, I grab the girl sitting next to me and play with her. I never thought apps could do that! Interactive experiences can be so good! The UI is so beautiful! I can say that until now, I have never come across another app that has impressed me so much. I have been using it till now, and I have added the author’s wechat account. I have been reporting the bug to the author continuously during the process of using it. It is because I like it so much that I reported the bug. You can read this article if you are interested.

I was moving bricks and I just got up from my chair and yelled! I got to work on Vary! I’m going to join my favorite app development job! Later on, we met up with the current development work and decided to do the following:

  • Insert new version when text editingEmojiFlash back when saving; (This bug caused me to lose the long article I edited several times)
  • Home information flow cache;
  • Message persistence;
  • A draft card;
  • The adapter;
  • IOS 12 compatibility testing;

At first, I was responsible for fixing the flash backoff problem when inserting new emojis. Finally, due to too much work, THIS part of work was handed back to Dandy. Hahaha ~ After a period of time, I began to optimize the height cache of the message flow card on the home page. Later, Dandy came to ask me about my progress almost every week. However, at that time, I was busy with tripartite and class design and other trivial matters, so I only had two extra hours to do my own work every day. So finally, I was tired of being asked by Dandy and directly forced myself to finish the first phase of optimization of the home page information flow card cache. However, there were still highly inaccurate problems occasionally, so I had to put them into the second phase of optimization.

Later, I couldn’t push the time, so I told Dandy to follow up after the winter vacation. I thought Dandy would “spare” me. Ha ha ha, since the winter vacation, I have made two main requirements of “card draft” and “timed saving”. In the following article, I’ll take a look at some of the questions and thoughts I encountered in developing these two interesting requirements without moving beyond the Vary core code.

Comb demand

The draft card

The requirements of the draft card are relatively easy to sort out, but the development process has been modified for several times due to my unfamiliarity with the relevant logic codes. The final development process is as follows:

  • Those who go onto university can go onto those who qualify. In the menu that goes onto university and those who go onto university can go onto those who qualify. Those who go onto university can go onto those who qualify.
  • Next time you click “+”, first determine whether there is a draft (cache), if there is a direct display of “module edit” page, and load the cache module, if not, go to the original logic;

Time to save

Dandy and Dandy discussed timing saving for several times. In the process of discussion, we found that the implementation idea of the “card draft” function written before was not correct, so we continued to rework until now. The final result of our discussion is:

  • “Text editing” module adopts the mode similar to “Graphite” and other online document editing applications, directly caching user edited content;
  • Other modules, such as “video”, “voice” and “location”, are cached when the “OK” event of each module is triggered because the operation logic is relatively simple and does not need to be retained for a long time.

Be familiar with the code

Development of Vary has not been continuous, but has been tinkered with whenever possible. According to above by combing the two requirements, emphatically saw than the relevant logic, let me feel surprise, than in the rendering of card is not using Native ability to apply colours to a drawing, how do I say sometimes don’t feel also, thought that this is a bit “caton” is just a small problem, have never thought when I saw this article, I deeply felt that I was wrong, the original webKit even this is a little bit of a problem.

Next, get familiar with the code. I found that each card was a WKWebView, which directly rendered the downloaded HTML string, and did a lot of JS code injection in Native. After seeing this, I began to feel a little frustrated, because I did not see what I wanted to see. The automatic typography engine in Vary wasn’t quite as “cool” as I thought it would be, it simply took a lot of “hard work” and didn’t have much to catch my eye. But I did see an implementation that surprised me so much I laughed out loud.

One of Dandy’s points for Vary in his article goes something like this:

I’m also exploring how technology can lead to innovation at the interactive level, such as Vary’s iOS App, which measures the area of a finger touching the screen to adjust the scrolling speed of a card: when you swipe with your finger, you only scroll one card at a time; When you swipe your whole finger across the screen, you scroll through multiple cards in succession, depending on the inertia of your sliding speed. This may sound silly, but it’s very easy to learn and very practical.

I actually laughed when I looked at the implementation. The core of this feature is the use of the majorRadius property of touch objects, and if you really want to guess what the implementation of Vary is, Recommend you carefully play after ten minutes can also guess out.

Then I started to look at the code for the “Module Edit” page, and the code in the module is very clear logic, well, the implementation idea that comes to mind intuitively right now. Than was the most interesting places to see in the realization of each module, one of the most let me excited is “image” and “voice” module, and after careful study, I have been rubbing their hands to give oneself psychological preparation, morning is a good research the realization of the two modules is good, but after I see the concrete implementation, such as a look at the time, the original only in the past for half an hour. For those of you who follow me regularly on Github, the “voice” module uses an open source library. The reason why the “picture” module can occupy such a large share in my heart is because of its beautiful UI. I thought the implementation was perfect, but actually what surprised me was the novel idea of developing students before, which was the most disappointing!

Official development

After getting to know each other piecemeal for quite a while, I went back and forth between reworking and continuing development and doing some minor refactoring, initially thinking of doing a major refactoring of Vary directly, but at this point in time I don’t want to do too much for myself.

The draft card

The core of the card draft revolves around NSFileManager. Because only save a single Data, there is no need to go on Core Data, but in the follow-up development of the card information flow on the home page, there is a place involving the cache of a series of the same Data, but also involves the update of part of the card, if this time to use NSFileManager cache Data management will be a little restrained, Core Data is the perfect place to do this.

First, the logic involved in the cache policy in Vary was written to encapsulate a simple NSFileManager based management class, PJCache, independent of the core business, as follows:

//
// PJCache.h
// Vary
//
// Created by PJHubs on 2019/2/10.
Copyright © 2019 Vary iOS Team. All rights reserved.
//


#import <Foundation/Foundation.h>

@interface PJCache : NSObject

// Create a file+ (BOOL)cacheFileSet:(NSString*)fileName contents:(NSString*)contents;

// Create a cache file with NSData+ (BOOL)cacheFileSetNSdata:(NSString*)fileName contents:(NSData*)contents;

// Read the cache file and return NSData+ (NSData*)cacheFileGetNSdata:(NSString*)fileName;

// Whether the cache file exists+ (BOOL)cacheFileExists:(NSString*)fileName;

// Cache file deleted+ (BOOL)cacheFileDelete:(NSString*)fileName;

@end

Copy the code
//
// PJCache.m
// Vary
//
// Created by PJHubs on 2019/2/10.
Copyright © 2019 Vary iOS Team. All rights reserved.
//

#import "PJCache.h"

@implementation PJCache

// Create a file+ (BOOL)cacheFileSet:(NSString*)fileName
           contents:(NSString*)contents {
    NSError *err;
    NSString *path = [self cacheFilePath:fileName];
    NSFileManager *fm = [NSFileManager defaultManager];
    if ([fm fileExistsAtPath:path]) {
        [fm removeItemAtPath:path error:&err];
    }
    return [fm createFileAtPath:path
                       contents:[contents dataUsingEncoding:NSUTF8StringEncoding]
                     attributes:nil];
}

// Create a cache file with NSData+ (BOOL)cacheFileSetNSdata:(NSString*)fileName
                 contents:(NSData*)contents {
    NSError *err;
    NSString *path = [self cacheFilePath:fileName];
    NSFileManager *fm = [NSFileManager defaultManager];
    if ([fm fileExistsAtPath:path]) {
        [fm removeItemAtPath:path error:&err];
    }
    return [fm createFileAtPath:path
                       contents:contents
                     attributes:nil];
}

// Read the cache file and return NSData+ (NSData*)cacheFileGetNSdata:(NSString*)fileName {
    NSString *path = [self cacheFilePath:fileName];
    NSFileManager *fm=[NSFileManager defaultManager];
    if ([fm fileExistsAtPath:path]) {
        return [fm contentsAtPath:path];
    }
    return nil;
}

// Check whether the cache file exists+ (BOOL)cacheFileExists:(NSString*)fileName {
    NSString *path = [self cacheFilePath:fileName];
    NSFileManager *fm=[NSFileManager defaultManager];
    return [fm fileExistsAtPath:path];
}

// Delete cached files+ (BOOL)cacheFileDelete:(NSString*)fileName {
    NSString *path = [self cacheFilePath:fileName];
    NSFileManager *fm=[NSFileManager defaultManager];
    if ([fm fileExistsAtPath:path]) {
        NSError *err;
        return [fm removeItemAtPath:path error:&err];
    }
    return YES;
}

@end

Copy the code

For example, if the cache data is too large, write and read data asynchronously. The card draft didn’t give much thought at first, but after adjusting the relevant new code according to the actual business logic, the cached preview card received the rendered HTML string. At that time, Dandy also mentioned a detail problem, “when there is cache, the user should directly load the content of the last unpublished card when creating the card next time”. I didn’t understand this sentence very well at that time, thinking that the rendered card should be loaded as quickly as possible when the user clicks “+”. In fact, you should encoder the card brother module, and cache the archived “module” object array directly after completion.

Automatically saved

At that time, I took a wrong path in fulfilling the requirement of “card draft”, which led to a complicated process in the development and design of “auto save” function based on this, because the final goal at that time was to complete the HTML string of card rendering. This led to the “autosave” every time to get the HTML string as the “save” success symbol, which cost more server resources, fortunately, in the process of many discussions with Dandy was timely stopped.

The trigger of the “auto save” function is displayed on the “Module Edit” page, which is mainly divided into two situations:

  • On the “File Editing” page, use the GCD to perform “cleaning judgment” for the user input content every 3s to ensure that the content cached each time is completely inconsistent with the content cached last time and must not be empty. When the “OK” event is triggered, bring the text content input by the end user, stop the cache and update the cache to the latest immediately, so as to avoid the loss of text content caused by the user entering a punctuation mark and exiting directly after completing the last cache 3s interval.

  • The “auto save” of other modules is also mentioned above, because the user does not linger in other modules in the non-” text “module for a long time (not excluding the possibility of forgetting), so this is relatively simple, in each module” ok “event, the cache is triggered once.

If the data structure to be saved is simple, you can also directly access NSFileManager.

conclusion

With these two things done, the main task of this release iteration is done. Because only less than three hours a day full of time to do iteration maintenance, many details still failed to consider good, than there are still many places need to optimize, but due to time relationship lead to it is difficult to put a lot of time, really hope to have a month of time to complete than do the maintenance of the a big well ~

I hope you enjoy experiencing Vary better and find a place where you can share your inner world seriously.