When the user participating in the session leaves, the app shuts down

Description
When the user participating in the session leaves, the app shuts down.

Which iOS Client SDK version?
Video SDK, 1.0.3

To Reproduce(If applicable)
Steps to reproduce the behavior:

  1. Two user join a session, one is an iOS and the other doesn’t matter
  2. The other person, not the iPhone user, leaves the session.
  3. I got a crash.
  4. Also, the same crash occurred when I deleted the delegate.

Smartphone (please complete the following information):

  • Device: iPhone Xs Max
  • OS: 14.4.2

Screenshots

Additional context

the source

import UIKit
import ZoomInstantSDK
import AVFoundation
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    
    @IBOutlet weak var joinSessionButton: UIButton!
    @IBOutlet weak var leaveSessionButton: UIButton!
    
    @IBOutlet weak var selfView: UIView!
    @IBOutlet weak var userView: UIView!

    private let userName = "iOS_test_user"
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print(JwtUtils.createToken())
        print("orientation : \(UIDevice.current.orientation.rawValue)")

        
        do{
            ZoomInstantSDK.shareInstance()?.delegate = self
        }catch{
            print(error)
        }
        joinSessionButton.rx.tap
            .subscribe(onNext: { [weak self] in
                self?.createSession()
            }, onError: { error in

            })
        
        leaveSessionButton.rx.tap
            .subscribe(onNext: { [weak self] in
                self?.leaveSession()
            }, onError: { error in

            })
        
    }

    func createSession(){
        let sessionContext = ZoomInstantSDKSessionContext()
        // Ensure that you do not hard code JWT or any other confidential credentials in your production app.
        sessionContext.token = JwtUtils.createToken()
        sessionContext.sessionName = "testsession"
        sessionContext.userName = userName
        sessionContext.sessionPassword = "testpassword"

        if let session = ZoomInstantSDK.shareInstance()?.joinSession(sessionContext) {
            // Session joined successfully.
            
        }
    }
    
    func leaveSession(){
        ZoomInstantSDK.shareInstance()?.leaveSession(true)
        print("leave")
    }
    
}


extension ViewController : ZoomInstantSDKDelegate{
    func onError(_ errorType: ZoomInstantSDKERROR, detail details: Int) {
        print("Zoom Event : onError : \(errorType) \(details)" )
    }
    func onSessionJoin() {
        print("Zoom Event : onSessionJoin")
    }
    
    func onSessionLeave() {
        print("Zoom Event : onSessionLeave")
        
    }
    
    func onUserJoin(_ helper: ZoomInstantSDKUserHelper!, users userArray: [ZoomInstantSDKUser]!) {
        print("Zoom Event : onUserJoin")
        userArray.forEach{ print("onUserJoin -> User : \($0)")}
        if !userArray.isEmpty {
            let canvas = userArray[0].getVideoCanvas()
            
            let result = canvas?.subscribe(with: userView, andAspectMode: .full_Filled)
            print("user result user -> \(result?.rawValue)")
        } else {
            print("user is empty")
        }
    }
    
    func onUserLeave(_ helper: ZoomInstantSDKUserHelper!, users userArray: [ZoomInstantSDKUser]!) {
        print("Zoom Event : onUserLeave")
        if userArray.isEmpty {
            userArray.forEach{ print("onUserLeave -> User : \($0)")}
        }
    }
    
    func onUserAudioStatusChanged(_ helper: ZoomInstantSDKAudioHelper!, user userArray: [ZoomInstantSDKUser]!) {
        print("Zoom Event : onUserAudioStatusChanged")
        userArray.forEach{ print("onUserAudioStatusChanged -> User : \($0)")}
    }
    
    func onUserVideoStatusChanged(_ helper: ZoomInstantSDKVideoHelper!, user userArray: [ZoomInstantSDKUser]!) {
        print("Zoom Event : onUserVideoStatusChanged")
        userArray.forEach{ print("onUserVideoStatusChanged -> User : \($0.getName())")}
        do{
            let userSelf = userArray.first(where:{ [weak self] user in
                print("userSelf \(user.getName()) \(self?.userName)")
                return user.getName() == self?.userName
            })
            print("userSelf \(userSelf)")
            if let userSelf = userSelf{
                let result = userSelf.getVideoCanvas().subscribe(with: self.selfView, andAspectMode: .panAndScan)
                print("self result 유저 -> \(result.rawValue)")
            } else{
                print("Self User가 비었음")
            }
        }catch{
            print("user self error : \(error)")
        }
    }
    func onLiveStreamStatusChanged(_ helper: ZoomInstantSDKLiveStreamHelper!, status: ZoomInstantSDKLiveStreamStatus) {
        // Use helper to perform live stream actions.
        // Status is the new live stream status.
        print("Zoom Event : onLiveStreamStatusChanged")
        switch status {
        case .inProgress:
            print("Live stream now in progress.")
        case .ended:
            print("Live stream has ended.")
        default:
            print("Live stream status unknown.")
        }
    }
    
    
    public func onUserShareStatusChanged(_ helper: ZoomInstantSDKShareHelper!, user: ZoomInstantSDKUser!, status: ZoomInstantSDKReceiveSharingStatus) {
        print("Zoom Event : onUserShareStatusChanged")
    }

    public func onChatNewMessageNotify(_ helper: ZoomInstantSDKChatHelper!, message chatMessage: ZoomInstantSDKChatMessage!) {
        print("Zoom Event : onChatNewMessageNotify")
    }

    public func onUserHostChanged(_ helper: ZoomInstantSDKUserHelper!, users user: ZoomInstantSDKUser!) {
        print("Zoom Event : onUserHostChanged")
    }

    public func onUserManagerChanged(_ user: ZoomInstantSDKUser!) {
        print("Zoom Event : onUserManagerChanged")
    }

    public func onUserNameChanged(_ user: ZoomInstantSDKUser!) {
        print("Zoom Event : onUserNameChanged")
    }

    public func onUserActiveAudioChanged(_ helper: ZoomInstantSDKUserHelper!, users userArray: [ZoomInstantSDKUser]!) {
        print("Zoom Event : onUserActiveAudioChanged")
    }

    public func onSessionNeedPassword(_ completion: ((String?, Bool) -> ZoomInstantSDKERROR)!) {
        print("Zoom Event : onSessionNeedPassword")
    }

    public func onSessionPasswordWrong(_ completion: ((String?, Bool) -> ZoomInstantSDKERROR)!) {
        print("Zoom Event : onSessionPasswordWrong")
    }

    public func onMixedAudioRawDataReceived(_ rawData: ZoomInstantSDKAudioRawData!) {
        print("Zoom Event : onMixedAudioRawDataReceived")
    }

    public func onOneWayAudioRawDataReceived(_ rawData: ZoomInstantSDKAudioRawData!, user: ZoomInstantSDKUser!) {
        print("Zoom Event : onOneWayAudioRawDataReceived")
    }
}

//
//  AppDelegate.swift
//  doctorOn
//
//  Created by 이영준 on 2021/05/24.
//

import UIKit
import ZoomInstantSDK

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        let initParams = ZoomInstantSDKInitParams()
         initParams.domain = "zoom.us"
         initParams.appGroupId = "com.test"
         initParams.enableLog = true
         
         let sdkInitReturnStatus = ZoomInstantSDK.shareInstance()?.initialize(initParams)

         switch sdkInitReturnStatus {
             case .Errors_Success:
                 print("SDK initialized successfully")
             default:
                 if let error = sdkInitReturnStatus {
                    print("SDK failed to initialize: \(error.rawValue) \(error)")
             }
        }
        return true
    }

    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        return .portrait
    }
    func applicationWillResignActive(_ application: UIApplication) {
        ZoomInstantSDK.shareInstance()?.appWillResignActive()
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        ZoomInstantSDK.shareInstance()?.appDidEnterBackgroud()
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        ZoomInstantSDK.shareInstance()?.appDidBecomeActive()
    }

    func applicationWillTerminate(_ application: UIApplication) {
        ZoomInstantSDK.shareInstance()?.appWillTerminate()
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}

import Foundation
import CryptoKit

private let ZOOM_KEY = "KEY"
private let ZOOM_SECRET = "SECERET"

struct Header: Encodable {
    let alg = "HS256"
    let typ = "JWT"
}

struct Payload: Encodable {
    let appKey: String
    let version: Int
    let iat: Int
    let exp: Int
    let userIdentity: String
    let pwd : String
    let tpc: String

    init(userIdentity: String, tpc: String, appKey: String = ZOOM_KEY, version: Int = 1, iat: Int = Int(NSDate().timeIntervalSince1970), exp: Int = (Int(NSDate().timeIntervalSince1970) + (3600 * 4))) {
        self.appKey = appKey
        self.version = version
        self.iat = iat
        self.exp = exp
        self.pwd = "opusone"
        self.userIdentity = userIdentity
        self.tpc = tpc
    }
    
    private enum CodingKeys : String, CodingKey {
        case appKey = "app_key", userIdentity = "user_identity",iat,exp,tpc,version
    }
}

class JwtUtils{
    static func createToken() -> String {
        let privateKey = SymmetricKey(data: ZOOM_SECRET.data(using: .utf8)!)

        let headerJSONData = try! JSONEncoder().encode(Header())
        let headerBase64String = headerJSONData.urlSafeBase64EncodedString()
        let payloadJSONData = try! JSONEncoder().encode(Payload(userIdentity: "test_ios\(Int.random(in: 0...100))", tpc: "testsession"))
        let payloadBase64String = payloadJSONData.urlSafeBase64EncodedString()

        let toSign = (headerBase64String + "." + payloadBase64String).data(using: .utf8)!

        let signature = HMAC<SHA256>.authenticationCode(for: toSign, using: privateKey)
        let signatureBase64String = Data(signature).urlSafeBase64EncodedString()

        let token = [headerBase64String, payloadBase64String, signatureBase64String].joined(separator: ".")
        return token
    }
}
extension Data {
    func urlSafeBase64EncodedString() -> String {
        return base64EncodedString()
                .replacingOccurrences(of: "+", with: "-")
                .replacingOccurrences(of: "/", with: "_")
                .replacingOccurrences(of: "=", with: "")
    }
}

Hey @banziha104,

Thanks for using the dev forum, and thank you for providing so much info!

I have seen this issue in the past, and what was causing the issue for me was the fact that both users in the session had the same user identity. Are both of your users using the same user identity?

Thanks!
Michael

Hi @Michael_Condon !

When create token, both users do not use the same identity.

When that error occurred, I was testing video calls between iOS, Web and Android, each with a prefix such as “ios_”,“and_” and “web_” and suffixes created by random numbers, so the same identity cannot be created.

Hey @banziha104,

Hmm that is interesting, can you reproduce one more time then email me your SDK logs and Crash file to DeveloperSupport@zoom.us? Please provide a link to this post.

Thanks!
Michael

Hey @Michael_Condon

I sent the log file I requested, and I got an email saying that I received it well through macro reply. Please check the log file :slight_smile:

Your request (#11164662) has been received, and is being reviewed by our support staff.

Hey @banziha104,

Yep, I have received it :slight_smile:
Thanks for sending that over, will follow up there.

Michael

Thanks @Michael_Condon !

Can I get a notification when this bug is fixed?

Hey @banziha104,

Yep! I will notify you here and on the DeveloperSupport thread when I have updates.

Thanks!
Michael