Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
ac35666208
commit
4537a40cea
@ -8,6 +8,9 @@ A comprehensive, reusable guide for integrating RevenueCat into iOS apps with Sw
|
||||
2. [Prerequisites](#prerequisites)
|
||||
3. [SDK Installation](#sdk-installation)
|
||||
4. [Configuration Architecture](#configuration-architecture)
|
||||
- [Option A: Swift Secrets File](#option-a-swift-secrets-file-simple)
|
||||
- [Option B: Separate Debug/Release Keys](#option-b-separate-debugrelease-keys-swift-files)
|
||||
- [Option C: xcconfig + Info.plist (Recommended)](#option-c-xcconfig--infoplist-recommended-for-xcode-15-projects)
|
||||
5. [PremiumManager Implementation](#premiummanager-implementation)
|
||||
6. [Paywall Implementation](#paywall-implementation)
|
||||
7. [Event Handling](#event-handling)
|
||||
@ -123,7 +126,7 @@ Add to `.gitignore`:
|
||||
**/Secrets.swift
|
||||
```
|
||||
|
||||
#### Option B: Separate Debug/Release Keys (Recommended for Production)
|
||||
#### Option B: Separate Debug/Release Keys (Swift Files)
|
||||
|
||||
Create two secrets files:
|
||||
|
||||
@ -143,6 +146,228 @@ enum Secrets {
|
||||
|
||||
Use build configurations or compiler flags to include the correct file.
|
||||
|
||||
#### Option C: xcconfig + Info.plist (Recommended for Xcode 15+ Projects)
|
||||
|
||||
This approach is the most robust for modern Xcode projects, especially those using `fileSystemSynchronizedGroups` (automatic file sync). It keeps secrets in xcconfig files and injects them into Info.plist at build time.
|
||||
|
||||
**Why use this approach:**
|
||||
- Secrets stay in gitignored xcconfig files
|
||||
- Build settings automatically switch between debug/release keys
|
||||
- Works with Xcode's auto-generated Info.plist replacement
|
||||
- No Swift code changes needed to switch keys
|
||||
|
||||
##### Step 1: Create xcconfig Structure
|
||||
|
||||
Create the following files in `YourApp/Configuration/`:
|
||||
|
||||
**`Base.xcconfig`** (committed to git):
|
||||
```
|
||||
// Base.xcconfig - Source of truth for all identifiers
|
||||
|
||||
COMPANY_IDENTIFIER = com.yourcompany
|
||||
BUNDLE_ID_NAME = YourApp
|
||||
PRODUCT_NAME = Your App
|
||||
DEVELOPMENT_TEAM = YOUR_TEAM_ID
|
||||
|
||||
APP_BUNDLE_IDENTIFIER = $(COMPANY_IDENTIFIER).$(BUNDLE_ID_NAME)
|
||||
```
|
||||
|
||||
**`Debug.xcconfig`** (committed to git):
|
||||
```
|
||||
// Debug.xcconfig
|
||||
#include "Base.xcconfig"
|
||||
#include "Secrets.debug.xcconfig"
|
||||
```
|
||||
|
||||
**`Release.xcconfig`** (committed to git):
|
||||
```
|
||||
// Release.xcconfig
|
||||
#include "Base.xcconfig"
|
||||
#include "Secrets.release.xcconfig"
|
||||
```
|
||||
|
||||
**`Secrets.debug.xcconfig`** (gitignored):
|
||||
```
|
||||
// Secrets.debug.xcconfig
|
||||
// ⚠️ DO NOT COMMIT THIS FILE
|
||||
|
||||
// RevenueCat Test API Key (starts with "test_")
|
||||
REVENUECAT_API_KEY = test_your_test_key_here
|
||||
```
|
||||
|
||||
**`Secrets.release.xcconfig`** (gitignored):
|
||||
```
|
||||
// Secrets.release.xcconfig
|
||||
// ⚠️ DO NOT COMMIT THIS FILE
|
||||
|
||||
// RevenueCat Production API Key (starts with "appl_")
|
||||
REVENUECAT_API_KEY = appl_your_production_key_here
|
||||
```
|
||||
|
||||
Add to `.gitignore`:
|
||||
```
|
||||
**/Secrets.debug.xcconfig
|
||||
**/Secrets.release.xcconfig
|
||||
```
|
||||
|
||||
##### Step 2: Create Info.plist at Project Root
|
||||
|
||||
**CRITICAL:** For projects using `fileSystemSynchronizedGroups`, the Info.plist MUST be placed at the **project root** (same level as the `.xcodeproj`), NOT inside the synced app folder. Otherwise Xcode will automatically add it to Copy Bundle Resources and cause build failures.
|
||||
|
||||
Create `Info.plist` at the project root (e.g., `YourApp/Info.plist`, NOT `YourApp/YourApp/Info.plist`):
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<true/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
|
||||
<!-- Add your usage descriptions -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Your camera usage description</string>
|
||||
|
||||
<!-- RevenueCat API Key - injected from xcconfig -->
|
||||
<key>RevenueCatAPIKey</key>
|
||||
<string>$(REVENUECAT_API_KEY)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
##### Step 3: Configure Xcode Project
|
||||
|
||||
1. **Set Build Configurations to use xcconfig files:**
|
||||
- Project → Info → Configurations
|
||||
- Set Debug to use `Debug.xcconfig`
|
||||
- Set Release to use `Release.xcconfig`
|
||||
|
||||
2. **Update Build Settings for the app target:**
|
||||
- Set `GENERATE_INFOPLIST_FILE = NO`
|
||||
- Set `INFOPLIST_FILE = Info.plist` (path relative to project root)
|
||||
|
||||
##### Step 4: Update PremiumManager to Read from Info.plist
|
||||
|
||||
```swift
|
||||
private static var apiKey: String {
|
||||
let key = (Bundle.main.object(forInfoDictionaryKey: "RevenueCatAPIKey") as? String) ?? ""
|
||||
let placeholders = [
|
||||
"",
|
||||
"YOUR_REVENUECAT_API_KEY_HERE",
|
||||
"test_YOUR_TEST_KEY_HERE",
|
||||
"appl_YOUR_PRODUCTION_KEY_HERE"
|
||||
]
|
||||
|
||||
#if DEBUG
|
||||
let prefix = key.split(separator: "_").first.map(String.init) ?? "missing"
|
||||
let keyStatus = key.isEmpty ? "empty" : "present"
|
||||
print("ℹ️ [PremiumManager] RevenueCatAPIKey \(keyStatus), prefix=\(prefix)")
|
||||
#endif
|
||||
|
||||
guard !placeholders.contains(key) else {
|
||||
#if DEBUG
|
||||
print("⚠️ [PremiumManager] RevenueCat API key not configured. Check Secrets.debug.xcconfig / Secrets.release.xcconfig")
|
||||
#endif
|
||||
return ""
|
||||
}
|
||||
return key
|
||||
}
|
||||
```
|
||||
|
||||
##### Common Pitfalls
|
||||
|
||||
**1. INFOPLIST_KEY_ prefix doesn't work for custom keys**
|
||||
|
||||
The `INFOPLIST_KEY_` build setting prefix (e.g., `INFOPLIST_KEY_RevenueCatAPIKey`) only works for Apple's predefined Info.plist keys. Custom keys like `RevenueCatAPIKey` will NOT be added to the generated Info.plist.
|
||||
|
||||
❌ **Does NOT work:**
|
||||
```
|
||||
// In xcconfig - custom keys are ignored
|
||||
INFOPLIST_KEY_RevenueCatAPIKey = $(REVENUECAT_API_KEY)
|
||||
```
|
||||
|
||||
✅ **Works:** Use a custom Info.plist with build setting substitution:
|
||||
```xml
|
||||
<key>RevenueCatAPIKey</key>
|
||||
<string>$(REVENUECAT_API_KEY)</string>
|
||||
```
|
||||
|
||||
**2. Info.plist in synced folder causes "Multiple commands produce Info.plist"**
|
||||
|
||||
If your project uses `fileSystemSynchronizedGroups` (Xcode 15+ default for new projects), placing Info.plist inside the app folder causes Xcode to automatically add it to Copy Bundle Resources, resulting in a build error.
|
||||
|
||||
❌ **Causes error:** `YourApp/YourApp/Info.plist`
|
||||
|
||||
✅ **Correct location:** `YourApp/Info.plist` (at project root, outside the synced folder)
|
||||
|
||||
**3. Wrong xcconfig include path**
|
||||
|
||||
The `#include` path in xcconfig files is relative to the xcconfig file itself.
|
||||
|
||||
❌ **Wrong (if files are in same directory):**
|
||||
```
|
||||
#include "YourApp/Configuration/Secrets.debug.xcconfig"
|
||||
```
|
||||
|
||||
✅ **Correct:**
|
||||
```
|
||||
#include "Secrets.debug.xcconfig"
|
||||
```
|
||||
|
||||
**4. Verify the key is in the built app**
|
||||
|
||||
After building, verify the key was properly substituted:
|
||||
```bash
|
||||
plutil -p ~/Library/Developer/Xcode/DerivedData/YourApp-*/Build/Products/Debug-iphonesimulator/Your\ App.app/Info.plist | grep RevenueCat
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
"RevenueCatAPIKey" => "test_your_actual_key"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PremiumManager Implementation
|
||||
@ -970,6 +1195,66 @@ Use the test API key (`test_`):
|
||||
2. Check bundle ID matches exactly
|
||||
3. Wait 1-2 minutes for transactions to appear
|
||||
|
||||
### API Key Shows in Build Settings But Not in App (xcconfig)
|
||||
|
||||
**Symptom:** `xcodebuild -showBuildSettings` shows `REVENUECAT_API_KEY = test_...` but the app logs show "RevenueCatAPIKey empty".
|
||||
|
||||
**Cause:** The `INFOPLIST_KEY_` prefix only works for Apple's predefined keys, not custom keys.
|
||||
|
||||
**Solution:**
|
||||
1. Create a custom `Info.plist` file at the project root (NOT inside the synced app folder)
|
||||
2. Add the key with build setting substitution: `<string>$(REVENUECAT_API_KEY)</string>`
|
||||
3. Set `GENERATE_INFOPLIST_FILE = NO` in build settings
|
||||
4. Set `INFOPLIST_FILE = Info.plist`
|
||||
|
||||
See [Option C: xcconfig + Info.plist](#option-c-xcconfig--infoplist-recommended-for-xcode-15-projects) for complete instructions.
|
||||
|
||||
### "Multiple commands produce Info.plist" Build Error
|
||||
|
||||
**Cause:** The Info.plist file is inside a `fileSystemSynchronizedGroups` folder (Xcode 15+ default), causing Xcode to automatically add it to Copy Bundle Resources.
|
||||
|
||||
**Solution:** Move Info.plist to the project root directory (same level as `.xcodeproj`), NOT inside the app source folder.
|
||||
|
||||
```
|
||||
YourProject/
|
||||
├── YourProject.xcodeproj
|
||||
├── Info.plist ← Correct location
|
||||
└── YourProject/
|
||||
└── (source files) ← NOT here
|
||||
```
|
||||
|
||||
### xcconfig Include Path Errors
|
||||
|
||||
**Symptom:** Build settings show empty or placeholder values even though the Secrets xcconfig files exist.
|
||||
|
||||
**Cause:** The `#include` path is relative to the xcconfig file itself, not the project root.
|
||||
|
||||
**Fix:** If `Debug.xcconfig` and `Secrets.debug.xcconfig` are in the same directory:
|
||||
|
||||
```
|
||||
// Wrong
|
||||
#include "YourApp/Configuration/Secrets.debug.xcconfig"
|
||||
|
||||
// Correct
|
||||
#include "Secrets.debug.xcconfig"
|
||||
```
|
||||
|
||||
### Verifying xcconfig Setup
|
||||
|
||||
Run these commands to debug your xcconfig setup:
|
||||
|
||||
```bash
|
||||
# Check if REVENUECAT_API_KEY is in build settings
|
||||
xcodebuild -showBuildSettings -scheme "YourScheme" -configuration Debug | grep REVENUECAT
|
||||
|
||||
# Check if key is in the built Info.plist
|
||||
plutil -p ~/Library/Developer/Xcode/DerivedData/YourApp-*/Build/Products/Debug-iphonesimulator/Your\ App.app/Info.plist | grep -i revenue
|
||||
|
||||
# Clean and rebuild after xcconfig changes
|
||||
rm -rf ~/Library/Developer/Xcode/DerivedData/YourApp-*
|
||||
xcodebuild clean build -scheme "YourScheme" -configuration Debug -destination "platform=iOS Simulator,name=iPhone 16 Pro"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
Loading…
Reference in New Issue
Block a user