Bo2SS

Bo2SS

2 Application Development (Part 2)

"Experience the full picture of the iOS knowledge system," today we continue with Application Development (Part 2).

In this article, we will discuss JSON parsing, layout frameworks, rich text, TDD/BDD, and coding standards in iOS development.

image

26 | JSON Parsing#

Background: How should data communication standards be determined between different programming languages? Therefore, a universal data format that is supported by various programming languages is needed.


Next, it's time for JSON to make its appearance.

Source: codebrainer

JSON stands for JavaScript Object Notation, which was originally designed as a subset of the JavaScript language, but ultimately became a common data format that is independent of programming languages.

Currently, many programming languages support the generation and parsing of JSON, so the JSON data format meets the needs mentioned in the background.


JSON is based on two structures:

  • A collection of key-value pairs: specific implementations include dictionaries, hash tables, objects, structs, etc., example: {"key1": "val1","key2": "val2"}.

  • An ordered list of values: specific implementations include arrays, vectors, lists, etc., example: [1,3,5].


Other characteristics of JSON:

  • Supports nesting: the above values can be any of the following types: string, number, object, array, boolean, null;

  • Does not support comments;

  • Horizontal tabs, newlines, and carriage returns are treated as spaces.


The logic of most languages can be converted into a syntax tree structure, which can be described using JSON. Therefore, JSON has many use cases:

  1. Describing business data, allowing it to be dynamically updated;

  2. Describing business logic to achieve dynamic business logic;

  3. Describing page layouts.


How to parse?

Apple provides the NSJSONSerialization (OC) / JSONSerialization (Swift) classes to parse JSON, and related third-party frameworks include JSONModel, Mantle, MJExtension, YYModel, all of which are encapsulations based on the native classes.

If higher parsing performance is desired, you can look into simdjson (released in February 2019), which claims to parse gigabytes of JSON files per second. Compared to traditional JSON parsing methods that recursively parse from the top down by byte, its optimization approach is parallel parsing, reference:

27 | What are the advantages of Flexbox compared to Auto Layout?#

The "status" of Flexbox: It is the layout approach adopted by well-known layout libraries such as React Native, Weex, and Texture (AsyncDisplayKit), and it is also the layout approach used by Apple's official UIStackView.

The implementation of the Flexbox algorithm in React Native and Weex is a C++ library called Yoga.


As shown in the figure, the main idea of the Flexbox algorithm is to allow the flex container to change the width, height, and order of its flex items, such as adapting (filling/preventing overflow) the available space by enlarging or shrinking items.

Source: W3C

For a more detailed explanation of Flexbox, you can refer to:


So, what are the advantages of Flexbox compared to Auto Layout?

  1. The layout methods provided are more convenient, comprehensive, and standardized;

  2. Responsive, good cross-platform compatibility: currently all browsers support it, and it is also supported in both iOS and Android.

The so-called responsive means that it does not directly manipulate the target but achieves the goal through a proxy.

28 | How to meet various rich text presentation requirements?#

What is rich text? It is a string with attributes.

image

  • It can contain text with different fonts, sizes, backgrounds, colors, and letter spacing;

  • It can also set properties such as paragraphs and mixed text and images.


Then, how to display rich text? There are two situations:

  1. Rich text described using HTML

Rich text described in this way can be directly displayed using the loadHTMLString:baseURL: method of the WKWebView control.

In addition, for image resources in HTML, since they need to be fetched via network requests, caching strategies can be considered to reduce the number of requests.

  1. Rich text described using native iOS code

Long list scenarios (returning multiple data items for front-end rendering at once) generally have higher performance requirements, so this way of describing rich text is used.

Rich text described in this way can be displayed using Apple's official TextKit or third-party YYText.

Among them, YYText has many highlights:

  1. Excellent performance in asynchronous text layout and rendering;

  2. Compatible with UILabel and UITextView;

  3. A custom NSMutableAttributedString category that not only simplifies the base class but also adds functionality for embedding UIView, CALayer, etc.


Summary:

  • HTML described rich text is more readable and easier to maintain;

  • Native code described rich text has higher performance.

So if you want to combine the advantages of both, you can use HTML to describe rich text and then convert the HTML to native code before displaying it (you can refer to the author's HTN project, which implements the ability to convert HTML code to native code).

That is Rich Text → HTML → Native Code → Display.

29 | How to implement TDD and BDD?#

Background: When writing code with a wide impact, there are many places that need to be checked, and correspondingly, the time cost of manual checks can be very high.

So, how can we improve the efficiency of verification after writing code?

The answer is synchronous development and testing, to discover problems as early as possible.


From the perspective of testing scope, testing can be divided into:

  • Unit testing (developer's responsibility)

  • Integration testing (testing team's responsibility)

  • System testing (testing team's responsibility)

Among them, unit testing is also called module testing, and this unit may be a method of a class or a function of a module; at the same time, developers should ensure that each unit has a clear responsibility.


From the perspective of development models, development methods can be divided into:

  • TDD (Test-driven development)

  • BDD (Behavior-driven development)

Source: testlodge


The development idea of TDD is:

  1. Write test cases first;

  2. Quickly write functional implementation code without considering code optimization;

  3. After the functionality is developed, refactor the code under the assurance of test cases to improve code quality.

Its test cases mainly target the smallest units in development and are suitable for unit testing.

In terms of thought, the difference between TDD and directly developing functionality after receiving functional requirements is:

  • First consider how to test the functionality, then consider how to write the code, which provides more time and space for code optimization;

  • Even if optimization is done several versions later, as long as it can pass the previously written test cases, code quality can be guaranteed.

PS: It has a bit of that "not forgetting the original intention" flavor~


BDD is the evolution of TDD, it:

  • Tests functionality based on behavior, using DSL (Domain Specific Language) to describe test cases;

  • Test cases look like documentation, making them more readable and easier to maintain.

Its test cases describe behaviors, have a broader testing scope, and are suitable for integration testing and system testing.

At the same time, thanks to the DSL language used in BDD (standard, normative, and highly readable), not only can developers efficiently discover problems using BDD, but it also facilitates the testing team to participate in writing.


The OC frameworks for BDD include Kiwi, Specta, Expecta, etc., and the Swift framework includes Quick.

The author recommends the Kiwi framework because it includes Specta's DSL mode, Expecta framework's expectation syntax, as well as Mocks, Stubs capabilities. For specific usage, you can refer to Kiwi Wiki (Specs | Expectations | Mocks and Stubs | Asynchronous Testing).

Mocks: Mock objects, such as simulating Null objects, instances of classes, instances of protocols.

Stubs: Stubs can make selectors or message patterns return fixed results, supporting both real and mock objects.


Summary:

  • Whether TDD or BDD, it is first write test cases (considering various exceptional conditions and input-output boundaries), then develop code;

  • Good modular architecture and TDD, BDD promote each other.

The author suggests: Prioritize using TDD and BDD for the development of basic capabilities, ensuring the stability of basic capabilities, and then consider core business when time allows.

30 | How to establish a suitable coding standard?#

Background: A team can more effectively avoid the problem of lack of mutual recognition among team members (caused by inconsistent code styles) only when there is a unified coding standard.


So what constitutes a good coding standard, and what aspects should be considered?

Source: quora

First, you can refer to some excellent companies' coding standards:


Next, here is a simple reference:

  1. Constants: Use type constants instead of macro definitions.

  2. Variables: Clearly reflect functionality, preferably with type suffixes; avoid using global variables to pass values, but rather pass values through parameters (to reduce coupling between functional modules).

  3. Properties: In OC, try to use get methods for lazy loading (to avoid unnecessary memory usage and redundant calculations); in Swift, if a property is read-only, the get clause can be omitted.

  4. Conditional Statements: Reduce or avoid default handling, especially when using Switch to handle enums (when writing Switch statements in Swift, if you do not add a default branch, the compiler will remind you to add branch handling when the enum has new values); reduce nested handling (to increase readability), and fully utilize guard syntax in Swift.

  5. Loop Statements: Reduce the use of continue and break (to increase readability), and in Swift, you can uniformly use guard instead.

  6. Functions: Function names should reflect their purpose; each function should handle the logic of the smallest unit, satisfying the single responsibility principle; avoid using global variables to pass data within functions (to reduce coupling and improve the accuracy of unit testing); pay attention to checking function parameters (to improve robustness), and Swift's guard syntax is also applicable for checking parameters.

  7. Classes: In OC, a class's header file should introduce as few other class header files as possible, using @class to declare, and then importing the necessary header files in the implementation file (using @class ensures code compilation passes, while using #import ensures code runs correctly, refer to The difference between @class and #import in OC——CSDN); for inheritance and protocol conformance situations, it is unavoidable to introduce header files from other classes, so try to reduce inheritance in code design (too many inheritance relationships are detrimental to code maintenance and modification, for example, when modifying the parent class, you also need to consider the impact on all subclasses).

  8. Categories: When adding method names in categories, try to add prefixes, and if it is a category of a system class, you must add a prefix (to avoid method name duplication issues); categorize public methods of a class into different categories for easier management and maintenance (especially suitable for scenarios where multiple people maintain their own different functional codes).

💡:

  • Clear code logic is the most basic and necessary condition for high-quality code. If the code is not clear, then other aspects such as extension, reuse, and elegance are out of the question.

  • The primary task of writing code is to make it understandable to others, avoiding over-engineering (engineering design targeted at specific business).

  • Reduce the use of overly new language features and black magic; if necessary, add more comments.


Finally, how to implement the coding standards?

The best way is Code Review. Through Code Review, you can:

  • Check whether the coding standards are being followed by team members;

  • Provide timely guidance to those who write code inconsistently.

Code Review can be divided into two steps:

  1. First, use static analysis tools (SwiftLint, OCLint) to conduct a comprehensive check of the submitted code.

  2. Then, conduct manual checks (the reviewer can like or comment), which not only achieves the effect of implementing coding standards but also promotes communication and mutual learning among members.


Alright, the Application Development section is about to come to an end (just one more article introducing iOS development learning materials as a bonus), and next we will start the Principles section, covering the iOS system kernel XNU, the principles behind iOS black magic... (🤫 leaving some room for imagination).

Bo2SS will do its best to explain them clearly, and we hope for your continued support~ See you next time!

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.