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:
- Two user join a session, one is an iOS and the other doesn’t matter
- The other person, not the iPhone user, leaves the session.
- I got a crash.
- 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: "")
}
}