Creating a carrier messaging app
Use TelephonyMessagingKit to send and receive SMS, MMS, and RCS messages.
Overview
When the person using a device chooses your app to be the default carrier messaging app, the system sends all SMS, MMS, and RCS messages received by the device to your app. You use this framework to send and receive messages with those services.
This article describes how to set up your app to be the default app for carrier messaging. To see how to also make your app the default app for internet-based messaging, see Preparing your app to be the default messaging app.
Configure your app with the required entitlement
To make your app eligible to become the default carrier messaging app, add the Default Carrier Messaging App entitlement, as described on the entitlement’s documentation page.
When someone uses the device in an environment that supports the use of TelephonyMessagingKit, they can choose a default carrier messaging app from the Settings app. If they choose your app, the system sends all SMS, MMS, and RCS messages to your app. If they choose a different app, your app doesn’t receive and can’t send carrier messages.
To test whether your app can send and receive carrier messages, check the TelephonyMessagingSession property isConfiguredForCarrierMessaging before you make any calls to TelephonyMessagingKit.
Handle app launch
If your app isn’t running and the device receives an SMS, MMS, or RCS message, the system launches your app in the background, and gives your app some CPU time to process the received messages. Use this time to collect the messages, add them to your app’s data model or a persistent data store, and then post a local notification.
The person using the device can launch your app by tapping on a local notification you post, or by launching it normally from the app icon. This means there may or may not be messages to collect immediately after the app launches.
Discover cellular services
The person using your app may have multiple cellular services active at one time. To get an array of cellular services, use the shared instance of TelephonyMessagingSession to get the cellularServices array. Each member of this array is an instance of CellularServiceState, which contains a unique identifier for the cellular service and a label string. These labels correspond to the names the person gives to the services in the Settings app, under Settings > Cellular > SIM > Cellular Plan Label.
Your app needs a cellular service identifier when posting messages to specify which cellular service sends the message. Received messages include a cellular service identifier, so you know which service sent them.
If you want to handle changes to the cellular services, such as someone removing a SIM or changing the labels associated with their services, monitor the session’s cellularServiceStateUpdates asynchronous sequence.
Receive messages
The shared instance of TelephonyMessagingSession also provides access to the device’s messaging services, as the properties smsService, mmsService, and rcsService. These properties are instances of the types SMSService, MMSService, and RCSService, respectively. Be aware that some or all of these may be available or unavailable at any time, based on carrier offerings and the device’s service plan, and whether your app is the default carrier messaging app.
Each service provides an asynchronous sequence called incomingMessageNotifications that you use to receive messages. Iterate over this sequence with a for-await-in loop to collect any messages that TelephonyMessagingKit queued while your app wasn’t running, and to receive new messages as they arrive. Each notification includes a message property to represent the incoming message, along with other service-specific properties, such as group information when using an RCSService.
The following example shows a loop that processes incoming SMS messages by calling a hypothetical storeSMSMessage(_:) method to add them to a local data store, so they are available for a view elsewhere in the app to display. The loop also sends local notifications with the User Notifications framework in case the app is running in the background, which can happen if messages arrive and the app isn’t running. If the app is running when messages arrive, sending the local notification does nothing.
guard TelephonyMessagingSession.shared.isConfiguredForCarrierMessaging else { return }
let notifications = try TelephonyMessagingSession.shared.smsService.incomingMessageNotifications
for await smsMessageNotification in notifications {
let message = smsMessageNotification.message
// Add message to data store.
storeSMSMessage(message)
// Post local notification.
let notificationContent = UNMutableNotificationContent()
notificationContent.title = "My Message App"
notificationContent.subtitle = "From: \(message.handle.phoneNumber)"
notificationContent.body = message.content.body
let request = UNNotificationRequest(identifier: "com.example.mymessageapp",
content: notificationContent,
trigger: nil)
try await UNUserNotificationCenter.current().add(request)
}The process is similar for receiving MMS and RCS messages.
Send messages
Your app can also send messages with SMS, MMS, or RCS. While there are differences in the sendMessage() methods exposed by SMSService, MMSService, and RCSService, they all require the following:
A CellularServiceID that indicates which cellular service the framework should use to send the message.
The phone number of a recipient, wrapped in a “handle” type specific to the messaging service, like SMSHandle. For MMS, you can send a message to multiple recipients as an array of handles. For RCS, the RCSHandle type can represent either a single recipient or a group of recipients.
A unique identifier for the message.
The message content. For SMS, the SMSContent type wraps a single string. For MMS, you create an MMSContent instance, which can contain multiple parts of different types. For RCS, there are different supporting types for different kinds of content, like text, acknowledgements of file transfers, geolocation coordinates, and composing indicators (such as “someone is typing”). In the RCS case, separate overloads of the
sendMessage()method handle each of these different content types.
If there are multiple cellular services in the session’s cellularServices array, your app can show a list to the person using the app and let them choose one to send the message. If the array contains only one cellular service, you can skip this and default to using the first member of the array.
The following example shows the basic case of sending an SMS text message to a single recipient. This example uses the first cellular service in the session’s cellularServices array; a more robust app needs to give someone the ability to choose which of their services to use if there are more than one.
guard TelephonyMessagingSession.shared.isConfiguredForCarrierMessaging else { return }
let service = TelephonyMessagingSession.shared.smsService
let cellularServices = try TelephonyMessagingSession.shared.cellularServices
let cellularServiceID = cellularServices[0].id
guard service.isViable(for: cellularServiceID) else { return }
let message = SMSMessage(
cellularServiceID: cellularServiceID,
handle: SMSHandle(phoneNumber: SAMPLE_PHONE_NUMBER),
messageID: SMSMessageID(rawValue: SAMPLE_SMS_MESSAGE_ID),
content: SMSContent(body: "Hello There.")
)
try await service.sendMessage(message)