react-native-custom-splash 2.1.4 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-custom-splash",
3
- "version": "2.1.4",
3
+ "version": "2.2.0",
4
4
  "description": "A custom splash screen module for React Native with native iOS and Android support, fully compatible with Expo",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -8,6 +8,7 @@ const {
8
8
  } = require('@expo/config-plugins');
9
9
  const path = require('path');
10
10
  const fs = require('fs');
11
+ const withCustomiOSSplash = require('./withCustomSplash');
11
12
 
12
13
  /**
13
14
  * Validate and normalize plugin configuration
@@ -437,38 +438,76 @@ const withSplashScreenIOS = (config, pluginConfig) => {
437
438
  const iosProjectPath = config.modRequest.platformProjectRoot;
438
439
  const projectName = config.modRequest.projectName;
439
440
 
440
- // Step 1: Update Info.plist
441
+ // Step 1: Update Info.plist to use LaunchScreen
441
442
  const infoPlistPath = path.join(iosProjectPath, projectName, 'Info.plist');
442
443
  if (fs.existsSync(infoPlistPath)) {
443
444
  let infoPlist = fs.readFileSync(infoPlistPath, 'utf8');
445
+
446
+ // Update to use LaunchScreen
444
447
  if (!infoPlist.includes('<key>UILaunchStoryboardName</key>')) {
445
448
  infoPlist = infoPlist.replace('</dict>\n</plist>', '\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n</dict>\n</plist>');
449
+ } else {
450
+ // Update existing value to LaunchScreen
451
+ infoPlist = infoPlist.replace(
452
+ /<key>UILaunchStoryboardName<\/key>\s*<string>.*?<\/string>/,
453
+ '<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>'
454
+ );
446
455
  }
456
+
457
+ // Remove UILaunchScreen dictionary if present
447
458
  infoPlist = infoPlist.replace(/<key>UILaunchScreen<\/key>[\s\S]*?<\/dict>/g, '');
459
+
448
460
  fs.writeFileSync(infoPlistPath, infoPlist);
449
461
  }
450
462
 
463
+ // Step 2: Delete old/existing storyboards
464
+ const oldStoryboards = [
465
+ path.join(iosProjectPath, projectName, 'SplashScreen.storyboard'),
466
+ path.join(iosProjectPath, projectName, 'LaunchScreen.storyboard'),
467
+ ];
468
+
469
+ oldStoryboards.forEach(storyPath => {
470
+ if (fs.existsSync(storyPath)) {
471
+ fs.unlinkSync(storyPath);
472
+ console.log(`šŸ—‘ļø Removed old storyboard: ${path.basename(storyPath)}`);
473
+ }
474
+ });
475
+
476
+ // Step 3: Remove SplashScreenLegacy from Images.xcassets
477
+ const legacyImagePath = path.join(iosProjectPath, projectName, 'Images.xcassets', 'SplashScreenLegacy.imageset');
478
+ if (fs.existsSync(legacyImagePath)) {
479
+ fs.rmSync(legacyImagePath, { recursive: true, force: true });
480
+ console.log('šŸ—‘ļø Removed SplashScreenLegacy imageset');
481
+ }
482
+
483
+ // Step 4: Copy user images to iOS assets
451
484
  let hasImage = false;
452
485
  let hasLogo = false;
453
486
 
454
487
  if (pluginConfig.image) {
455
488
  hasImage = await copyImageToIOS(projectRoot, pluginConfig.image, 'splash_image', iosProjectPath, projectName);
489
+ if (hasImage) {
490
+ console.log('āœ… Background image copied: splash_image');
491
+ }
456
492
  }
493
+
457
494
  if (pluginConfig.logo) {
458
495
  hasLogo = await copyImageToIOS(projectRoot, pluginConfig.logo, 'splash_logo', iosProjectPath, projectName);
496
+ if (hasLogo) {
497
+ console.log('āœ… Logo image copied: splash_logo');
498
+ }
459
499
  }
460
500
 
501
+ // Step 5: Create fresh LaunchScreen.storyboard with user images
461
502
  const launchScreenPath = path.join(iosProjectPath, projectName, 'LaunchScreen.storyboard');
462
503
  const storyboardContent = createLaunchScreenStoryboard(hasImage, hasLogo, pluginConfig.backgroundColor);
463
504
  fs.writeFileSync(launchScreenPath, storyboardContent);
464
505
 
465
- const legacyPath = path.join(iosProjectPath, projectName, 'Images.xcassets', 'SplashScreenLegacy.imageset');
466
- if (fs.existsSync(legacyPath)) {
467
- fs.rmSync(legacyPath, { recursive: true, force: true });
468
- console.log('šŸ—‘ļø Removed SplashScreenLegacy');
469
- }
506
+ console.log('āœ… LaunchScreen.storyboard created with YOUR images!');
507
+ console.log(` - Background: ${hasImage ? 'splash_image' : 'color only'}`);
508
+ console.log(` - Logo: ${hasLogo ? 'splash_logo' : 'none'}`);
509
+ console.log(` - Color: ${pluginConfig.backgroundColor}`);
470
510
 
471
- console.log('āœ… iOS LaunchScreen configured!');
472
511
  return config;
473
512
  },
474
513
  ]);
@@ -492,6 +531,6 @@ module.exports = (config, props = {}) => {
492
531
 
493
532
  return withPlugins(config, [
494
533
  [withSplashScreenAndroid, pluginConfig],
495
- [withSplashScreenIOS, pluginConfig],
534
+ [withCustomiOSSplash, pluginConfig],
496
535
  ]);
497
536
  };
@@ -0,0 +1,216 @@
1
+ const { withDangerousMod } = require('@expo/config-plugins');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ /**
6
+ * COMPLETE iOS Splash Screen Setup
7
+ * This runs AFTER Expo's default splash and overwrites everything
8
+ */
9
+ function withCustomiOSSplash(config, pluginConfig) {
10
+ return withDangerousMod(config, [
11
+ 'ios',
12
+ async (config) => {
13
+ const projectRoot = config.modRequest.projectRoot;
14
+ const iosPath = config.modRequest.platformProjectRoot;
15
+ const projectName = config.modRequest.projectName;
16
+
17
+ console.log('\nšŸš€ Setting up custom iOS splash screen...\n');
18
+
19
+ // 1. Update Info.plist
20
+ const plistPath = path.join(iosPath, projectName, 'Info.plist');
21
+ if (fs.existsSync(plistPath)) {
22
+ let plist = fs.readFileSync(plistPath, 'utf8');
23
+
24
+ // Force LaunchScreen
25
+ if (!plist.includes('<key>UILaunchStoryboardName</key>')) {
26
+ plist = plist.replace(
27
+ '</dict>\n</plist>',
28
+ '\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n</dict>\n</plist>'
29
+ );
30
+ } else {
31
+ plist = plist.replace(
32
+ /<key>UILaunchStoryboardName<\/key>\s*<string>.*?<\/string>/,
33
+ '<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>'
34
+ );
35
+ }
36
+
37
+ // Remove UILaunchScreen dictionary
38
+ plist = plist.replace(/<key>UILaunchScreen<\/key>[\s\S]*?<\/dict>/g, '');
39
+
40
+ fs.writeFileSync(plistPath, plist);
41
+ console.log('āœ… Info.plist updated');
42
+ }
43
+
44
+ // 2. Delete ALL old storyboards
45
+ const storyboards = [
46
+ path.join(iosPath, projectName, 'SplashScreen.storyboard'),
47
+ path.join(iosPath, projectName, 'LaunchScreen.storyboard'),
48
+ ];
49
+
50
+ storyboards.forEach(sb => {
51
+ if (fs.existsSync(sb)) {
52
+ fs.unlinkSync(sb);
53
+ console.log(`šŸ—‘ļø Deleted: ${path.basename(sb)}`);
54
+ }
55
+ });
56
+
57
+ // 3. Delete SplashScreenLegacy
58
+ const legacyPath = path.join(iosPath, projectName, 'Images.xcassets', 'SplashScreenLegacy.imageset');
59
+ if (fs.existsSync(legacyPath)) {
60
+ fs.rmSync(legacyPath, { recursive: true });
61
+ console.log('šŸ—‘ļø Deleted: SplashScreenLegacy');
62
+ }
63
+
64
+ // 4. Delete SplashScreen imageset (from Expo)
65
+ const splashImagePath = path.join(iosPath, projectName, 'Images.xcassets', 'SplashScreen.imageset');
66
+ if (fs.existsSync(splashImagePath)) {
67
+ fs.rmSync(splashImagePath, { recursive: true });
68
+ console.log('šŸ—‘ļø Deleted: SplashScreen');
69
+ }
70
+
71
+ // 5. Copy user images
72
+ const assetsPath = path.join(iosPath, projectName, 'Images.xcassets');
73
+ let hasImage = false;
74
+ let hasLogo = false;
75
+
76
+ if (pluginConfig.image) {
77
+ const srcImage = path.join(projectRoot, pluginConfig.image);
78
+ if (fs.existsSync(srcImage)) {
79
+ const destDir = path.join(assetsPath, 'splash_image.imageset');
80
+ fs.mkdirSync(destDir, { recursive: true });
81
+
82
+ // Copy for all scales
83
+ ['', '@2x', '@3x'].forEach(scale => {
84
+ const destFile = path.join(destDir, `splash_image${scale}.png`);
85
+ fs.copyFileSync(srcImage, destFile);
86
+ });
87
+
88
+ // Create Contents.json
89
+ const contents = {
90
+ images: [
91
+ { idiom: 'universal', filename: 'splash_image.png', scale: '1x' },
92
+ { idiom: 'universal', filename: 'splash_image@2x.png', scale: '2x' },
93
+ { idiom: 'universal', filename: 'splash_image@3x.png', scale: '3x' }
94
+ ],
95
+ info: { author: 'xcode', version: 1 }
96
+ };
97
+ fs.writeFileSync(path.join(destDir, 'Contents.json'), JSON.stringify(contents, null, 2));
98
+
99
+ hasImage = true;
100
+ console.log('āœ… Copied: splash_image');
101
+ }
102
+ }
103
+
104
+ if (pluginConfig.logo) {
105
+ const srcLogo = path.join(projectRoot, pluginConfig.logo);
106
+ if (fs.existsSync(srcLogo)) {
107
+ const destDir = path.join(assetsPath, 'splash_logo.imageset');
108
+ fs.mkdirSync(destDir, { recursive: true });
109
+
110
+ // Copy for all scales
111
+ ['', '@2x', '@3x'].forEach(scale => {
112
+ const destFile = path.join(destDir, `splash_logo${scale}.png`);
113
+ fs.copyFileSync(srcLogo, destFile);
114
+ });
115
+
116
+ // Create Contents.json
117
+ const contents = {
118
+ images: [
119
+ { idiom: 'universal', filename: 'splash_logo.png', scale: '1x' },
120
+ { idiom: 'universal', filename: 'splash_logo@2x.png', scale: '2x' },
121
+ { idiom: 'universal', filename: 'splash_logo@3x.png', scale: '3x' }
122
+ ],
123
+ info: { author: 'xcode', version: 1 }
124
+ };
125
+ fs.writeFileSync(path.join(destDir, 'Contents.json'), JSON.stringify(contents, null, 2));
126
+
127
+ hasLogo = true;
128
+ console.log('āœ… Copied: splash_logo');
129
+ }
130
+ }
131
+
132
+ // 6. Create NEW LaunchScreen.storyboard
133
+ const storyboard = createStoryboard(hasImage, hasLogo, pluginConfig.backgroundColor);
134
+ const storyboardPath = path.join(iosPath, projectName, 'LaunchScreen.storyboard');
135
+ fs.writeFileSync(storyboardPath, storyboard);
136
+
137
+ console.log('āœ… Created: LaunchScreen.storyboard');
138
+ console.log(` Background: ${hasImage ? 'splash_image āœ“' : 'color only'}`);
139
+ console.log(` Logo: ${hasLogo ? 'splash_logo āœ“' : 'none'}`);
140
+ console.log(` Color: ${pluginConfig.backgroundColor}\n`);
141
+
142
+ return config;
143
+ },
144
+ ]);
145
+ }
146
+
147
+ function createStoryboard(hasImage, hasLogo, bgColor) {
148
+ const color = bgColor.replace('#', '');
149
+ const r = parseInt(color.substr(0, 2), 16) / 255;
150
+ const g = parseInt(color.substr(2, 2), 16) / 255;
151
+ const b = parseInt(color.substr(4, 2), 16) / 255;
152
+
153
+ const bgImageView = hasImage ? `
154
+ <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="splash_image" translatesAutoresizingMaskIntoConstraints="NO" id="bgImg">
155
+ <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
156
+ </imageView>` : '';
157
+
158
+ const logoImageView = hasLogo ? `
159
+ <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="splash_logo" translatesAutoresizingMaskIntoConstraints="NO" id="logoImg">
160
+ <rect key="frame" x="132" y="348" width="150" height="150"/>
161
+ <constraints>
162
+ <constraint firstAttribute="width" constant="150" id="logoW"/>
163
+ <constraint firstAttribute="height" constant="150" id="logoH"/>
164
+ </constraints>
165
+ </imageView>` : '';
166
+
167
+ const bgConstraints = hasImage ? `
168
+ <constraint firstItem="bgImg" firstAttribute="top" secondItem="view" secondAttribute="top"/>
169
+ <constraint firstItem="bgImg" firstAttribute="leading" secondItem="view" secondAttribute="leading"/>
170
+ <constraint firstItem="bgImg" firstAttribute="trailing" secondItem="view" secondAttribute="trailing"/>
171
+ <constraint firstItem="bgImg" firstAttribute="bottom" secondItem="view" secondAttribute="bottom"/>` : '';
172
+
173
+ const logoConstraints = hasLogo ? `
174
+ <constraint firstItem="logoImg" firstAttribute="centerX" secondItem="view" secondAttribute="centerX"/>
175
+ <constraint firstItem="logoImg" firstAttribute="centerY" secondItem="view" secondAttribute="centerY"/>` : '';
176
+
177
+ const resources = [];
178
+ if (hasImage) resources.push(' <image name="splash_image" width="1242" height="2688"/>');
179
+ if (hasLogo) resources.push(' <image name="splash_logo" width="512" height="512"/>');
180
+
181
+ return `<?xml version="1.0" encoding="UTF-8"?>
182
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
183
+ <device id="retina6_1" orientation="portrait" appearance="light"/>
184
+ <dependencies>
185
+ <deployment identifier="iOS"/>
186
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
187
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
188
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
189
+ </dependencies>
190
+ <scenes>
191
+ <scene sceneID="EHf-IW-A2E">
192
+ <objects>
193
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
194
+ <view key="view" contentMode="scaleToFill" id="view">
195
+ <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
196
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
197
+ <subviews>${bgImageView}${logoImageView}
198
+ </subviews>
199
+ <viewLayoutGuide key="safeArea" id="safeArea"/>
200
+ <color key="backgroundColor" red="${r}" green="${g}" blue="${b}" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
201
+ <constraints>${bgConstraints}${logoConstraints}
202
+ </constraints>
203
+ </view>
204
+ </viewController>
205
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
206
+ </objects>
207
+ <point key="canvasLocation" x="53" y="375"/>
208
+ </scene>
209
+ </scenes>
210
+ <resources>
211
+ ${resources.join('\n')}
212
+ </resources>
213
+ </document>`;
214
+ }
215
+
216
+ module.exports = withCustomiOSSplash;