← Back to list

Swift: Control & Monitor Cellular Network Traffic Routing

CTSlicingManager! A SECRETLY Added New API in 26.3!

Itsuki · 2026-03-03 10:11 · 3 claps · 5.8 min read
#swift #ios-app-development #ios-development #network-slicing #network-traffic
Open on Medium ↗
Wiki topics: 📱 · Mobile Development 🔧 · Data Engineering

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.

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

  1. Activate the slice before making network connections
  2. 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