AppClip has received a lot of attention as a new feature of iOS14, but I think that there are few chances to see it actually used compared to the widget announced at the same time.
Anyca released AppClip in October, which provides a function to browse publicly available car information. In this article, I would like to introduce some of the stumbling blocks in the release of AppClip on Anyca and how to avoid them.
This article is the 16th day article of DeNA Advent Calendar 2020.
AppClip is a new feature that can be used from iOS 14 that can provide some functions of the app without installing the app from the App Store. You can display and start the UI called AppClip card starting from a specific URL, NFC tag, QR code, etc.
If you have an iOS device with iOS 14 installed, please scan the QR code below. Anyca's AppClip card will be displayed, and if you press the display button, Anyca's AppClip will start even if the app is not installed, and the detailed information screen of the car will be displayed.
In this way, it is possible to smoothly provide the functions of the application to the user, so the threshold for getting the user to use the application can be significantly lowered.
Although it is a convenient AppClip like this, there are some restrictions such as not being able to use some frameworks and restricting the acquisition of privacy information compared to ordinary apps. The biggest limitation of this is the binary size.
The binary size of AppClip is limited to 10MB in order to launch AppClip quickly. This 10MB constraint is very strict, as it is imposed on the uncompressed size, not on the compressed app size as in the ipa format.
For example, the Anyca app is about 65MB in ipa format, but it is about 140MB in uncompressed state. In order to provide some functions of the app as AppClip, it is necessary to cut out only what is necessary to provide the functions and reduce the size to about 1/10.
Even if the created AppClip exceeds 10MB, it can be used normally during development. However, when I try to apply for the app including AppClip, the following error occurs.
ITMS-90865: Thinned app clip size is too large - The universal variant app clip /Payload/xxx.app exceeds the maximum allowable size of 10MB. For details about app thinning, see: https://help.apple.com/xcode/mac/current/#/devbbdc5ce4f.
To check the size of the AppClip before applying, archive the app containing the AppClip and export the AppClip with Bitcode and App Thinning enabled in AdHoc. When the export is complete, a file called App Thinning Size Report.txt
will be generated with ipa. You can check the size of AppClip by checking the contents of this file.
App Thinning Size Report for All Variants of anyca-Release
:
Variant: Clip.ipa
Supported variant descriptors: Universal
App + On Demand Resources size: 4 MB compressed, 10.3 MB uncompressed
App size: 4 MB compressed, 10.3 MB uncompressed
On Demand Resources size: Zero KB compressed, Zero KB uncompressed
If xx.x MB uncompressed
in the part of App size: xx.x MB compressed, xx.x MB uncompressed
is less than 10MB, it means that the size limit of AppClip can be passed.
You don't have to use all the code, libraries, and resources that your current app uses to provide the functionality you use with AppClip. When creating a new target for AppClip, remove all unused views, features, code, resources, and libraries from the AppClip target.
Since it is not used in AppClip in the code, there may be some parts that you want to exclude from the build target. In such a case, the AppClip target defines APP_CLIP
as a compiler flag, and uses the preprocessor to exclude some code from the build when building for AppClip.
#if !APP_CLIP
//Unnecessary processing in AppClip
#endif
Basically, by narrowing down the target files, I think that you can avoid the size limit in most cases. However, in the case of Anyca, this alone could not be less than 10MB, and some other measures had to be taken.
In Anyca, there is a part where the UI is defined by its own layout file, and this layout file is included in the resource in JSON format. Also, some designs used custom fonts, which also had to be included in the resource.
Each of these files is not that big, and if you use ipa, compression will work, so you do not have to worry about it in normal application development, but in AppClip, the file size in the uncompressed state Affects the size limit, so the more files you have, the easier it is to get caught up in the limit.
Anyca decided to reduce the size of the AppClip by zipping these files at build time.
First, I added a script to the build phase of AppClip, and when the resource copy was completed, I zipped the target file and deleted the original data.
cd "${TARGET_BUILD_DIR}/${EXECUTABLE_FOLDER_PATH}"
zip fonts.zip *.ttf
zip layouts.zip layout_*.json
rm -f *.ttf
rm -f layout_*.json
Then, when you start AppClip, unzip the zip file and use the unzipped resources.
func decompressLayoutFiles(destinationPath: String) {
let bundlePath = Bundle(for: type(of: self)).bundlePath
guard let contents = try? FileManager.default.contentsOfDirectory(atPath: bundlePath) else { return }
//If there is a layout compressed file, expand it
if let compressedFile = contents.first(where: { $0 == "layouts.zip" }) {
SSZipArchive.unzipFile(atPath: "\(bundlePath)/\(compressedFile)", toDestination: destinationPath)
}
}
func registerCompressed(bundle: Bundle? = .main) {
guard let compressedFile = bundle?.path(forResource: "fonts", ofType: "zip"),
let cacheDirectory = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first else { return }
do {
let fontDirectory = "\(cacheDirectory)/Fonts"
if !FileManager.default.fileExists(atPath: fontDirectory) {
try FileManager.default.createDirectory(atPath: fontDirectory, withIntermediateDirectories: false)
}
SSZipArchive.unzipFile(atPath: compressedFile, toDestination: fontDirectory)
let contents = try FileManager.default.contentsOfDirectory(atPath: fontDirectory)
for content in contents where content.hasSuffix("ttf") {
guard let fontData = NSData(contentsOfFile: "\(fontDirectory)/\(content)"),
let dataProvider = CGDataProvider(data: fontData),
let cgFont = CGFont(dataProvider) else { continue }
var errorRef: Unmanaged<CFError>? = nil
CTFontManagerRegisterGraphicsFont(cgFont, &errorRef)
}
} catch {
// do nothing
}
}
By compressing and decompressing the files on our own in this way, we were able to reduce the size by several MB.
I think the above content will make it a lot slimmer, but if you want to reduce the size one more time, set the optimization level of the Swift compiler to size priority.
SWIFT_OPTIMIZATION_LEVEL = -Osize
This reduced the size by about 20% compared to the normal optimization level -O
.
Just like a regular app, AppClip under development can be debugged from Xcode. At this time, by specifying the startup URL in the runtime environment variable _XCAppClipURL
, it is possible to test the state in which AppClip is started based on this URL.
To actually read the URL from the QR code or NFC tag and start AppClip By adding the Local Experience of AppClip from the developer settings of the development terminal, when reading the QR code or NFC tag including the set URL You can launch AppClip on.
It is possible to provide AppClip tests to testers via TestFlight, but it can only be launched from the TestFlight app and you can only specify up to 3 launch URLs.
There are two types of AppClip, Default AppClip Experience
and Advanced AppClip Experience
, and you can set the contents of the AppClip card displayed when AppClip is started from App Store Connect.
AppClip launch using QR code or NFC tag can only be used in Advanced AppClip Experience
, so if you want to launch with these as triggers, you need to set both.
There are still few apps provided by AppClip, and how to use them is still being groped. Although the size limit is strict, the user can touch the app much more smoothly than installing the app via the App Store, so I think it will be a very fun feature if it can be incorporated well.
Currently, Anyca is on display at b8ta in Yurakucho, and you can also try AppClip from the QR code installed here. Please try it when you come near us.
In addition, DeNA's official Twitter account @DeNAxTech publishes not only blog articles but also presentation materials at various study sessions. If you are interested, please follow us.
Recommended Posts