Preface:

Every developer has a dream of architecture in mind. Although we can’t directly give system-level architecture like the leaders, we can accumulate some of our own architectural insights and improve them slowly in the daily coding process

Because the schools themselves a person working on the App, and demand is also not clear, often changing requirements (in the school organization to write the common fault of the project), so the process of writing is really more write more annoyed, so had to do to have a small part of the development of reconstruction, the following is a summary in this small white in the reconstruction process some opinions, have to say, The design patterns I talked about in my undergraduate study were really useful, but I couldn’t understand them at that time, until I found that all of them could be set as prototypes).

Several directions of architecture:

  • View layer organization and call design
  • Local persistence
  • Network layer design (network layer will say more general)
  • Dynamic deployment (Web App/Hybrid App/ React-native)

Steps of architectural design:

  • Problem classification, module (this is very important)
  • Make clear the dependency between each module, design a good set of module communication specifications and design modules
  • Keep a certain amount of advance for architecture (blood lessons)
  • Implement the basic modules first, and then combine the basic modules to form the initial architecture

The main thing is: top-down design, bottom-up implementation, quantitative data and then optimization

Agile principles: Open to extension – closed to change


What is a good app architecture?

  • The code is neat and the classification is clear: each module is only responsible for the transactions within the module
  • With little or no documentation, the business side can get started
  • The train of thought and method should be unified, do not multivariate as far as possible
  • No horizontal dependencies, no cross-layer access if necessary (roughly how topological sorting works)

1. When a requirement needs multi-business cooperation development, if it is directly dependent, it will lead to some business engineers at the upper end of the dependency layer idling in the early stage, while engineers at the lower end of the dependency layer have heavy tasks, resulting in delay (it will crush the time of QA old rail, and then find PM to tear *… (2) When a new business is to be opened, it will be difficult to set up the development environment of the new business if the existing businesses are directly dependent on each other and the new business depends on an old business, because all related businesses must be packed into the development environment before the new business can be developed. 3. When a page that is relied on by other businesses is modified, such as changing its name, the modification area involved will be particularly large. The impact is the result of increased task volume and maintenance costs.

Corresponding solutions: rely on sinking, if A, B, C three modules exist horizontal dependence, so the introduction of A new node D, A, B, C implementation dependent on sinking, when A call B of A page, the request to the Mediater, then by the Mediater through certain means for instance of the page to B business, back to A line.

  • Where there should be restrictions on the business side, the flexibility should be created for the business side – the conditions for implementation
  • Easy to test, easy to expand
  • Maintain a certain amount of advance
  • Fewer interfaces and fewer interface parameters
  • A high performance

A few words about non-cross-layer access:

Cross-layer access is when data flows to a module with which it has no docking relationship. Sometimes cross-layer access is unavoidable. For example, the signal in the bottom layer of the network changes from 2G to 3G to 4G. This may require cross-layer notification to the View. But this kind of situation is not much, once appear to want to do everything possible in this layer fix or to the upper or lower layer fix, try not to cross the layer of the situation. Cross-layer access also increases the degree of coupling, which can be significant when a layer needs to be replaced as a whole.

Testability:

Minimize dependencies to facilitate mock. In addition, if you have a highly modular architecture, scaling is very easy.


Architectural layers:

Summary:

There is a lot of talk about ‘three-tier ARCHITECTURE MVC’, so much so that many people think that three-tier architecture is MVC, and MVC is three-tier architecture. It’s not. In fact, there is no concept of Controller in the three-tier architecture, and the description of the three-tier architecture focuses on the logical relationship between modules. MVC has the concept of Controller, which describes the direction of data flow.

Three layer architecture

There are only three types of module roles:

  • Data manager
  • Data processor
  • What data presenter means is that, broadly speaking, there are only three layers of software, and each layer plays a role. The other fourth and fifth layers are generally divided out of one of the three layers, and finally can be summarized into a layer of the three layers, so it is common to use the three-layer architecture to describe.

View Layer design:

Once the architecture of the View layer is implemented or finalized, there is very little room for modification after the App is released. Because it is most closely related to the business, it is important to take a good measure when making decisions.

View layer architecture is one of the factors that affect the iteration cycle of the business side:

This is because the View layer architecture is the closest underlying architecture to the business

View layer architecture knowledge mainly includes:

  • Good coding/implementation specifications
  • Appropriate design patterns (MVC, MVCS, MVVM, VIPER)
  • According to the business situation of the ViewController to do a good split (thin), provide some small tools to facilitate development

View layer code specification :(not necessarily point 4)

ViewDidload: do the addSubview thing

ViewWillAppear: Strictly speaking, this is usually used to update Form data instead of modifying view position. Here’s why:

Timing: First, Autolayout happens after viewWillAppear, so I usually put it in -ViewwillLayOutSubView or -viewDidLayOutSubViews. Because viewWillAppear is called every time the page is about to be displayed, viewWillLayoutSubviews are called in lifeCycle after viewWillAppear but only when the page element needs to be adjusted, Repeated additions to Constraints are avoided

4 viewDidAppear to do things like add a listener

5 attribute initialization is entrusted to getters (lazy loading), which requires that all attributes use getters and setters, and getter and setter methods are written at the end of the.m file, which can improve development efficiency. The other way to think about it is to put all the properties into the setUpPropertyConfig method, and then setUpPropertyConfig into viewDidLoad, either way, no difference.

#pragma mark - life cycle

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self.view addSubview:self.label];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.label.frame = CGRectMake(1, 2, 3, 4);
}

#pragma mark - getters and setters

- (UILabel *)label
{
    if (_label == nil) {
        _label = [[UILabel alloc] init];
        _label.text = @"1234"; _label.font = [UIFont systemFontOfSize:12]; . . }return _label;
}
@end
Copy the code

6. Each delegate method is written to an area, using #pragma mark – UITableViewDelegate. I haven’t been aware of this before, thank you)

7 VC inside as far as possible do not have private methods

It’s not a delegate method, it’s not an Event response method, it’s not a Life cycle method, it’s a private method, These private methods are generally used for date conversion, image cropping and other such auxiliary small functions. These small functions are usually isolated as module tools or system Util classes.

8. View layout method:

It’s a combination of storyboard+ XIB + code

Take the analysis of the @ tang qiao script: portal: https://gist.github.com/tangqiaoboy/b149d03cfd0cd0c2f7a1 this is controversial.

In fact, to implement simple things, it’s as simple as Code, to implement complex things, Code is simpler than StoryBoard.

Therefore, I would like to retrieve the code for complex navigation. I would also like to xiB for simple, static cells and encapsulated widgets.

There are also a few points that I am currently not capable of giving a correct opinion:

A. Whether to enable the service side to derive the ViewController uniformly. B.


#MVC MVC architecture foundation please look like printing notes.

What each module is responsible for:

  • M should do: 1, provide data to the ViewController (network fetch API+ local cache fetch API) 2, provide interface to the ViewController to store data (local cache storage/update new API) 3, provide abstract basic components of the business (generally I choose a Manager (including 1, 2) come out to be responsible for), for Controller scheduling
  • C Should do: (VC’s own View is equivalent to a Container of View managed by C) 1. Manage the life cycle of View Container 2. Generate all View instances and put them into View Container (i.e. C. View) 3. Through the cooperation with Model, to complete the business corresponding to the event.
  • V should do: 1. Respond to events that are not relevant to the business, animate them, click on feedback (if appropriate, try to put it in View), etc. 2. Interface element expression

The following are variations of MVCS and MVVM MVC design patterns

There may be other design patterns, but I’m limited, so I’ll just cover these two

First, the fat Model and the thin Model:

  • The fat Model (the basic idea of MVVM) contains some weak business logic. The goal of the fat Model is that once the Controller gets the data from the fat Model, it can apply it directly to the View with very little or no extra action.

  • Thin Model :(the basic idea of MVCS) the thin Model is only responsible for the expression of business data, and all business, strong and weak, are thrown into the Controller. The goal of a thin Model is to write as fine-grained a Model as possible, and then use helper classes or methods to abstract the weak business, leaving the strong business to the Controller.

First of all, whether MVVM or MVCS, the consensus is that controllers will become very large and difficult to maintain and test as software grows. However, the premise of the two architecture ideas is different. MVCS thinks that Controller does part of the work of Model and needs to be separated into Store, while MVVM thinks Controller does too much data processing. Therefore, MVVM frees the task of data processing from the Controller, making the Controller only need to focus on the work of data allocation, while the ViewModel takes charge of data processing and makes the View respond to the change of ViewModel through notification mechanism.

MVCS:

Conceptually, the part it splits is the Model part, a Store. This Store is dedicated to data access. But from a practical point of view, it takes apart the Controller.

The premise of MVCS is that it assumes that you are a thin Model and that the data is stored and processed in the Controller. So with MVCS, it starts out as a split Controller. Because a Controller does the job of storing data, it becomes so large that it takes the part of the Controller that is responsible for accessing data and gives it to another object, the Store. After this adjustment, the entire structure becomes a true MVCS.

The MVVM:

After the maturity of ReactiveCocoa, the signal mechanism of ViewModel and View finally has a relatively elegant implementation in iOS (View and Model cannot communicate directly in MVC, and Controller needs to act as a coordinator). MVVM is also essentially an idea derived from MVC. The problem MVVM is trying to solve is to minimize Controller tasks.

MVVM is built based on the architectural idea of fat Model, and then split into two parts of fat Model: Model and ViewModel. I want to add an additional explanation to this argument: What the fat Model does is to reduce the load for the Controller first, and then remove the ViewModel based on the fat Model, which is not inconsistent with the general recognition in the industry that MVVM is essentially to reduce the load for the Controller. Because what the fat Model does is also to reduce the burden of the Controller.

In addition, MVVM frees the task of data processing from Controller, which is not inconsistent with the fat Model split by MVVM. To liberate controllers, you first have to have a fat Model, and then split the fat Model into models and viewModels.

In MVVM, the Controller plays the following role:

  • The absence of C in the name of MVVM creates the illusion that MVVM does not need Controller. In fact, Controller must be involved in MVVM. Although MVVM weakens the sense of existence of Controller to some extent, It also slimmed down the Controller (which is the main purpose of MVVM). In fact, MVVM should be model-view-Controller-view architecture, it is not without Controller.

  • The Controller is sandwiched between the View and the ViewModel to do: 1, the main thing is to bind the View to the ViewModel. Logically, the Controller knows which View to display, and the Controller knows which ViewModel to use, but the View and ViewModel don’t know each other, so the Controller is responsible for controlling their binding. 2. General UI logic processing

In a word:

On top of MVC, separate the Controller into a ViewModel that handles data processing, which is the MVVM.

  • Is ReactiveCocoa required for MVVM? Of course not, it’s just that Apple itself doesn’t provide a binding method that works well in this case. Although there are KVO, Notification, block, and delegate for data communication to achieve binding, none of them are as elegant and simple as RACSignal provided by ReactiveCocoa. If ReactiveCocoa is not used, The binding relationship may not be as loose as it is good, but it is still MVVM.

Again I cannot well explain the deep: if you need to know, can look: https://www.teehanlax.com/blog/model-view-viewmodel-for-ios/

About which design patterns to use on the project:

MVC is actually a very high-level abstraction, which means that there are countless architectural ways that can be derived under the MVC system, but all the same, it must conform to the MVC specification. So here’s my advice:

  • As long as it is not the core logic of Controller, you can consider splitting it out, and then defining it as an independent module in the architecture, and designing the implementation, but don’t split it for the sake of splitting it.
  • The separated modules can be reused as much as possible and strong business relevance can be reduced.
  • The granularity of separation should be as large as possible, and the packaging should be transparent.

Network layer:

First, cross-layer access:

About cross-layer data flow:

When A< -b < -c exists. When C has an event, it tells B in some way, and THEN B executes the corresponding logic. Once the notification is not properly handled, it is difficult to guarantee that A level business engineers will not deal with this detail in the future. Once A business engineer generates A processing operation at Layer A, either to supplement logic or to execute A business, A portion of the processing code for this detail is scattered at layer A. However, the former should not be scattered in the A layer, the latter may be the demand. In addition, since layer B is abstracted from layer A, the execution of supplementary logic may conflict with layer B’s handling logic for this event, which is undesirable. But sometimes data flows across layers are unavoidable: for example, when signals switch from 2G to 3G to 4G to Wi-Fi, they need to cross layers.

Consider the following:

How data is delivered to the business layer:

The schemes adopted by most apps at the network layer mainly focus on these three types: Delegate, Notification, and Block. Generally, they are used in combination. Here I can only talk about personal choice. After all, personal involvement is limited, so I can only talk about the benefits based on the mode I have adopted.

Select Delegate when the task to be done after the callback is consistent on every callback, and Select Block when the task to be done after the callback is not guaranteed to be consistent on every callback.

  • Delegate n. (Apple’s native web requests are Delegate. But AFN uses blocks for callbacks. Therefore, I generally use AFN to do the network (after all, it is convenient and easy), so I generally use the following form to request:
// The request initiates the Block using AFN, and the callback uses the delegate mode, so that the callback function can be relatively unified and easy to maintain on the business side. [AFNetworkingAPI callApiWithParam:self.param successed:^(Response *response){if ([self.delegate respondsToSelector:@selector(successWithResponse:)]) {
            [self.delegate successedWithResponse:response];
        }
    } failed:^(Request *request, NSError *error){
        if([self.delegate respondsToSelector:@selector(failedWithResponse:)]) { [self failedWithRequest:request error:error]; }}];Copy the code

Reason: Using a Delegate does a good job of avoiding cross-layer access and limiting the form of response code, which is more maintainable than Notification, which addresses the need for cross-layer data flow.

However, when using Notification, you must agree on a naming convention, otherwise it will cause a disaster in later maintenance.

Choice between intensive AND discrete API calls:

  • Intensive API calls are all API calls with a single class that takes the API name, API parameters, and callback landing point (block, delegate, etc.) as parameters. It then executes a method like startRequest, and it takes off to call the API based on these parameters, and then gets the API data and lands at the specified landing point.

  • The discrete API calls look like this: an API corresponds to an APIManager, and then the APIManager just needs to provide parameters to take off. The API name and landing method are integrated into the APIManager.

What data is delivered to the business layer? In fact, this belongs to the VM layer of MVVM to do, really is everywhere is design mode…

Ideally, you want API data delivered so that it can be displayed directly by the View without further processing. The first thing to say is that it’s very rare. In addition, this approach makes the View and API too closely related, which should not happen. Here’s an example:

@protocol AdapatorProtocol <NSObject> - (NSDictionary)reformDataWithManager:(APIManager *)manager; @property (nonatomic, strong) id< AdapatorProtocol > XAdapator;#pragma mark - APIManagerDelegate- (void)apiManagerDidSuccess:(APIManager *)manager { NSDictionary *XData = [manager fetchDataWithReformer:self. XAdapator]; [self.XView configWithData:XData]; } in APIManager, fetchDataWithReformer looks like this: - (NSDictionary)fetchDataWithReformer:(id< AdapatorProtocol >)adapator{if (adapator == nil) {
        return self.rawData;
    } else{//adapaor processes the datareturn[adapator reformDataWithManager:self]; }}Copy the code

Benefits of using the adapter pattern:

  • Reduce Controller stress, reduce code complexity, and increase flexibility by switching reformer at any time without switching business logic to deal with the data needs of different views
  • When dealing with single-view-to-multiple apis, and in the case of single-APi-to-multiple Views, Reformer provides a very elegant way to respond to this need, separating the transformation logic from the principal business logic and avoiding maintenance disasters.
  • The transformation logic is centralized, and the number of transformations is reduced to only one. The conversion logic of data archetypes is used at least twice, first to map JSON to the corresponding archetype, and second to convert the archetype to data that can be processed by the View. Reformer in one step. In addition, the conversion logic is in the Adapator. If API data changes in the future, just find the corresponding Adapator and change it, which is convenient for later maintenance
  • Business data and business are properly isolated. This way, if the business logic changes in the future, you can just switch to an Adapator. If other businesses have the same data conversion logic, other businesses can use the Adapator without rewriting it. In addition, if the Controller is modified (for example, the UI interaction mode is changed), it can be relieved to change the controller without worrying about the processing of business data.

Network layer optimization:

1, the use of cache (local mix +URL cache) to reduce the number of requests, can not send requests as far as possible not send requests, must send requests, can merge requests as far as possible merge requests. 3. Generally, the project has multiple servers. When the application starts, get the ping values of all the IP addresses in the local list, and then change the HOST in Dev_URL to the fastest IP address we found. In addition, the list of local IP addresses will need to be maintained through an API, usually pinged at the first startup every day and then updated locally. 4, relatively large data compression and upload.


Data persistence:

NSUserDefault: Generally, it is small scale data, weak business related data, NSUserDefault changes will affect the time of App startup, sensitive data do not put NSUserDefault, although it is really convenient to access NSUserDefault. 2. KeyChain: A KeyChain is a reversible encryption storage mechanism provided by Apple. It is widely used to store passwords. In addition, because the data in Keychain can be retained as long as the system is not reinstalled after App uninstallation, and the iCloud synchronization feature, everyone will store the unique identifier of the user here. Therefore, sensitive small data that needs to be encrypted or stored in iCloud is generally stored in the Keychain. 3. File: Mainly includes:

1,Plist 2,archive: 3,Stream (direct file storage) : Suitable for large data and frequently used, but files are generally traversed to get, so it is recommended to set up database index for files

4. Innumerable subschemes based on database (YYCache, FMDB, SQLite, CoreData). The data in the database should be business-related and not a large file, such as a large picture or video. Generally, the file is saved and the file path is stored in the data. It is recommended to use YYCache as a one-stop service.

Therefore, when there is a need for persistence, we first consider what means to persist it.

Database remember to do thread processing (the principle is the same as iOS property thread safety), for example, SQLite library recommended Serialized (default) : serial queue access, although it will be slow to lose, but easy to maintain.

The persistence layer has datacenters that are responsible for interfacing View layer modules or businesses and interact with each other through records. DataCenter provides business-friendly interfaces to the upper layer, which are typically robust: for example, returning data that meets user filtering criteria. DataCenter then schedules tables in this interface, does a series of business logic, and eventually generates record objects that are delivered to the View layer business.

DataCenter involves data assembly and data manipulation across tables in order to fulfill the tasks delivered by the View layer. Data assembly varies depending on the requirements of the View layer and is therefore a strong business. Cross-table data operations are essentially a combination of single-table data operations that the DataCenter schedules to get the desired base data for assembly. In this case, single-table data operations are weak, and these weak operations are performed by the Table mapping object. The Table object is used to generate the corresponding SQL statement through QueryCommand and delivered to the database engine to retrieve the data, which is then delivered to the DataCenter.

That’s about it. I’ll add it after the refactoring