optimizely / swift-sdk

Swift SDK for Optimizely Feature Experimentation and Optimizely Full Stack (legacy)
https://www.optimizely.com/products/full-stack/
Apache License 2.0
21 stars 30 forks source link

Crashes in ProjectConfig.swift #448

Closed robinvan0418 closed 2 years ago

robinvan0418 commented 2 years ago

Hi there,

we observed some crashes in our app after implementing the OptimizelySDK. It seems to be very similar to this closed issue: https://github.com/optimizely/swift-sdk/issues/421

This crash occurred on iOS 14.x and 15.x devices.

Crashed: com.apple.main-thread
0  libobjc.A.dylib                0x2b50 objc_release + 16
1  Optimizely                     0x19008 outlined consume of Project? + 4386328584
2  Optimizely                     0x98d94 ProjectConfig.deinit + 19 (ProjectConfig.swift:19)
3  Optimizely                     0x98e10 ProjectConfig.__deallocating_deinit + 19 (ProjectConfig.swift:19)
4  libswiftCore.dylib             0x388cb4 _swift_release_dealloc + 56
5  Optimizely                     0x9d60 AtomicProperty.__deallocating_deinit + 19 (AtomicProperty.swift:19)
6  libswiftCore.dylib             0x388cb4 _swift_release_dealloc + 56
7  Optimizely                     0x63e60 @objc OptimizelyClient.__ivar_destroyer + 4386635360 (OptimizelyClient.swift:4386635360)
8  libobjc.A.dylib                0x7d28 object_cxxDestructFromClass(objc_object*, objc_class*) + 116
9  libobjc.A.dylib                0x4b74 objc_destructInstance + 80
10 libobjc.A.dylib                0xe1c8 _objc_rootDealloc + 80
11 Optimizely                     0x727ec partial apply for closure #1 in OptimizelyClient.sendImpressionEvent(experiment:variation:userId:attributes:flagKey:ruleType:enabled:) + 4386695148 (<compiler-generated>:4386695148)
12 libswiftCore.dylib             0x388cb4 _swift_release_dealloc + 56
13 Optimizely                     0x2da80 partial apply for closure #2 in closure #1 in DefaultDatafileHandler.startPeriodicUpdates(sdkKey:updateInterval:datafileChangeNotification:) + 4386413184 (<compiler-generated>:4386413184)
14 libswiftCore.dylib             0x388cb4 _swift_release_dealloc + 56
15 libsystem_blocks.dylib         0x17e8 _Block_release + 200
16 libdispatch.dylib              0x4670 _dispatch_client_callout + 20
17 libdispatch.dylib              0x12b70 _dispatch_main_queue_callback_4CF + 944
18 CoreFoundation                 0x51d84 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
19 CoreFoundation                 0xbf5c __CFRunLoopRun + 2540
20 CoreFoundation                 0x1f468 CFRunLoopRunSpecific + 600
21 GraphicsServices               0x138c GSEventRunModal + 164
22 UIKitCore                      0x5195d0 -[UIApplication _run] + 1100
23 UIKitCore                      0x297f74 UIApplicationMain + 364
24 ???                           0x69c0 main + 10 (Scheduler.swift:10)
25 ???                            0x105729aa4 (Missing)

This crash also happens at the ProjectConfig and occurred on iOS 13.x, 14.x and 15.x devices.

Crashed: com.apple.NSURLSession-delegate
0  libobjc.A.dylib                0x15e0 objc_msgSend + 32
1  libswiftCore.dylib             0x1fe4c4 _StringObject.getSharedUTF8Start() + 36
2  libswiftCore.dylib             0x1fe4e4 _StringObject.sharedUTF8.getter + 24
3  libswiftCore.dylib             0x1f25bc _stringCompareInternal(_:_:expecting:) + 444
4  Optimizely                     0x957f0 ProjectConfig.ProjectObserver.update(project:) + 168 (ProjectConfig.swift:168)
5  Optimizely                     0x9a354 specialized ProjectConfig.init(datafile:) + 58 (ProjectConfig.swift:58)
6  Optimizely                     0x58120 OptimizelyClient.configSDK(datafile:) + 4373758240 (ProjectConfig.swift:4373758240)
7  Optimizely                     0x57ad4 closure #1 in OptimizelyClient.start(resourceTimeout:completion:) + 127 (OptimizelyClient.swift:127)
8  Optimizely                     0x29624 closure #1 in closure #1 in DefaultDatafileHandler.downloadDatafile(sdkKey:returnCacheIfNoChange:resourceTimeoutInterval:completionHandler:) + 107 (DefaultDatafileHandler.swift:107)
9  Optimizely                     0x2dd48 partial apply for closure #1 in closure #1 in DefaultDatafileHandler.downloadDatafile(sdkKey:returnCacheIfNoChange:resourceTimeoutInterval:completionHandler:) + 4373585224 (<compiler-generated>:4373585224)
10 Optimizely                     0x2972c thunk for @escaping @callee_guaranteed (@in_guaranteed URL?, @guaranteed NSURLResponse?, @guaranteed Error?) -> () + 4373567276 (<compiler-generated>:4373567276)
11 CFNetwork                      0x81b5c CFURLResponseCreateWithHTTPResponse + 12444
12 CFNetwork                      0x9cde0 _CFHTTPMessageSetResponseProxyURL + 15788
13 libdispatch.dylib              0x2914 _dispatch_call_block_and_release + 32
14 libdispatch.dylib              0x4660 _dispatch_client_callout + 20
15 libdispatch.dylib              0xbde4 _dispatch_lane_serial_drain + 672
16 libdispatch.dylib              0xc98c _dispatch_lane_invoke + 444
17 libdispatch.dylib              0x171a8 _dispatch_workloop_worker_thread + 656
18 libsystem_pthread.dylib        0x10f4 _pthread_wqthread + 288
19 libsystem_pthread.dylib        0xe94 start_wqthread + 8
jaeopt commented 2 years ago

@robinvan0418 Thanks for sharing. We'll take a look. What is the version of the swfit sdk?

jaeopt commented 2 years ago

@robinvan0418 Can you also share a code snippet for Optimizely SDK initialization. I see an unexpected flow (async init inside datafile download completion handler) in the posted crash report.

robinvan0418 commented 2 years ago

@jaeopt The sdk version is 3.10.0

MainViewController
var optimizelyWrapperFactory: OptimizelyWrapperFactoryProtocol = Resolver.optimizely.resolve()
func onAppStart() {
    optimizelyWrapperFactory.createInstance(sdkKey: optimizelySDKKey)
    let optimizelyWrapper = optimizelyWrapperFactory.instance

    optimizelyWrapper?.initialize {
            onMainThreadAsync { [unowned self] in
            // Access flags
        }
    }
}
OptimizelyWrapperFactory
func createInstance(sdkKey: String) {
    let optimizelyClient = OptimizelyClient(
        sdkKey: sdkKey,
        userProfileService: OptimizelyCustomUserProfileService(),
        periodicDownloadInterval: Constants.OptimizelyConfig.periodicDownloadInterval
    )

    instance = OptimizelyWrapper(appLogger: appLogger,
                                 cookieManager: cookieManager,
                                 client: optimizelyClient,
                                 trackingRepository: trackingRepository)
}
OptimizelyWrapper

func initialize(completion: @escaping () -> Void) {
    client.start(resourceTimeout: nil) { result in
        switch result {
        case .success:
            self.cookieManager.get(cookie: .browserId) { browserId in
                guard let browserId = browserId else {
                    completion()
                    return
                }
                self.browserId = browserId
                self.isClientInitialized = true
                completion()
            }
        case .failure(let error):
            self.isClientInitialized = false
            completion()
        }
    }
}
jaeopt commented 2 years ago

@robinvan0418 Thanks for sharing the codes. They all look good to me. We'll take a look.

robinvan0418 commented 2 years ago

Hi @jaeopt, will there be a release anytime soon so we can try this out?

jaeopt commented 2 years ago

@robinvan0418 we'll release the patch this week and get you updated. Tx!

jaeopt commented 2 years ago

@robinvan0418 we released the patch 3.10.1. Let us know if it helps to fix your crashes.

robinvan0418 commented 2 years ago

@jaeopt So far we had no crashes with the patch. Looks good :) Thanks!