superwall / Superwall-iOS

Remotely configure every aspect of your paywall and double your revenue.
https://superwall.com
MIT License
86 stars 22 forks source link

[FEATURE REQUEST] Ignore Superwall calls if not configured #152

Closed finnvoor closed 1 year ago

finnvoor commented 1 year ago

We have an app with event tracking, and call Superwall.shared.register in a function that also sends the events to 3rd party analytics.

We also have an app extension, which I added event tracking to by calling the same methods, but I did not call Superwall.configure because the extension does not need paywalls.

When testing this code from Xcode, no errors were thrown because all our paywall code is ifdef'd out behind #if DEBUG, because seeing the paywall was annoying when debugging. When running from TestFlight however, we were seeing very strange issues with the app crashing randomly, and some features just stalling/not working.

After debugging for days I realised this was due to calling Superwall.shared.register without calling Superwall.configure, resulting in thousands of various calls inside Superwall breaking.

This is my mistake for not realising I was calling register, but I think it would be much better if the Superwall SDK ignored all register/other calls before configure is called, or even just immediately terminated the program to avoid these hard to debug issues. For now I have wrapped all register calls in guard Superwall.isInitialized.

2023-07-19 17:04:26.591537+0100 iMessageExtension[643:29230] [error] error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (256)
2023-07-19 17:04:26.591550+0100 iMessageExtension[643:29583] [error] error:     NSSQLiteErrorDomain : 14
CoreData: error:     NSSQLiteErrorDomain : 14
CoreData: error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (256)
2023-07-19 17:04:26.591603+0100 iMessageExtension[643:29230] [error] error: userInfo:
CoreData: error: userInfo:
2023-07-19 17:04:26.591634+0100 iMessageExtension[643:29583] [error] error: storeType: SQLite
CoreData: error: storeType: SQLite
2023-07-19 17:04:26.591677+0100 iMessageExtension[643:29583] [error] error: configuration: (null)
CoreData: error: configuration: (null)
2023-07-19 17:04:26.591651+0100 iMessageExtension[643:29230] [error] error:     NSFilePath : /var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application Support/SuperwallKit_Model.sqlite
2023-07-19 17:04:26.591749+0100 iMessageExtension[643:29583] [error] error: URL: file:///var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application%20Support/SuperwallKit_Model.sqlite
CoreData: error:     NSFilePath : /var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application Support/SuperwallKit_Model.sqlite
2023-07-19 17:04:26.591804+0100 iMessageExtension[643:29230] [error] error:     NSSQLiteErrorDomain : 14
CoreData: error:     NSSQLiteErrorDomain : 14
2023-07-19 17:04:26.591897+0100 iMessageExtension[643:29230] [error] error: storeType: SQLite
CoreData: error: storeType: SQLite
2023-07-19 17:04:26.592113+0100 iMessageExtension[643:29230] [error] error: configuration: (null)
CoreData: error: configuration: (null)
2023-07-19 17:04:26.592165+0100 iMessageExtension[643:29230] [error] error: URL: file:///var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application%20Support/SuperwallKit_Model.sqlite
CoreData: error: URL: file:///var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application%20Support/SuperwallKit_Model.sqlite
CoreData: annotation: options:
CoreData: error: URL: file:///var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application%20Support/SuperwallKit_Model.sqlite
CoreData: annotation: options:
CoreData: annotation:     NSInferMappingModelAutomaticallyOption : 1
CoreData: annotation:     NSMigratePersistentStoresAutomaticallyOption : 1
CoreData: annotation:     NSInferMappingModelAutomaticallyOption : 1
2023-07-19 17:04:26.593731+0100 iMessageExtension[643:29282] [error] error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (256)
CoreData: error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (256)
2023-07-19 17:04:26.593793+0100 iMessageExtension[643:29282] [error] error: userInfo:
CoreData: annotation:     NSMigratePersistentStoresAutomaticallyOption : 1
CoreData: error: userInfo:
2023-07-19 17:04:26.593845+0100 iMessageExtension[643:29282] [error] error:     NSFilePath : /var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application Support/SuperwallKit_Model.sqlite
CoreData: error:     NSFilePath : /var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application Support/SuperwallKit_Model.sqlite
2023-07-19 17:04:26.593898+0100 iMessageExtension[643:29282] [error] error:     NSSQLiteErrorDomain : 14
CoreData: error:     NSSQLiteErrorDomain : 14
2023-07-19 17:04:26.593943+0100 iMessageExtension[643:29282] [error] error: storeType: SQLite
CoreData: error: storeType: SQLite
2023-07-19 17:04:26.593993+0100 iMessageExtension[643:29282] [error] error: configuration: (null)
CoreData: error: configuration: (null)
2023-07-19 17:04:26.594192+0100 iMessageExtension[643:29282] [error] error: URL: file:///var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application%20Support/SuperwallKit_Model.sqlite
CoreData: error: URL: file:///var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application%20Support/SuperwallKit_Model.sqlite
CoreData: annotation: options:
CoreData: annotation:     NSInferMappingModelAutomaticallyOption : 1
CoreData: annotation:     NSMigratePersistentStoresAutomaticallyOption : 1
2023-07-19 17:04:26.595367+0100 iMessageExtension[643:29497] [error] error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (256)
CoreData: error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (256)
2023-07-19 17:04:26.595411+0100 iMessageExtension[643:29583] [error] error: <NSPersistentStoreCoordinator: 0x289cd9d50>: Attempting recovery from error encountered during addPersistentStore: 0x28626ee50 Error Domain=NSCocoaErrorDomain Code=256 "(null)" UserInfo={NSFilePath=/var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application Support/SuperwallKit_Model.sqlite, NSSQLiteErrorDomain=14}
2023-07-19 17:04:26.595428+0100 iMessageExtension[643:29497] [error] error: userInfo:
CoreData: error: userInfo:
CoreData: error: <NSPersistentStoreCoordinator: 0x289cd9d50>: Attempting recovery from error encountered during addPersistentStore: 0x28626ee50 Error Domain=NSCocoaErrorDomain Code=256 "(null)" UserInfo={NSFilePath=/var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application Support/SuperwallKit_Model.sqlite, NSSQLiteErrorDomain=14}
2023-07-19 17:04:26.595477+0100 iMessageExtension[643:29497] [error] error:     NSFilePath : /var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application Support/SuperwallKit_Model.sqlite
CoreData: error:     NSFilePath : /var/mobile/Containers/Data/PluginKitPlugin/366AC482-FE36-4A46-B005-6A09C85A3E21/Library/Application Support/SuperwallKit_Model.sqlite
2023-07-19 17:04:26.595523+0100 iMessageExtension[643:29497] [error] error:     NSSQLiteErrorDomain : 14
CoreData: error:     NSSQLiteErrorDomain : 14
2023-07-19 17:04:26.595567+0100 iMessageExtension[643:29497] [error] error: storeType: SQLite
CoreData: error: storeType: SQLite
yusuftor commented 1 year ago

Yes to access anything on Superwall.shared you need to run Superwall.configure first, but understand you don't need to add that functionality in an app extension. Using isInitialized for shared code seems the way to go here if you want to use shared code.

Thanks for the suggestions. We actually log Superwall has not been configured. Please call Superwall.configure() to the console if you access Superwall.shared before calling configure. But since you ifdef'd out all register calls that wouldn't have shown when debugging. It also creates an assertion failure (i.e. a crash during debug) immediately. We don't want to crash an app in production if we can help it and we assume that developers have tested their code before pushing to production. Ignoring register would lead to more confusion over why a paywall wasn't showing.

I would also advise against ifdefing core functionality like paywall code so that you don't get unexpected issues in production

finnvoor commented 1 year ago

We don't want to crash an app in production if we can help it

The issue is Superwall is crashing apps in production if you call registerEvent before configure, just with extremely confusing and difficult to debug issues. If you do not call configure, Superwall will slow down your app and randomly crash with misleading error messages. RevenueCat, on the other hand, will instantly fatalError with the obvious error message "Purchases has not been configured. Please call Purchases.configure()".

It seems to me like if an SDK is going to cause a crash in production, it might as well give an obvious error message with the crash. Yes it was developer error, but giving a more obvious error message will save debugging time. This could have been much harder to discover (and still occur if I hadn't ifdef'd superwall) if there was some kind of race condition where registerEvent is only occasionally called before configure.

yusuftor commented 1 year ago

We're gonna discuss this internally and get back to you!

yusuftor commented 1 year ago

Investigated this more – the errors you were receiving were due to a recursive call inside the SDK to initialise shared when the app tried to log the error message. This shouldn't have been happening. This will be fixed in the next update and so only the error message Superwall has not been configured. Please call Superwall.configure() will show in the logs – but the app won't crash in production. Thanks for spotting this and very sorry you experienced this issue!