Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2022 System Services

WWDC22 · 21 min · System Services

Enhance voice communication with Push to Talk

We’re coming in loud and clear to help you bring walkie-talkie communication to your app — over! Discover how you can add prominent system UI to your Push to Talk app, enabling rapid communication with the tap of a button. We’ll introduce you to the PushToTalk framework and show you how to configure your apps to transmit and receive audio — even from the background. To get the most out of this session, we recommend familiarity with handling audio transmission on your app backend. We also recommend a basic understanding of APNs.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 14 snippets

Creating a Channel Manager swift · at 6:52 ↗
func setupChannelManager() async throws {
    channelManager = try await PTChannelManager.channelManager(delegate: self,
                                                               restorationDelegate: self)
}
Joining a Channel swift · at 7:33 ↗
func joinChannel(channelUUID: UUID) {
    let channelImage = UIImage(named: "ChannelIcon")
    channelDescriptor = PTChannelDescriptor(name: "Awesome Crew", image: channelImage)
  
    // Ensure that your channel descriptor and UUID are persisted to disk for later use.
    channelManager.requestJoinChannel(channelUUID: channelUUID, 
                                      descriptor: channelDescriptor)
}
PTChannelManagerDelegate didJoinChannel swift · at 8:11 ↗
func channelManager(_ channelManager: PTChannelManager, 
                    didJoinChannel channelUUID: UUID,
                    reason: PTChannelJoinReason) {
    // Process joining the channel
    print("Joined channel with UUID: \(channelUUID)")
}

func channelManager(_ channelManager: PTChannelManager,
                    receivedEphemeralPushToken pushToken: Data) {
    // Send the variable length push token to the server
    print("Received push token")
}
PTChannelManagerDelegate failedToJoinChannel swift · at 8:45 ↗
func channelManager(_ channelManager: PTChannelManager, 
                    failedToJoinChannel channelUUID: UUID, 
                    error: Error) {
    let error = error as NSError

    switch error.code {
    case PTChannelError.channelLimitReached.rawValue:
        print("The user has already joined a channel")
    default:
        break
    }
}
PTChannelManagerDelegate didLeaveChannel swift · at 9:00 ↗
func channelManager(_ channelManager: PTChannelManager,
                    didLeaveChannel channelUUID: UUID,
                    reason: PTChannelLeaveReason) {
    // Process leaving the channel
    print("Left channel with UUID: \(channelUUID)")
}
PTChannelRestorationDelegate swift · at 9:22 ↗
func channelDescriptor(restoredChannelUUID channelUUID: UUID) -> PTChannelDescriptor {
    return getCachedChannelDescriptor(channelUUID)
}
Provide channel descriptor updates swift · at 10:12 ↗
func updateChannel(_ channelDescriptor: PTChannelDescriptor) async throws {
    try await channelManager.setChannelDescriptor(channelDescriptor, 
                                                  channelUUID: channelUUID)
}
Provide service status updates swift · at 10:20 ↗
func reportServiceIsReconnecting() async throws {
    try await channelManager.setServiceStatus(.connecting, channelUUID: channelUUID)
}

func reportServiceIsConnected() async throws {
    try await channelManager.setServiceStatus(.ready, channelUUID: channelUUID)
}
Start transmission from within your app swift · at 11:48 ↗
func startTransmitting() {
    channelManager.requestBeginTransmitting(channelUUID: channelUUID)
}

// PTChannelManagerDelegate

func channelManager(_ channelManager: PTChannelManager, 
                    failedToBeginTransmittingInChannel channelUUID: UUID,
                    error: Error) {
    let error = error as NSError

    switch error.code {
    case PTChannelError.callIsActive.rawValue:
        print("The system has another ongoing call that is preventing transmission.")
    default:
        break
    }
}
Stop transmission from within your app swift · at 12:22 ↗
func stopTransmitting() {
    channelManager.stopTransmitting(channelUUID: channelUUID)
}

func channelManager(_ channelManager: PTChannelManager, 
                    failedToStopTransmittingInChannel channelUUID: UUID, 
                    error: Error) {
    let error = error as NSError

    switch error.code {
    case PTChannelError.transmissionNotFound.rawValue:
        print("The user was not in a transmitting state")
    default:
        break
    }
}
Responding to begin transmission delegate events swift · at 12:41 ↗
func channelManager(_ channelManager: PTChannelManager,
                    channelUUID: UUID, 
                    didBeginTransmittingFrom source: PTChannelTransmitRequestSource) {
    print("Did begin transmission from: \(source)")
}

func channelManager(_ channelManager: PTChannelManager,
                    didActivate audioSession: AVAudioSession) {
    print("Did activate audio session")
    // Configure your audio session and begin recording
}
Responding to end transmission delegate events swift · at 13:19 ↗
func channelManager(_ channelManager: PTChannelManager,
                    channelUUID: UUID, 
                    didEndTransmittingFrom source: PTChannelTransmitRequestSource) {
    print("Did end transmission from: \(source)")
}

func channelManager(_ channelManager: PTChannelManager,
                    didDeactivate audioSession: AVAudioSession) {
    print("Did deactivate audio session")
    // Stop recording and clean up resources
}
Receiving Push to Talk Pushes swift · at 15:29 ↗
func incomingPushResult(channelManager: PTChannelManager, 
                        channelUUID: UUID, 
                        pushPayload: [String : Any]) -> PTPushResult {

    guard let activeSpeaker = pushPayload["activeSpeaker"] as? String else {
        // If no active speaker is set, the only other valid operation 
        // is to leave the channel
        return .leaveChannel
    }

    let activeSpeakerImage = getActiveSpeakerImage(activeSpeaker)    
    let participant = PTParticipant(name: activeSpeaker, image: activeSpeakerImage)
    return .activeRemoteParticipant(participant)
}
Stop receiving audio swift · at 17:03 ↗
func stopReceivingAudio() {
    channelManager.setActiveRemoteParticipant(nil, channelUUID: channelUUID)
}

Resources