Skip to contentSkip to navigationSkip to topbar
On this page
Looking for more inspiration?Visit the
(information)
You're in the right place! Segment documentation is now part of Twilio Docs. The content you are used to is still here—just in a new home with a refreshed look.

Middleware for iOS


(warning)

End-of-Support for Analytics-iOS in March 2026

End-of-support (EoS) for the Analytics-iOS SDK is scheduled for March 2026. Segment's future development efforts concentrate on the new Analytics-Swift SDK. If you'd like to migrate to Analytics-Swift, see the migration guide.

Middlewares are simple functions invoked by the Segment libraries, which give you a way to add information to the events you collect using the Segment SDKs. They can be used to monitor, modify, or reject events. Source Middlewares are available on analytics-ios 3.6.0 and later.

You can access the middleware API in both Objective-C and Swift.

(information)

Info

Note: Destination Middlewares are not available for iOS.


Use

use page anchor

Middleware is any Objective-C class that conforms to the following protocol.

1
@protocol SEGMiddleware
2
@required
3
- (void)context:(SEGContext *_Nonnull)context next:(S
4
EGMiddlewareNext _Nonnull)next;
5
@end

Segment also provides a block-centric helper class to make it easier to create middlewares using anonymous functions on the fly. (See examples below)

1
typedef void (^SEGMiddlewareBlock)(SEGContext *_Nonnull context, SEGMiddlewareNext _Nonnull next);
2
3
@interface SEGBlockMiddleware : NSObject <SEGMiddleware>
4
@property (nonnull, nonatomic, readonly) SEGMiddlewareBlock block;
5
6
- (instancetype _Nonnull)initWithBlock:(SEGMiddlewareBlock _Nonnull)block;
7
8
@end

The context object encapsulates everything about an event in the stream. You invoke the next callback function when the current middleware is done processing the event, and can pass the processed event down to the next middleware in the chain.

The SEGContext object is not very information rich by itself. Typically you must use eventType and payload to get more information about an event.

1
@interface SEGContext : NSObject <NSCopying>
2
3
@property (nonatomic, readonly, nonnull) SEGAnalytics *_analytics;
4
@property (nonatomic, readonly) SEGEventType eventType;
5
6
@property (nonatomic, readonly, nullable) NSString *userId;
7
@property (nonatomic, readonly, nullable) NSString *anonymousId;
8
@property (nonatomic, readonly, nullable) NSError *error;
9
@property (nonatomic, readonly, nullable) SEGPayload *payload;
10
@property (nonatomic, readonly) BOOL debug;
11
12
- (instancetype _Nonnull)initWithAnalytics:(SEGAnalytics *_Nonnull)analytics;
13
14
- (SEGContext *_Nonnull)modify:(void (^_Nonnull)(id<SEGMutableContext> _Nonnull ctx))modify;
15
16
@end

Look at the SEGEventType carefully, and notice that middleware can handle track , identify and other Segment analytics APIs. Even calls like reset , flush and openURL go through and can be processed by the middleware pipeline.

1
typedef NS_ENUM(NSInteger, SEGEventType) {
2
// Should not happen, but default state
3
SEGEventTypeUndefined,
4
// Core Tracking Methods
5
SEGEventTypeIdentify,
6
SEGEventTypeTrack,
7
SEGEventTypeScreen,
8
SEGEventTypeGroup,
9
SEGEventTypeAlias,
10
11
// General utility
12
SEGEventTypeReset,
13
SEGEventTypeFlush,
14
15
// Remote Notification
16
SEGEventTypeReceivedRemoteNotification,
17
SEGEventTypeFailedToRegisterForRemoteNotifications,
18
SEGEventTypeRegisteredForRemoteNotifications,
19
SEGEventTypeHandleActionWithForRemoteNotification,
20
21
// Application Lifecycle
22
SEGEventTypeApplicationLifecycle,
23
24
// Misc.
25
SEGEventTypeContinueUserActivity,
26
SEGEventTypeOpenURL,
27
};

There are almost as many SEGPayload subclasses as there are SEGEventType enums. Subclassed payloads may contain call specific information, For example, the SEGTrackPayload contains event as well as properties .

1
@interface SEGTrackPayload : SEGPayload
2
3
@property (nonatomic, readonly) NSString *event;
4
5
@property (nonatomic, readonly, nullable) NSDictionary *properties;
6
7
@end

Finally, to use a middleware, you must provide it to the SEGAnalyticsConfiguration object prior to the initialization of SEGAnalytics.

1
@interface SEGAnalyticsConfiguration : NSObject
2
3
/**
4
* Set custom source middleware. Will be run before all integrations
5
*/
6
@property (nonatomic, strong, nullable) NSArray<id<SEGMiddleware>> *sourceMiddleware;
7
8
// ...
9
@end

Once initialized, the list of middleware used in SEGAnalytics cannot be changed.


The following examples are written in Swift to show that the middleware API works just as well in Swift as in Objective-C.

Initialize middleware

initialize-middleware page anchor

The following example shows how to initialize middleware.

SwiftObjective-C
1
let mixpanelIntegration = SEGMixpanelIntegrationFactory.instance()
2
let amplitudeIntegration = SEGAmplitudeIntegrationFactory.instance()
3
let config = AnalyticsConfiguration(writeKey: "YOUR_WRITEKEY_HERE")
4
5
config.trackApplicationLifecycleEvents = true
6
config.trackDeepLinks = true
7
config.recordScreenViews = true
8
9
config.use(mixpanelIntegration)
10
config.use(amplitudeIntegration)
11
12
config.sourceMiddleware = [
13
turnScreenIntoTrack,
14
enforceEventTaxonomy,
15
customizeAllTrackCalls,
16
dropSpecificEvents,
17
blockScreenCallsToAmplitude,
18
]
19
20
Analytics.setup(with: config)

Change event names and add attributes

change-event-names-and-add-attributes page anchor

The following examples show how to changing event names, and add custom attributes.

SwiftObjective-C
1
let customizeAllTrackCalls = BlockMiddleware { (context, next) in
2
if context.eventType == .track {
3
next(context.modify { ctx in
4
guard let track = ctx.payload as? TrackPayload else {
5
return
6
}
7
let newEvent = "[New] \(track.event)"
8
var newProps = track.properties ?? [:]
9
newProps["customAttribute"] = "Hello"
10
ctx.payload = TrackPayload(
11
event: newEvent,
12
properties: newProps,
13
context: track.context,
14
integrations: track.integrations
15
)
16
})
17
} else {
18
next(context)
19
}
20
}

The following example turns one kind call into another. NOTE: This is only applicable to Source Middleware.

SwiftObjective-C
1
let turnScreenIntoTrack = BlockMiddleware { (context, next) in
2
if context.eventType == .screen {
3
next(context.modify { ctx in
4
guard let screen = ctx.payload as? ScreenPayload else {
5
return
6
}
7
let event = "\(screen.name) Screen Tracked"
8
ctx.payload = TrackPayload(
9
event: event,
10
properties: screen.properties,
11
context: screen.context,
12
integrations: screen.integrations
13
)
14
ctx.eventType = .track
15
})
16
} else {
17
next(context)
18
}
19
}

The following example completely blocks specific events from a list.

SwiftObjective-C
1
let dropSpecificEvents = BlockMiddleware { (context, next) in
2
let validEvents = [
3
"Application Opened",
4
"Order Completed",
5
"Home Screen Tracked",
6
"AnalyticsIOSTestApp. Screen Tracked",
7
]
8
if let track = context.payload as? TrackPayload {
9
if !validEvents.contains(track.event) {
10
print("Dropping Rogue Event '\(track.event)'")
11
// not calling next results in an event being discarded
12
return
13
}
14
}
15
next(context)
16
}

Block specific call types to a specific destination

block-specific-call-types-to-a-specific-destination page anchor

The following example blocks only screen calls from reaching the Amplitude destination.

SwiftObjective-C
1
let blockScreenCallsToAmplitude = BlockMiddleware { (context, next) in
2
if let screen = context.payload as? ScreenPayload {
3
next(context.modify { ctx in
4
ctx.payload = ScreenPayload(
5
name: screen.name,
6
properties: screen.properties,
7
context: screen.context,
8
integrations: ["Amplitude": false]
9
)
10
})
11
return
12
}
13
next(context)
14
}

If you use the Braze (Appboy) destination in either cloud or device mode you can save Braze costs by "debouncing" duplicate Identify calls from Segment by adding the open-source Middleware tool(link takes you to an external page) to your implementation. More information about this tool and how it works is available in the project's README(link takes you to an external page).