react-native-webrtc/react-native-callkeep

New Architecture Setup issue in iOS - Import RNCallKeep No such module

Closed this issue · 4 comments

Hello everyone.
I’m trying to integrate react-native-callkeep into my React Native iOS project, but I’m facing an issue when using Swift for AppDelegate.swift.

Environment

  • React Native: 0.81.0
  • react-native-callkeep: 4.3.16
  • Xcode: e.g., 15.4
  • CocoaPods: e.g., 1.15.2
  • iOS: e.g., 17.5 (simulator & device)
  • Device: e.g., iPhone 15 Pro simulator / physical device
  • Hermes: On
  • New Architecture (Fabric/Turbo): Disabled (also reproduced with Enabled)

I can update the above with exact numbers if needed, but the core issue reproduces consistently across our machines.

Current Behavior

Attempting to use CallKeep from a Swift AppDelegate.swift fails because the module cannot be imported or referenced from Swift:

  • Using import RNCallKeep → Xcode reports No such module 'RNCallKeep'.
  • Using an Objective‑C bridging header to expose the header → Swift can compile the bridging header, but calls like RNCallKeep.setup(...) in Swift produce Use of unresolved identifier 'RNCallKeep' or header-not-found errors depending on the header path tried.

This prevents initializing CallKeep in didFinishLaunchingWithOptions as shown in the Objective‑C example from the docs.

Expected Behavior

RNCallKeep should be consumable from Swift AppDelegate.swift either by:

  1. Importing as a Swift module (import RNCallKeep), or
  2. Via a documented bridging-header approach that reliably exposes RNCallKeep to Swift.

Steps To Reproduce

  1. Create a new RN app with a Swift AppDelegate (or migrate AppDelegate to Swift).
    Example: npx react-native@0.81.0 init CallKeepSwift --template react-native-template-typescript and convert AppDelegate to Swift if needed.
  2. Install the library: yarn add react-native-callkeep@4.3.16.
  3. cd ios && pod install (autolinking picks up the pod).
    Also tried explicitly adding the pod to Podfile; same result.
  4. In AppDelegate.swift, attempt to initialize CallKeep on launch (see snippet below).
    Tried both import RNCallKeep and bridging-header imports.
  5. Build the iOS app in Xcode → build fails with the errors shown below.
Image

Code Snippets

AppDelegate.swift

import UIKit
import React

// Attempt 1: Import as a Swift module (fails)
import RNCallKeep // → No such module 'RNCallKeep'

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    // Attempt 2: Call via bridging header (fails at call site)
    // Expectation: same as Objective‑C example [RNCallKeep setup:@{ @"appName": @"MyApp" }];
    RNCallKeep.setup([
      "appName": "MyApp",
      "maximumCallGroups": 1,
      "maximumCallsPerCallGroup": 1,
      "supportsVideo": true
    ])

    return true
  }
}

Bridging Header (when using bridging approach)

// MyApp-Bridging-Header.h
#import <RNCallKeep/RNCallKeep.h> // also tried "RNCallKeep.h"

Podfile (relevant parts)

platform :ios, '13.0'
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

project 'MyApp', {
  'Debug' => :debug,
  'Release' => :release
}

# Tried with and without frameworks
use_frameworks! :linkage => :static # also tried removing this line entirely

target 'MyApp' do
  config = use_native_modules!

  use_react_native!(
    :hermes_enabled => true,
    :app_path => "#{Pod::Config.instance.installation_root}/.."
  )

  # Autolinking picks up CallKeep; also tried explicitly adding:
  # pod 'react-native-callkeep', :path => '../node_modules/react-native-callkeep'

  post_install do |installer|
    react_native_post_install(installer)
  end
end

Build Output / Errors

/ios/MyApp/AppDelegate.swift:3:8: error: no such module 'RNCallKeep'
import RNCallKeep
       ^~~~~~~~~~
Use of unresolved identifier 'RNCallKeep'
'RNCallKeep/RNCallKeep.h' file not found

(Depending on whether the project uses use_frameworks!, the bridging header path, and cache state.)

What I Tried

  • Cleaned build folder & deleted DerivedData.
  • pod deintegrate && pod install --repo-update.
  • Toggled use_frameworks! (no line, :linkage => :static, and dynamic).
    When enabled, Swift import still fails; when disabled, bridging header import sometimes finds the header but RNCallKeep remains unresolved in Swift.
  • Explicitly adding the pod (instead of relying on autolinking) → same result.
  • Verified that RNCallKeep appears in Pods/ and in Podfile.lock at version 4.3.16.

Questions for Maintainers

  1. Is Swift import (import RNCallKeep) officially supported for 4.3.16? If yes, what are the correct module name and Podspec settings for Swift consumption?
  2. If the recommended path is a bridging header, what is the correct header path (<RNCallKeep/RNCallKeep.h> vs "RNCallKeep.h"), and are the headers marked public_header_files in the podspec so they're visible to Swift?
  3. Are there known compatibility issues with React Native 0.81.x or with use_frameworks! that would prevent Swift import?
  4. Should initialization be done exclusively from JavaScript now (i.e., RNCallKeep.setup(...) on the JS side), and if so, can the iOS docs clarify what (if anything) must be done in AppDelegate when using Swift?

Additional Context

  • The same project builds fine until adding the RNCallKeep usage/import in Swift.
  • Other Objective‑C based RN pods are accessible from Swift in this project using the bridging-header approach.

Minimal Reproduction (optional)

I can provide a stripped repo if needed. Steps above reproduce consistently on a fresh RN 0.81 project with a Swift AppDelegate.


Thank you for maintaining this library! Happy to test any Podspec changes or provide more logs/screens if that helps.

you need to bridge the object c to switf, coz this is written in object c

I'm also getting same issue. is there any fix?

@elarafyarpit Here are the Steps

  1. Create a bridging header file in the ios/appname folder.

  2. Add the file path in Xcode → Build Settings → Swift Compiler – General → Objective-C Bridging Header (SWIFT_OBJC_BRIDGING_HEADER).

  3. Add the following setup in your AppDelegate.swift:

import UIKit
import React_RCTAppDelegate
import ReactAppDependencyProvider
import UserNotifications
import Firebase
import RNBootSplash
import PushKit
import RNVoipPushNotification
// ❌ Do NOT import CallKeep here

@main
class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
var reactNativeDelegate: ReactNativeDelegate?
var reactNativeFactory: RCTReactNativeFactory?
var voipRegistry: PKPushRegistry?

func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// 🔹 Firebase
FirebaseApp.configure()

// 🔹 CallKeep setup
let localizedAppName = Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"] as? String
let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String
RNCallKeep.setup([
  "appName": localizedAppName ?? appName ?? "App",
  "supportsVideo": true,
  "includesCallsInRecents": false, // iOS 10+ only
])

// 🔹 VoIP Push registration
RNVoipPushNotificationManager.voipRegistration()
voipRegistry = PKPushRegistry(queue: .main)
voipRegistry?.delegate = self
voipRegistry?.desiredPushTypes = [.voIP]

// 🔹 Notifications delegate
UNUserNotificationCenter.current().delegate = self

// 🔹 React Native setup
let delegate = ReactNativeDelegate()
let factory = RCTReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()
reactNativeDelegate = delegate
reactNativeFactory = factory

window = UIWindow(frame: UIScreen.main.bounds)
factory.startReactNative(
  withModuleName: "appName",
  in: window,
  launchOptions: launchOptions
)

return true

}

// MARK: - PushKit Delegates
func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
RNVoipPushNotificationManager.didUpdate(credentials, forType: type.rawValue)
}

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
guard let stream = payload.dictionaryPayload["stream"] as? [String: Any],
let createdCallerName = stream["created_by_display_name"] as? String else {
completion()
return
}

let uuid = UUID().uuidString
let videoIncluded = stream["video"] as? String
let hasVideo = videoIncluded != "false"

RNVoipPushNotificationManager.addCompletionHandler(uuid, completionHandler: completion)
RNVoipPushNotificationManager.didReceiveIncomingPush(with: payload, forType: type.rawValue)

// 🔹 CallKeep report
RNCallKeep.reportNewIncomingCall(
  uuid,
  handle: createdCallerName,
  handleType: "generic",
  hasVideo: hasVideo,
  localizedCallerName: createdCallerName,
  supportsHolding: false,
  supportsDTMF: false,
  supportsGrouping: false,
  supportsUngrouping: false,
  fromPushKit: true,
  payload: stream,
  withCompletionHandler: nil
)

}

// MARK: - Notifications
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.sound, .alert, .badge])
}
}
4. Important: Do not import CallKeep directly in AppDelegate.swift.

Here you go.....

@elarafyarpit Here are the Steps

  1. Create a bridging header file in the ios/appname folder.
  2. Add the file path in Xcode → Build Settings → Swift Compiler – General → Objective-C Bridging Header (SWIFT_OBJC_BRIDGING_HEADER).
  3. Add the following setup in your AppDelegate.swift:

import UIKit import React_RCTAppDelegate import ReactAppDependencyProvider import UserNotifications import Firebase import RNBootSplash import PushKit import RNVoipPushNotification // ❌ Do NOT import CallKeep here

@main class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate, UNUserNotificationCenterDelegate { var window: UIWindow? var reactNativeDelegate: ReactNativeDelegate? var reactNativeFactory: RCTReactNativeFactory? var voipRegistry: PKPushRegistry?

func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { // 🔹 Firebase FirebaseApp.configure()

// 🔹 CallKeep setup
let localizedAppName = Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"] as? String
let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String
RNCallKeep.setup([
  "appName": localizedAppName ?? appName ?? "App",
  "supportsVideo": true,
  "includesCallsInRecents": false, // iOS 10+ only
])

// 🔹 VoIP Push registration
RNVoipPushNotificationManager.voipRegistration()
voipRegistry = PKPushRegistry(queue: .main)
voipRegistry?.delegate = self
voipRegistry?.desiredPushTypes = [.voIP]

// 🔹 Notifications delegate
UNUserNotificationCenter.current().delegate = self

// 🔹 React Native setup
let delegate = ReactNativeDelegate()
let factory = RCTReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()
reactNativeDelegate = delegate
reactNativeFactory = factory

window = UIWindow(frame: UIScreen.main.bounds)
factory.startReactNative(
  withModuleName: "appName",
  in: window,
  launchOptions: launchOptions
)

return true

}

// MARK: - PushKit Delegates func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) { RNVoipPushNotificationManager.didUpdate(credentials, forType: type.rawValue) }

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { guard let stream = payload.dictionaryPayload["stream"] as? [String: Any], let createdCallerName = stream["created_by_display_name"] as? String else { completion() return }

let uuid = UUID().uuidString
let videoIncluded = stream["video"] as? String
let hasVideo = videoIncluded != "false"

RNVoipPushNotificationManager.addCompletionHandler(uuid, completionHandler: completion)
RNVoipPushNotificationManager.didReceiveIncomingPush(with: payload, forType: type.rawValue)

// 🔹 CallKeep report
RNCallKeep.reportNewIncomingCall(
  uuid,
  handle: createdCallerName,
  handleType: "generic",
  hasVideo: hasVideo,
  localizedCallerName: createdCallerName,
  supportsHolding: false,
  supportsDTMF: false,
  supportsGrouping: false,
  supportsUngrouping: false,
  fromPushKit: true,
  payload: stream,
  withCompletionHandler: nil
)

}

// MARK: - Notifications func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.sound, .alert, .badge]) } } 4. Important: Do not import CallKeep directly in AppDelegate.swift.

Here you go.....

Okay, Thanks. It's working.