How does iOS obtain the cache path of the night mode launch image?

How does iOS obtain the cache path of the night mode launch image?

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
  1. ├── 1FFD332B-EBA0-40C9-8EEE-BEC9AEF7C41A@3x.ktx
  2. ├── 96920D11-6312-4D69-BBDB-AFBB52DBDDB3@3x.ktx
  3. ├── 98F7B5B1-5B3B-478B-93A8-ED3DE6492AD1@3x.ktx
  4. └── 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:

  1. xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx

Combined with the content of Apple's official document NSUUID:

  1. `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:

  1. (lldb) platform status
  2. Platform: ios-simulator
  3. Triple: x86_64h-apple-macosx
  4. OS Version: 10.15.6 (19G2021)
  5. Kernel: Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1~1/RELEASE_X86_64
  6. Hostname: 127.0.0.1
  7. WorkingDir: /
  8. SDK Path: "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk"  
  9. 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:

  1. file ~/Library/Developer/CoreSimulator/Devices/1F9B22C5-E446-4881-AFE4-3373E3513C59/data/Library/FrontBoard/applicationState.db

Output:

  1. 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:

  1. sqlite> .schema  
  2. CREATE   TABLE   schema (version INT   NOT   NULL );
  3. CREATE   TABLE key_tab (id INTEGER   PRIMARY   KEY , key TEXT NOT   NULL , UNIQUE ( key ));
  4. CREATE   TABLE application_identifier_tab (id INTEGER   PRIMARY   KEY , application_identifier TEXT NOT   NULL , UNIQUE (application_identifier));
  5. 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 ));
  6. CREATE   INDEX kvs_keys ON kvs( key );
  7. CREATE   INDEX kvs_application_identifiers ON kvs(application_identifier);
  8. 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
  9. /* 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.

  1. sqlite > .schema key_tab
  2. CREATE   TABLE key_tab (id INTEGER   PRIMARY   KEY , key TEXT NOT   NULL , UNIQUE ( key ));
  3. sqlite> .width 2 50
  4. sqlite> select * from key_tab;
  5. id key  
  6. --------------------------------------------------  
  7. 1SBLaunchImageIngestionInfo
  8. 2XBApplicationSnapshotManifest
  9. 3 _SBScenes
  10. 4 SBApplicationShortcutItems
  11. 5 compatibilityInfo
  12. 6 SBApplicationRecentlyUpdated
  13. 7 SBApplicationRecentlyUpdatedTimerStartDate

kvs_debug is responsible for connecting the three tables above in series:

  1. kvs.application_identifier=application_identifier_tab.id
  2. 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

  1. sqlite> .width 15 32 8
  2. sqlite> SELECT * FROM kvs_debug WHERE application_identifier = 'test.SplashTest' ;
  3. application_ide key value
  4. ------------------------------------------------------------  
  5. test.SplashTest _SBScenes bplist00
  6. test.SplashTest XBApplicationSnapshotManifest bplist00
  7. test.SplashTest SBApplicationRecentlyUpdated 0
  8. 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.

  1. +(void) load {
  2. 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);
  3. printf( "%p" , lib);
  4. [self dump:@ "/Users/test/Library/Developer/CoreSimulator/Devices/1F9B22C5-E446-4881-AFE4-3373E3513C59/data/Library/FrontBoard/XBApplicationSnapshotManifest.plist" ];
  5. }
  6.  
  7. +(void)dump:(NSString *)path {
  8. NSData *data0 = [NSData dataWithContentsOfFile:path];
  9. NSPropertyListFormat f = -1;
  10. NSError *error = nil;
  11. NSData *data1 = [NSPropertyListSerialization propertyListWithData:data0 options:NSPropertyListReadStreamError format:&f error:&error];
  12. if (f==kCFPropertyListXMLFormat_v1_0) {
  13. NSLog(@ "kCFPropertyListXMLFormat_v1_0" );
  14. }
  15. id obj = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:data1 error:&error];
  16. NSLog(@ "%@" ,obj);
  17. }

Dump results:

  1. <XBApplicationSnapshotManifestImpl: 0x600000050d80; clientCount: 0> {
  2. containerIdentity = 0x0;
  3. snapshots = {
  4. <XBApplicationSnapshotGroup: 0x600002e65ea0; identifier: test.SplashTest - { DEFAULT   GROUP }> {
  5. <XBApplicationSnapshot: 0x7fcb53f04d90; identifier: CE275D00-5732-4AFD-88FD-00BAE541EC12; launchInterfaceIdentifier: __from_UILaunchStoryboardName__; contentType: GeneratedDefault; referenceSize: {375, 812}; interfaceOrientation: LandscapeLeft; userInterfaceStyle: Dark> {
  6. creationDate = September 27, 2020 at 6:08:50 PM GMT+8;
  7. keepsImageAccessUntilExpiration = NO ;
  8. hasGenerationContext = NO ;
  9. context = {
  10. contentType = GeneratedDefault;
  11. fullScreen = YES;
  12. referenceSize = {375, 812};
  13. interfaceOrientation = LandscapeLeft;
  14. userInterfaceStyle = Dark;
  15. additionalContext = {
  16. statusBarSettings = <XBStatusBarSettings: 0x600002c580f0; hidden: YES; style: 0x0; backgroundActivityEnabled: NO >;
  17. }
  18. }
  19. imageContext = {
  20. scale = 3.0;
  21. opaque = YES;
  22. fileRelativeLocation = default ;
  23. fileFormat = png;
  24. }
  25. };
  26. <XBApplicationSnapshot: 0x7fcb57004830; identifier: B9DAB53E-29D9-47D2-873E-5772DE9220D1; launchInterfaceIdentifier: __from_UILaunchStoryboardName__; contentType: GeneratedDefault; referenceSize: {375, 812}; interfaceOrientation: Portrait; userInterfaceStyle: Light> {
  27. creationDate = September 27, 2020 at 6:08:50 PM GMT+8;
  28. lastUsedDate = September 27, 2020 at 6:08:50 PM GMT+8;
  29. keepsImageAccessUntilExpiration = NO ;
  30. hasGenerationContext = NO ;
  31. context = {
  32. contentType = GeneratedDefault;
  33. fullScreen = YES;
  34. referenceSize = {375, 812};
  35. interfaceOrientation = Portrait;
  36. userInterfaceStyle = Light;
  37. additionalContext = {
  38. statusBarSettings = <XBStatusBarSettings: 0x600002c5c1a0; hidden: NO ; style: 0x0; backgroundActivityEnabled: NO >;
  39. }
  40. }
  41. imageContext = {
  42. scale = 3.0;
  43. opaque = YES;
  44. fileRelativeLocation = default ;
  45. fileFormat = png;
  46. }
  47. };
  48. <XBApplicationSnapshot: 0x7fcb57004b60; identifier: 6B84614D-0867-4048-BE04-8E22E6742DDF; launchInterfaceIdentifier: __from_UILaunchStoryboardName__; contentType: GeneratedDefault; referenceSize: {375, 812}; interfaceOrientation: Portrait; userInterfaceStyle: Dark> {
  49. creationDate = September 27, 2020 at 6:08:50 PM GMT+8;
  50. keepsImageAccessUntilExpiration = NO ;
  51. hasGenerationContext = NO ;
  52. context = {
  53. contentType = GeneratedDefault;
  54. fullScreen = YES;
  55. referenceSize = {375, 812};
  56. interfaceOrientation = Portrait;
  57. userInterfaceStyle = Dark;
  58. additionalContext = {
  59. statusBarSettings = <XBStatusBarSettings: 0x600002c5c2d0; hidden: NO ; style: 0x0; backgroundActivityEnabled: NO >;
  60. }
  61. }
  62. imageContext = {
  63. scale = 3.0;
  64. opaque = YES;
  65. fileRelativeLocation = default ;
  66. fileFormat = png;
  67. }
  68. };
  69. <XBApplicationSnapshot: 0x7fcb57005140; identifier: D3E8D00C-EE33-466B-98A6-7E60865D8001; launchInterfaceIdentifier: __from_UILaunchStoryboardName__; contentType: GeneratedDefault; referenceSize: {375, 812}; interfaceOrientation: LandscapeLeft; userInterfaceStyle: Light> {
  70. creationDate = September 27, 2020 at 6:08:50 PM GMT+8;
  71. keepsImageAccessUntilExpiration = NO ;
  72. hasGenerationContext = NO ;
  73. context = {
  74. contentType = GeneratedDefault;
  75. fullScreen = YES;
  76. referenceSize = {375, 812};
  77. interfaceOrientation = LandscapeLeft;
  78. userInterfaceStyle = Light;
  79. additionalContext = {
  80. statusBarSettings = <XBStatusBarSettings: 0x600002c5c400; hidden: YES; style: 0x0; backgroundActivityEnabled: NO >;
  81. }
  82. }
  83. imageContext = {
  84. scale = 3.0;
  85. opaque = YES;
  86. fileRelativeLocation = default ;
  87. fileFormat = png;
  88. }
  89. };
  90. };
  91. }
  92. }

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.

  1. <XBApplicationSnapshot: 0x7fcb57004b60; identifier: 6B84614D-
  2. 0867-4048-BE04-8E22E6742DDF; launchInterfaceIdentifier:
  3. __from_UILaunchStoryboardName__; contentType: GeneratedDefault;
  4. referenceSize: {375, 812}; interfaceOrientation: Portrait;
  5. userInterfaceStyle: Dark>

Finally, use XBApplicationSnapshot's _relativePath to splice the real path of the startup image

  1. ~/Library/Developer/CoreSimulator/Devices/1F9B22C5-E446-4881-
  2. AFE4-3373E3513C59/data/Containers/Data/Application
  3. /FA902232-17D2-495F-B23E-410349A9921C/Library/SplashBoard/Snapshots
  4. /test.SplashTest - { DEFAULT   GROUP }/6B84614D-0867-4048-
  5. 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.

<<:  Apple iOS 14 downgrade released

>>:  Google tests end-to-end encryption for Android Messages app

Recommend

How to play the new online marketing game well?

Internet marketing has developed in the wave of t...

Baidu bidding promotion - keyword lowest CPC bidding strategy

The core of measuring search marketing is ROI. CP...

Why do some people sing tone-deaf?

Recently, I often hear my colleague (finally onli...

This article tells you how difficult it is to successfully launch a spacecraft

In a space launch, any small fault may cause the ...

K12 online education activity operation sop!

Breaking down the basic meaning of referral and t...

In-depth analysis of event operations (I): Preparation before the event

In the next few articles, I will introduce to you...

3 elements for beginners of APP operation and promotion strategy!

Operation and promotion require the most user res...