Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force closing the app or updating to a newer version, causes the Segment.Analytics.init(configuration:) to call fatalError() #333

Closed
leontedev opened this issue Apr 23, 2024 · 11 comments
Assignees
Labels

Comments

@leontedev
Copy link

Describe the bug
Ever since the changes made in 1.5.3 - the new Analytics init(configuration:) causes the app to crash if:

  • the app was updated
  • force closing the app and starting it again

It appears that in these 2 cases, the Analytics.deinit is not called and causes an issue on the next app's launch, when the Analytics object is initialised with a writeKey which is still "active".

To Reproduce
Steps to reproduce the behavior:

  1. Use analytics-swift version higher than 1.5.2
  2. Force close the app
  3. Try to open the app again, it will crash instantly on this fatalError call: https://github.com/segmentio/analytics-swift/blob/main/Sources/Segment/Analytics.swift#L65
@GavrilikArt
Copy link

Describe the bug Ever since the changes made in 1.5.3 - the new Analytics init(configuration:) causes the app to crash if:

  • the app was updated
  • force closing the app and starting it again

It appears that in these 2 cases, the Analytics.deinit is not called and causes an issue on the next app's launch, when the Analytics object is initialised with a writeKey which is still "active".

To Reproduce Steps to reproduce the behavior:

  1. Use analytics-swift version higher than 1.5.2
  2. Force close the app
  3. Try to open the app again, it will crash instantly on this fatalError call: https://github.com/segmentio/analytics-swift/blob/main/Sources/Segment/Analytics.swift#L65

I noticed the same issue on my project 😞 .

@bsneed
Copy link
Contributor

bsneed commented Apr 23, 2024

If you have a crash report or stack trace, could you post it please @GavrilikArt @leontedev ?

@GavrilikArt
Copy link

GavrilikArt commented Apr 23, 2024

If you have a crash report or stack trace, could you post it please @GavrilikArt @leontedev ?

Hey, here it is:

Crashed: com.apple.main-thread
0 libswiftCore.dylib 0x398c0 assertionFailure(::file:line🎏) + 264
1 App 0x1ba8720 specialized Analytics.init(configuration:) + 65 (Analytics.swift:65)
2 App 0x1b98114 Analytics.init(configuration:) + 4391682324 (:4391682324)
3 App 0x1b95f8c static SegmentIntegration.configure(devKey:trackApplicationLifecycleEvents:) + 25 (SegmentIntegration.swift:25)
4 App 0x9008 closure #2 in AppDelegate.application(
:didFinishLaunchingWithOptions:) + 658 (AppDelegate.swift:658)

I removed most if it, because only those lines are relevant.
The version that was used is 1.5.9.

@bsneed
Copy link
Contributor

bsneed commented Apr 23, 2024

I'd actually like to see the whole thing if you wouldn't mind. It'd show me what was happening overall. The contents or a description of the closure that's located in appDidFinishLaunching would be useful too since I'm guessing it's async or something.

@GavrilikArt
Copy link

GavrilikArt commented Apr 23, 2024

I'd actually like to see the whole thing if you wouldn't mind. It'd show me what was happening overall. The contents or a description of the closure that's located in appDidFinishLaunching would be useful too since I'm guessing it's async or something.

Ok.

Crashed: com.apple.main-thread
0 libswiftCore.dylib 0x398c0 assertionFailure(::file:line🎏) + 264
1 App 0x1ba8720 specialized Analytics.init(configuration:) + 65 (Analytics.swift:65)
2 App 0x1b98114 Analytics.init(configuration:) + 4391682324 (:4391682324)
3 App 0x1b95f8c static SegmentIntegration.configure(devKey:trackApplicationLifecycleEvents:) + 25 (SegmentIntegration.swift:25)
4 App 0x9008 closure #2 in AppDelegate.application(
:didFinishLaunchingWithOptions:) + 658 (AppDelegate.swift:658)
5 App 0x180e74 closure #1 in SignalProtocol<>.bind(to:context:setter:) + 109 (Bindable.swift:109)
6 App 0x1c3628 closure #1 in SignalProtocol.observeNext(with:) + 56 (SignalProtocol.swift:56)
7 App 0x195cb4 AtomicObserver.on(:) + 101 (Observer.swift:101)
8 App 0x1bb6b0 partial apply for closure #1 in closure #1 in closure #1 in SignalProtocol.receive
(on:) + 51 (SignalProtocol+Threading.swift:51)
9 App 0x18a9d8 closure #1 in static ExecutionContext.immediateOnMain.getter + 61 (ExecutionContext.swift:61)
10 App 0x199408 protocol witness for Scheduler.schedule(
:) in conformance ExecutionContext + 39 (Scheduler.swift:39)
11 App 0x1bb418 closure #1 in closure #1 in SignalProtocol.receive(on:) + 50 (SignalProtocol+Threading.swift:50)
12 App 0x195cb4 AtomicObserver.on(:) + 101 (Observer.swift:101)
13 App 0x196448 protocol witness for ObserverProtocol.on(
:) in conformance AtomicObserver<A, B> + 4364411976 (:4364411976)
14 App 0x195b2c ObserverProtocol.receive(:) + 137 (Observer.swift:137)
15 App 0x1b20a0 closure #2 in closure #1 in SignalProtocol.prefix
(untilOutputFrom:) + 370 (SignalProtocol+Filtering.swift:370)
16 App 0x195cb4 AtomicObserver.on(
:) + 101 (Observer.swift:101)
17 App 0x196448 protocol witness for ObserverProtocol.on(:) in conformance AtomicObserver<A, B> + 4364411976 (:4364411976)
18 App 0x195b2c ObserverProtocol.receive(
:) + 137 (Observer.swift:137)
19 App 0x1b8fac closure #1 in closure #1 in SignalProtocol<>.ignoreNils() + 56 (SignalProtocol+Optional.swift:56)
20 App 0x195cb4 AtomicObserver.on(:) + 101 (Observer.swift:101)
21 App 0x196448 protocol witness for ObserverProtocol.on(
:) in conformance AtomicObserver<A, B> + 4364411976 (:4364411976)
22 App 0x195b2c ObserverProtocol.receive(:) + 137 (Observer.swift:137)
23 App 0x1b4f50 closure #1 in closure #1 in SignalProtocol.map
(:) + 37 (SignalProtocol+Monad.swift:37)
24 App 0x195d98 thunk for @escaping @callee_guaranteed (@in_guaranteed Signal<A, B>.Event) -> () + 4364410264 (:4364410264)
25 App 0x1c4acc Subject.on(:) + 83 (Subjects.swift:83)
26 App 0x1c6054 ReplayOneSubject.on(
:) + 185 (Subjects.swift:185)
27 App 0x1c57c4 protocol witness for ObserverProtocol.on(:) in conformance Subject<A, B> + 4364605380 (:4364605380)
28 App 0x1c4fbc SubjectProtocol.send(
:) + 36 (Subjects.swift:36)
29 App 0x197adc Published.wrappedValue.setter + 44 (Published.swift:44)
30 App 0x45c2c Window.state.setter + 44 (Window.swift:44)
31 App 0x47208 closure #2 in Window.setupMaintenanceMode(configuration:) + 217 (Window.swift:217)
32 App 0x180e74 closure #1 in SignalProtocol<>.bind(to:context:setter:) + 109 (Bindable.swift:109)
33 App 0x1c3628 closure #1 in SignalProtocol.observeNext(with:) + 56 (SignalProtocol.swift:56)
34 App 0x195cb4 AtomicObserver.on(:) + 101 (Observer.swift:101)
35 App 0x1bb6b0 partial apply for closure #1 in closure #1 in closure #1 in SignalProtocol.receive
(on:) + 51 (SignalProtocol+Threading.swift:51)
36 App 0x18a9d8 closure #1 in static ExecutionContext.immediateOnMain.getter + 61 (ExecutionContext.swift:61)
37 App 0x199408 protocol witness for Scheduler.schedule(
:) in conformance ExecutionContext + 39 (Scheduler.swift:39)
38 App 0x1bb418 closure #1 in closure #1 in SignalProtocol.receive(on:) + 50 (SignalProtocol+Threading.swift:50)
39 App 0x195cb4 AtomicObserver.on(:) + 101 (Observer.swift:101)
40 App 0x196448 protocol witness for ObserverProtocol.on(
:) in conformance AtomicObserver<A, B> + 4364411976 (:4364411976)
41 App 0x195b2c ObserverProtocol.receive(:) + 137 (Observer.swift:137)
42 App 0x1b20a0 closure #2 in closure #1 in SignalProtocol.prefix
(untilOutputFrom:) + 370 (SignalProtocol+Filtering.swift:370)
43 App 0x195cb4 AtomicObserver.on(
:) + 101 (Observer.swift:101)
44 App 0x196448 protocol witness for ObserverProtocol.on(:) in conformance AtomicObserver<A, B> + 4364411976 (:4364411976)
45 App 0x195b2c ObserverProtocol.receive(
:) + 137 (Observer.swift:137)
46 App 0x1be374 closure #1 in SignalProtocol.scan(::) + 118 (SignalProtocol+Transforming.swift:118)
47 App 0x1960e4 AtomicObserver.attach(:) + 73 (Observer.swift:73)
48 App 0x19a9c4 Signal.observe(with:) + 43 (Signal.swift:43)
49 App 0x1b1b98 closure #1 in SignalProtocol.prefix
(untilOutputFrom:) + 363 (SignalProtocol+Filtering.swift:363)
50 App 0x1960e4 AtomicObserver.attach(
:) + 73 (Observer.swift:73)
51 App 0x19a9c4 Signal.observe(with:) + 43 (Signal.swift:43)
52 App 0x1bb06c closure #1 in SignalProtocol.receive(on:) + 49 (SignalProtocol+Threading.swift:49)
53 App 0x1960e4 AtomicObserver.attach(:) + 73 (Observer.swift:73)
54 App 0x19a9c4 Signal.observe(with:) + 43 (Signal.swift:43)
55 App 0x1c36dc SignalProtocol.observeNext(with:) + 4364596956
56 App 0x180dec SignalProtocol<>.bind
(to:context:setter:) + 107 (Bindable.swift:107)
57 App 0x46f04 Window.setupMaintenanceMode(configuration:) + 214 (Window.swift:214)
58 App 0x45e9c closure #1 in Window.init(frame:configurationRepository:launchedFromPush:) + 102 (Window.swift:102)
59 App 0x180e74 closure #1 in SignalProtocol<>.bind(to:context:setter:) + 109 (Bindable.swift:109)
60 App 0x1c3628 closure #1 in SignalProtocol.observeNext(with:) + 56 (SignalProtocol.swift:56)
61 App 0x195cb4 AtomicObserver.on(
:) + 101 (Observer.swift:101)
62 App 0x1bb6b0 partial apply for closure #1 in closure #1 in closure #1 in SignalProtocol.receive(on:) + 51 (SignalProtocol+Threading.swift:51)
63 App 0x18aec4 thunk for @escaping @callee_guaranteed () -> () + 4364365508
64 libdispatch.dylib 0x213c _dispatch_call_block_and_release + 32
65 libdispatch.dylib 0x3dd4 _dispatch_client_callout + 20
66 libdispatch.dylib 0x125a4 _dispatch_main_queue_drain + 988
67 libdispatch.dylib 0x121b8 _dispatch_main_queue_callback_4CF + 44
68 CoreFoundation 0x3751c CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 16
69 CoreFoundation 0x34218 __CFRunLoopRun + 1996
70 CoreFoundation 0x33968 CFRunLoopRunSpecific + 608
71 GraphicsServices 0x34e0 GSEventRunModal + 164
72 UIKitCore 0x22aedc -[UIApplication _run] + 888
73 UIKitCore 0x22a518 UIApplicationMain + 340
74 App 0xb9b0 main + 70 (AppDelegate.swift:70)
75 ??? 0x1ba73ed84 (Missing)

@bsneed
Copy link
Contributor

bsneed commented Apr 23, 2024

Thanks @GavrilikArt. Given that your application is coming from a CLEAN start, it seems that something else might be starting the analytics instance other than your static SegmentIntegration.configure(...). We keep a list of active write keys, so there's definitely another one that's been allocated somewhere in your app start up process. It's not an issue of deinit() not getting called from a previous run. If you put a breakpoint on Analytics.init, you should see the places it's getting hit from in your stack trace when you reproduce it.

@leontedev
Copy link
Author

I'd actually like to see the whole thing if you wouldn't mind. It'd show me what was happening overall. The contents or a description of the closure that's located in appDidFinishLaunching would be useful too since I'm guessing it's async or something.

In my case that is true: the closure which calls the Segment configuration and init in appDidFinishLaunching is async.

@bsneed
Copy link
Contributor

bsneed commented Apr 24, 2024

It's highly likely your closure is getting called twice somehow. As I mentioned previously, you can put a breakpoint on Analytics.init and see who the callers are and when they're occurring. As the log shows it's a completely fresh run, so there wouldn't be another analytics instance in place w/ a matching write key unless your code is making one.

@bsneed bsneed closed this as completed Apr 24, 2024
@leontedev
Copy link
Author

You are right! Thanks @bsneed! 🙏
Question, why throw a fatalError though? It seems a bit drastic, especially when the app is already live and some users are crashing. Maybe a compiler flag to crash only on DEBUG build types could be used instead? 🤔

@bsneed
Copy link
Contributor

bsneed commented Apr 29, 2024

@leontedev great, glad that worked out! Yeah, I hear what you're saying and believe me, it wasn't my first choice. We found customers creating two instances of analytics with the same write key, which in turn basically corrupted the storage system (two separate instances writing to all the same files and trying to send the same files to segment). We didn't feel that some DEBUG only thing was sufficient to get their attention to this anti-pattern. Some places have analytics disabled while in DEBUG, some places point all that stuff to the appropriate places in their CI, etc. all of which result in someone never seeing the fatalError call get hit.

@leontedev
Copy link
Author

I see! Thanks for explaining! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants