logo
On this page

PK battles

Prerequisites

Warning

Before you begin, make sure you complete the following:

  • Follow the integration steps by referring to the Quick start with co-hosting.

  • Please contact technical support to activate the Stream Mixing service.

What’s PK battles?

PK battle is a friendly competition between two live streams where audiences can see the engaged interactions between both hosts.

Quick start

Hosts can send a PK battle request to the host they want to connect with after they started their own live streams. And both live streams will be connected upon the PK battle request being accepted.

Start the PK battle

To start a PK battle, you will need to call the ZegoLiveStreamingManager.shared.sendPKBattleRequest(anotherHostUserID); method to send a PK battle request first. Once the host accepts your request, the PK battle starts.

Untitled
ZegoLiveStreamingManager.shared.sendPKBattleRequest(anotherHostUserID: userID, callback: { errorCode, requestID in
    if errorCode != 0 {
        sender.setTitle("PK", for: .normal)
    }
})
1
Copied!

Stop the PK battle

To stop the PK battle, call the ZegoLiveStreamingManager.shared.stopPKBattle() method.

Listen to PK battle Events

To listen to PK battle events,call the ZegoLiveStreamingManager.shared.addLiveManagerDelegate(...) method.In general, when you receive a PK request, you can display a pop-up window for the user to choose whether to accept or reject.if accept,call ZegoLiveStreamingManager.shared.acceptIncomingPKBattleRequest(),if reject, call ZegoLiveStreamingManager.getInstance().rejectPKBattleStartRequest(),You can also customize your own business logic to handle the corresponding PK event.

Untitled
func onIncomingPKRequestReceived(requestID: String, anotherHostUser: ZegoUIKitUser, anotherHostLiveID: String, customData: String?) {
    let alterView = UIAlertController(title: "receive pk request", message: "", preferredStyle: .alert)
    self.pkAlterView = alterView
    let acceptButton: UIAlertAction = UIAlertAction(title: "accept", style: .default) { [weak self] action in
        self?.liveStreamingVC?.liveManager.acceptIncomingPKBattleRequest(requestID, anotherHostLiveID: anotherHostLiveID, anotherHostUser: anotherHostUser)
    }
    let rejectButton: UIAlertAction = UIAlertAction(title: "reject", style: .cancel) { [weak self] action in
        self?.liveStreamingVC?.liveManager.rejectPKBattleStartRequest(requestID)
    }
    alterView.addAction(acceptButton)
    alterView.addAction(rejectButton)
    liveStreamingVC?.present(alterView, animated: true)
}
1
Copied!

Demo source code

For a detailed demo source code, click here.

To customize your own PK battle logic and process as needed, the ZegoLiveStreamingManager contains a bunch of methods for you to do further customizations. Before you make your customization, check the method intro part first.

Method intro

Send a PK battle request

Untitled
public func sendPKBattleRequest(anotherHostUserID: String, timeout: UInt32 = 10, customData: String, callback: UserRequestCallback?)
1
Copied!
  • anotherHostUserID: to send a PK battle request, you will need to specify the user ID of the host you want to connect with. Remember the host you invite must has started a live stream, otherwise, an error will return via the method you called. For the error info and cause, check it in the callback.

  • timeout: this can be used to set the timeout duration of the PK battle request you sent. After it timed out, the host who sent the request will receive a callback notification via the onOutgoingPKRequestTimeout.

  • customData: this can be used to customize the info that you want the host you invited to receive, and the invited host will receive the info you set via onIncomingPKRequestReceived.

Sample code:

Untitled
ZegoLiveStreamingManager.shared.sendPKBattleRequest(anotherHostUserID: userID, callback: { errorCode, requestID in
    if errorCode != 0 {
        sender.setTitle("PK", for: .normal)
    }
})
1
Copied!

Cancel the PK battle request

Untitled
public func cancelPKBattleRequest(customData: String?, callback: UserRequestCallback?)
1
Copied!

The PK battle request can be canceled by calling this method when the request is not timed out and the request didn't get any responses. After it has been canceled, the invited host will receive a callback notification via the onIncomingPKRequestCancelled.

Sample code:

Untitled
ZegoLiveStreamingManager.shared.cancelPKBattleRequest(customData: nil) { errorCode, requestID in
    sender.setTitle("PK", for: .normal)
}
1
Copied!

Respond to the PK battle request

To receive a PK battle request, you can listen to and set up the onIncomingPKRequestReceived.

Accept the PK battle request

To accept the PK battle request, call the acceptIncomingPKBattleRequest method. And the peer host will receive notification via the onOutgoingPKRequestAccepted, more details can be checked in PK request is accepted.

Untitled
ZegoLiveStreamingManager.shared.acceptIncomingPKBattleRequest(requestID, anotherHostLiveID: anotherHostLiveID, anotherHostUser: anotherHostUser)
1
Copied!

Reject the PK battle request

To reject the PK battle request, call the rejectPKBattleStartRequest method. And the peer host will receive notification via the onOutgoingPKRequestRejected, and can tell why the request was rejected through the rejectCode. More details can be checked in PK request is rejected.

Untitled
ZegoLiveStreamingManager.shared.rejectPKBattleStartRequest(requestID)
1
Copied!

Sample code for responding to the PK request

Untitled
func onIncomingPKRequestReceived(requestID: String, anotherHostUser: ZegoUIKitUser, anotherHostLiveID: String, customData: String?) {
    let alterView = UIAlertController(title: "receive pk request", message: "", preferredStyle: .alert)
    self.pkAlterView = alterView
    let acceptButton: UIAlertAction = UIAlertAction(title: "accept", style: .default) { [weak self] action in
        self?.liveStreamingVC?.liveManager.acceptIncomingPKBattleRequest(requestID, anotherHostLiveID: anotherHostLiveID, anotherHostUser: anotherHostUser)
    }
    let rejectButton: UIAlertAction = UIAlertAction(title: "reject", style: .cancel) { [weak self] action in
        self?.liveStreamingVC?.liveManager.rejectPKBattleStartRequest(requestID)
    }
    alterView.addAction(acceptButton)
    alterView.addAction(rejectButton)
    liveStreamingVC?.present(alterView, animated: true)
}
1
Copied!

Listen to the sent PK battle request

PK battle request is accepted

When the sent PK battle request is accepted, you can receive callback notifications or customize your business logic by listening to or setting up the onOutgoingPKRequestAccepted.

Untitled
func onOutgoingPKRequestAccepted() {
    //...
}
1
Copied!

PK battle request is rejected

When the sent PK battle request is rejected, You can receive callback notifications or customize your business logic by listening to or setting up the onOutgoingPKRequestRejected.

Note

The PK battle request will be rejected automatically when the invited host is in a busy state. Busy state: the host has not initiated his live stream yet, the host is in a PK battle with others, the host is being invited, and the host is sending a PK battle request to others.

Untitled
func onOutgoingPKRequestRejected(reason: Int, anotherHostUser: ZegoUIKitUser) {
    if reason == ZegoLiveStreamingPKBattleRejectCode.host_reject.rawValue {
        uikitLiveVC?.view.makeToast("another host busy",position: .center)
    } else {
        uikitLiveVC?.view.makeToast("pk is rejected",position: .center)
    }
    pkButton?.setTitle("PK", for: .normal)
}
1
Copied!

Among which, the ZegoLiveStreamingPKBattleRejectCode can use to declare why the invited host rejected your request, the definition is as follows:

Untitled
@objc public enum ZegoLiveStreamingPKBattleRejectCode: Int {
    case host_reject
    case use_not_host
    case in_pk
    case live_not_started
    case already_send
    case already_received
}
1
Copied!

PK battle request is time out

If the invited host didn't respond after the timeout duration, the PK battle request timed out by default. While the Live Streaming Kit updates the internal state while won't trigger any default behaviors. You can receive callback notifications or customize your business logic by listening to or setting up the ZegoLiveStreamingListener.onOutgoingPKBattleRequestTimeout.

Sample code:

Untitled
func onOutgoingPKRequestTimeout(requestID: String, anotherHost: ZegoUIKitUser) {
    pkButton?.setTitle("PK", for: .normal)
    uikitLiveVC?.view.makeToast("send pk timeout", position: .center)
}
1
Copied!

PK battle request failed to be sent

In some cases, PK battle requests can't be sent successfully, for example, the host's app is not started. And the sendPKBattleRequest returns an error when the PK battle request failed to be sent, you can tell and handle these errors by the value returned by the sendPKBattleRequest.

Sample code:

Untitled
ZegoLiveStreamingManager.shared.sendPKBattleRequest(anotherHostUserID: userID, callback: { errorCode, requestID in
    if errorCode != 0 {
        self?.uikitLiveVC?.view.makeToast("send pkBattle fail:\(errorCode)",duration: 1.0, position: .center)
        //...update UI
        sender.setTitle("PK", for: .normal)
    }
})
1
Copied!

Listen to received PK battle request

Receives a PK battle request

When receiving a PK battle request, You can receive callback notifications or customize your business logic by listening to or setting up the onIncomingPKRequestReceived.

Sample code:

Untitled
func onIncomingPKRequestReceived(requestID: String, anotherHostUser: ZegoUIKitUser, anotherHostLiveID: String, customData: String?) {
    let alterView = UIAlertController(title: "receive pk request", message: "", preferredStyle: .alert)
    self.pkAlterView = alterView
    let acceptButton: UIAlertAction = UIAlertAction(title: "accept", style: .default) { [weak self] action in
        //...
    }
    let rejectButton: UIAlertAction = UIAlertAction(title: "reject", style: .cancel) { [weak self] action in
        //...
    }
    alterView.addAction(acceptButton)
    alterView.addAction(rejectButton)
    liveStreamingVC?.present(alterView, animated: true)
}
1
Copied!

Received PK battle request has been canceled

You can receive callback notifications or customize your business logic by listening to or setting up the onIncomingPKBattleRequestCancelled when the PK battle request has been canceled.

Untitled
func onIncomingPKRequestCancelled(anotherHostLiveID: String, anotherHostUser: ZegoUIKitUser, customData: String?) {
    self.pkAlterView?.dismiss(animated: true)
}
1
Copied!

Received PK battle request is timed out

You can receive callback notifications or customize your business logic by listening to or setting up the onIncomingPKRequestTimeout when the received PK battle request has timed out.

Sample code:

Untitled
func onIncomingPKRequestTimeout(requestID: String, anotherHostUser: ZegoUIKitUser) {
    self.pkAlterView?.dismiss(animated: true)
}
1
Copied!

Mute the peer host during the PK battle

When a PK battle starts, both hosts can mute the connected host during the battle as needed. And after the peer host has been muted, the audience can't hear the voice from the muted livestream.

Untitled
public func muteAnotherHostAudio(_ mute: Bool, callback: ZegoUIKitCallBack?) 
1
Copied!

And you can also get the mute state of the peer host via the ZegoLiveStreamingManager.shared.isAnotherHostMuted.

Sample code:

Untitled
@objc func muteButtonClick(_ sender: UIButton) {
    let pkUserMuted: Bool = ZegoLiveStreamingManager.shared.isAnotherHostMuted
    ZegoLiveStreamingManager.shared.muteAnotherHostAudio(!pkUserMuted, callback: nil)
}
1
Copied!

Custom prebuilt UI

Customizable methods unique to the PK battle feature

In addition to the above-mentioned methods used for customization, the ZegoUIKitPrebuiltLiveStreamingVCDelegate in ZegoUIKitPrebuiltLiveStreamingVC is provided for customizing some UI and features that are unique to the PK battle feature.

Untitled
@objc public protocol ZegoUIKitPrebuiltLiveStreamingVCDelegate: AnyObject {
    //...
    @objc optional func getPKBattleForegroundView(_ parentView: UIView, userInfo: ZegoUIKitUser) -> UIView?
    @objc optional func getPKBattleTopView(_ parentView: UIView, userList: [ZegoUIKitUser]) -> UIView?
    @objc optional func getPKBattleBottomView(_ parentView: UIView, userList: [ZegoUIKitUser]) -> UIView?
}
1
Copied!

To be specific, if you want to place custom views above, below, and on top of PKView, you can check the following sample code as a reference:

Untitled
class ViewController: UIViewController {

    var userID: String?
    var userName: String?

    override func viewDidLoad() {
        super.viewDidLoad()
        //... 
    }

    @IBAction func startLive(_ sender: Any) {
        let config: ZegoUIKitPrebuiltLiveStreamingConfig = ZegoUIKitPrebuiltLiveStreamingConfig.host(enableCoHosting: true)
        let liveVC: ZegoUIKitPrebuiltLiveStreamingVC = ZegoUIKitPrebuiltLiveStreamingVC(self.appID, appSign: self.appSign, userID: self.userID ?? "", userName: self.userName ?? "", liveID: self.roomIDTextField.text ?? "", config: config)
        liveVC.modalPresentationStyle = .fullScreen
        liveVC.delegate = self // set listener
        //...
        self.present(liveVC, animated: true, completion: nil)
    }

    @IBAction func watchLive(_ sender: Any) {
        let config: ZegoUIKitPrebuiltLiveStreamingConfig = ZegoUIKitPrebuiltLiveStreamingConfig.audience(enableCoHosting: true)
        let liveVC: ZegoUIKitPrebuiltLiveStreamingVC = ZegoUIKitPrebuiltLiveStreamingVC(self.appID, appSign: self.appSign, userID: self.userID ?? "", userName: self.userName ?? "", liveID: self.roomIDTextField.text ?? "", config: config)
        liveVC.modalPresentationStyle = .fullScreen
        liveVC.delegate = self // set listener
        //...
        self.present(liveVC, animated: true, completion: nil)
    }
}

extension ViewController: ZegoUIKitPrebuiltLiveStreamingVCDelegate {

    func getPKBattleForegroundView(_ parentView: UIView, userInfo: ZegoUIKitUser) -> UIView? {
        let view = UIView()
        let button: UIButton = UIButton()
        button.frame = CGRect(x: 30, y: 30, width: 80, height: 40)
        view.addSubview(button)
        return view
    }
    
    func getPKBattleTopView(_ parentView: UIView, userList: [ZegoUIKitUser]) -> UIView? {
        let view = UIView()
        view.backgroundColor = UIColor.red
        return view
    }
    
    func getPKBattleBottomView(_ parentView: UIView, userList: [ZegoUIKitUser]) -> UIView? {
        let view = UIView()
        view.backgroundColor = UIColor.blue
        return view
    }
}


1
Copied!

The effect will be like this:

Demo source code

Previous

Quick start (with cohosting)

Next

Send virtual gifts