react-native-webrtc / react-native-webrtc/react-native-callkeep

Library not working with Expo SDK 54

Open
#869 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
1.1k
Forks
511
Avg merge
9h 12m
Merged PRs (30d)
2

Description

Hello. We have been constantly trying to work around this by using everything listed on here, but it is not working for some reason.
The app was initially built in Expo SDK 54 and has since been ejected. We have tried keeping the imports of RNCallkeep in swift file and that didn't work, as has been the case with everyone - "RNCallKeep not found" error. Also tried listing it in the Bridging-Header file and that's when these two errors arose-

1.) Failed to emit precompiled header '/Users/mc559/Library/Developer/Xcode/DerivedData/[AppName]-gthpjwqbmzgdepbcdzmuhlqwpyuk/Build/Intermediates.noindex/PrecompiledHeaders/[AppName]-Bridging-Header-swift_1LS8Z4CYI0BYD-clang_1MEDN0KIR5C77.pch' for bridging header '/Users/mc559/Desktop/[AppName]/ios/[AppName]/[AppName]-Bridging-Header.h'

2.) RNCallKeep - RNCallKeep.h:15:9 'React/RCTEventEmitter.h' file not found

Here is the package.json:

{ "main": "index.js", "version": "1.0.0", "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", "android": "expo run:android", "ios": "expo run:ios", "web": "expo start --web", "lint": "expo lint" }, "dependencies": { "@expo/vector-icons": "^15.0.3", "@gorhom/bottom-sheet": "^5.2.8", "@notifee/react-native": "^9.1.8", "@react-native-async-storage/async-storage": "^2.2.0", "@react-native-camera-roll/camera-roll": "^7.10.2", "@react-native-community/cli": "^20.0.2", "@react-native-community/datetimepicker": "^8.5.1", "@react-native-community/geolocation": "^3.4.0", "@react-native-community/netinfo": "^11.4.1", "@react-native-menu/menu": "^2.0.0", "@react-native-picker/picker": "^2.11.4", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/material-top-tabs": "^7.4.10", "@react-navigation/native": "^7.1.8", "@react-navigation/native-stack": "^7.8.6", "@reduxjs/toolkit": "^2.11.2", "axios": "^1.13.2", "core-js": "^3.47.0", "dayjs": "^1.11.19", "event-target-shim": "^6.0.2", "expo": "~54.0.29", "expo-asset": "~12.0.11", "expo-audio": "~1.1.0", "expo-clipboard": "~8.0.8", "expo-constants": "~18.0.12", "expo-contacts": "~15.0.11", "expo-crypto": "~15.0.8", "expo-dev-client": "~6.0.20", "expo-device": "~8.0.10", "expo-document-picker": "~14.0.8", "expo-file-system": "~19.0.21", "expo-font": "~14.0.10", "expo-haptics": "~15.0.8", "expo-image": "~3.0.11", "expo-image-picker": "~17.0.10", "expo-linear-gradient": "~15.0.8", "expo-linking": "~8.0.10", "expo-notifications": "~0.32.15", "expo-router": "~6.0.19", "expo-screen-capture": "~8.0.9", "expo-splash-screen": "~31.0.12", "expo-status-bar": "~3.0.9", "expo-symbols": "~1.0.8", "expo-system-ui": "~6.0.9", "expo-video": "~3.0.15", "expo-web-browser": "~15.0.10", "jwt-decode": "^4.0.0", "mediasoup-client": "^3.18.3", "moment": "^2.30.1", "react": "19.1.0", "react-dom": "19.1.0", "react-native": "0.81.5", "react-native-appearance": "^0.3.4", "react-native-callkeep": "^4.3.16", "react-native-chart-kit": "^6.12.0", "react-native-circular-progress-indicator": "^4.4.2", "react-native-country-codes-picker": "^2.3.5", "react-native-device-info": "^15.0.1", "react-native-file-viewer": "^2.1.5", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.28.0", "react-native-get-random-values": "^2.0.0", "react-native-gifted-chat": "^3.2.2", "react-native-haptic-feedback": "^2.3.3", "react-native-image-modal": "^3.0.13", "react-native-incall-manager": "^4.2.1", "react-native-ios-context-menu": "^3.2.1", "react-native-ios-utilities": "^5.2.0", "react-native-keyboard-controller": "^1.20.2", "react-native-keychain": "^10.0.0", "react-native-pager-view": "^7.0.2", "react-native-paper": "^5.14.5", "react-native-permissions": "^5.4.4", "react-native-picker-select": "^9.3.1", "react-native-qr-svg": "^1.5.0", "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", "react-native-share": "^12.2.1", "react-native-simple-biometrics": "^3.0.2", "react-native-svg": "^15.15.1", "react-native-tab-view": "^4.2.1", "react-native-uuid": "^2.0.3", "react-native-video": "^6.18.0", "react-native-virtualized-view": "^1.0.0", "react-native-voip-push-notification": "^3.3.3", "react-native-web": "~0.21.0", "react-native-webrtc": "^124.0.7", "react-native-webview": "^13.16.0", "react-native-worklets": "0.5.1", "react-redux": "^9.2.0", "redux-persist": "^6.0.0", "rn-emoji-keyboard": "^1.7.0", "socket.io-client": "^4.8.1", "zeego": "^3.0.6" }, "devDependencies": { "@types/react": "~19.1.0", "eslint": "^9.25.0", "eslint-config-expo": "~10.0.0", "typescript": "~5.9.2" }, "private": true }

And here is AppDelegate.swift:

`import Expo
import React
import ReactAppDependencyProvider
import PushKit
import WebRTC

@UIApplicationMain
public class AppDelegate: ExpoAppDelegate, PKPushRegistryDelegate, CXProviderDelegate {
var window: UIWindow?

var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
var reactNativeFactory: RCTReactNativeFactory?

public override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {

let localizedAppName = Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"] as? String
let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as! String
RNCallKeep.setup([
"appName": localizedAppName ?? appName,
"supportsVideo": true,
"includesCallsInRecents": false,
])
RNVoipPushNotificationManager.voipRegistration()

let options = WebRTCModuleOptions.sharedInstance()
options.enableMultitaskingCameraAccess = true

let delegate = ReactNativeDelegate()
let factory = ExpoReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()

reactNativeDelegate = delegate
reactNativeFactory = factory
bindReactNativeFactory(factory)

#if os(iOS) || os(tvOS)
window = UIWindow(frame: UIScreen.main.bounds)
factory.startReactNative(
withModuleName: "main",
in: window,
launchOptions: launchOptions)
#endif

return super.application(application, didFinishLaunchingWithOptions: launchOptions)

}

// Linking API
public override func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
}

// Universal Links
public override func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: https://github.com/escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
}

// handle updated push credentials
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
RTCAudioSession.sharedInstance().audioSessionDidActivate(AVAudioSession.sharedInstance())
}

func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
RTCAudioSession.sharedInstance().audioSessionDidDeactivate(AVAudioSession.sharedInstance())
}

func provider(_ provider: CXProvider, performEndCall action: CXEndCallAction) {
print("🔴 System call ended: (action.callUUID.uuidString)")
RTCAudioSession.sharedInstance().audioSessionDidDeactivate(AVAudioSession.sharedInstance())
action.fulfill()
}

// handle updated push credentials
func pushRegistry(
_ registry: PKPushRegistry,
didUpdate credentials: PKPushCredentials,
for type: PKPushType
) {
RNVoipPushNotificationManager.didUpdate(credentials, forType: type.rawValue)
}

// handle incoming pushes
func pushRegistry(
_ registry: PKPushRegistry,
didReceiveIncomingPushWith payload: PKPushPayload,
for type: PKPushType,
completion: https://github.com/escaping () -> Void
) {

// process the payload
let uuid = payload.dictionaryPayload["uuid"]
let hasVideoValue = payload.dictionaryPayload["hasVideo"]
let handle = payload.dictionaryPayload["handle"]
let caller = payload.dictionaryPayload["callerName"]

RNVoipPushNotificationManager.addCompletionHandler(uuid, completionHandler: completion)

RNVoipPushNotificationManager.didReceiveIncomingPush(
with: payload,
forType: type.rawValue
)

// display the incoming call notification
RNCallKeep.reportNewIncomingCall(
uuid,
handle: handle,
handleType: "generic",
hasVideo: hasVideo,
localizedCallerName: caller,
supportsHolding: true,
supportsDTMF: true,
supportsGrouping: true,
supportsUngrouping: true,
fromPushKit: true,
payload: nil,
withCompletionHandler: completion
)

}
}

class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
// Extension point for config-plugins

override func sourceURL(for bridge: RCTBridge) -> URL? {
// needed to return the correct URL for expo-dev-client.
bridge.bundleURL ?? bundleURL()
}

override func bundleURL() -> URL? {
#if DEBUG
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
#else
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
}
}`

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with AppDelegate.swift, the [AppName]-Bridging-Header.h file, and RNCallKeep.h, then reproduce the iOS build using the Expo SDK 54 project and its expo run:ios script. Done means RNCallKeep is found and the bridging-header errors, including the missing React/RCTEventEmitter.h file, no longer occur.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react, swift
Domain
mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.