Undefined symbol: protocol witness table for

I do have a problem when generating a custom framework for a modular App. It all goes well with the framework attached to an example application. The symbols are there, but when I compile with BUILD_LIBRARY_FOR_DISTRIBUTION=YES the symbols are stripped from the PWT.

Undefined symbols for architecture x86_64:
  "protocol witness table for MoviesModuleDisplay.RemoteModel : RxFlow.Stepper in MoviesModuleDisplay", referenced from:
      Movies.SceneDelegate.scene(_: __C.UIScene, willConnectTo: __C.UISceneSession, options: __C.UISceneConnectionOptions) -> () in SceneDelegate.o
  "protocol witness table for MoviesModuleDisplay.DisplayFlow : RxFlow.Flow in MoviesModuleDisplay", referenced from:
      Movies.SceneDelegate.scene(_: __C.UIScene, willConnectTo: __C.UISceneSession, options: __C.UISceneConnectionOptions) -> () in SceneDelegate.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The command I use to generate the framework:

xcodebuild -workspace "MoviesModuleDisplay.xcworkspace" -configuration "Release" -scheme "MoviesModuleDisplay" clean BUILD_LIBRARY_FOR_DISTRIBUTION=YES SKIP_INSTALL=NO OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE="bitcode" ENABLE_BITCODE=YES -destination "generic/platform=iOS Simulator" -archivePath "fastlane/products/archives/1_MoviesModuleDisplay.xcarchive" archive | tee '/Users/carles/Library/Logs/fastlane/xcbuild/2021-11-07/28926/xcodebuild.log'

The offending classes that implement a protocol:

import Foundation
import RxSwift
import RxCocoa
import RxFlow
import MoviesModuleService
import MoviesModuleView

open class DisplayFlow: Flow {
    public var root: Presentable {
        return self.rootViewController
    }

    private lazy var rootViewController: UINavigationController = {
        let viewController = NavigationController()
        return viewController
    }()

    private let services: API

    public init(with services: API = API.shared) {
        self.services = services
    }

    deinit {
        print("\(type(of: self)): \(#function)")
    }

    public func navigate(to step: Step) -> FlowContributors {
        guard let step = step as? DisplayStep else { return .none }

        switch step {
        case .list:
            return navigationToListScreen()
        case .detail(let id):
            return navigationToDetailScreen(with: id)
        }
    }

    private func navigationToListScreen() -> FlowContributors {
        let viewModel = ListViewModel(serivices: self.services)
        let viewController = ListViewController()
        viewController.viewModel = viewModel

        self.rootViewController.pushViewController(viewController, animated: false)
        return .one(flowContributor: .contribute(withNextPresentable: viewController, withNextStepper: viewController.viewModel))
    }

    private func navigationToDetailScreen(with id: Int) -> FlowContributors {
        let viewModel = DetailViewModel(id: id, services: self.services)
        let viewController = DetailViewController()
        viewController.viewModel = viewModel

        self.rootViewController.pushViewController(viewController, animated: true)
        return .none
    }
}

The other class:

import Foundation
import RxSwift
import RxCocoa
import RxFlow

open class RemoteModel: Stepper {
    public let steps: PublishRelay<Step> = PublishRelay()
    
    public enum RemoteModelError {
        case internetError(String)
        case serverMessage(String)
    }

    public let loading: BehaviorRelay<Bool> = BehaviorRelay(value: false)
    public let error: PublishSubject<RemoteModelError> = PublishSubject()
    
    let disposable = DisposeBag()
}

How I call these methods:

 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let scene = (scene as? UIWindowScene) else { return }
        
        window = UIWindow(windowScene: scene)
        
        let appFlow = DisplayFlow()
        let appStepper = ListViewModel()

        AppDelegate.coordinator.coordinate(flow: appFlow, with:appStepper, allowStepWhenDismissed: true)

        Flows.use(appFlow, when: .created) { [weak self] root in
            self?.window?.rootViewController = root
            self?.window?.makeKeyAndVisible()
            appStepper.readyToEmitSteps()
        }
    }

The thing is I think I've tried everything, disabling stripping (dead code stripping), remove optimization, adding -ObjC and -load_all, that this classes subclass NSObject, and I've found so little about this, just instructions to clean the cache of XCode, which I've tried and I think it is not the solution in my case. I've even listed the available methods with nm -gU , and when I build the framework for distribution there are effectively less methods than when I list the one compiled for debug and test in the same project that the framework is attached to. Building the framework with Debug options bears the same results, yet if I copy the intermediate built framework of its original project, and copy to the other application that uses the framework it works, in my opinion meaning that somewhere in the process of building the famework with BUILD_LIBRARY_FOR_DISTRIBUTION those symbols are stripped. I've tried removing BUILD_LIBRARY_FOR_DISTRIBUTION and the symbols are there but the framework crashes at runtime (don't know the cause yet), but I understand that I have to build with this option to distribute a framework.

Any hint?

Terms of Service

Privacy Policy

Cookie Policy