Swift: Control & Monitor Cellular Network Traffic Routing
CTSlicingManager! A SECRETLY Added New API in 26.3!
Swift: Control & Monitor Cellular Network Traffic Routing
CTSlicingManager! A SECRETLY Added New API in 26.3!

First of all, what is Network slicing?
It allows carriers to partition their network resources to provide different levels of service quality for specific types of apps.
Why do we need it?
For example, we might have some streaming app or some real time gaming that requires super low latency.
Do we want to rely on the carrier’s default cellular internet without slice-specific routing?
I mean, we could, and that’s what we used to do!
But!
Starting from 26.3, we got this super awesome [CTSlicingManager](https://developer.apple.com/documentation/coretelephony/ctslicingmanager)! That we can use to
- Get current network traffic routing information, and
- Slice some network! To match what we need!

The usage is really simple! In case you haven’t get a chance to check it out yet, please allow me to share it here with you really quick!
Set Up
Two entitlements we need to add here.
Simply select 5G Network Slicing from the Capabilities and both will be added.

Select the corresponding App Categories and Traffic Categories.

For example, if the app opens network connections using the network service types video, voice, and call signaling, we must include video-2, voice-4, and callsignaling-5 values for 5G Network Slicing Traffic Category entitlement.
Now, does it mean that we are guaranteed to be able to ask for slicing in those categories?
Unfortunately, nope!
They are also couple other factors that determined whether if we will be able to slice the network for a specific category or not.
- The carrier’s network supports the specific slice category (for example, a carrier may support communication slices, but not gaming slices).
- The device and network conditions allow network slicing.
Get Network-slicing Categories Available
Since we are not guaranteed to be able to actually slice, the first thing here is to check what network-slicing app categories are actually available to our app!
As simple as get the [shared](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/shared) instance of the [CTSlicingManager](https://developer.apple.com/documentation/coretelephony/ctslicingmanager) and try await on the [availableSliceAppCategories](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/availablesliceappcategories) property.
private let slicingManager = CTSlicingManager.shared
func fetchAvailableCategories() {
Task {
do {
self.availableSliceAppCategories =
try await CTSlicingManager.shared
.availableSliceAppCategories
} catch(let error) {
self.handleError(error)
}
}
}
private func handleError(_ error: Error) {
if let error = error as? POSIXError {
switch error.code {
case .ENOTSUP:
print("Network slicing isn't currently available")
return
case .EINVAL:
print("Invalid parameter or system error occurs")
return
default:
break
}
}
print("Error performing request: \(error)")
}
The [AppCategory](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/appcategory) is the App categories for network slicing (I am repeating myself…), ie: the programmatic counter part of the 5G Network Slicing App Category entitlement values.
A little note on the error handling here. Getting the [availableSliceAppCategories](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/availablesliceappcategories) property may throws two errors.
[POSIXError.ENOTSUP](https://developer.apple.com/documentation/foundation/posixerror/enotsup): network slicing is not currently available.[POSIXError.EINVAL](https://developer.apple.com/documentation/foundation/posixerror/einval): invalid parameter or system error.
Slice The Network
Hopefully your target [AppCategory](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/appcategory) is included in the [availableSliceAppCategories](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/availablesliceappcategories) above so that we can activate it by calling [activatePreferredSliceForCategory(_:)](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/activatepreferredsliceforcategory(_:)).
func activateSliceForCategory(_ category: CTSlicingManager.AppCategory) {
guard self.availableSliceAppCategories.contains(category) else {
return
}
guard self.enabledSlicingCategory == nil else {
return
}
Task {
do {
try await self.slicingManager.activatePreferredSliceForCategory(
category
)
} catch(let error) {
self.handleError(error)
}
}
}
Note!
To have the networking to actually route through the activated slice, please
- Activate the slice before making network connections
- Set one of the following properties, depending on the networking implementation:
[serviceClass](https://developer.apple.com/documentation/Network/NWParameters/serviceClass-swift.property) when using the Networking framework,[networkServiceType](https://developer.apple.com/documentation/Foundation/URLSessionConfiguration/networkServiceType) when using[URLSessionConfiguration](https://developer.apple.com/documentation/Foundation/URLSessionConfiguration), and[networkServiceType](https://developer.apple.com/documentation/Foundation/URLRequest/networkServiceType-swift.property) when using[URLRequest](https://developer.apple.com/documentation/Foundation/URLRequest)
Get Current Slice State (Device-Wise)
Now that we have made the slicing request and we are not getting an error, but is our slice actually gets activated successfully?
Or Is there any other apps also slicing the network?
To answer our questions, we can inspect this [activeSlices](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/activeslices) property which contains information about currently active network slices on the device.
func fetchActiveSlices() {
Task {
do {
self.activeSlices = try await self.slicingManager.activeSlices
} catch(let error) {
self.handleError(error)
}
}
}
Disable Slicing
We are done with our day! (I mean, our app is done with its day…)
Let’s be nice to disable the slice instead of occupying that seat!
func stopSlicing() {
Task {
do {
try await self.slicingManager.disableSlicing()
self.enabledSlicingCategory = nil
self.fetchActiveSlices()
} catch(let error) {
self.handleError(error)
}
}
}
After calling [disableSlicing()](https://developer.apple.com/documentation/coretelephony/ctslicingmanager/disableslicing()) , the subsequent network calls will be route back to use the carrier’s default cellular internet without slice-specific routing.
Little Code Snippet
import CoreTelephony
import SwiftUI
@Observable
class NetworkTrafficManager {
private(set) var availableSliceAppCategories:
[CTSlicingManager.AppCategory] = []
private(set) var enabledSlicingCategory: CTSlicingManager.AppCategory? = nil
private(set) var activeSlices: [CTSlicingManager.Slice] = []
private let slicingManager = CTSlicingManager.shared
init() {
self.fetchAvailableCategories()
self.fetchActiveSlices()
}
// Network-slicing app categories available to the app
// This property returns an array of CTSlicingManager.AppCategory values that meet all of the following requirements:
// - The carrier’s network supports them.
// - The app’s entitlements (5G Network Slicing App Category & 5G Network Slicing Traffic Category) include them.
// - The device and network currently make them available.
func fetchAvailableCategories() {
Task {
do {
self.availableSliceAppCategories =
try await CTSlicingManager.shared
.availableSliceAppCategories
} catch(let error) {
self.handleError(error)
}
}
}
// fetch Information about currently active network slices on the device
// Use this function to validate that the preferred slice activates successfully.
//
// To monitor for changes, use a timer and poll on the activeSlices property
func fetchActiveSlices() {
Task {
do {
self.activeSlices = try await self.slicingManager.activeSlices
} catch(let error) {
self.handleError(error)
}
}
}
// Activates a preferred network slice for new connections
func activateSliceForCategory(_ category: CTSlicingManager.AppCategory) {
guard self.availableSliceAppCategories.contains(category) else {
return
}
guard self.enabledSlicingCategory == nil else {
return
}
Task {
do {
try await self.slicingManager.activatePreferredSliceForCategory(
category
)
self.enabledSlicingCategory = category
// refetch active slices since we have made some changes.
self.fetchActiveSlices()
} catch(let error) {
self.handleError(error)
}
}
}
// stop using network slicing
// After calling this method, new network connections that your app establishes use the carrier’s default cellular internet without slice-specific routing.
func stopSlicing() {
Task {
do {
try await self.slicingManager.disableSlicing()
self.enabledSlicingCategory = nil
self.fetchActiveSlices()
} catch(let error) {
self.handleError(error)
}
}
}
private func handleError(_ error: Error) {
if let error = error as? POSIXError {
switch error.code {
case .ENOTSUP:
print("Network slicing isn't currently available")
return
case .EINVAL:
print("Invalid parameter or system error occurs")
return
default:
break
}
}
print("Error performing request: \(error)")
}
}
extension CTSlicingManager.AppCategory {
var title: String {
switch self {
case .gaming:
"Gaming"
case .communication:
"Communication"
case .streaming:
"Streaming"
@unknown default:
"Unknown"
}
}
}
extension CTSlicingManager.TrafficClass {
var title: String {
switch self {
case .any:
"Any"
case .background:
"Background"
case .responsiveData:
"Responsive Data"
case .avStreaming:
"AV Streaming"
case .responsiveAV:
"Responsive AV"
case .video:
"Video"
case .voice:
"Voice"
case .signaling:
"Signaling"
@unknown default:
"Unknown"
}
}
}
struct NetworkTrafficRoutingDemo: View {
@State private var networkTrafficManager = NetworkTrafficManager()
var body: some View {
NavigationStack {
List {
Section("Slicing App Categories Available") {
if networkTrafficManager.availableSliceAppCategories.isEmpty
{
Text(
"No Network-slicing app categories available to the app."
)
.foregroundStyle(.secondary)
}
ForEach(
networkTrafficManager.availableSliceAppCategories,
id: \.rawValue
) { category in
HStack {
Text(category.title)
Spacer()
if self.networkTrafficManager.enabledSlicingCategory
== category
{
Button(
action: {
self.networkTrafficManager.stopSlicing()
},
label: {
Text("De-Activate")
}
)
} else {
Button(
action: {
self.networkTrafficManager
.activateSliceForCategory(category)
},
label: {
Text("Activate")
}
)
}
}
}
}
Section("Active Network Slice") {
if networkTrafficManager.activeSlices.isEmpty {
Text("No Network slice currently active on the device.")
.foregroundStyle(.secondary)
}
ForEach(
networkTrafficManager.activeSlices,
id: \.networkInterfaceName
) { slice in
VStack(
alignment: .leading,
spacing: 8,
content: {
Text("Interface: \(slice.networkInterfaceName)")
Text(
"Traffic Class: \(slice.trafficClass?.title, default: "Unkown")"
)
Text("Category: \(slice.appCategory.title)")
}
)
}
}
}
.navigationTitle("Network Traffic Routing")
.navigationBarTitleDisplayMode(.inline)
}
}
}
Of course, carrier does not exist on simulators so make sure to test it on a real device!
Thank you for reading!
That’s it for this little super simple article!
But hopefully, you get at least a teeny tiny bit of useful information!
PS: For some reasons, the 26.3 iOS simulator does not come with Xcode 26.3? Is it just me?
26.4 beta comes with XCode 26.4 though!
Anyway!
Happy slicing!
메타데이터
- post_id
- a64e1f6129ea
- slug
- swift-control-monitor-cellular-network-traffic-routing-a64e1f6129ea
- url
- https://medium.com/@itsuki.enjoy/swift-control-monitor-cellular-network-traffic-routing-a64e1f6129ea
- canonical_url
- https://medium.com/@itsuki.enjoy/swift-control-monitor-cellular-network-traffic-routing-a64e1f6129ea
- author_url
- https://medium.com/@itsuki.enjoy
- status
- ok
- fetched_at
- 2026-06-22 00:13:37