Crashes with iOS SDK Over The Air CustomBundle.swift and

Hello,

since we are using the Over the air tool for our iOS apps using the iOS Crowdin SKD we were suffering for crashes in an early access in the app.

Is there any issue open related to those crashes?

The crashes happens a lot and the logs points to the CustomBundle.swift and the LocalizationDataSource.swift.
Here are some logs related:

CustomBundle.swift crash

rashed: NSOperationQueue 0x101d5b2b0 (QOS: UNSPECIFIED)
0  CrowdinSDK                     0x22b28 specialized FolderBundle.init(path:) + 33 (CustomBundle.swift:33)
1  CrowdinSDK                     0x22ec8 specialized DictionaryBundle.init(path:fileName:dictionary:) + 4381142728 (CustomBundle.swift:4381142728)
2  CrowdinSDK                     0x3e6f0 LocalizationProvider.setupPluralsBundle() + 4381255408 (LocalizationProvider.swift:4381255408)
3  CrowdinSDK                     0x3e2a0 LocalizationProvider.setupPlurals() + 143 (LocalizationProvider.swift:143)
4  CrowdinSDK                     0x3f344 specialized LocalizationProvider.setup(with:strings:plurals:) + 136 (LocalizationProvider.swift:136)
5  CrowdinSDK                     0x3e11c closure #1 in LocalizationProvider.fetchRemoteLocalization(completion:) + 4381253916 (<compiler-generated>:4381253916)
6  CrowdinSDK                     0x3de1c thunk for @escaping @callee_guaranteed (@guaranteed [String]?, @guaranteed String, @guaranteed [String : String]?, @guaranteed [AnyHashable : Any]?) -> () + 4381253148 (<compiler-generated>:4381253148)
7  CrowdinSDK                     0x31d20 thunk for @escaping @callee_unowned @convention(block) (@unowned NSArray?, @unowned NSString, @unowned NSDictionary?, @unowned NSDictionary?) -> () + 4381203744 (<compiler-generated>:4381203744)
8  CrowdinSDK                     0x12278 closure #1 in CrowdinRemoteLocalizationStorage.fetchData(completion:errorHandler:) + 78 (CrowdinRemoteLocalizationStorage.swift:78)
9  CrowdinSDK                     0xcee4 closure #1 in CrowdinLocalizationDownloader.download(strings:plurals:xliffs:jsons:with:timestamp:for:) + 52 (CrowdinLocalizationDownloader.swift:52)
10 CrowdinSDK                     0x16194 thunk for @escaping @callee_guaranteed () -> () + 4381090196 (<compiler-generated>:4381090196)
11 Foundation                     0x51c1c __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 24
12 Foundation                     0x63f94 -[NSBlockOperation main] + 104
13 Foundation                     0x3c214 __NSOPERATION_IS_INVOKING_MAIN__ + 24
14 Foundation                     0x4d5dc -[NSOperation start] + 788
15 Foundation                     0x50c68 __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 24
16 Foundation                     0x5ee10 __NSOQSchedule_f + 184
17 libdispatch.dylib              0x12830 _dispatch_block_async_invoke2 + 148
18 libdispatch.dylib              0x3a30 _dispatch_client_callout + 20
19 libdispatch.dylib              0x6eec _dispatch_continuation_pop + 500
20 libdispatch.dylib              0x6558 _dispatch_async_redirect_invoke + 584
21 libdispatch.dylib              0x15164 _dispatch_root_queue_drain + 396
22 libdispatch.dylib              0x1596c _dispatch_worker_thread2 + 164
23 libsystem_pthread.dylib        0x1080 _pthread_wqthread + 228
24 libsystem_pthread.dylib        0xe5c start_wqthread + 8

LocalizationDataSource.swift crash

Crashed: NSOperationQueue 0x1027073d0 (QOS: UNSPECIFIED)
0  libobjc.A.dylib                0x3110 objc_release + 16
1  libswiftCore.dylib             0x38f0dc swift_arrayDestroy + 124
2  libswiftCore.dylib             0xc8438 _DictionaryStorage.deinit + 624
3  libswiftCore.dylib             0xc8460 _DictionaryStorage.__deallocating_deinit + 16
4  libswiftCore.dylib             0x39c424 _swift_release_dealloc + 56
5  CrowdinSDK                     0x3c394 PluralsLocalizationDataSource.__deallocating_deinit + 48 (LocalizationDataSource.swift:48)
6  libswiftCore.dylib             0x39c424 _swift_release_dealloc + 56
7  CrowdinSDK                     0x3e874 LocalizationProvider.setupStrings() + 4382910580 (LocalizationProvider.swift:4382910580)
8  CrowdinSDK                     0x3f340 specialized LocalizationProvider.setup(with:strings:plurals:) + 135 (LocalizationProvider.swift:135)
9  CrowdinSDK                     0x3e11c closure #1 in LocalizationProvider.fetchRemoteLocalization(completion:) + 4382908700 (<compiler-generated>:4382908700)
10 CrowdinSDK                     0x3de1c thunk for @escaping @callee_guaranteed (@guaranteed [String]?, @guaranteed String, @guaranteed [String : String]?, @guaranteed [AnyHashable : Any]?) -> () + 4382907932 (<compiler-generated>:4382907932)
11 CrowdinSDK                     0x31d20 thunk for @escaping @callee_unowned @convention(block) (@unowned NSArray?, @unowned NSString, @unowned NSDictionary?, @unowned NSDictionary?) -> () + 4382858528 (<compiler-generated>:4382858528)
12 CrowdinSDK                     0x12278 closure #1 in CrowdinRemoteLocalizationStorage.fetchData(completion:errorHandler:) + 78 (CrowdinRemoteLocalizationStorage.swift:78)
13 CrowdinSDK                     0xcee4 closure #1 in CrowdinLocalizationDownloader.download(strings:plurals:xliffs:jsons:with:timestamp:for:) + 52 (CrowdinLocalizationDownloader.swift:52)
14 CrowdinSDK                     0x16194 thunk for @escaping @callee_guaranteed () -> () + 4382744980 (<compiler-generated>:4382744980)
15 Foundation                     0x50a9c __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 24
16 Foundation                     0x62e14 -[NSBlockOperation main] + 104
17 Foundation                     0x3b1c4 __NSOPERATION_IS_INVOKING_MAIN__ + 24
18 Foundation                     0x4c4e0 -[NSOperation start] + 788
19 Foundation                     0x4fae8 __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 24
20 Foundation                     0x5dc90 __NSOQSchedule_f + 184
21 libdispatch.dylib              0x12830 _dispatch_block_async_invoke2 + 148
22 libdispatch.dylib              0x3a2c _dispatch_client_callout + 20
23 libdispatch.dylib              0x6eec _dispatch_continuation_pop + 500
24 libdispatch.dylib              0x65f0 _dispatch_async_redirect_invoke + 740
25 libdispatch.dylib              0x15164 _dispatch_root_queue_drain + 396
26 libdispatch.dylib              0x1596c _dispatch_worker_thread2 + 164
27 libsystem_pthread.dylib        0x1080 _pthread_wqthread + 228
28 libsystem_pthread.dylib        0xe5c start_wqthread + 8
1 Like

Hi, it’s strange, mine SDK works well. Did you follow this guide step by step?

Did this crash started after you change something? Or they started just after you set up the SDK?

The crash happens since we set the SDK and enabled the OTA feature.

Hello @sunwebgroup @robert.rodriguez

Can you provide me with the project id or name? I’d like to check some logs and settings from my side.

Thanks in advance.

By the way, can you try to reproduce this crash with our sample project?

Hello,
The project id
ID: 446422

1 Like

Hello all, just wondering if this a common issue. I’m also plan to set up iOS SDK, did tests and it works nice with sample project that is shared here.

Now will try with mine, hope it also will work :sweat_smile::partying_face:

The sad thing is that the crash not happens always, we have the crash in our monitoring system we have never been able to reproduce it.

We are considering to disable the Over the air feature for our projects due to the number of crashes in our customers, while we try to identify the problem.

Would you have some time to investigate the possible reason for this crash? Maybe it is somehow related to specific settings you may use? Please message me directly or continue this tread as well. Any additional details on which I can build my internal tests would be greatly appreciated.

Hello Dima,
we will try to look in depth by the end to this week. I will keep you posted.
Thanks in advance

1 Like

Sure, take your time. You can continue this topic or message me directly.

1 Like

Hello @Dima,
Sorry for the late reply on this, we have been gathering some more information and investigating this issue we are having, so lets see if we can pin point the issue :slight_smile:

Some numbers:

  • During the last month this has happened around 200 times (0.5% of the sessions)
  • We haven’t been able to reproduce it, but we got this information from our logging system

We see two different kind of crashes (the complete crash log can be found in the first message of this thread):

  • one occurring with CustomBundle.swift
  • and the other one occurring with LocalizationDataSource.swift.

However both crashes seem to be originated for the same reason which is during the execution of LocalizationProvider.setup(with:strings:plurals:) after the new strings have been downloaded from the server.

This is how we are configuring CrowdinSDK in our app:

    public func setup(id: String) {
        let crowdinProviderConfig = CrowdinProviderConfig(hashString: id, sourceLanguage: "en_US")
        let crowdinSDKConfig = CrowdinSDKConfig.config().with(crowdinProviderConfig: crowdinProviderConfig)
        CrowdinSDK.startWithConfig(crowdinSDKConfig) {}
    }

This setup method is part of an private library we have, and the method is called from the AppDelegate.

I hope this clears out the scenario a little bit, let me know if I can provide you with further information.

Hi @marc.garcia not the huge expert in this filed, but in case you use different setup method, not the one that is supported, probably it’s expected. Does the same happens with test Crowdin project using default setups from their GitHub?

Hello, @marc.garcia thanks for the update. It sounds interesting. I’m wondering whether you’ll have a chance to follow @AnnaRockstray reply and re-produce the same behavior with the default project/methods?

Hello @AnnaRockstray and @Dima ,
I’m not sure I understand what you mean by “default setups”. As far as I know Crowdin is configured using these three lines:

let crowdinProviderConfig = CrowdinProviderConfig(hashString: id, sourceLanguage: "en_US")
let crowdinSDKConfig = CrowdinSDKConfig.config().with(crowdinProviderConfig: crowdinProviderConfig)
CrowdinSDK.startWithConfig(crowdinSDKConfig) {}

This is what I also see in Crowdin’s Github. What we are doing is to encapsulate there three lines in a setup method, and call it from the AppDelegate:

@UIApplicationMain
class AppDelegate: UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Some stuff

        TranslationsOverTheAirManager.shared.setup(id: "{ourCrowdinID}")
        
        // More things
    }
}

Do you mean to call these three lines directly from the AppDelegate?

Hello @marc.garcia, yes, please try to follow the AppDelegate setup without any encapsulations or so, just these very basic options that are written here:

Sure, we will apply these changes and monitor if this solves the problem. I will come back to this thread once we have some more information.
Thanks

1 Like

Hello @Dima,
We tried this approach about putting all the setup in the AppDelegate, the code now looks like this:

@UIApplicationMain
class AppDelegate: UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Some other stuff

        let crowdinProviderConfig = CrowdinProviderConfig(hashString: {ourHashString}, sourceLanguage: "en_US")
        let crowdinSDKConfig = CrowdinSDKConfig.config().with(crowdinProviderConfig: crowdinProviderConfig)
        CrowdinSDK.startWithConfig(crowdinSDKConfig) {}        
        
        return true
    }
}

But we are still seeing this crash on our analytics system.

Hello @marc.garcia thanks for your tests, much appreciated. We’ll check a few assumptions from my side, most likely, with the development team’s close involvement. Please note that it may take some time.

1 Like

Hello @Dima,
Is there any update on this?
On our side, we have been able to reproduce the crash once, this is the Xcode screenshot:

Looks like the same variable is trying to be accessed from two different threads and this is causing the crash. Of course this is a “thread” race, so that’s why it’s so complicated to reproduce.

We have also detected that after initializing Crowdin we are setting the language the user selected in a previous session, which is why this whole Downloading process is happening twice. We will look into this and check if we can modify the code so we avoid doing this language set. I let you know how it went.

Also, and this is for you to decide, should Crowdin be thread safe when accessing to this particular variable and allow multiple downloads to be done at once?

Thanks

1 Like