SwiftUI and Zoom SDK

@Michael_Condon

SwiftUI has a unique entry point compared to the traditional AppDelegate which occurs in swift and objective c.

SwiftUI uses @main like below

@main
struct MyNewApp: App {
    var body: some Scene {
        WindowGroup {
                AppView()
        }
    }
}

In SwiftUI we can get access to the AppDelegate by creating a new class, creating a Window variable and setting up the ZoomSDK here

class AppDelegate: NSObject, UIApplicationDelegate {
    static let Shared = AppDelegate()
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        setupSDK(sdkKey: zoomSdkKey, sdkSecret: zoomSdkSecret)
        print("didFinishLaunchingWithOptions")

        return true
    }
        func applicationWillEnterForeground(_ application: UIApplication) {
        print("applicationWillEnterForeground")
    }
    func applicationDidBecomeActive(_ application: UIApplication) {
        print("applicationDidBecomeActive")
    }
    func setupSDK(sdkKey: String, sdkSecret: String) {
        let context = MobileRTCSDKInitContext()
        context.domain = "zoom.us"
        context.enableLog = true
        
        let sdkInitializedSuccessfully = MobileRTC.shared().initialize(context)
        
        if sdkInitializedSuccessfully == true, let authorizationService = MobileRTC.shared().getAuthService() {
            authorizationService.clientKey = sdkKey
            authorizationService.clientSecret = sdkSecret
            authorizationService.delegate = self
            authorizationService.sdkAuth()
        }
    }
}

To assign the AuthorizationServiceDelege to self we have to make an extension to our custom AppDelegate

extension AppDelegate: MobileRTCAuthDelegate {

    // Result of calling sdkAuth(). MobileRTCAuthError_Success represents a successful authorization.
    func onMobileRTCAuthReturn(_ returnValue: MobileRTCAuthError) {
        switch returnValue {
        case .success:
            print("SDK successfully initialized.")
        case .keyOrSecretEmpty:
            assertionFailure("SDK Key/Secret was not provided. Replace sdkKey and sdkSecret at the top of this file with your SDK Key/Secret.")
        case .keyOrSecretWrong, .unknown:
            assertionFailure("SDK Key/Secret is not valid.")
        default:
            assertionFailure("SDK Authorization failed with MobileRTCAuthError: \(returnValue).")
        }
    }

    // Result of calling logIn(). 0 represents a successful login attempt.
    func onMobileRTCLoginReturn(_ returnValue: Int) {
        switch returnValue {
        case 0:
            print("Successfully logged in")
            // This alerts the ViewController that login was successful.            NotificationCenter.default.post(name: Notification.Name("userLoggedIn"), object: nil)
        case 1002:
            print("Password incorrect")
        default:
            print("Could not log in. Error code: \(returnValue)")
        }
    }

    // Result of calling logoutRTC(). 0 represents a successful log out attempt.
    func onMobileRTCLogoutReturn(_ returnValue: Int) {
        switch returnValue {
        case 0:
            print("Successfully logged out")
        default:
            print("Could not log out. Error code: \(returnValue)")
        }
    }
}

Then back at your entry point add a UIApplicationDelegateAdaptor

@main
struct m_CareApp: App {  
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
   ...
}

Then for example, in the SwiftUI view in which you want to show the conference call you can do something like this. With getMainWindowToShareWithAppDelegate being the key functions.

struct WelcomeToZoom: View {
    
    @State var startInstandMeeting: Bool = false
    @State var loginToMeeting: Bool = false
    @State var meetingNumber: String = ""
    @State var meetingPassword: String = ""
    
    var body: some View {
        VStack(alignment: .center, spacing: 10){
            Spacer()
            HStack{
                Spacer()
                TextField("Enter Meeting Number", text: $meetingNumber)
                Spacer()
            }.padding()
            HStack{
                Spacer()
                TextField("Enter Meeting Passcode", text: $meetingPassword)
                Spacer()
            }.padding()
            Button(action: {
                if meetingPassword.count > 0{
                    self.startInstandMeeting.toggle()
                }
            }) {
                Label("Start Meeting", systemImage: "photo")
            }
            .padding()
            
            if self.startInstandMeeting{
                StartZoomVC(delegate: StartZoomVC.Delegate(), zoomMeetingNumber: $meetingNumber, zoomPasscode: $meetingPassword)
            }
            Spacer()
        }
    }
}

struct StartZoomVC: UIViewControllerRepresentable {  
    
    @Binding var meetingNumber: String
    @Binding var passCode: String
    
    private let delegate: MobileRTCMeetingServiceDelegate
    //private let authDelegate: MobileRTCAuthDelegate
    
    init(delegate: MobileRTCMeetingServiceDelegate, zoomMeetingNumber: Binding<String>, zoomPasscode: Binding<String>) {
        self._meetingNumber = zoomMeetingNumber
        self._passCode = zoomPasscode
        
        self.delegate = delegate
    }
    
    func makeUIViewController(context: Context) -> UIViewController {
        print("making the view controller")
        let newVC = UIViewController()
        newVC.view.tag = 83838383
        newVC.view.backgroundColor = .red
        return newVC
    }
    
    func updateUIViewController(_ taskViewController: UIViewController, context: Context) {
        askPermissionsForCameraFeed()
        print("check video permissions")
      
    }
    
    func askPermissionsForCameraFeed() {
        AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in
            if response {
                //access granted
                getMainWindowToShareWithAppDelegate()
            } else {
                print("wtf")
            }
        }
    }
    
    func getMainWindowToShareWithAppDelegate(){
        
        DispatchQueue.main.async {
            let scene = UIApplication.shared.connectedScenes.first
            let windowSceneDelegate = scene?.delegate as? UIWindowSceneDelegate
            let window = (windowSceneDelegate?.window)!
            UIApplication.shared.delegate = AppDelegate.Shared
            let delegate = UIApplication.shared.delegate as! AppDelegate
            delegate.window = window
            print("finding window to provide to zoom sdk")
            joinMeeting(meetingNumber: meetingNumber, meetingPassword: passCode)
        }
        
        
    }
    func startMeeting() {
        // 5. Obtain the MobileRTCMeetingService from the Zoom SDK, this service can start meetings, join meetings, leave meetings, etc.
        if let meetingService = MobileRTC.shared().getMeetingService() {
            
            //6. Set the ViewContoller to be the MobileRTCMeetingServiceDelegate
            meetingService.delegate = delegate

            /*** 5. Create a MobileRTCMeetingStartParam to provide the MobileRTCMeetingService with the necessary info to start an instant meeting. In this case we will use MobileRTCMeetingStartParam4LoginlUser(), since the user has logged into Zoom. ***/
            let startMeetingParameters = MobileRTCMeetingStartParam4LoginlUser()

            // 6. Call the startMeeting function in MobileRTCMeetingService. The Zoom SDK will handle the UI for you, unless told otherwise.
            meetingService.startMeeting(with: startMeetingParameters)
        }
    }
    
    func logIn(email: String, password: String) {
        // 2. Obtain the MobileRTCAuthService from the Zoom SDK, this service can log in a Zoom user, log out a Zoom user, authorize the Zoom SDK etc.
        if let authorizationService = MobileRTC.shared().getAuthService() {
             // 3. Call the login function in MobileRTCAuthService. This will attempt to log in the user.
            authorizationService.login(withEmail: email, password: password, rememberMe: false)
        }else {
            print("authorization service failed")
        }
    }
    
    func joinMeeting(meetingNumber: String, meetingPassword: String) {
            // Obtain the MobileRTCMeetingService from the Zoom SDK, this service can start meetings, join meetings, leave meetings, etc.
            if let meetingService = MobileRTC.shared().getMeetingService() {


                // Create a MobileRTCMeetingJoinParam to provide the MobileRTCMeetingService with the necessary info to join a meeting.
                // In this case, we will only need to provide a meeting number and password.
                // 2. Set the ViewContoller to be the MobileRTCMeetingServiceDelegate
                meetingService.delegate = delegate
                let joinMeetingParameters = MobileRTCMeetingJoinParam()
                joinMeetingParameters.meetingNumber = meetingNumber
                joinMeetingParameters.password = meetingPassword

                // Call the joinMeeting function in MobileRTCMeetingService. The Zoom SDK will handle the UI for you, unless told otherwise.
                // If the meeting number and meeting password are valid, the user will be put into the meeting. A waiting room UI will be presented or the meeting UI will be presented.
                DispatchQueue.main.async {
                    meetingService.joinMeeting(with: joinMeetingParameters)
                }
               
            }
        }

}


 extension StartZoomVC {
    class Delegate: NSObject, MobileRTCMeetingServiceDelegate {

        func onMeetingError(_ error: MobileRTCMeetError, message: String?) {
           
            switch error {
            case .passwordError:
                print("Could not join or start meeting because the meeting password was incorrect.")
            case .success:
                print("onMeetingError : MobileRTCMeetError_Success")
            default:
                print("Could not join or start meeting with MobileRTCMeetError: \(error) \(message ?? "")")
            }
        }
        func onMeetingEndedReason(_ reason: MobileRTCMeetingEndReason) {
            print("Join meeting end reason.")
        }
        // Is called when the user joins a meeting.
        func onJoinMeetingConfirmed() {
            print("Join meeting confirmed.")
        }
        // Is called upon meeting state changes.
        func onMeetingStateChange(_ state: MobileRTCMeetingState) {
           print("onMeetingStateChange")
            switch state {
            case .connecting:
                print("Connecting")
            case .disconnecting:
                print("disconnecting")
            case .ended:
                print("ended")
            case .failed:
                print("failed")
            case .idle:
                print("idle")
            case .inMeeting:
                print("in meeting")
            case .inWaitingRoom:
                print("in waiting room")
            case .joinBO:
                print("join bo")
            case .leaveBO:
                print("leave bo")
            case .locked:
                print("locked")
            case .reconnecting:
                print("reconnecting")
            case .unknow:
                print("unknown")
            case .unlocked:
                print("unlocked")
            case .waitingExternalSessionKey:
                print("waiting external session key")
            case .waitingForHost:
                print("waiting for host")
            case .webinarDePromote:
                print("de promote")
            case .webinarPromote:
                print("promote")
            default:
                print("bad things happened")
            }
        }
     }
 }
4 Likes