Rxswift: Runtime errors with Xcode 9.3 / Swift 4.1

Created on 25 Jan 2018  ·  46Comments  ·  Source: ReactiveX/RxSwift

Short description of the issue:

combineLatest/zip operators fail at runtime with latest Xcode 9.3b1 / Swift 4.1 running against iOS 11.3 OR 11.2.

Expected outcome:

combineLatest/zip operators execute without crashing.

What actually happens:

Intermittent bad access errors.

Self contained code example that reproduces the issue:

Run the iOS Test Suite with Xcode 9.3b1.

It seems disabling the tests in Observable+CombineLatestTests+arity.swift, Observable+ZipTests.swift, and Observable+ZipTests+arity.swift will allow the test suite to pass, so I assume the focus will be on the zip/combineLatest operators.

RxSwift/RxCocoa/RxBlocking/RxTest version/commit

RxSwift 4.1.1

Platform/Environment

  • [x] iOS
  • [ ] macOS
  • [ ] tvOS
  • [ ] watchOS
  • [ ] playgrounds

How easy is to reproduce? (chances of successful reproduce after running the self contained code)

  • [x] easy, 100% repro
  • [ ] sometimes, 10%-100%
  • [ ] hard, 2% - 10%
  • [ ] extremely hard, %0 - 2%

Xcode version:

Xcode 9.3b1

Most helpful comment

I'll give a day or two to test it out. If nobody reports any significant issues, I'll cut a new release.

All 46 comments

Hi @tcldr ,

why do you think that this is a bug with RxSwift and not with Xcode 9.3b1?

Hi @kzaher, yes it could be 9.3b1. It's hard for me to say without intimate knowledge of the codebase / relevant changes in 9.3b1 – hence raising the issue.

I would expect someone with experience of the codebase might be better placed than I to either a) highlight the issue to Apple so that it can be resolved without modifying the RxSwift codebase, or b) identify a workaround if a resolution to the particular issue isn't forthcoming for whatever reason.

I'm having the same issues; specifically using RxCocoa.

Ex. When I'm trying to access an rx value Xcode raises a warning message

An internal error occurred. Source editor functionality is limited. Attempting to restore

Software Specs
Xcode: Version 9.2 (9C40b)
RxSwift: ~> 4.0
RxCocoa: ~> 4.0
Operating System: macOS Sierra 10.13.3
Cocoapods: (1.4.0.rc.1)

Hi @tcldr ,

Unfortunately familiarity with Rx code doesn't help, this is Swift compiler bug.

If somebody has time to pull out a small repro and report to Apple, that would be great.

screen shot 2018-01-27 at 21 23 56

@kzaher that's done: https://bugs.swift.org/browse/SR-6860

Temporary workaround – albeit one with a performance penalty – is to set the swift optimisation level to 'no optimisation' until we have a fix in the Swift compiler.

@tcldr Great, good job. That's all we can do.

Still an issue in Xcode 9.3 beta 3. Setting optimization level did not work, however, explicitly specifying a comparer (eg combineLatest {$0 == $1}) solves the issue.

@zierka I wouldn't worry about it. I've seen @jrose-apple noticing the https://bugs.swift.org/browse/SR-6860 issue and creating a rdar.

I doubt that they can ship Xcode 9.3 with such a serious regression and not cause a havoc in the ecosystem.

This issue appears to be resolved in 9.3b3! I haven't done extensive testing, but running the test suite, tests that RxSwift used to fail now pass on macOS and iOS simulator.

(Although the test_controlPropertyBindsValue has to be disabled on macOS as it doesn't compile but I believe that's an unrelated issue.)

@kzaher and now unfortunately a separate issue with 9.3b3:
https://bugs.swift.org/browse/SR-7064

A new iOS project with the following View Controller demonstrates the issue:

// ENSURE BUILD OPTIMISATION IS ENABLED

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    let disposeBag = DisposeBag()

    override func loadView() {
        let button = UIButton(frame: .zero)
        button.backgroundColor = .red
        self.view = button
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = view as! UIButton

        let reductionSubject = ReplaySubject <Void>.create(bufferSize: 1)
        let reducerStream = reductionSubject
            .flatMapLatest{ _ in button.rx.tap }
            .startWith(())
            .do(onNext: { print("state: \($0)") })

        reducerStream.subscribe(reductionSubject).disposed(by: disposeBag)
    }
}

Hi @tcldr ,

I've been contacted by Apple.
I'm currently updating https://github.com/apple/swift-source-compat-suite to support swift 4.0. I'm hoping that that should help Swift compiler team.

Thanks for the update – that's great news.

Perhaps it may be worth adding to the test suite some of the idiomatic RxSwift/RxCocoa usages so that we pick up regressions like those in SR-7064? Or at least add them if/when we discover them?

Speaking of SR-7064, it now looks like SR-7064 is fixed on the swift master/4.1 branches also.

Hi @tcldr,

I understand why you might think that is a good idea from practical point of view, but I’m against adding and maintaining Swift compiler and UIKit regression tests inside this repo, although that is accidentally the case currently.

I’m looking at you mr. #1526.

I'm having issues as well... Crashlytics is complaining about TakeUntil.

Hi @tcldr @kzaher,

Do we have any workaround for this? Facing the runtime crash on 9.3 Release version.

Still an issue for us as well. XCode 9.3 Release version. RxCocoa 4.1.2 & RxSwift 4.1.2. Debug config works just fine. Release config 100% crash rate.

Or an overkill disable optimisation at the end of podfile :

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      if config.name == 'Debug'
        config.build_settings['OTHER_SWIFT_FLAGS'] = ['$(inherited)', '-Onone']
        config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Owholemodule'
      else
        # below line disables optimisation for release build
        config.build_settings['OTHER_SWIFT_FLAGS'] = ['$(inherited)', '-Onone']
        config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Owholemodule'
      end
    end
  end
end

EDIT : This did not work either :( i guess there are some settings which i am not aware of

It looks like using Swift compiler optimizations is an experimental feature :trollface: , ugh 😦

Is there any workaround other than returning back to xcode 9.2 ?

@ergunkocak that's the only workaround that we've found (other than turning off Swift compiler optimizations, which is a non-starter).

@kzaher we were able to reduce the bug we're seeing down to a pretty simple example. Presenting this view controller and tapping its button is a 100% crasher on Xcode 9.3 / Swift 4.1 (and RxSwift 4.1.2) with compiler optimizations set to Release ([-O]):

import UIKit
import RxSwift

class ViewController: UIViewController {

    private let disposeBag = DisposeBag()

    private let publishSubject = PublishSubject<Void>()
    private let buttonPublishSubject = PublishSubject<Void>()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        // Add button
        let button = UIButton(type: .system)
        button.frame = view.bounds
        button.setTitle("I'll crash your app.", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)

        view.addSubview(button)

        // Setup observables
        buttonPublishSubject
            .subscribe(onNext: publishSubject.onNext) // ___CRASH___
//            .bind(to: publishSubject) // using `bind` avoids the crasher
//            .subscribe(onNext: { self.publishSubject.onNext($0) }) // also does not crash
            .disposed(by: disposeBag)

        publishSubject
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: {
                print("button tapped")
            })
            .disposed(by: disposeBag)
    }

    @objc
    func buttonTapped(_ sender: Any) {
        self.buttonPublishSubject.onNext(())
    }
}

As you can see, passing publishSubject.onNext directly to an subscribe(onNext... call seems to be the root cause of our crash. We've confirmed that the crash does _not_ occur when built with Xcode 9.2, nor when compiler optimizations are set to Debug ([-Onone])

We're not totally certain that this is the same issue seen by others in this thread, but it seems close enough that we thought we'd post it here - please let me know if you like us to open another ticket, and/or more information is needed.

Hi @worthbak ,

This looks to me like a Swift compiler bug.

Even more so when you say

We've confirmed that the crash does not occur when built with Xcode 9.2, nor when compiler optimizations are set to Debug ([-Onone])

Why do you feel that this is a bug in this repo?

@kzaher that's fair - it almost certainly is a bug in the Swift compiler. I posted it here a) because thus far, RxSwift is the only part of our codebase causes a trap in this way, and b) because the issue only occurs on release builds, and many developers will not see it during regular development. They'll see it later in the development process, see that the stack trace points to RxSwift, and probably come to this repo first to look for help. As such, it's in your interest to have some info about the issue in this repo.

In any case, I'll open an issue for the Swift compiler team. Thanks.

EDIT: comment added to this Swift issue: https://bugs.swift.org/browse/SR-7064

Oh dear, what a pain. I'm not looking forward to running into more of these.

Unfortunately I don't think this a single bug, but a whole collection of bugs introduced by the Swift 4.1 optimiser. As such, I think it's really important that, when anyone discovers an issue you raise the issue with the Swift team via https://bugs.swift.org – it's the only way to get them fixed.

Also, to improve the chance of the Swift team fixing the bug, it's best to try and isolate the bug into as tidy a reproducible project as possible. In some cases you'll be more successful than others, but the easier you make it for the Swift team to reproduce the more likely they are to fix it.

For SR-6860 that ended up being just a couple of lines of code completely outside of RxSwift. You can then place that in a swift file named, for example, MyBug.swift and run xcrun swiftc -o mybug MyBug.swift from the terminal, to see if it crashes and get the stack trace if so. Then it's just a simple case of pasting the offending code and stack trace into your bug report as shown in SR-6860.

For SR-7064 I wasn't able to isolate the bug from RxSwift and so it was necessary to build a workspace that demonstrated the issue in-situ. (In fact, the only way I could isolate SR-6860 was by following this method.) In this case it's best to include a copy of the RxSwift code so that when the person working on the bug runs the project, they don't need to jump through hoops to get RxSwift debug symbols visible in the debugger. Also, you can create an Xcode scheme in the project that applies optimisations.

The basic steps have similarities with creating an RxSwift playground:

  • Create a new Xcode workspace for you bug.
  • Download the latest Source archive from the releases tab of RxSwift (The 4.1.2 Source Archive is here for example) and add the Rx project (not workspace) to your new workspace.
  • Create a new Xcode project, drag into your workspace, link to your workspace copy of the Rx frameworks.
  • Create a scheme that compiles with optimisations and demonstrates the bug.

You can see an example by downloading the attachment with SR-7064.

When I've followed these steps, the bugs I've submitted have all been resolved within a couple of weeks.

@worthbak it's great to link Swift bugs that affect RxSwift in this repo.

I was trying to say that we can't do anything about those.

@tcldr Perfect write-up thnx.

@tcldr @kzaher ditto - thanks for the writeup! I've created a contained workspace that include RxSwift and reproduces the bug (see attached), and will update my comment on the aforementioned Swift issue.

RxSwift_Crash-Swift_4_1.zip

Follow up - the issue is fixed on Swift master: https://bugs.swift.org/browse/SR-7064

So we will be able to use it on next xcode release? Which is probably 3 months later?

@ergunkocak yeah, sounds like that. The issue seems pretty easy to mitigate:

somePublishSubject
            .subscribe(onNext: otherPublishSubject.onNext) // ___CRASH___
//            .bind(to: otherPublishSubject) // using `bind` avoids the crasher
//            .subscribe(onNext: { self.otherPublishSubject.onNext($0) }) // also does not crash
            .disposed(by: disposeBag)

Basically, don't pass an onNext function to a subscribe(onNext:) call. This seems easy to avoid, but assumes that the issue doesn't crop up elsewhere - not a particularly safe assumption.

It seems that it's still occurring with Xcode 9.3.1, with the example workspace linked above (https://bugs.swift.org/browse/SR-7064).
The test app still crashes as detailed in the steps to reproduce.

@r-mckay if you can repro that, can you please report this to Apple?

@kzaher yes no problem!
Here is their very quick answer:

Unfortunately this fix didn't make it into swiftlang-902.0.48.
It will be included in an upcoming release, starting with swiftlang-902.0.52.

At least 13.000 people is using this library.
I will not curse, i will not curse, i will not curse ...

Just for information: Xcode 9.4 beta 2 includes Apple Swift version 4.1.1 (swiftlang-902.0.53 clang-902.0.39.2)
I couldn't reproduce the crashes above with this version.

@kzaher when do we cut a release ? This bug is fixed already with Version 9.3.1 (9E501) of Xcode

@bobgodwinx According to this thread

https://github.com/ReactiveX/RxSwift/issues/1555#issuecomment-388231728

and https://bugs.swift.org/browse/SR-7064

it doesn't look to me like 4.1 is stable at the moment.

@kzaher I did the testing myself yesterday and today I tested creashing-scheme and non-crashing-scheme and everything works with the xcode release of 10-May-2018 please do verify because to me looks pretty good to go with this xcode Version 9.3.1 (9E501)

Hi @bobgodwinx ,

I have

Apple Swift version 4.1 (swiftlang-902.0.48 clang-902.0.37.1)
Target: x86_64-apple-darwin17.4.0

Xcode 9.3.1 was released May 10, 2018. This is the version of Xcode I have that contains swiftlang-902.0.48.

According to https://bugs.swift.org/browse/SR-7064 and Romain

Hello Erik Eckstein, sorry to bother you.
Just reporting that this behaviour seems to still be reproductible with Xcode 9.3.1 - Apple Swift version 4.1 (swiftlang-902.0.48 clang-902.0.37.1). Well, I reproduced it with the RxSwift_Crash-Swift_4_1 archive linked above, if someone else can confirm this.
Thank you very much!

and then again Erik Eckstein

Thanks for reporting!
Unfortunately this fix didn't make it into swiftlang-902.0.48.
It will be included in an upcoming release, starting with swiftlang-902.0.52.

Are you saying that both Erik Eckstein (member of Apple team) and Romain are reporting false claims on that bug report when they say that swiftlang-902.0.48 contains crashing bugs inside RxSwift.

@kzaher I think the fix made it into swiftlang-902.0.48 because the version that was giving problems was swiftlang-902.0.41 please check it yourself because it doesn't crash for me. I can't post my screen recording in here. But I just tried the App again and it's working.

Hi @bobgodwinx ,

unfortunately if it does't crash for you or me that doesn't prove that fix entered swiftlang-902.0.48 and that those claims are false.

It's simpler to wait for next Xcode release. It should probably be out in a week or two.

XCode 9.4 is out now, i assume this is good news?

I'll give a day or two to test it out. If nobody reports any significant issues, I'll cut a new release.

xcode 9.4 Swift version : "Apple Swift version 4.1.2 (swiftlang-902.0.54 clang-902.0.39.2)"
so it should be fixed right?. I am also testing in the meantime.

Update : My project seems not crashing any more.
xcode: 9.4
RxCocoa: 4.1.2 (So without updating Rx library)
CocoaPods: 1.5.3

Apparently back to normal!! ❤️

Ok, nobody reported any issues, I'll try to merge some minor PR and cut a new release.

Was this page helpful?
0 / 5 - 0 ratings