diff --git a/docs/REVENUECAT_INTEGRATION_GUIDE.md b/docs/REVENUECAT_INTEGRATION_GUIDE.md index ae6aeec..2224d78 100644 --- a/docs/REVENUECAT_INTEGRATION_GUIDE.md +++ b/docs/REVENUECAT_INTEGRATION_GUIDE.md @@ -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 + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + CFBundleDisplayName + $(PRODUCT_NAME) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + UIApplicationSupportsIndirectInputEvents + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + + NSCameraUsageDescription + Your camera usage description + + + RevenueCatAPIKey + $(REVENUECAT_API_KEY) + + +``` + +##### 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 +RevenueCatAPIKey +$(REVENUECAT_API_KEY) +``` + +**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: `$(REVENUECAT_API_KEY)` +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