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 the communication data specification be determined for data communication 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, thus becoming an open standard.

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 string, number, object, array, boolean, or 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 business data 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) class to parse JSON, and related third-party frameworks include JSONModel, Mantle, MJExtension, and YYModel, all of which are encapsulations based on the native class.

If higher parsing performance is pursued, you can learn about simdjson (released in February 2019), which claims to parse gigabyte JSON files per second. Compared to traditional JSON parsing methods that recursively parse byte by byte from top to bottom, its optimization idea lies in 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) 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 and good cross-platform compatibility: all browsers currently support it, and it is also supported on iOS and Android.

The so-called responsive means not directly manipulating the target but achieving the purpose 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 displayed directly 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 that has a significant 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 checking 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 mode, 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 the test cases to improve code quality.

Its test cases mainly target the smallest units in development, 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 optimizing the code;

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

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


BDD is an evolution of TDD, which:

  • Conducts functional testing based on behavior, using DSL (Domain Specific Language) to describe test cases;

  • Test cases look like documents, 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 participation of the testing team 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 the DSL pattern of Specta, the expectation syntax of the Expecta framework, and the capabilities of Mocks and Stubs. For specific usage, you can refer to the Kiwi Wiki (Specs | Expectations | Mocks and Stubs | Asynchronous Testing).

Mocks: Mock objects, such as simulating Null objects, instances of classes, or 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 to write test cases (considering various exceptional conditions and the boundaries of input and output) before developing code;

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

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

30 | How to establish a suitable coding standard?#

Background: A team needs a unified coding standard to more effectively avoid the problem of team members lacking a sense of mutual recognition (caused by inconsistent code styles).


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, instead pass values through parameters (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 the default branch is not added, 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, use guard uniformly 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 (reduce coupling and improve the accuracy of unit testing); check the function's input parameters (to improve robustness), and Swift's guard syntax is also applicable for checking input parameters.

  7. Classes: In OC, minimize the inclusion of other class header files in a class's header file, using @class to declare, and then include the necessary header files in the implementation file using #import (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 adherence situations, it is unavoidable to include header files from other classes, so try to reduce inheritance in code design (too many inheritance relationships are not conducive to code maintenance and modification, for example, modifying the parent class also requires considering 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, a prefix must be added (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 respective functional codes).

💡:

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

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

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


Finally, how to implement coding standards effectively?

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 (reviewers can like and 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 space for imagination).

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

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