Do you know how iOS gets the cache path of the night mode launch image? Baidu APP technical team once published a late-night hidden trap - iOS startup image abnormality repair solution. The article shares some research on the startup image, but through the content of the article, we still cannot solve this problem: How does iOS obtain the cache path of the night mode startup image? [[353923]] After a series of studies, the author obtained a solution for the simulator scenario and now shares the author's research records. Solution 1: Try to determine whether the cached image belongs to night mode by the startup image file name We first refine the information provided by Deep Night Pit - iOS startup image abnormality repair solution. The original text provides the following two pieces of information: - The file name of the cached startup image has a rule, but we don't know the rule
- File names of the 4 startup images
- ├── 1FFD332B-EBA0-40C9-8EEE-BEC9AEF7C41A@3x.ktx
- ├── 96920D11-6312-4D69-BBDB-AFBB52DBDDB3@3x.ktx
- ├── 98F7B5B1-5B3B-478B-93A8-ED3DE6492AD1@3x.ktx
- └── D9D48845-8565-42CE-A834-479CC9CC8BAD@3x.ktx
Through the four file names, we can find that the naming of the four pictures conforms to the following rules: - xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx
Combined with the content of Apple's official document NSUUID: - `NSUUID`conform to RFC 4122 version 4 and are created with random bytes.
We can draw the following conclusions: - The 4 file names are dynamically generated by NSUUID
- The file name contains only version 4 and no other valid information.
Solution 2: Analyze through system files After solution 1 failed, we speculated that iOS saved the cache path information of the night mode startup image in other ways. After a series of tests, the author discovered the applicationState.db file. applicationState.db The system will save various information such as program status through applicationState.db, of course, it will also include the cache path information of the night mode startup image. The file analyzed in this article is located at ~/Library/Developer/CoreSimulator/Devices/1F9B22C5-E446-4881-AFE4-3373E3513C59/data/Library/FrontBoard/applicationState.db Among them, 1F9B22C5-E446-4881-AFE4-3373E3513C59 represents the device ID of the iOS simulator. The complete list of simulator IDs can be viewed using the command plutil -p ~/Library/Developer/CoreSimulator/Devices/device_set.plist Test environment In order to facilitate the analysis of system files, this article takes the iOS 14 simulator as the target for analysis. The version information is as follows: - (lldb) platform status
- Platform: ios-simulator
- Triple: x86_64h-apple-macosx
- OS Version: 10.15.6 (19G2021)
- Kernel: Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1~1/RELEASE_X86_64
- Hostname: 127.0.0.1
- WorkingDir: /
- SDK Path: "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk"
- No devices are available.
In addition, the subsequent code will assume that the Bundle identifier is test.SplashTest Parsing applicationState.db First try to get the applicationState.db file type through the file command: - file ~/Library/Developer/CoreSimulator/Devices/1F9B22C5-E446-4881-AFE4-3373E3513C59/data/Library/FrontBoard/applicationState.db
Output: - SQLite 3.x database , last written using SQLite version 3032003
The test is successful, and the output of the file command shows that the file type is SQLite 3.x. db structure Next, dump the file using SQLite-related tools, and we can get the following information: - sqlite> .schema
- CREATE TABLE schema (version INT NOT NULL );
- CREATE TABLE key_tab (id INTEGER PRIMARY KEY , key TEXT NOT NULL , UNIQUE ( key ));
- CREATE TABLE application_identifier_tab (id INTEGER PRIMARY KEY , application_identifier TEXT NOT NULL , UNIQUE (application_identifier));
- CREATE TABLE kvs (id INTEGER PRIMARY KEY , application_identifier INT REFERENCES application_identifier_tab(id), key INT REFERENCES key_tab(id), value BLOB, UNIQUE (application_identifier, key ));
- CREATE INDEX kvs_keys ON kvs( key );
- CREATE INDEX kvs_application_identifiers ON kvs(application_identifier);
- CREATE VIEW kvs_debug AS SELECT application_identifier_tab.application_identifier, key_tab. key , value FROM application_identifier_tab, key_tab, kvs WHERE kvs.application_identifier=application_identifier_tab.id AND kvs. key =key_tab.id
- /* kvs_debug(application_identifier, "key" ,value) */;
application_identifier_tab saves the list of applications installed on the device (id is the primary key, application_identifier is the Bundle identifier of the APP) key_tab is responsible for recording constant strings. After testing, the path of the night mode startup map cache path belongs to XBApplicationSnapshotManifest. - sqlite > .schema key_tab
- CREATE TABLE key_tab (id INTEGER PRIMARY KEY , key TEXT NOT NULL , UNIQUE ( key ));
- sqlite> .width 2 50
- sqlite> select * from key_tab;
- id key
-
- 1SBLaunchImageIngestionInfo
- 2XBApplicationSnapshotManifest
- 3 _SBScenes
- 4 SBApplicationShortcutItems
- 5 compatibilityInfo
- 6 SBApplicationRecentlyUpdated
- 7 SBApplicationRecentlyUpdatedTimerStartDate
kvs_debug is responsible for connecting the three tables above in series: - kvs.application_identifier=application_identifier_tab.id
- kvs.key =key_tab.id
Through test.SplashTest, you can get 4 results, of which the second one is responsible for saving snapshot related information - sqlite> .width 15 32 8
- sqlite> SELECT * FROM kvs_debug WHERE application_identifier = 'test.SplashTest' ;
- application_ide key value
-
- test.SplashTest _SBScenes bplist00
- test.SplashTest XBApplicationSnapshotManifest bplist00
- test.SplashTest SBApplicationRecentlyUpdated 0
- test.SplashTest compatibilityInfo bplist00
Export XBApplicationSnapshotManifest After some research, we found that the value corresponding to XBApplicationSnapshotManifest is the persistence result of the XBApplicationSnapshotManifestImpl class in the SplashBoard library. Therefore, we can dump the contents of value through the following code. - +(void) load {
- void *lib = dlopen( "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/SplashBoard.framework/SplashBoard" , RTLD_NOW);
- printf( "%p" , lib);
- [self dump:@ "/Users/test/Library/Developer/CoreSimulator/Devices/1F9B22C5-E446-4881-AFE4-3373E3513C59/data/Library/FrontBoard/XBApplicationSnapshotManifest.plist" ];
- }
-
- +(void)dump:(NSString *)path {
- NSData *data0 = [NSData dataWithContentsOfFile:path];
- NSPropertyListFormat f = -1;
- NSError *error = nil;
- NSData *data1 = [NSPropertyListSerialization propertyListWithData:data0 options:NSPropertyListReadStreamError format:&f error:&error];
- if (f==kCFPropertyListXMLFormat_v1_0) {
- NSLog(@ "kCFPropertyListXMLFormat_v1_0" );
- }
- id obj = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:data1 error:&error];
- NSLog(@ "%@" ,obj);
- }
Dump results: - <XBApplicationSnapshotManifestImpl: 0x600000050d80; clientCount: 0> {
- containerIdentity = 0x0;
- snapshots = {
- <XBApplicationSnapshotGroup: 0x600002e65ea0; identifier: test.SplashTest - { DEFAULT GROUP }> {
- <XBApplicationSnapshot: 0x7fcb53f04d90; identifier: CE275D00-5732-4AFD-88FD-00BAE541EC12; launchInterfaceIdentifier: __from_UILaunchStoryboardName__; contentType: GeneratedDefault; referenceSize: {375, 812}; interfaceOrientation: LandscapeLeft; userInterfaceStyle: Dark> {
- creationDate = September 27, 2020 at 6:08:50 PM GMT+8;
- keepsImageAccessUntilExpiration = NO ;
- hasGenerationContext = NO ;
- context = {
- contentType = GeneratedDefault;
- fullScreen = YES;
- referenceSize = {375, 812};
- interfaceOrientation = LandscapeLeft;
- userInterfaceStyle = Dark;
- additionalContext = {
- statusBarSettings = <XBStatusBarSettings: 0x600002c580f0; hidden: YES; style: 0x0; backgroundActivityEnabled: NO >;
- }
- }
- imageContext = {
- scale = 3.0;
- opaque = YES;
- fileRelativeLocation = default ;
- fileFormat = png;
- }
- };
- <XBApplicationSnapshot: 0x7fcb57004830; identifier: B9DAB53E-29D9-47D2-873E-5772DE9220D1; launchInterfaceIdentifier: __from_UILaunchStoryboardName__; contentType: GeneratedDefault; referenceSize: {375, 812}; interfaceOrientation: Portrait; userInterfaceStyle: Light> {
- creationDate = September 27, 2020 at 6:08:50 PM GMT+8;
- lastUsedDate = September 27, 2020 at 6:08:50 PM GMT+8;
- keepsImageAccessUntilExpiration = NO ;
- hasGenerationContext = NO ;
- context = {
- contentType = GeneratedDefault;
- fullScreen = YES;
- referenceSize = {375, 812};
- interfaceOrientation = Portrait;
- userInterfaceStyle = Light;
- additionalContext = {
- statusBarSettings = <XBStatusBarSettings: 0x600002c5c1a0; hidden: NO ; style: 0x0; backgroundActivityEnabled: NO >;
- }
- }
- imageContext = {
- scale = 3.0;
- opaque = YES;
- fileRelativeLocation = default ;
- fileFormat = png;
- }
- };
- <XBApplicationSnapshot: 0x7fcb57004b60; identifier: 6B84614D-0867-4048-BE04-8E22E6742DDF; launchInterfaceIdentifier: __from_UILaunchStoryboardName__; contentType: GeneratedDefault; referenceSize: {375, 812}; interfaceOrientation: Portrait; userInterfaceStyle: Dark> {
- creationDate = September 27, 2020 at 6:08:50 PM GMT+8;
- keepsImageAccessUntilExpiration = NO ;
- hasGenerationContext = NO ;
- context = {
- contentType = GeneratedDefault;
- fullScreen = YES;
- referenceSize = {375, 812};
- interfaceOrientation = Portrait;
- userInterfaceStyle = Dark;
- additionalContext = {
- statusBarSettings = <XBStatusBarSettings: 0x600002c5c2d0; hidden: NO ; style: 0x0; backgroundActivityEnabled: NO >;
- }
- }
- imageContext = {
- scale = 3.0;
- opaque = YES;
- fileRelativeLocation = default ;
- fileFormat = png;
- }
- };
- <XBApplicationSnapshot: 0x7fcb57005140; identifier: D3E8D00C-EE33-466B-98A6-7E60865D8001; launchInterfaceIdentifier: __from_UILaunchStoryboardName__; contentType: GeneratedDefault; referenceSize: {375, 812}; interfaceOrientation: LandscapeLeft; userInterfaceStyle: Light> {
- creationDate = September 27, 2020 at 6:08:50 PM GMT+8;
- keepsImageAccessUntilExpiration = NO ;
- hasGenerationContext = NO ;
- context = {
- contentType = GeneratedDefault;
- fullScreen = YES;
- referenceSize = {375, 812};
- interfaceOrientation = LandscapeLeft;
- userInterfaceStyle = Light;
- additionalContext = {
- statusBarSettings = <XBStatusBarSettings: 0x600002c5c400; hidden: YES; style: 0x0; backgroundActivityEnabled: NO >;
- }
- }
- imageContext = {
- scale = 3.0;
- opaque = YES;
- fileRelativeLocation = default ;
- fileFormat = png;
- }
- };
- };
- }
- }
Partial class diagram of SplashBoard Through the class information, it is organized as follows (only key attributes are included): Get the cache path of the night mode startup image Generally speaking, based on the above content, we can make a reasonable guess about the process of iOS obtaining the cache path of the night mode startup image. When starting up, the startup image list of test.SplashTest - {DEFAULT GROUP} will be obtained through the identifier of XBApplicationSnapshotGroup. Then use the information such as userInterfaceStyle = Dark; and interfaceOrientation = Portrait; of XBApplicationSnapshot to determine which interface should be used at startup. - <XBApplicationSnapshot: 0x7fcb57004b60; identifier: 6B84614D-
- 0867-4048-BE04-8E22E6742DDF; launchInterfaceIdentifier:
- __from_UILaunchStoryboardName__; contentType: GeneratedDefault;
- referenceSize: {375, 812}; interfaceOrientation: Portrait;
- userInterfaceStyle: Dark>
Finally, use XBApplicationSnapshot's _relativePath to splice the real path of the startup image - ~/Library/Developer/CoreSimulator/Devices/1F9B22C5-E446-4881-
- AFE4-3373E3513C59/data/Containers/Data/Application
- /FA902232-17D2-495F-B23E-410349A9921C/Library/SplashBoard/Snapshots
- /test.SplashTest - { DEFAULT GROUP }/6B84614D-0867-4048-
- BE04-8E22E6742DDF@3x.ktx
Summarize This article conducts a series of analyses on applicationState.db and finally realizes the demand of obtaining the cache path of the night mode startup image under the simulator. |