Showing
21 changed files
with
19 additions
and
958 deletions
1 | -# CampaignViewController Investigation Report | ||
2 | - | ||
3 | -## Issue | ||
4 | -"Unknown class CampaignViewController in Interface Builder file" error when using SPM, while XIB files work perfectly. | ||
5 | - | ||
6 | -## Investigation Findings | ||
7 | - | ||
8 | -### 1. Package.swift Analysis | ||
9 | -✅ **GOOD**: `CampaignViewController.swift` is properly included in SPM target | ||
10 | -- File location: `SwiftWarplyFramework/SwiftWarplyFramework/screens/CampaignViewController.swift` | ||
11 | -- SPM path: `"SwiftWarplyFramework/SwiftWarplyFramework"` | ||
12 | -- The file is automatically included since no explicit `sources` parameter excludes it | ||
13 | - | ||
14 | -### 2. File Structure Comparison | ||
15 | -✅ **GOOD**: File is in correct location | ||
16 | -- `CampaignViewController.swift` is in `screens/` directory (same as other working controllers) | ||
17 | -- Other working controllers: `ProfileViewController`, `CouponViewController`, `MyRewardsViewController` | ||
18 | - | ||
19 | -### 3. Class Declaration Analysis | ||
20 | - | ||
21 | -#### ❌ **PROBLEM IDENTIFIED**: Missing Convenience Initializer | ||
22 | - | ||
23 | -**CampaignViewController.swift:** | ||
24 | -```swift | ||
25 | -@objc public class CampaignViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, CLLocationManagerDelegate, WKUIDelegate, UIScrollViewDelegate { | ||
26 | - @IBOutlet weak var webview: WKWebView! | ||
27 | - | ||
28 | - // NO convenience initializer for storyboard loading | ||
29 | - // NO explicit bundle handling | ||
30 | -} | ||
31 | -``` | ||
32 | - | ||
33 | -**ProfileViewController.swift (WORKING):** | ||
34 | -```swift | ||
35 | -@objc public class ProfileViewController: UIViewController { | ||
36 | - @IBOutlet weak var tableView: UITableView! | ||
37 | - | ||
38 | - // MARK: - Initializers | ||
39 | - public convenience init() { | ||
40 | - self.init(nibName: "ProfileViewController", bundle: Bundle.frameworkBundle) | ||
41 | - } | ||
42 | - | ||
43 | - public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { | ||
44 | - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) | ||
45 | - } | ||
46 | - | ||
47 | - required init?(coder: NSCoder) { | ||
48 | - super.init(coder: coder) | ||
49 | - } | ||
50 | -} | ||
51 | -``` | ||
52 | - | ||
53 | -### 4. Key Differences | ||
54 | - | ||
55 | -| Aspect | CampaignViewController | ProfileViewController (Working) | | ||
56 | -|--------|----------------------|--------------------------------| | ||
57 | -| **Convenience Init** | ❌ Missing | ✅ Present with Bundle.frameworkBundle | | ||
58 | -| **Bundle Handling** | ❌ No explicit bundle | ✅ Uses Bundle.frameworkBundle | | ||
59 | -| **Storyboard vs XIB** | 📱 Storyboard-based | 📄 XIB-based | | ||
60 | -| **Class Declaration** | ✅ Public | ✅ Public | | ||
61 | -| **Access Modifiers** | ✅ Correct | ✅ Correct | | ||
62 | - | ||
63 | -### 5. Root Cause Analysis | ||
64 | - | ||
65 | -The issue is **NOT** with SPM inclusion but with **bundle resolution for storyboard-based view controllers**. | ||
66 | - | ||
67 | -**Why XIB controllers work:** | ||
68 | -- XIB controllers have explicit `Bundle.frameworkBundle` initializers | ||
69 | -- They handle bundle resolution correctly for SPM | ||
70 | - | ||
71 | -**Why CampaignViewController fails:** | ||
72 | -- Storyboard tries to instantiate the class but can't resolve the correct bundle | ||
73 | -- No convenience initializer to guide bundle resolution | ||
74 | -- SPM creates different bundle structure than CocoaPods | ||
75 | - | ||
76 | -### 6. Bundle Structure Difference | ||
77 | - | ||
78 | -**CocoaPods:** | ||
79 | -- Bundle: `SwiftWarplyFramework.framework` | ||
80 | -- Storyboard can find classes easily | ||
81 | - | ||
82 | -**SPM:** | ||
83 | -- Bundle: `SwiftWarplyFramework_SwiftWarplyFramework.bundle` | ||
84 | -- Storyboard needs explicit bundle guidance | ||
85 | - | ||
86 | -## Recommended Solutions | ||
87 | - | ||
88 | -### Option 1: Add Bundle-Aware Initializers (Recommended) | ||
89 | -Add the missing initializers to `CampaignViewController.swift`: | ||
90 | - | ||
91 | -```swift | ||
92 | -// MARK: - Initializers | ||
93 | -public convenience init() { | ||
94 | - let storyboard = UIStoryboard(name: "Main", bundle: Bundle.frameworkBundle) | ||
95 | - let vc = storyboard.instantiateViewController(withIdentifier: "CampaignViewController") | ||
96 | - // Handle initialization properly | ||
97 | -} | ||
98 | - | ||
99 | -public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { | ||
100 | - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) | ||
101 | -} | ||
102 | - | ||
103 | -required init?(coder: NSCoder) { | ||
104 | - super.init(coder: coder) | ||
105 | -} | ||
106 | -``` | ||
107 | - | ||
108 | -### Option 2: Convert to XIB-based (Alternative) | ||
109 | -Convert `CampaignViewController` from storyboard to XIB format like other controllers. | ||
110 | - | ||
111 | -### Option 3: Update Storyboard Loading Code | ||
112 | -Ensure any code that loads the storyboard uses `Bundle.frameworkBundle`: | ||
113 | - | ||
114 | -```swift | ||
115 | -let storyboard = UIStoryboard(name: "Main", bundle: Bundle.frameworkBundle) | ||
116 | -let vc = storyboard.instantiateViewController(withIdentifier: "CampaignViewController") | ||
117 | -``` | ||
118 | - | ||
119 | -## Conclusion | ||
120 | - | ||
121 | -The issue is specifically with **storyboard bundle resolution in SPM**, not with class inclusion or module specifications. The XIB files work because they have proper bundle-aware initializers, while the storyboard-based `CampaignViewController` lacks this SPM-compatible initialization pattern. |
1 | -# CampaignViewController XIB Conversion - COMPLETION REPORT | ||
2 | - | ||
3 | -## 🎉 **ALL FIXES SUCCESSFULLY IMPLEMENTED!** | ||
4 | - | ||
5 | -### ✅ **Fix 1: XIB Initializers Added to CampaignViewController.swift** | ||
6 | -**STATUS: COMPLETE** | ||
7 | -```swift | ||
8 | -// MARK: - Initializers | ||
9 | -public convenience init() { | ||
10 | - self.init(nibName: "CampaignViewController", bundle: Bundle.frameworkBundle) | ||
11 | -} | ||
12 | - | ||
13 | -public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { | ||
14 | - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) | ||
15 | -} | ||
16 | - | ||
17 | -required init?(coder: NSCoder) { | ||
18 | - super.init(coder: coder) | ||
19 | -} | ||
20 | -``` | ||
21 | - | ||
22 | -### ✅ **Fix 2: XIB Added to Package.swift Resources** | ||
23 | -**STATUS: COMPLETE** | ||
24 | -```swift | ||
25 | -.process("screens/CampaignViewController/CampaignViewController.xib") | ||
26 | -``` | ||
27 | -Added to SPM resources array - SPM will now properly bundle the XIB file. | ||
28 | - | ||
29 | -### ✅ **Fix 3: Module Specifications Removed from XIB** | ||
30 | -**STATUS: COMPLETE** | ||
31 | -```xml | ||
32 | -<!-- BEFORE --> | ||
33 | -<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CampaignViewController" customModule="SwiftWarplyFramework" customModuleProvider="target"> | ||
34 | - | ||
35 | -<!-- AFTER --> | ||
36 | -<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CampaignViewController"> | ||
37 | -``` | ||
38 | - | ||
39 | -### ✅ **Fix 4: Webview Outlet Connected** | ||
40 | -**STATUS: COMPLETE** (Done by user) | ||
41 | -```xml | ||
42 | -<outlet property="webview" destination="RP2-DM-ew4" id="IfP-kr-pPH"/> | ||
43 | -``` | ||
44 | - | ||
45 | -## 🏆 **FINAL VERIFICATION:** | ||
46 | - | ||
47 | -### **MyRewardsViewController Instantiation Code:** | ||
48 | -```swift | ||
49 | -private func openCampaignViewController(with index: Int) { | ||
50 | - let vc = SwiftWarplyFramework.CampaignViewController(nibName: "CampaignViewController", bundle: Bundle.frameworkBundle) | ||
51 | - vc.campaignUrl = contestUrls[index] | ||
52 | - vc.showHeader = false | ||
53 | - self.navigationController?.pushViewController(vc, animated: true) | ||
54 | -} | ||
55 | -``` | ||
56 | -✅ **PERFECT** - Uses XIB-based instantiation with proper bundle | ||
57 | - | ||
58 | -### **CampaignViewController Architecture:** | ||
59 | -- ✅ **XIB-based initialization** - SPM compatible | ||
60 | -- ✅ **Bundle-aware instantiation** - Uses `Bundle.frameworkBundle` | ||
61 | -- ✅ **Proper outlet connections** - webview connected | ||
62 | -- ✅ **SPM resource inclusion** - XIB bundled correctly | ||
63 | -- ✅ **Module-agnostic XIB** - No hardcoded module references | ||
64 | - | ||
65 | -## 🎯 **RESULT:** | ||
66 | - | ||
67 | -**CampaignViewController is now fully SPM-compatible and follows the same proven pattern as all other XIB-based controllers in your framework.** | ||
68 | - | ||
69 | -### **What This Achieves:** | ||
70 | -1. **Eliminates "Unknown class" errors** in SPM builds | ||
71 | -2. **Consistent architecture** across all view controllers | ||
72 | -3. **Proper bundle resolution** for XIB files | ||
73 | -4. **Future-proof implementation** for SPM compatibility | ||
74 | - | ||
75 | -### **Testing Recommendations:** | ||
76 | -1. Clean and rebuild your SPM package | ||
77 | -2. Test CampaignViewController instantiation in your client app | ||
78 | -3. Verify webview loads correctly | ||
79 | -4. Confirm navigation works as expected | ||
80 | - | ||
81 | -**🚀 Your XIB conversion is complete and ready for production!** |
1 | -# CampaignViewController XIB Conversion Verification Report | ||
2 | - | ||
3 | -## 📋 **Verification Summary** | ||
4 | - | ||
5 | -I've thoroughly reviewed your XIB conversion implementation. Here's what I found: | ||
6 | - | ||
7 | -## ✅ **What's Working Correctly:** | ||
8 | - | ||
9 | -### 1. **MyRewardsViewController.swift - Perfect Implementation** | ||
10 | -```swift | ||
11 | -private func openCampaignViewController(with index: Int) { | ||
12 | - let vc = SwiftWarplyFramework.CampaignViewController(nibName: "CampaignViewController", bundle: Bundle.frameworkBundle) | ||
13 | - vc.campaignUrl = contestUrls[index] | ||
14 | - vc.showHeader = false | ||
15 | - self.navigationController?.pushViewController(vc, animated: true) | ||
16 | -} | ||
17 | -``` | ||
18 | -✅ **EXCELLENT**: You've correctly updated the instantiation to use XIB-based loading with `Bundle.frameworkBundle` | ||
19 | - | ||
20 | -### 2. **CampaignViewController.xib - Good Structure** | ||
21 | -✅ **GOOD**: XIB file exists and has proper structure | ||
22 | -✅ **GOOD**: Contains WKWebView with proper constraints | ||
23 | -✅ **GOOD**: Has File's Owner connection to view | ||
24 | - | ||
25 | -### 3. **File Organization** | ||
26 | -✅ **GOOD**: Moved to proper directory structure (`screens/CampaignViewController/`) | ||
27 | -✅ **GOOD**: Both .swift and .xib files are present | ||
28 | - | ||
29 | -## ❌ **Critical Issues Found:** | ||
30 | - | ||
31 | -### 1. **Missing XIB-Based Initializers in CampaignViewController.swift** | ||
32 | -**PROBLEM**: The Swift file lacks the essential XIB-compatible initializers that all other controllers have. | ||
33 | - | ||
34 | -**MISSING CODE**: | ||
35 | -```swift | ||
36 | -// MARK: - Initializers | ||
37 | -public convenience init() { | ||
38 | - self.init(nibName: "CampaignViewController", bundle: Bundle.frameworkBundle) | ||
39 | -} | ||
40 | - | ||
41 | -public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { | ||
42 | - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) | ||
43 | -} | ||
44 | - | ||
45 | -required init?(coder: NSCoder) { | ||
46 | - super.init(coder: coder) | ||
47 | -} | ||
48 | -``` | ||
49 | - | ||
50 | -### 2. **XIB Module Specification Issue** | ||
51 | -**PROBLEM**: The XIB still has hardcoded module specifications: | ||
52 | -```xml | ||
53 | -customClass="CampaignViewController" customModule="SwiftWarplyFramework" customModuleProvider="target" | ||
54 | -``` | ||
55 | - | ||
56 | -**SHOULD BE**: | ||
57 | -```xml | ||
58 | -customClass="CampaignViewController" | ||
59 | -``` | ||
60 | - | ||
61 | -### 3. **Missing XIB from Package.swift** | ||
62 | -**PROBLEM**: The new XIB file is not included in SPM resources. | ||
63 | - | ||
64 | -**MISSING LINE** in Package.swift resources: | ||
65 | -```swift | ||
66 | -.process("screens/CampaignViewController/CampaignViewController.xib") | ||
67 | -``` | ||
68 | - | ||
69 | -### 4. **Outlet Connection Issue** | ||
70 | -**PROBLEM**: XIB has no `webview` outlet connection, but Swift file expects `@IBOutlet weak var webview: WKWebView!` | ||
71 | - | ||
72 | -## 🔧 **Required Fixes:** | ||
73 | - | ||
74 | -### Fix 1: Add XIB Initializers to CampaignViewController.swift | ||
75 | -Add these initializers right after the class declaration: | ||
76 | - | ||
77 | -```swift | ||
78 | -@objc public class CampaignViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, CLLocationManagerDelegate, WKUIDelegate, UIScrollViewDelegate { | ||
79 | - @IBOutlet weak var webview: WKWebView! | ||
80 | - | ||
81 | - // MARK: - Initializers | ||
82 | - public convenience init() { | ||
83 | - self.init(nibName: "CampaignViewController", bundle: Bundle.frameworkBundle) | ||
84 | - } | ||
85 | - | ||
86 | - public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { | ||
87 | - super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) | ||
88 | - } | ||
89 | - | ||
90 | - required init?(coder: NSCoder) { | ||
91 | - super.init(coder: coder) | ||
92 | - } | ||
93 | - | ||
94 | - // ... rest of your code | ||
95 | -``` | ||
96 | - | ||
97 | -### Fix 2: Remove Module Specifications from XIB | ||
98 | -Remove `customModule="SwiftWarplyFramework" customModuleProvider="target"` from the XIB File's Owner. | ||
99 | - | ||
100 | -### Fix 3: Add XIB to Package.swift Resources | ||
101 | -Add this line to the resources array: | ||
102 | -```swift | ||
103 | -.process("screens/CampaignViewController/CampaignViewController.xib") | ||
104 | -``` | ||
105 | - | ||
106 | -### Fix 4: Connect webview Outlet in XIB | ||
107 | -Connect the WKWebView in the XIB to the `webview` outlet in File's Owner. | ||
108 | - | ||
109 | -## 🎯 **Priority Order:** | ||
110 | -1. **CRITICAL**: Add XIB initializers to Swift file | ||
111 | -2. **CRITICAL**: Add XIB to Package.swift resources | ||
112 | -3. **HIGH**: Remove module specifications from XIB | ||
113 | -4. **MEDIUM**: Fix webview outlet connection | ||
114 | - | ||
115 | -## 📊 **Current Status:** | ||
116 | -- ✅ Instantiation code: **PERFECT** | ||
117 | -- ❌ Swift initializers: **MISSING** | ||
118 | -- ❌ SPM resources: **MISSING** | ||
119 | -- ⚠️ XIB module specs: **NEEDS FIX** | ||
120 | -- ⚠️ Outlet connections: **NEEDS FIX** | ||
121 | - | ||
122 | -Once these fixes are applied, your XIB conversion will be complete and SPM-compatible! |
... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
4 | 4 | ||
5 | **Get started with SwiftWarplyFramework in just 5 minutes!** | 5 | **Get started with SwiftWarplyFramework in just 5 minutes!** |
6 | 6 | ||
7 | -**Version**: 2.2.9 | **Minimum iOS**: 17.0 | **Swift**: 5.0+ | 7 | +**Version**: 2.2.10 | **Minimum iOS**: 17.0 | **Swift**: 5.0+ |
8 | 8 | ||
9 | --- | 9 | --- |
10 | 10 | ||
... | @@ -53,7 +53,7 @@ Choose your preferred installation method: | ... | @@ -53,7 +53,7 @@ Choose your preferred installation method: |
53 | ``` | 53 | ``` |
54 | https://git.warp.ly/open-source/warply_sdk_framework.git | 54 | https://git.warp.ly/open-source/warply_sdk_framework.git |
55 | ``` | 55 | ``` |
56 | -4. Select **Version**: `2.2.9` or **Up to Next Major** | 56 | +4. Select **Version**: `2.2.10` or **Up to Next Major** |
57 | 5. Click **Add Package** | 57 | 5. Click **Add Package** |
58 | 6. Select **SwiftWarplyFramework** and click **Add Package** | 58 | 6. Select **SwiftWarplyFramework** and click **Add Package** |
59 | 59 | ||
... | @@ -62,7 +62,7 @@ Add to your `Package.swift` dependencies: | ... | @@ -62,7 +62,7 @@ Add to your `Package.swift` dependencies: |
62 | 62 | ||
63 | ```swift | 63 | ```swift |
64 | dependencies: [ | 64 | dependencies: [ |
65 | - .package(url: "https://git.warp.ly/open-source/warply_sdk_framework.git", from: "2.2.9") | 65 | + .package(url: "https://git.warp.ly/open-source/warply_sdk_framework.git", from: "2.2.10") |
66 | ] | 66 | ] |
67 | ``` | 67 | ``` |
68 | 68 | ||
... | @@ -89,7 +89,7 @@ platform :ios, '17.0' | ... | @@ -89,7 +89,7 @@ platform :ios, '17.0' |
89 | target 'YourApp' do | 89 | target 'YourApp' do |
90 | use_frameworks! | 90 | use_frameworks! |
91 | 91 | ||
92 | - pod 'SwiftWarplyFramework', :git => 'https://git@git.warp.ly/open-source/warply_sdk_framework.git', :tag => '2.2.9' | 92 | + pod 'SwiftWarplyFramework', :git => 'https://git@git.warp.ly/open-source/warply_sdk_framework.git', :tag => '2.2.10' |
93 | end | 93 | end |
94 | ``` | 94 | ``` |
95 | 95 | ... | ... |
MODULE_NAME_ANALYSIS.md
deleted
100644 → 0
1 | -# Module Name Analysis - SPM vs XIB Configuration | ||
2 | - | ||
3 | -## The Configuration Mystery Solved | ||
4 | - | ||
5 | -After examining the Package.swift file, I can now explain exactly what's happening with the module names. | ||
6 | - | ||
7 | -## Package.swift Configuration | ||
8 | - | ||
9 | -```swift | ||
10 | -let package = Package( | ||
11 | - name: "SwiftWarplyFramework", // Package name | ||
12 | - products: [ | ||
13 | - .library( | ||
14 | - name: "SwiftWarplyFramework", // Library name | ||
15 | - targets: ["SwiftWarplyFramework"] // Target name | ||
16 | - ), | ||
17 | - ], | ||
18 | - targets: [ | ||
19 | - .target( | ||
20 | - name: "SwiftWarplyFramework", // Target name | ||
21 | - path: "SwiftWarplyFramework/SwiftWarplyFramework", // Path to source | ||
22 | - // ... resources and dependencies | ||
23 | - ), | ||
24 | - ] | ||
25 | -) | ||
26 | -``` | ||
27 | - | ||
28 | -## The Root Cause | ||
29 | - | ||
30 | -The issue is **NOT** with our Package.swift configuration. Everything is correctly named `SwiftWarplyFramework`. The problem is elsewhere. | ||
31 | - | ||
32 | -## Evidence Analysis | ||
33 | - | ||
34 | -### 1. Package Configuration ✅ | ||
35 | -- **Package name**: `SwiftWarplyFramework` | ||
36 | -- **Library name**: `SwiftWarplyFramework` | ||
37 | -- **Target name**: `SwiftWarplyFramework` | ||
38 | -- **All consistent and correct** | ||
39 | - | ||
40 | -### 2. XIB Configuration ✅ | ||
41 | -- **customModule**: `SwiftWarplyFramework` | ||
42 | -- **Matches the package/target name perfectly** | ||
43 | - | ||
44 | -### 3. Runtime Behavior ❌ | ||
45 | -- **Error shows**: `_TtC41SwiftWarplyFramework_SwiftWarplyFramework40MyRewardsBannerOffersScrollTableViewCell` | ||
46 | -- **Bundle path**: `SwiftWarplyFramework_SwiftWarplyFramework.bundle` | ||
47 | - | ||
48 | -## The Real Issue | ||
49 | - | ||
50 | -The mangled name `SwiftWarplyFramework_SwiftWarplyFramework` suggests that **SPM is somehow duplicating the module name** during the build process, even though our configuration is correct. | ||
51 | - | ||
52 | -## Possible Causes | ||
53 | - | ||
54 | -### 1. SPM Build System Bug | ||
55 | -SPM might be generating incorrect module names due to the directory structure: | ||
56 | -``` | ||
57 | -SwiftWarplyFramework/ # Root directory | ||
58 | -└── SwiftWarplyFramework/ # Target directory | ||
59 | - └── SwiftWarplyFramework/ # Source files | ||
60 | -``` | ||
61 | - | ||
62 | -### 2. Client Integration Issue | ||
63 | -The way the client app is integrating the SPM package might be causing module name duplication. | ||
64 | - | ||
65 | -### 3. Xcode/SPM Version Issue | ||
66 | -This could be a known issue with certain versions of Xcode or SPM. | ||
67 | - | ||
68 | -## Debugging Steps | ||
69 | - | ||
70 | -### 1. Check Client's Package.resolved | ||
71 | -The client's `Package.resolved` file might show how SPM is resolving the module name. | ||
72 | - | ||
73 | -### 2. Check Build Logs | ||
74 | -The actual build logs would show what module name SPM is using during compilation. | ||
75 | - | ||
76 | -### 3. Test with Simplified Structure | ||
77 | -We could test if flattening the directory structure resolves the issue. | ||
78 | - | ||
79 | -## Immediate Solutions to Try | ||
80 | - | ||
81 | -### Option 1: Update XIB Module Name | ||
82 | -Change XIB files to use `SwiftWarplyFramework_SwiftWarplyFramework` as the module name. | ||
83 | - | ||
84 | -### Option 2: Remove Module Specification | ||
85 | -Remove `customModule` and `customModuleProvider` from XIB files entirely. | ||
86 | - | ||
87 | -### Option 3: Add Explicit Module Name | ||
88 | -Add an explicit `swiftSettings` to the target to force the module name. | ||
89 | - | ||
90 | -## Recommendation | ||
91 | - | ||
92 | -I recommend trying **Option 2 first** (removing module specification from XIB files) as this is the least invasive and most likely to work across different SPM configurations. | ||
93 | - | ||
94 | -## Next Steps | ||
95 | - | ||
96 | -1. Test removing module specification from one XIB file | ||
97 | -2. If that works, apply to all XIB files | ||
98 | -3. If that doesn't work, try updating to the duplicated module name | ||
99 | -4. Document the final solution for future reference | ||
100 | - | ||
101 | -The key insight is that our Package.swift is correct, but SPM is somehow generating a different module name than expected during the build process. |
OBJC_ATTRIBUTES_FIX_SUMMARY.md
deleted
100644 → 0
1 | -# @objc Attributes Fix for SPM Class Resolution | ||
2 | - | ||
3 | -## Problem | ||
4 | -XIB files in SPM couldn't find Swift classes due to name mangling differences between CocoaPods and SPM, causing: | ||
5 | -``` | ||
6 | -Unknown class _TtC41SwiftWarplyFramework_SwiftWarplyFramework40MyRewardsBannerOffersScrollTableViewCell in Interface Builder file. | ||
7 | -*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UITableViewCell 0x105f2d060> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key collectionView.' | ||
8 | -``` | ||
9 | - | ||
10 | -## Solution | ||
11 | -Added explicit `@objc(ClassName)` attributes to all cell classes to provide stable, predictable class names for XIB file instantiation. | ||
12 | - | ||
13 | -## Classes Updated | ||
14 | - | ||
15 | -### Table View Cells: | ||
16 | -1. **MyRewardsBannerOffersScrollTableViewCell** - `@objc(MyRewardsBannerOffersScrollTableViewCell)` | ||
17 | -2. **MyRewardsOffersScrollTableViewCell** - `@objc(MyRewardsOffersScrollTableViewCell)` | ||
18 | -3. **ProfileCouponFiltersTableViewCell** - `@objc(ProfileCouponFiltersTableViewCell)` | ||
19 | -4. **ProfileHeaderTableViewCell** - `@objc(ProfileHeaderTableViewCell)` | ||
20 | -5. **ProfileQuestionnaireTableViewCell** - `@objc(ProfileQuestionnaireTableViewCell)` | ||
21 | -6. **ProfileCouponTableViewCell** - `@objc(ProfileCouponTableViewCell)` | ||
22 | - | ||
23 | -### Collection View Cells: | ||
24 | -7. **MyRewardsOfferCollectionViewCell** - `@objc(MyRewardsOfferCollectionViewCell)` | ||
25 | -8. **MyRewardsBannerOfferCollectionViewCell** - `@objc(MyRewardsBannerOfferCollectionViewCell)` | ||
26 | -9. **ProfileFilterCollectionViewCell** - `@objc(ProfileFilterCollectionViewCell)` | ||
27 | - | ||
28 | -## What This Fix Does | ||
29 | - | ||
30 | -### Before: | ||
31 | -- Swift name mangling in SPM: `_TtC41SwiftWarplyFramework_SwiftWarplyFramework40MyRewardsBannerOffersScrollTableViewCell` | ||
32 | -- XIB files couldn't find the class | ||
33 | -- Fell back to generic `UITableViewCell` | ||
34 | -- Crashed when trying to connect `collectionView` outlet | ||
35 | - | ||
36 | -### After: | ||
37 | -- Explicit Objective-C name: `MyRewardsBannerOffersScrollTableViewCell` | ||
38 | -- XIB files can reliably find the class | ||
39 | -- Proper class instantiation | ||
40 | -- All outlets connect correctly | ||
41 | - | ||
42 | -## Benefits | ||
43 | - | ||
44 | -1. **Stable Class Names** - XIB files always find the correct class regardless of Swift name mangling | ||
45 | -2. **SPM Compatibility** - Works consistently in SPM environments | ||
46 | -3. **CocoaPods Compatibility** - Maintains backward compatibility | ||
47 | -4. **No Swift Regression** - All Swift features and improvements remain intact | ||
48 | -5. **Industry Standard** - Common practice for Swift frameworks using XIB files | ||
49 | - | ||
50 | -## Testing | ||
51 | - | ||
52 | -After this fix, you should see: | ||
53 | -- No more "Unknown class" errors | ||
54 | -- Successful XIB instantiation | ||
55 | -- Proper outlet connections | ||
56 | -- No crashes related to key-value coding compliance | ||
57 | - | ||
58 | -## Files Modified | ||
59 | - | ||
60 | -1. `SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsBannerOffersScrollTableViewCell/MyRewardsBannerOffersScrollTableViewCell.swift` | ||
61 | -2. `SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsOffersScrollTableViewCell/MyRewardsOffersScrollTableViewCell.swift` | ||
62 | -3. `SwiftWarplyFramework/SwiftWarplyFramework/cells/ProfileCouponFiltersTableViewCell/ProfileCouponFiltersTableViewCell.swift` | ||
63 | -4. `SwiftWarplyFramework/SwiftWarplyFramework/cells/ProfileHeaderTableViewCell/ProfileHeaderTableViewCell.swift` | ||
64 | -5. `SwiftWarplyFramework/SwiftWarplyFramework/cells/ProfileQuestionnaireTableViewCell/ProfileQuestionnaireTableViewCell.swift` | ||
65 | -6. `SwiftWarplyFramework/SwiftWarplyFramework/cells/ProfileCouponTableViewCell/ProfileCouponTableViewCell.swift` | ||
66 | -7. `SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsOfferCollectionViewCell/MyRewardsOfferCollectionViewCell.swift` | ||
67 | -8. `SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsBannerOfferCollectionViewCell/MyRewardsBannerOfferCollectionViewCell.swift` | ||
68 | -9. `SwiftWarplyFramework/SwiftWarplyFramework/cells/ProfileFilterCollectionViewCell/ProfileFilterCollectionViewCell.swift` | ||
69 | - | ||
70 | -This fix should resolve the XIB class resolution issue and eliminate the `NSUnknownKeyException` crashes in SPM environments. |
... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
2 | 2 | ||
3 | ## 🚀 Essential Guide for Developers | 3 | ## 🚀 Essential Guide for Developers |
4 | 4 | ||
5 | -**Version**: 2.2.9 | **iOS**: 17.0+ | **Swift**: 5.0+ | 5 | +**Version**: 2.2.10 | **iOS**: 17.0+ | **Swift**: 5.0+ |
6 | 6 | ||
7 | --- | 7 | --- |
8 | 8 | ||
... | @@ -10,7 +10,7 @@ | ... | @@ -10,7 +10,7 @@ |
10 | 10 | ||
11 | ```ruby | 11 | ```ruby |
12 | # Podfile | 12 | # Podfile |
13 | -pod 'SwiftWarplyFramework', :git => 'https://git@git.warp.ly/open-source/warply_sdk_framework.git', :tag => ‘2.2.9’ | 13 | +pod 'SwiftWarplyFramework', :git => 'https://git@git.warp.ly/open-source/warply_sdk_framework.git', :tag => ‘2.2.10’ |
14 | ``` | 14 | ``` |
15 | 15 | ||
16 | --- | 16 | --- |
... | @@ -323,7 +323,7 @@ func safeAPICall() { | ... | @@ -323,7 +323,7 @@ func safeAPICall() { |
323 | ## 🔍 Debug Info | 323 | ## 🔍 Debug Info |
324 | 324 | ||
325 | ```swift | 325 | ```swift |
326 | -print("SDK Version: 2.2.9") | 326 | +print("SDK Version: 2.2.10") |
327 | print("App UUID: \(WarplySDK.shared.appUuid)") | 327 | print("App UUID: \(WarplySDK.shared.appUuid)") |
328 | print("Merchant ID: \(WarplySDK.shared.merchantId)") | 328 | print("Merchant ID: \(WarplySDK.shared.merchantId)") |
329 | print("Language: \(WarplySDK.shared.applicationLocale)") | 329 | print("Language: \(WarplySDK.shared.applicationLocale)") | ... | ... |
SPM_implementation_plan.md
deleted
100644 → 0
This diff is collapsed. Click to expand it.
1 | Pod::Spec.new do |spec| | 1 | Pod::Spec.new do |spec| |
2 | 2 | ||
3 | spec.name = "SwiftWarplyFramework" | 3 | spec.name = "SwiftWarplyFramework" |
4 | - spec.version = "2.2.9" | 4 | + spec.version = "2.2.10" |
5 | spec.summary = "A framework used for several functionalities." | 5 | spec.summary = "A framework used for several functionalities." |
6 | 6 | ||
7 | spec.description = "This is the Warply framework used for react native or swift apps for analytics, push notifications and the functionality of the app." | 7 | spec.description = "This is the Warply framework used for react native or swift apps for analytics, push notifications and the functionality of the app." |
... | @@ -17,7 +17,7 @@ Pod::Spec.new do |spec| | ... | @@ -17,7 +17,7 @@ Pod::Spec.new do |spec| |
17 | 17 | ||
18 | spec.platform = :ios, "17.0" | 18 | spec.platform = :ios, "17.0" |
19 | 19 | ||
20 | - spec.source = { :git => "https://git.warp.ly/open-source/warply_sdk_framework.git", :tag => "2.2.9" } | 20 | + spec.source = { :git => "https://git.warp.ly/open-source/warply_sdk_framework.git", :tag => "2.2.10" } |
21 | # spec.public_header_files = "SwiftWarplyFramework.framework/Headers/*.h" | 21 | # spec.public_header_files = "SwiftWarplyFramework.framework/Headers/*.h" |
22 | 22 | ||
23 | # ==> OLD | 23 | # ==> OLD | ... | ... |
... | @@ -7,7 +7,7 @@ | ... | @@ -7,7 +7,7 @@ |
7 | <key>Pods-SwiftWarplyFramework.xcscheme_^#shared#^_</key> | 7 | <key>Pods-SwiftWarplyFramework.xcscheme_^#shared#^_</key> |
8 | <dict> | 8 | <dict> |
9 | <key>orderHint</key> | 9 | <key>orderHint</key> |
10 | - <integer>1</integer> | 10 | + <integer>0</integer> |
11 | </dict> | 11 | </dict> |
12 | </dict> | 12 | </dict> |
13 | </dict> | 13 | </dict> | ... | ... |
This diff is collapsed. Click to expand it.
... | @@ -7,7 +7,7 @@ | ... | @@ -7,7 +7,7 @@ |
7 | <key>SwiftWarplyFramework.xcscheme_^#shared#^_</key> | 7 | <key>SwiftWarplyFramework.xcscheme_^#shared#^_</key> |
8 | <dict> | 8 | <dict> |
9 | <key>orderHint</key> | 9 | <key>orderHint</key> |
10 | - <integer>0</integer> | 10 | + <integer>1</integer> |
11 | </dict> | 11 | </dict> |
12 | </dict> | 12 | </dict> |
13 | </dict> | 13 | </dict> | ... | ... |
No preview for this file type
... | @@ -35,24 +35,6 @@ public class MyEmptyClass { | ... | @@ -35,24 +35,6 @@ public class MyEmptyClass { |
35 | #endif | 35 | #endif |
36 | } | 36 | } |
37 | 37 | ||
38 | - // NEW: Debug method to verify bundle contents | ||
39 | - public static func debugBundleContents() { | ||
40 | - #if DEBUG | ||
41 | - let bundle = Bundle.frameworkBundle | ||
42 | - print("🔍 [WarplySDK] Using bundle: \(bundle)") | ||
43 | - print("🔍 [WarplySDK] Bundle path: \(bundle.bundlePath)") | ||
44 | - | ||
45 | - // Check for XIB files | ||
46 | - let xibFiles = ["ProfileCouponFiltersTableViewCell", "ProfileHeaderTableViewCell", "ProfileQuestionnaireTableViewCell"] | ||
47 | - for xibName in xibFiles { | ||
48 | - if let xibPath = bundle.path(forResource: xibName, ofType: "nib") { | ||
49 | - print("✅ [WarplySDK] Found XIB: \(xibName) at \(xibPath)") | ||
50 | - } else { | ||
51 | - print("❌ [WarplySDK] Missing XIB: \(xibName)") | ||
52 | - } | ||
53 | - } | ||
54 | - #endif | ||
55 | - } | ||
56 | } | 38 | } |
57 | 39 | ||
58 | // MARK: - Bundle Extensions for SPM Support | 40 | // MARK: - Bundle Extensions for SPM Support |
... | @@ -77,19 +59,4 @@ extension Bundle { | ... | @@ -77,19 +59,4 @@ extension Bundle { |
77 | #endif | 59 | #endif |
78 | } | 60 | } |
79 | 61 | ||
80 | - // NEW: Safe XIB loading with fallback | ||
81 | - static func safeLoadNib(named name: String) -> UINib? { | ||
82 | - let bundle = Bundle.frameworkBundle | ||
83 | - | ||
84 | - // First, check if the XIB exists | ||
85 | - guard bundle.path(forResource: name, ofType: "nib") != nil else { | ||
86 | - print("❌ [WarplySDK] XIB file '\(name).xib' not found in bundle: \(bundle)") | ||
87 | - #if DEBUG | ||
88 | - MyEmptyClass.debugBundleContents() | ||
89 | - #endif | ||
90 | - return nil | ||
91 | - } | ||
92 | - | ||
93 | - return UINib(nibName: name, bundle: bundle) | ||
94 | - } | ||
95 | } | 62 | } | ... | ... |
... | @@ -7,103 +7,27 @@ | ... | @@ -7,103 +7,27 @@ |
7 | 7 | ||
8 | import UIKit | 8 | import UIKit |
9 | 9 | ||
10 | -public class XIBLoader { | 10 | +public struct XIBLoader { |
11 | - | ||
12 | - /// Safely load a view controller from XIB with proper bundle resolution | ||
13 | - public static func loadViewController<T: UIViewController>( | ||
14 | - _ type: T.Type, | ||
15 | - nibName: String? = nil | ||
16 | - ) -> T? { | ||
17 | - let actualNibName = nibName ?? String(describing: type) | ||
18 | - | ||
19 | - guard Bundle.frameworkBundle.path(forResource: actualNibName, ofType: "nib") != nil else { | ||
20 | - print("❌ [WarplySDK] XIB file '\(actualNibName).xib' not found") | ||
21 | - return nil | ||
22 | - } | ||
23 | - | ||
24 | - return T(nibName: actualNibName, bundle: Bundle.frameworkBundle) | ||
25 | - } | ||
26 | - | ||
27 | - /// Safely load a table view cell from XIB | ||
28 | - public static func loadTableViewCell<T: UITableViewCell>( | ||
29 | - _ type: T.Type, | ||
30 | - nibName: String? = nil | ||
31 | - ) -> T? { | ||
32 | - let actualNibName = nibName ?? String(describing: type) | ||
33 | - | ||
34 | - guard let nib = Bundle.safeLoadNib(named: actualNibName) else { | ||
35 | - return nil | ||
36 | - } | ||
37 | - | ||
38 | - let objects = nib.instantiate(withOwner: nil, options: nil) | ||
39 | - return objects.first as? T | ||
40 | - } | ||
41 | - | ||
42 | - /// Verify all required XIB files are present | ||
43 | - public static func verifyXIBFiles() -> [String: Bool] { | ||
44 | - let requiredXIBs = [ | ||
45 | - "ProfileViewController", | ||
46 | - "CouponViewController", | ||
47 | - "MyRewardsViewController", | ||
48 | - "ProfileCouponFiltersTableViewCell", | ||
49 | - "ProfileHeaderTableViewCell", | ||
50 | - "ProfileQuestionnaireTableViewCell", | ||
51 | - "MyRewardsOffersScrollTableViewCell", | ||
52 | - "ProfileCouponTableViewCell", | ||
53 | - "ProfileFilterCollectionViewCell", | ||
54 | - "MyRewardsOfferCollectionViewCell", | ||
55 | - "MyRewardsBannerOfferCollectionViewCell", | ||
56 | - "MyRewardsBannerOffersScrollTableViewCell" | ||
57 | - ] | ||
58 | - | ||
59 | - var results: [String: Bool] = [:] | ||
60 | - let bundle = Bundle.frameworkBundle | ||
61 | - | ||
62 | - for xibName in requiredXIBs { | ||
63 | - let exists = bundle.path(forResource: xibName, ofType: "nib") != nil | ||
64 | - results[xibName] = exists | ||
65 | - | ||
66 | - if exists { | ||
67 | - print("✅ [WarplySDK] XIB found: \(xibName)") | ||
68 | - } else { | ||
69 | - print("❌ [WarplySDK] XIB missing: \(xibName)") | ||
70 | - } | ||
71 | - } | ||
72 | - | ||
73 | - return results | ||
74 | - } | ||
75 | 11 | ||
76 | /// Safe registration of table view cells with XIB | 12 | /// Safe registration of table view cells with XIB |
77 | - public static func registerTableViewCell( | 13 | + public static func registerTableViewCell<T: UITableViewCell>( |
78 | _ tableView: UITableView, | 14 | _ tableView: UITableView, |
79 | - cellClass: AnyClass, | 15 | + cellClass: T.Type, |
80 | nibName: String, | 16 | nibName: String, |
81 | identifier: String | 17 | identifier: String |
82 | ) { | 18 | ) { |
83 | - if let nib = Bundle.safeLoadNib(named: nibName) { | 19 | + let nib = UINib(nibName: nibName, bundle: Bundle.frameworkBundle) |
84 | tableView.register(nib, forCellReuseIdentifier: identifier) | 20 | tableView.register(nib, forCellReuseIdentifier: identifier) |
85 | - print("✅ [WarplySDK] Registered table view cell: \(nibName)") | ||
86 | - } else { | ||
87 | - print("❌ [WarplySDK] Failed to register table view cell: \(nibName)") | ||
88 | - // Register a basic UITableViewCell as fallback | ||
89 | - tableView.register(UITableViewCell.self, forCellReuseIdentifier: identifier) | ||
90 | - } | ||
91 | } | 21 | } |
92 | 22 | ||
93 | /// Safe registration of collection view cells with XIB | 23 | /// Safe registration of collection view cells with XIB |
94 | - public static func registerCollectionViewCell( | 24 | + public static func registerCollectionViewCell<T: UICollectionViewCell>( |
95 | _ collectionView: UICollectionView, | 25 | _ collectionView: UICollectionView, |
96 | - cellClass: AnyClass, | 26 | + cellClass: T.Type, |
97 | nibName: String, | 27 | nibName: String, |
98 | identifier: String | 28 | identifier: String |
99 | ) { | 29 | ) { |
100 | - if let nib = Bundle.safeLoadNib(named: nibName) { | 30 | + let nib = UINib(nibName: nibName, bundle: Bundle.frameworkBundle) |
101 | collectionView.register(nib, forCellWithReuseIdentifier: identifier) | 31 | collectionView.register(nib, forCellWithReuseIdentifier: identifier) |
102 | - print("✅ [WarplySDK] Registered collection view cell: \(nibName)") | ||
103 | - } else { | ||
104 | - print("❌ [WarplySDK] Failed to register collection view cell: \(nibName)") | ||
105 | - // Register basic UICollectionViewCell as fallback | ||
106 | - collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: identifier) | ||
107 | - } | ||
108 | } | 32 | } |
109 | } | 33 | } | ... | ... |
... | @@ -338,11 +338,6 @@ import UIKit | ... | @@ -338,11 +338,6 @@ import UIKit |
338 | ) | 338 | ) |
339 | } | 339 | } |
340 | 340 | ||
341 | - // Debug: Verify XIB files are available | ||
342 | - #if DEBUG | ||
343 | - let _ = XIBLoader.verifyXIBFiles() | ||
344 | - MyEmptyClass.debugBundleContents() | ||
345 | - #endif | ||
346 | } | 341 | } |
347 | 342 | ||
348 | public override func viewWillAppear(_ animated: Bool) { | 343 | public override func viewWillAppear(_ animated: Bool) { | ... | ... |
... | @@ -239,11 +239,6 @@ import UIKit | ... | @@ -239,11 +239,6 @@ import UIKit |
239 | ) | 239 | ) |
240 | } | 240 | } |
241 | 241 | ||
242 | - // Debug: Verify XIB files are available | ||
243 | - #if DEBUG | ||
244 | - let _ = XIBLoader.verifyXIBFiles() | ||
245 | - MyEmptyClass.debugBundleContents() | ||
246 | - #endif | ||
247 | } | 242 | } |
248 | 243 | ||
249 | // MARK: Function | 244 | // MARK: Function | ... | ... |
XIB_BUNDLING_FIX_SUMMARY.md
deleted
100644 → 0
1 | -# XIB Bundling Fix Summary for SPM | ||
2 | - | ||
3 | -## Problem | ||
4 | -The SwiftWarplyFramework was crashing when used via SPM with the error: | ||
5 | -``` | ||
6 | -*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UITableViewCell 0x101a7a0c0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key collectionView.' | ||
7 | -``` | ||
8 | - | ||
9 | -This indicated that XIB files were not being properly loaded from the correct bundle in SPM environments. | ||
10 | - | ||
11 | -## Root Cause | ||
12 | -- XIB files were bundled correctly in Package.swift but the bundle resolution was inconsistent | ||
13 | -- The framework was using `Bundle.frameworkBundle` but this wasn't always resolving to the correct bundle for XIB files in SPM | ||
14 | -- No error handling or debugging capabilities to identify bundle loading issues | ||
15 | - | ||
16 | -## Solution Overview | ||
17 | -We implemented a comprehensive fix with the following components: | ||
18 | - | ||
19 | -### 1. Enhanced Bundle Resolution (MyEmptyClass.swift) | ||
20 | -- **Added debugging capabilities** with `debugBundleContents()` method | ||
21 | -- **Improved Bundle.frameworkBundle** to always use `Bundle.module` for SPM | ||
22 | -- **Added safe XIB loading** with `Bundle.safeLoadNib(named:)` method | ||
23 | -- **Enhanced error handling** with fallback mechanisms | ||
24 | - | ||
25 | -### 2. New XIBLoader Utility Class (XIBLoader.swift) | ||
26 | -- **Centralized XIB loading** with proper error handling | ||
27 | -- **Safe registration methods** for table view and collection view cells | ||
28 | -- **XIB verification utilities** to check if all required XIB files are present | ||
29 | -- **Fallback mechanisms** when XIB files are missing | ||
30 | - | ||
31 | -### 3. Updated Package.swift | ||
32 | -- **Changed from `.copy` to `.process`** for XIB files to ensure proper bundling | ||
33 | -- This ensures XIB files are processed correctly by SPM's build system | ||
34 | - | ||
35 | -### 4. Updated View Controllers and Cells | ||
36 | -Updated the following files to use safe XIB loading: | ||
37 | -- `ProfileViewController.swift` | ||
38 | -- `MyRewardsViewController.swift` | ||
39 | -- `ProfileCouponFiltersTableViewCell.swift` | ||
40 | -- `MyRewardsOffersScrollTableViewCell.swift` | ||
41 | - | ||
42 | -## Key Changes Made | ||
43 | - | ||
44 | -### 1. MyEmptyClass.swift | ||
45 | -```swift | ||
46 | -// NEW: Debug method to verify bundle contents | ||
47 | -public static func debugBundleContents() { | ||
48 | - #if DEBUG | ||
49 | - let bundle = Bundle.frameworkBundle | ||
50 | - print("🔍 [WarplySDK] Using bundle: \(bundle)") | ||
51 | - print("🔍 [WarplySDK] Bundle path: \(bundle.bundlePath)") | ||
52 | - | ||
53 | - // Check for XIB files | ||
54 | - let xibFiles = ["ProfileCouponFiltersTableViewCell", "ProfileHeaderTableViewCell", "ProfileQuestionnaireTableViewCell"] | ||
55 | - for xibName in xibFiles { | ||
56 | - if let xibPath = bundle.path(forResource: xibName, ofType: "nib") { | ||
57 | - print("✅ [WarplySDK] Found XIB: \(xibName) at \(xibPath)") | ||
58 | - } else { | ||
59 | - print("❌ [WarplySDK] Missing XIB: \(xibName)") | ||
60 | - } | ||
61 | - } | ||
62 | - #endif | ||
63 | -} | ||
64 | - | ||
65 | -// NEW: Safe XIB loading with fallback | ||
66 | -static func safeLoadNib(named name: String) -> UINib? { | ||
67 | - let bundle = Bundle.frameworkBundle | ||
68 | - | ||
69 | - // First, check if the XIB exists | ||
70 | - guard bundle.path(forResource: name, ofType: "nib") != nil else { | ||
71 | - print("❌ [WarplySDK] XIB file '\(name).xib' not found in bundle: \(bundle)") | ||
72 | - #if DEBUG | ||
73 | - MyEmptyClass.debugBundleContents() | ||
74 | - #endif | ||
75 | - return nil | ||
76 | - } | ||
77 | - | ||
78 | - return UINib(nibName: name, bundle: bundle) | ||
79 | -} | ||
80 | -``` | ||
81 | - | ||
82 | -### 2. XIBLoader.swift (New File) | ||
83 | -```swift | ||
84 | -public class XIBLoader { | ||
85 | - /// Safe registration of table view cells with XIB | ||
86 | - public static func registerTableViewCell( | ||
87 | - _ tableView: UITableView, | ||
88 | - cellClass: AnyClass, | ||
89 | - nibName: String, | ||
90 | - identifier: String | ||
91 | - ) { | ||
92 | - if let nib = Bundle.safeLoadNib(named: nibName) { | ||
93 | - tableView.register(nib, forCellReuseIdentifier: identifier) | ||
94 | - print("✅ [WarplySDK] Registered table view cell: \(nibName)") | ||
95 | - } else { | ||
96 | - print("❌ [WarplySDK] Failed to register table view cell: \(nibName)") | ||
97 | - // Register a basic UITableViewCell as fallback | ||
98 | - tableView.register(UITableViewCell.self, forCellReuseIdentifier: identifier) | ||
99 | - } | ||
100 | - } | ||
101 | - | ||
102 | - /// Safe registration of collection view cells with XIB | ||
103 | - public static func registerCollectionViewCell( | ||
104 | - _ collectionView: UICollectionView, | ||
105 | - cellClass: AnyClass, | ||
106 | - nibName: String, | ||
107 | - identifier: String | ||
108 | - ) { | ||
109 | - if let nib = Bundle.safeLoadNib(named: nibName) { | ||
110 | - collectionView.register(nib, forCellWithReuseIdentifier: identifier) | ||
111 | - print("✅ [WarplySDK] Registered collection view cell: \(nibName)") | ||
112 | - } else { | ||
113 | - print("❌ [WarplySDK] Failed to register collection view cell: \(nibName)") | ||
114 | - // Register basic UICollectionViewCell as fallback | ||
115 | - collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: identifier) | ||
116 | - } | ||
117 | - } | ||
118 | - | ||
119 | - /// Verify all required XIB files are present | ||
120 | - public static func verifyXIBFiles() -> [String: Bool] { | ||
121 | - // Implementation checks all XIB files and returns status | ||
122 | - } | ||
123 | -} | ||
124 | -``` | ||
125 | - | ||
126 | -### 3. Package.swift | ||
127 | -```swift | ||
128 | -resources: [ | ||
129 | - .process("Media.xcassets"), | ||
130 | - .process("fonts"), | ||
131 | - .process("Main.storyboard"), | ||
132 | - // Change from .copy to .process for XIB files to ensure proper bundling | ||
133 | - .process("cells/ProfileCouponFiltersTableViewCell/ProfileCouponFiltersTableViewCell.xib"), | ||
134 | - .process("cells/ProfileHeaderTableViewCell/ProfileHeaderTableViewCell.xib"), | ||
135 | - .process("cells/ProfileQuestionnaireTableViewCell/ProfileQuestionnaireTableViewCell.xib"), | ||
136 | - // ... all other XIB files | ||
137 | -] | ||
138 | -``` | ||
139 | - | ||
140 | -### 4. View Controller Updates | ||
141 | -```swift | ||
142 | -// OLD: Direct XIB registration | ||
143 | -tableView.register(UINib(nibName: "ProfileHeaderTableViewCell", bundle: Bundle.frameworkBundle), forCellReuseIdentifier: "ProfileHeaderTableViewCell") | ||
144 | - | ||
145 | -// NEW: Safe XIB registration with error handling | ||
146 | -private func registerTableViewCells() { | ||
147 | - let cellConfigs = [ | ||
148 | - ("ProfileHeaderTableViewCell", "ProfileHeaderTableViewCell"), | ||
149 | - ("ProfileQuestionnaireTableViewCell", "ProfileQuestionnaireTableViewCell"), | ||
150 | - // ... other cells | ||
151 | - ] | ||
152 | - | ||
153 | - for (nibName, identifier) in cellConfigs { | ||
154 | - XIBLoader.registerTableViewCell( | ||
155 | - tableView, | ||
156 | - cellClass: UITableViewCell.self, | ||
157 | - nibName: nibName, | ||
158 | - identifier: identifier | ||
159 | - ) | ||
160 | - } | ||
161 | - | ||
162 | - // Debug: Verify XIB files are available | ||
163 | - #if DEBUG | ||
164 | - let _ = XIBLoader.verifyXIBFiles() | ||
165 | - MyEmptyClass.debugBundleContents() | ||
166 | - #endif | ||
167 | -} | ||
168 | -``` | ||
169 | - | ||
170 | -## Benefits of This Solution | ||
171 | - | ||
172 | -### 1. **Robust Error Handling** | ||
173 | -- Graceful fallback when XIB files are missing | ||
174 | -- Detailed logging to identify issues | ||
175 | -- Prevents crashes with fallback cell registration | ||
176 | - | ||
177 | -### 2. **Better Debugging** | ||
178 | -- Debug output shows exactly which bundle is being used | ||
179 | -- Lists all XIB files and their availability | ||
180 | -- Easy to identify bundle resolution issues | ||
181 | - | ||
182 | -### 3. **Consistent Bundle Resolution** | ||
183 | -- Always uses the correct bundle for SPM (`Bundle.module`) | ||
184 | -- Maintains CocoaPods compatibility | ||
185 | -- Centralized bundle logic | ||
186 | - | ||
187 | -### 4. **Maintainable Code** | ||
188 | -- Centralized XIB loading logic in XIBLoader class | ||
189 | -- Consistent patterns across all view controllers | ||
190 | -- Easy to add new XIB files | ||
191 | - | ||
192 | -## Testing the Fix | ||
193 | - | ||
194 | -After implementing these changes: | ||
195 | - | ||
196 | -1. **Build the framework** to ensure no compilation errors | ||
197 | -2. **Test in SPM client** to verify XIB files load correctly | ||
198 | -3. **Check debug output** to confirm bundle resolution | ||
199 | -4. **Verify UI rendering** to ensure all cells display properly | ||
200 | - | ||
201 | -## Debug Output to Expect | ||
202 | - | ||
203 | -When running in debug mode, you should see output like: | ||
204 | -``` | ||
205 | -🔍 [WarplySDK] Using bundle: Bundle.module | ||
206 | -🔍 [WarplySDK] Bundle path: /path/to/bundle | ||
207 | -✅ [WarplySDK] Found XIB: ProfileCouponFiltersTableViewCell at /path/to/xib | ||
208 | -✅ [WarplySDK] Registered table view cell: ProfileHeaderTableViewCell | ||
209 | -✅ [WarplySDK] Registered collection view cell: ProfileFilterCollectionViewCell | ||
210 | -``` | ||
211 | - | ||
212 | -## Files Modified | ||
213 | - | ||
214 | -1. **SwiftWarplyFramework/SwiftWarplyFramework/MyEmptyClass.swift** - Enhanced bundle resolution | ||
215 | -2. **SwiftWarplyFramework/SwiftWarplyFramework/XIBLoader.swift** - New utility class | ||
216 | -3. **Package.swift** - Changed XIB bundling from .copy to .process | ||
217 | -4. **SwiftWarplyFramework/SwiftWarplyFramework/screens/ProfileViewController/ProfileViewController.swift** - Safe XIB loading | ||
218 | -5. **SwiftWarplyFramework/SwiftWarplyFramework/screens/MyRewardsViewController/MyRewardsViewController.swift** - Safe XIB loading | ||
219 | -6. **SwiftWarplyFramework/SwiftWarplyFramework/cells/ProfileCouponFiltersTableViewCell/ProfileCouponFiltersTableViewCell.swift** - Safe XIB loading | ||
220 | -7. **SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsOffersScrollTableViewCell/MyRewardsOffersScrollTableViewCell.swift** - Safe XIB loading | ||
221 | - | ||
222 | -## Build Fix Applied | ||
223 | - | ||
224 | -**Issue**: Missing UIKit import in MyEmptyClass.swift | ||
225 | -- **Error**: `cannot find type 'UINib' in scope` | ||
226 | -- **Fix**: Added `import UIKit` to MyEmptyClass.swift since we introduced UIKit dependencies (`UINib`, `UITableView`, `UICollectionView`) | ||
227 | - | ||
228 | -This comprehensive fix should resolve the `NSUnknownKeyException` crash and ensure XIB files are properly loaded in SPM environments. |
XIB_INVESTIGATION_REPORT.md
deleted
100644 → 0
1 | -# XIB Investigation Report - SPM Class Resolution Issue | ||
2 | - | ||
3 | -## Investigation Summary | ||
4 | - | ||
5 | -I have thoroughly investigated the XIB bundling and class resolution issue. Here are my findings: | ||
6 | - | ||
7 | -## ✅ What's Working Correctly | ||
8 | - | ||
9 | -### 1. XIB File Configuration | ||
10 | -**GOOD NEWS**: The XIB files are configured correctly! | ||
11 | - | ||
12 | -**MyRewardsBannerOffersScrollTableViewCell.xib**: | ||
13 | -```xml | ||
14 | -<tableViewCell ... customClass="MyRewardsBannerOffersScrollTableViewCell" customModule="SwiftWarplyFramework" customModuleProvider="target"> | ||
15 | -``` | ||
16 | - | ||
17 | -**ProfileHeaderTableViewCell.xib**: | ||
18 | -```xml | ||
19 | -<tableViewCell ... customClass="ProfileHeaderTableViewCell" customModule="SwiftWarplyFramework" customModuleProvider="target"> | ||
20 | -``` | ||
21 | - | ||
22 | -- ✅ **Class names are clean** (not mangled) | ||
23 | -- ✅ **Module is correctly set** to "SwiftWarplyFramework" | ||
24 | -- ✅ **Module provider is "target"** (correct for SPM) | ||
25 | - | ||
26 | -### 2. Swift Class Configuration | ||
27 | -**ALSO GOOD**: Our @objc attributes are correctly applied: | ||
28 | - | ||
29 | -```swift | ||
30 | -@objc(MyRewardsBannerOffersScrollTableViewCell) | ||
31 | -public class MyRewardsBannerOffersScrollTableViewCell: UITableViewCell { | ||
32 | -``` | ||
33 | - | ||
34 | -### 3. XIB Bundling | ||
35 | -**WORKING**: Our debug output shows XIB files are found and loaded: | ||
36 | -``` | ||
37 | -✅ [WarplySDK] Found XIB: MyRewardsBannerOffersScrollTableViewCell at .../MyRewardsBannerOffersScrollTableViewCell.nib | ||
38 | -``` | ||
39 | - | ||
40 | -## ❌ The Real Problem | ||
41 | - | ||
42 | -### Root Cause Analysis | ||
43 | -The issue is **NOT** with our XIB configuration or @objc attributes. The problem is more fundamental: | ||
44 | - | ||
45 | -**The error shows the mangled name**: `_TtC41SwiftWarplyFramework_SwiftWarplyFramework40MyRewardsBannerOffersScrollTableViewCell` | ||
46 | - | ||
47 | -This suggests that **somewhere in the runtime**, the system is still trying to resolve the class using the mangled Swift name instead of our clean @objc name. | ||
48 | - | ||
49 | -## 🔍 Key Findings | ||
50 | - | ||
51 | -### 1. XIB Files Are Correctly Configured | ||
52 | -- Class names: `MyRewardsBannerOffersScrollTableViewCell` (clean) | ||
53 | -- Module: `SwiftWarplyFramework` | ||
54 | -- Module provider: `target` | ||
55 | - | ||
56 | -### 2. Swift Classes Have Correct @objc Attributes | ||
57 | -- `@objc(MyRewardsBannerOffersScrollTableViewCell)` is properly applied | ||
58 | - | ||
59 | -### 3. The Disconnect | ||
60 | -- XIB expects: `MyRewardsBannerOffersScrollTableViewCell` | ||
61 | -- Runtime is looking for: `_TtC41SwiftWarplyFramework_SwiftWarplyFramework40MyRewardsBannerOffersScrollTableViewCell` | ||
62 | - | ||
63 | -## 🤔 Possible Causes | ||
64 | - | ||
65 | -### 1. Module Name Mismatch in SPM | ||
66 | -The XIB file specifies `customModule="SwiftWarplyFramework"`, but in SPM, the actual module name might be different (like `SwiftWarplyFramework_SwiftWarplyFramework`). | ||
67 | - | ||
68 | -### 2. Build System Issue | ||
69 | -The @objc attributes might not be taking effect during the SPM build process. | ||
70 | - | ||
71 | -### 3. Runtime Class Registration | ||
72 | -The class might not be properly registered with the Objective-C runtime under the @objc name. | ||
73 | - | ||
74 | -## 📋 Recommended Next Steps | ||
75 | - | ||
76 | -### Option 1: Module Name Investigation | ||
77 | -Check what the actual module name is in SPM vs what's in the XIB files. | ||
78 | - | ||
79 | -### Option 2: XIB Module Configuration | ||
80 | -Try changing the XIB files to use the actual SPM module name or remove the module specification entirely. | ||
81 | - | ||
82 | -### Option 3: Alternative @objc Approach | ||
83 | -Try using just `@objc` without the explicit name, or use `@objc` with the full module-qualified name. | ||
84 | - | ||
85 | -### Option 4: Bundle vs Module Issue | ||
86 | -The problem might be that we're using `customModuleProvider="target"` when we should be using something else for SPM. | ||
87 | - | ||
88 | -## 🎯 Immediate Action Items | ||
89 | - | ||
90 | -1. **Verify the actual module name** being used in SPM | ||
91 | -2. **Test XIB without module specification** (remove `customModule` and `customModuleProvider`) | ||
92 | -3. **Check if other XIB files have the same issue** or if it's specific to this one | ||
93 | -4. **Consider using programmatic cell creation** as a fallback if XIB issues persist | ||
94 | - | ||
95 | -## Conclusion | ||
96 | - | ||
97 | -The XIB files and Swift classes are configured correctly. The issue appears to be a deeper SPM module name resolution problem where the runtime is still using mangled names despite our @objc attributes. |
... | @@ -9,7 +9,7 @@ SwiftWarplyFramework is an iOS SDK that provides loyalty program functionality, | ... | @@ -9,7 +9,7 @@ SwiftWarplyFramework is an iOS SDK that provides loyalty program functionality, |
9 | - Minimum iOS Version: 17.0 | 9 | - Minimum iOS Version: 17.0 |
10 | - Swift Version: 5.0+ | 10 | - Swift Version: 5.0+ |
11 | - Distribution: CocoaPods + Swift Package Manager (SPM) | 11 | - Distribution: CocoaPods + Swift Package Manager (SPM) |
12 | -- Framework Version: 2.2.9 | 12 | +- Framework Version: 2.2.10 |
13 | 13 | ||
14 | ### Dependencies | 14 | ### Dependencies |
15 | - RSBarcodes_Swift (~> 5.2.0) - Barcode scanning and generation | 15 | - RSBarcodes_Swift (~> 5.2.0) - Barcode scanning and generation | ... | ... |
migration_plan.md
deleted
100644 → 0
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment