Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

TestFlight builds can crash unless Swift Language Version is set to Swift 6 #3606

Unanswered
AndrewBardallis asked this question in Q&A
Discussion options

Description

We have a very large, modularized project that has been using more and more TCA over the years. For context, a cursory search returns about 97 different modules that list TCA as a dependency. Recently, we attempted to update from TCA 1.12.1 to TCA 1.17.1 but needed to roll back the update when we saw crashes spike in our Beta Testing Group for just two of our many TCA features.

In both cases, the trace will have only two lines on the thread that crashes:

EXC_BAD_ACCESS   Attempted to deference garbage pointer 0x3e4234f7.

Thread 15
0    libswiftCore.dylib        _swift_getObjectType
1    App                       closure #1 in FeatureClient.init(service:)

We have also noticed the following peculiar characteristics of this issue:

  • The crash ONLY occurs on Archived Release builds installed on physical devices.
  • It does NOT occur on Archived Release builds on simulators.
  • It does NOT occur on locally built/ran Release builds on physical devices or simulators.
  • It does NOT occur on Debug builds on physical devices or simulators.

As strange as it is, multiple developers have double checked that list with 100% reproducibility. This appears to be only an issue in builds created for TestFlight/App Store when ran on physical devices.

Dependency management

We manage our dependencies using uber/needle and this has worked well for our previous architecture as well as TCA. We use a Builder that has access to our feature's dependencies from Needle that's responsible for initializing the SwiftUI/UIKit+TCA stack.

For instance, let's assume our FeatureNeedleComponent is set up with some service dependency to make requests to the backend:

class FeatureBuilder {
    private let component: FeatureNeedleComponent
    
    init(component: FeatureNeedleComponent) {
        self.component = component
    }

    func build() -> FeatureView {
        FeatureView(store: Store(initialState: .init()) {
            FeatureReducer(
                client: FeatureClient(service: component.service)
            )
        }
    }
}


@Reducer
struct FeatureReducer {
    // State
    // Action
    
    let client: FeatureClient
    
    var body: some ReducerOf<Self> {
        Reduce { state, action in 
            switch action {
            case .onAppear:
                return .run { send in 
                    let response = try await client.getWidgets()
                    await send(.getWidgetsResult(.success(response)))
                } catch: {...}
            ...
            }
        }
    }
}

struct FeatureClient {
    let getWidgets: () async throws -> [Widget]
}

extension FeatureClient {
    init(service: FeatureService) {
        self.getWidgets = {
            try await service.getWidgets()
        }
    }
}

In this pseudo code setup, we would see the crash as soon as try await service.getWidgets() is called.

The client itself can be called (i.e. if we were to replace the closure with just self.getWidgets = {[]}, but if the service that's passed in is accessed, it will crash.

What fixes this?

We have thus far found 2 ways to fix this (and I use "fix" loosely).

  1. Undo the change that introduced this issue in swift-composable-architecture.
    We did a manual bisect and isolated the issue to commit cad094a. The commit prior to this one does not exhibit the crash. I then set up our project with this commit of swift-composable-architecture as a local dependency and began reverting changes. I reverted the change in Effect.swift as well as the change in the .map {} of each of file in Reducer/Reducers (ForEachReducer.swift, IfCaseLetReducer.swift, IfLetReducer.swift, PresentationReducer.swift, Scope.swift, StackReducer.swift). I did not change anything in Internal/, TestStore.swift or ViewStore.swift. After pushing to a Draft PR and getting a build through CI that was signed for Distribution, we found the crash was no longer occurring. We all collectively shrugged and wondered if the fact that self was no longer being captured by .map {} had anything to do with it.

  2. Set the module to use "SWIFT_VERSION": "6.0".
    With the swift-composable-architecture dependency set back to a remote version that exhibits the issue, we updated the language version of one of our two problematic modules to Swift 6.0. We made the necessary changes for that to compile and retested. We no longer saw a crash in that feature and continued to see a crash in the feature that remained on Swift 5.0.

Now what?

We're a little stuck. We want to update to the latest version of TCA to take advantage of all the great features it has to offer. While it's very encouraging that we can update the language version to Swift 6.0 and see the issue resolved, this isn't a fully viable solution in the near term for a couple reasons:

  • We are still completely in the dark on why these 2 out of 97 modules exhibited the crash and the others did not. Even if we update these 2 to use Swift 6.0, how will we release beyond Beta with a high level of confidence that this issue will not show up somewhere else that we just haven't discovered yet? Beta is great, but it's not a catch all. The not-so-easy answer is...
  • Update all our modules to Swift 6.0. While this is certainly something we're working on, this is a massive undertaking at our scale, especially considering that we also need to balance this with demands coming from the business.

Checklist

This issue hasn't been addressed in an existing GitHub issue or discussion.

It kinda smells like this discussion, but we're not using @Dependencies, the stack trace is different enough, and that discussion lacked all the details needed to determine if this was likely to be the same issue. Tried to do our diligence here, but this felt like it warranted a new post.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No response

Actual behavior

No response

Reproducing project

No response

The Composable Architecture version information

cad094a

Destination operating system

iOS 16.0+ (most testing done on 18.2)

Xcode version information

16.2 (16C5032a)

Swift Compiler version information

swift-driver version: 1.115.1 Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)
Target: arm64-apple-macosx15.0
You must be logged in to vote

Replies: 2 comments · 2 replies

Comment options

Hi @AndrewBardallis! Thanks for the detailed and puzzling report. Seems very strange that the issue only affects TestFlight builds.

I imagine it is a Swift or Apple tooling bug around Swift 6 mode, so I'm not sure there's anything actionable on our side. If there are Swift 6-compatible changes we could make to work around the issue, we're certainly open to them.

We also wonder if the issue could be caused by Needle being in Swift 5 mode. Have you investigated what it would take to make Needle Swift 6-compatible? It looks like it wouldn't take too much, and it's possible that could work around the issue.

I'm going to convert this to a discussion for now, since it doesn't seem to be a bug particular to our library.

You must be logged in to vote
2 replies
@AndrewBardallis
Comment options

Thanks for the reply @stephencelis! Yes, after chewing on this for some time, we agree that this isn't rooted in TCA. That just happened to be where we were able to get our arms around it. Thanks for converting to a discussion.

While we didn't try to make Needle Swift 6 compatible, we did try just taking Needle out of the equation by using what were essentially mock services that the Builder just initialized and passed in on the spot. But even so, we still saw the exact same problem.

@AndrewBardallis
Comment options

Just another update here if you're interested @stephencelis. We found that by Archiving a Release build we could reproduce the crash w/o going through our CI process (so it's nothing to do with the certs which makes much more sense). Further, we also found that with SWIFT_VERSION set back to 5.0, if we set both of the following on one of the problematic modules, the crash was also resolved on that module:

STRIP_INSTALLED_PRODUCT=NO (Rather than YES)
SWIFT_OPTIMIZATION_LEVEL=No optimization [-Onone]

We're submitting this to Apple in hopes that this may point them in the right direction.

Comment options

@stephencelis We've been in touch with Apple developer support who were able to reproduce the crash and have acknowledged that this is an issue with the swift compiler.

I've opened a PR with their response and suggested mitigation step here.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
2 participants
Converted from issue

This discussion was converted from issue #3604 on February 21, 2025 19:27.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.