In IOS development, it's easy to write an app, but it's important to write an app. First, let's look at the development requirements for an App:
Write an App that shows all the music albums associated with Lady Gaga on Spotify, which can be found at the following URL:
Https://api.spotify.com/v1/search?q=lady+gaga&type=album
Demand analysis
First, get the development requirements, the most important thing is to clear the development details. There are a lot of things that we don't know about that need to be communicated with the Product manager and the designer: is the display for TableView or CollectionView? What information needs to be displayed for each music album? If the number of albums is too high, which albums do we display first? What extensions do you need to expand this App in addition to displaying information? Is there a requirement for the size of this product? How many days do I need to finish?
After discussion, the consensus is to make the following App:
So we know, is to do a tableView, each Cell corresponding to an album information, the left is a picture, the right is the album name. Click on the Cell to see the corresponding album large map.
Building a schema
First of all, this App is simple, so we can do it with the most basic MVC.
Model section:
Only need a Model, for Album, corresponding to each album information;
View section:
The part of the body can be completed in Storyboard;
It is best to create a new subclass of UITableViewCell that corresponds to the UI for setting up the album;
Viewcontroller section:
One of the Viewcontroller is Tableviewcontroller, responsible for the reality of all the album information;
Another viewcontroller is responsible for displaying detail info, such as the big picture of the album;
Network section:
Fetch the album information from the network, and fetch the image data according to the album's image URL;
Detail implementation
Network section:
Both Fetchalbums and Downloadimage are available with Apple's own urlsession and jsonserialization, or they can be alamofire with an excellent third-party library. Because this app is relatively simple, alamofire advantage is not obvious, and the introduction of third-party libraries will increase the volume of the app, so it is recommended to use the former. This is basically the implementation of the following two functions:
Func fetchalbums( with URL: String, completion : @escaping ( _ albums: [Album]?, _ error : nserror?) Void)
Func downloadimage(_ URL: String) , UIImage?
For the first function fetchalbums, because network requests are time-consuming work, we typically handle them with a background thread rather than the main thread (the UI thread), which keeps the UI running smoothly. Closures are used to call back when asynchronous multithreading is complete, and error is to monitor network requests for errors.
For the second function downloadimage, the simplest way is to get the corresponding data via the URL and then get the image with the corresponding data. The reason for the return to optional is that there may be a problem with the URL or a network request error, and nil is returned.
From the point of view of API design, the above downloadimage is not the best design. The best design is that we can know what went wrong, like this:
Enum downloadimageerror: Error {
case invalidurl
case invaliddata
}
Func downloadimage(_ URL: String) throws , UIImage {
guard Let aurl = url(string: url) else {
throw downloadimageerror. Invalidurl
}
do {
let data = try data(contentsof: aurl)
if let image = UIImage(data: data) {
return image
} else {
throw downloadimageerror. Invaliddata
}
} catch {
throw downloadimageerror. Invalidurl
}
}
For Albumscontroller, we used the proxy mode (Delegate), the TableView agent to the Albumscontroller. We can only implement the corresponding DataSource and delegate methods. For DataSource, there are two methods that must be implemented, which are:
Func TableView(_ tableView: uitableview, numberofrowsinsection section : int) , int
Func TableView(_ tableView: uitableview, cellforrowat Indexpath : indexpath) - UITableViewCell
At the same time, albumscontroller inside, there are two arrays, one for the album ([Album]), one for the picture ([UIImage]), so we just download the data once, and put it into the corresponding array, then there is no need to make the related network request again. In other words, the two arrays play a role in caching.
The specific implementation is: First in the viewdidload () request the server to fetch the corresponding data. Then set the corresponding number of TableView according to the number of albums. In a specific line, we can determine the corresponding album according to Indexpath. According to the corresponding album image URL, we can get the corresponding image, then cached in the image array. Because we re-use the TableView Cell, so if you do not cache the image and every time the network request, because the delay is very serious and will cause the image flicker.
The jump between the last two Viewcontroller can be achieved with Navigationcontroller.
Custom Albumcell can ensure that the App is very extensible. At the same time, in order to deal with some of the album name too long Label can not display the problem, you may use autoshrink to deal with.
Optimized expansion
The above design and implementation is more idealized, now we have to consider a boundary situation, if the network is not stable, how to do?
A simple solution is to download the data when the network is good, save it in the cache and storage, and then we can get the data from the storage even if the network is interrupted and the App crashes.
This introduces the appearance mode (facade), creating a new class named Libraryapi, which provides two interfaces:
Func getalbums(completion : @escaping (_ albums: [Album ]?, _ error : nserror?) Void)
Func getImage(_ URL: String) throws - UIImage
The method here differs from the previous Network in that the Getalbums method first attempts to fetch the data from the storage, if not, to access the network, and then to deposit the value from the network into the storage. The implementation of this is a bit complex, involving two large modules and multi-threaded operations, but we do not have to care about the internal implementation of the method, but only care about the interface, this is the advantages of the appearance pattern. At the same time, the Libraryapi class is best used in singleton mode (singleton) because it should be considered as a global API to be accessed by various viewcontroller, and this design also saves resources.
Optimized App Flow
Another optimization point is that if we start to get a lot of data--like 10,000 albums, what do we do?
The correct approach is paging. We can take only 20 to show on the TableView. When the user is sliding to the end, we can take the following 20, then we have a total of 40 in memory can be displayed, and so on. The advantage of this is that we do not need to download all the data, layout the TableView in the quickest and smoothest way, and add the corresponding album data according to the user's needs.
The last optimization point is that if the user slides up and down quickly, how can we load the picture with the fastest speed?
The answer is to use Operationqueue to deal with, the current cell is visible, we will resume the process of downloading pictures, or suspend. This ensures that we use limited memory and CPU to the most efficient download user needs, the current picture to see.
It is worth mentioning that we can also draw on the idea of ASDK to further optimize the program.
Summarize
This article starts with a simple TableView app that talks about 4 steps to developing an app: Requirements analysis, architecture, detail implementation, and optimization expansion. A brief introduction of multithreading and several design patterns, we hope to help.
APP Development Steps