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 ↗Code shown on screen · 14 snippets
Creating a Channel Manager
func setupChannelManager() async throws {
channelManager = try await PTChannelManager.channelManager(delegate: self,
restorationDelegate: self)
} Joining a Channel
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
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
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
func channelManager(_ channelManager: PTChannelManager,
didLeaveChannel channelUUID: UUID,
reason: PTChannelLeaveReason) {
// Process leaving the channel
print("Left channel with UUID: \(channelUUID)")
} PTChannelRestorationDelegate
func channelDescriptor(restoredChannelUUID channelUUID: UUID) -> PTChannelDescriptor {
return getCachedChannelDescriptor(channelUUID)
} Provide channel descriptor updates
func updateChannel(_ channelDescriptor: PTChannelDescriptor) async throws {
try await channelManager.setChannelDescriptor(channelDescriptor,
channelUUID: channelUUID)
} Provide service status updates
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
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
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
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
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
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
func stopReceivingAudio() {
channelManager.setActiveRemoteParticipant(nil, channelUUID: channelUUID)
} Resources
Related sessions
-
19 min -
19 min -
1 min