What are the correct steps to develop an app?

What are the correct steps to develop an app?

In iOS development, it is easy to write an App, but it takes a lot of effort to write a good App. First, let's look at the requirements for developing an App:

Write an app that displays all music albums related to Lady Gaga on Spotify. The relevant information can be found at the following URL:

https://api.spotify.com/v1/search?q=lady+gaga&type=album

[[182108]]

Demand Analysis

First, we need to get the development requirements, and most importantly, we need to clarify the development details. There are many things we don’t know and need to communicate with product managers and designers: Should we use TableView or CollectionView for display? What information should be displayed for each music album? If there are too many albums, which albums should we display first? In addition to displaying information, what other extended functions does this app need? Is there a size requirement for this product? How many days will it take to complete?

After discussion, everyone agreed to make an app like this:

Lady Gaga

So we know that we need to make a tableView, where each Cell corresponds to an album information, with the picture on the left and the album name on the right. Click on the cell to see the corresponding album picture.

Building the Architecture

First of all, this App is relatively simple, and we can make it with the most basic MVC.

  • Model part:

Only one model is needed, Album, which corresponds to the information of each album;

  • View part:

The main part can be completed in Storyboard;

***Create a new subclass of UITableViewCell to set the album UI;

  • ViewController part:

One of the ViewControllers is a TableViewController, which is responsible for displaying the information of all albums;

Another ViewController is responsible for displaying detail information, such as a large photo of the album;

  • Network section:

Responsible for fetching album information from the Internet; and fetching image data according to the album's image URL;

Basic Architecture

Details

  • Network section:

Both fetchAlbums and downloadImage can be implemented using Apple's own URLSession and JSONserialization, or you can use the excellent third-party library AlamoFire. Because this app is relatively simple, AlamoFire's advantages are not obvious, and introducing a third-party library will increase the size of the app, so it is recommended to use the former. Basically, it is to implement the following two functions:

  1. func fetchAlbums( with url: String, completion : @escaping (_ albums: [Album]?, _ error : NSError?) -> Void)
  2. func downloadImage(_ url: String) -> UIImage?

For the first function fetchAlbums, because network requests are time-consuming and labor-intensive, we usually use background threads instead of main threads (UI threads) to process them, so as to keep the UI running smoothly. Closures are used to allow callbacks after asynchronous multithreading is completed, and error is used to monitor whether network requests have errors.

For the second function downloadImage, the simplest way is to get the corresponding data through the URL, and then get the image through the corresponding data. The reason for returning optional is that there may be a problem with the URL or the network request error, in which case nil is returned.

From the perspective of API design, the above downloadImage is not a perfect design. The perfect design is that we can know where the error occurs, such as the following:

  1. enum DownloadImageError: Error {
  2. case InvalidURL
  3. case InvalidData
  4. }
  5.   
  6. func downloadImage(_ url: String) throws -> UIImage {
  7. guard let aUrl = URL(string: url) else {
  8. throw DownloadImageError.InvalidURL
  9. }
  10.   
  11. do {
  12. let data = try Data(contentsOf: aUrl)
  13. if let image = UIImage(data: data) {
  14. return image
  15. } else {
  16. throw DownloadImageError.InvalidData
  17. }
  18. } catch {
  19. throw DownloadImageError.InvalidURL
  20. }
  21. }
  • ViewController part:

For AlbumsController, we use the delegate mode, that is, the tableView is delegated to AlbumsController. We only need to implement the corresponding dataSource and delegate methods. For dataSource, there are two methods that must be implemented, which are:

  1. func tableView(_ tableView: UITableView, numberOfRowsInSection section : Int ) -> Int  
  2. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

At the same time, there are two arrays in AlbumsController, one for albums ([Album]) and one for images ([UIImage?]), so we only need to download the data once and store it in the corresponding array, and then there is no need to make related network requests again. In other words, these two arrays play the role of cache.

The specific implementation is: first request the server to retrieve the corresponding data in viewDidLoad(). Then set the corresponding number of rows in TableView according to the number of albums. In a specific row, we can determine the corresponding album according to indexPath. According to the image URL of the corresponding album, we can get the corresponding image and then cache it in the image array. Since we reuse the Cell of TableView, if we do not cache the image and make a network request every time, the delay will be very serious and the image will flicker.

***The jump between two ViewControllers can be achieved using navigationController.

  • View part:

Customizing AlbumCell can ensure the scalability of the App. At the same time, in order to deal with the problem that some album names are too long and the Label cannot be displayed, autoshrink can be used to handle it.

App operation process

Optimization and expansion

The above design and implementation are ideal. Now we need to consider a boundary situation. What if the network is unstable?

A simple solution is to download the data when the network is good and store it in cache and storage. Then, even if the network is interrupted or the App crashes, we can still get the corresponding data from the storage.

Here we introduce the facade pattern and create a new class named LibraryAPI, which provides two interfaces:

  1. func getAlbums(completion : @escaping (_ albums: [Album]?, _ error : NSError?) -> Void)
  2. func getImage(_ url: String) throws -> UIImage

The difference between the method here and the previous Network is that the getAlbums method will first try to retrieve the corresponding data from the storage. If not, it will access the Network and then store the value obtained from the Network in the storage. The implementation here is a bit complicated, involving two modules and multi-threaded operations, but we don’t have to care about the internal implementation of the method, but only the interface. This is the advantage of the appearance mode. At the same time, the LibraryAPI class should use the singleton mode, because it should be regarded as a global API to be accessed by each ViewController, and this design also saves resources.

Optimized App process

Another optimization point is, if we get a lot of data at the beginning - for example, 10,000 albums, what should we do?

The correct approach is paging. We can first take only 20 and display them on the TableView. When the user is about to scroll to the bottom, we can take the next 20, and then we have a total of 40 to display in memory, and so on. The advantage of this is that we don't need to download all the data, and lay out the TableView in the fastest and smoothest way, while adding corresponding album data according to user needs.

***An optimization point is, if the user scrolls up and down very quickly, how can we load the picture as quickly as possible?

The answer is to use operationQueue to handle it. When the current cell is visible, we resume the process of downloading pictures, otherwise we suspend it. This ensures that we use limited memory and CPU to efficiently download the pictures that users need and want to see.

It is worth mentioning that you can also learn from the ideas of ASDK to further optimize the program.

Summarize

This article starts with a simple tableView App and discusses the four steps of developing an App: demand analysis, building architecture, detailed implementation, and optimization and expansion. It also briefly introduces multithreading and several design patterns, hoping to be helpful to everyone.

<<:  Aite Tribe Stories (8): Following the Dream and Starting a Business

>>:  Understanding the RxJava threading model

Recommend

In the new era, what kind of learning ability do we need?

Bacon once said: Knowledge is power. However, onl...

Many countries may face EU prosecution for alleged bias towards the public

According to foreign media reports, after the Vol...

Dialogue with cocos author: In-depth exploration of cocos one-stop solution

Chukong Technology released a blockbuster news, a...

Tesla electric truck price announced: starting price is less than 1 million RMB

We all know that the pure electric sedans and SUV...

The National Day routine is here, I won’t tell ordinary people about it!

The annual National Day holiday is coming! Are yo...

Understand these 8 questions before competing with Tmall marketing IP

A complete and qualified Tmall marketing IP plan ...

iQIYI Channel Information Stream Advertising Knowledge

With the continuous advancement of Internet techn...

Why are there no two identical leaves in the world? Here comes the truth!

Why are there no two identical leaves in the worl...

Where is the most beautiful karst in China?

In China There is a kind of landscape It not only...