After several years of development, the iphone has changed a lot, such as a more varied screen, more size, more memory, and a change in CPU architecture. iOS is also changing with the iphone, such as AutoLayout, size classes, Split view controller, and so on. The changes to these technologies and devices have also caused me a lot of problems in the development process, not only that Apple has been trying to help us use the same set of code to develop universal apps that adapt to multiple devices by constantly introducing new technologies. On the other hand, although the universal app in the development process, the convenience of our developers, but for the user is not so friendly, because of the need to adapt to a variety of devices, so contains all the device code, but really in the run, we do not need so much relevant code and resources.
For example, one of the following graphs is an app running on iphone 6+, using various resources related to the situation:
In the Sup out of the iphone 6+ on the real-time use of the relevant resources and code, compared with the check part, more is not Sup out of the part. We can imagine that we downloaded an app (provided the app is universal), and then at least half of the code and resources we don't need, occupy our space in vain. This is not good for the user experience. To solve this problem, Apple has given a new solution in iOS 9:
- App slicing when you submit your iOS 9 package file to the App Store, Apple compiles your resources and executables, and then generates a specific executable file for each device. Eventually, the device will only download the content that adapts to its characteristics and what it uses. These features include graphics performance (original word: graphics capabilities), memory level, CPU architecture, size classes, screen scaling, and so on.
- The resources on the Demand Resouces application are downloaded only when they are needed, and can be removed if other resources require space.
- Bitcode when you submit your app to the App Store, Bitcode can be submitted as an intermediate product. Programs that contain Bitcode configuration will be compiled and linked on the App store. Bitcode allows Apple to re-optimize the binaries of our program at a later stage without requiring us to resubmit a new version to the App Store.
These three technologies add up, unified called app thinning.
Getting started
Open the initial project in this section and then choose to run on ipad Air 2, which works as follows:
A finder window is also opened with the simulator up:
This finder window can be opened because a script is added to the program that executes each time it is run, and the script is in the following location:
echo "App Size in KB: `du -sk \"${CONFIGURATION_BUILD_DIR}/${EXECUTABLE_NAME}.app\"`"if [ "${CONFIGURATION}" = "Debug" ]; thenopen ${CONFIGURATION_BUILD_DIR}fi
Right-click on the old CA maps in the Finder and select Show Package contents as follows:
The instructions in the note are as follows:
1. Assets.car is the Assets.xcassets file that was compiled by Xcode.
2. Old CA maps is an executable file that is actually running on the device.
3. Santa Cruz PNGs This is a picture file, but it is not compiled into the Assets.car file because it is not placed in the assets.xcassets, but is placed in the top-level file of the project.
4. Sd_map.bundle This is the map image file, but will be nearly 120MB.
Measuring your work
This chapter describes some of the app's slimming related things, so we must be able to measure whether the app is reduced. The project has a built-in script (in the code above) that can output the size of the app in the build process. See the following locations:
Slicing up app slicing
App slicing contains two parts: Executable shard (executable slicing) and resource shard (resource slicing).
executable slicing refers to the device when downloading the app will be based on the device's information only download the relevant executable file should be the device, and will not contain other devices and the architecture of the executable files to achieve the reduction of the app installation package. And this feature does not require us to do too much, the APP store is supported by default.
By default, the packages submitted to the App Store contain all of the content, which is in the configuration file, and the App Store automatically creates the executable file that corresponds to each type. This is supported on the ios9+.
Being Smart with resources
Resource Slicing need a small part of our simple work to achieve. If you use resource slicing, make sure that our resources are managed asset catalogs. In Xcode 7, you can mark the Memory and Graphics of a resource's used device as follows:
Your First Fix
Introduced Santa Cruz PNGs at the beginning of this file because it was placed in the main bundle, so it cannot be compiled into the assets.car, and thus cannot use resource slicing. Let's take a look at how we can modify it to make it work:
After selecting new Image set, name the newly added set named Santa Cruz, followed by the following actions:
Correct the left side of the content should be deleted, including within the Finder should also be deleted
Then run the app on different devices, and finally find that the size of the Asset.car file is inconsistent. This is because when installed, the corresponding resources are installed according to the device.
lazily (down) loading content
Apple offers On-demand resources technology, or ODR. The ODR allows you to store resources on Apple's servers and then download them when your app is in use. Nsbundleresourcerequest is the class that handles the ODR and uses this class to download the corresponding resource via tag. ODR is available for images, data, OpenGL shaders, SpriteKit particles, watchkit complications, and so on.
Wire things and use tags
Below we modify the code, to achieve the download of resources, modify the Mapchromeviewcontroller.swift corresponding method is as follows:
Private Func Downloadanddisplaymapoverlay () {//Displayoverlayfrombundle (Nsbundle.mainbundle ()) guard Let Bundletitle = Mapoverlaydata?. Bundletitle else {return} l ET bundleresource = nsbundleresourcerequest (tags: [Bundletitle]) Bundleresource.beginaccessingresourceswithcompletionhandler {[weak self] in nsoperationqueue.mainqueue (). Addoperationwithblock ({(), Void in if error = = nil {self?. Displayoverlayfrombundle (Bundleresource.bundle)}}}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
At this point we run the code, it may output errors in the console, because our corresponding bundle does not have a tag, we need to add tag to the bundle:
Then we recompile to run our program, and then follow the above to view the size of the compiled run program, found a lot smaller. Comparing the files generated by the previous compilation, it is found that the running file contains no bundles.
If your app is on the App Store, it may be slow to download this resource file. However, in the development process, Xcode will use the local network as a server, and then on the device can be downloaded to, so in the process of development if the computer is turned off, the ODR will not be used.
Make it download faster
In the process of using ODR, if the bundle is large, it may be time-consuming to download the process, and the user does not know it during the download, so the user experience is not good. We can again resource the download process to give the user some hints, modify the following code:
//Add for the newly added Progressview is that the program has been added on the private func Downloadanddisplaymapoverlay () {///Displayoverlayfrombundle (NSBundle. Mainbundle ()) guard Let Bundletitle = Mapoverlaydata?. Bundletitle else {return} let Bundleresource = nsbundleresourcerequest (Tags: [bundletitle]) Bundleresource. loadingpriority = nsbundleresourcerequestloadingpriorityurgent//Add Loadingprogressview. observedprogress = Bundleresource. Progress//Add Loadingprogressview. Hidden = false//Add UIApplication. Sharedapplication ().networkactivityindicatorvisible = TRUE//add Bundleresource.beginaccessingresourceswithcompletionhandler {[Weak self] (error)- > Void in nsoperationqueue.mainqueue () .addoperationwithblock ({(), Void in self?.loadingprogressview.hidden = TRUE// add uiapplication.sharedapplication () .networkactivityindicatorvisible = false//add if error = = Nil {self?< Span class= "Hljs-preprocessor" >.displayoverlayfrombundle (Bundleresource.bundle) } }) }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21st
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21st
- 22
- 23
- 24
- 25
- 26
- 27
If a user has already downloaded a bundle, it will not be downloaded again the next time it is used.
The many flavors of tagging
Although added Progressview, in the experience is a little better, but need to pay attention to the test is the use of the local network, so relatively fast, but if submitted to the App Store, it is likely to download is relatively slow, if the user does not have wifi that may not be used, So we need to do some other tweaks.
Initial Install tags
With initial install tags, we can set which bundles will be downloaded when our app is initialized for installation. Here are some of the three types of ODR downloads:
* Initial Install Tags in the IPA download time together to download
* prefetched Tag order after the program download is complete, download the corresponding resources, and then arrange them in order.
* prefetched Tag order on-Demand download
Here's where the configuration is:
Purging content
The application uses the ODR to download the corresponding bundle, but sometimes we need to clean up some downloaded bundles that are not used. Look at how to view the downloaded ODR before you describe how to delete it:
Set A resource to be purged
Add the following code to the Mapchromeviewcontroller.swift:
NewAdd is the newly added code var overlaybundleresource:nsbundleresourcerequest? NewAdd private func Downloadanddisplaymapoverlay () {//Displayoverlayfrombundle (NSBundle. Mainbundle ()) guard Let Bundletitle = Mapoverlaydata?. Bundletitle else {return} let Bundleresource = nsbundleresourcerequest (Tags: [bundletitle]) Overlaybundle Resource = bundleresource//NewAdd Bundleresource. loadingpriority = nsbundleresourcerequestloadingpriorityurgent//Add Loadingprogressview. observedprogress = Bundleresource. Progress//Add Loadingprogressview. Hidden = false//Add UIApplication. Sharedapplication (). Networkactivityindicatorvisible = TRUE//Add Bundleresource. beginaccessingresourceswithcompletionhandler {[Weak self] (error), Voidin nsoperationqueue.mainqueue () .addoperationwithblock ({(), Void in self?.loadingprogressview.hidden = TRUE// add uiapplication.sharedapplication () .networkactivityindicatorvisible = false//add if error = = Nil {self?< Span class= "Hljs-preprocessor" >.displayoverlayfrombundle (Bundleresource.bundle) }}}}}//New add override func Viewdiddisappear (animated:bool) {Super.viewdiddisappear (animated)//Tell the system to end the access Overlaybundleresource to the resource? .endaccessingresources ()}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21st
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21st
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
The above code, I do not know when testing the time will be deleted, I also simulated the memory warning, if who is clear, please tell me, thank you.
insisted on a few days at noon finished, this note, a note 13, very tired.
"Go" app slimming