Network tracking and interception SDK support
Network tracking and interception is supported in iOS SDK version 2.2.4 and up. For information on updating your SDK, see iOS integration.
Track network request
To manually track network requests:
let event = InterceptedTaskData(
start: 1234,
url: "https://www.example.com",
duration: 100,
method: "POST",
requestHeaders: ["sample":"header"],
responseHeaders: ["sample":"header"],
httpProtocol: "http/1.1",
initiator: "sample",
status: "ok",
statusCode: 200,
cached: false,
requestBody: "{\"example\":\"body\"}",
responseBody: "{\"example\":\"body\"}"
)
Smartlook.instance.track(interceptedEvent: event)
Network request parameters
Network request parameters | Description |
---|---|
start | Unix timestamp marking the start of request |
duration | Duration of the request |
url | Request url |
method | HTTP method |
httpProtocol | Protocol used to execute the request (e.g. "http/1.1") |
initiator | The technology/framework used to execute the request (e.g. "OkHttp") |
status | Request was successful or failed |
statusCode | HTTP status code |
cached = TRUE | Shown if the response body was obtained from cache |
requestBody | String representation of the request body. Maximum allowed number of characters is 10000 (every character beyond this limit is dropped) |
responseBody | String representation of response body. Maximum allowed number of characters is 10000 (every character beyond this limit is dropped) |
requestHeaders | Map of request headers |
responseHeaders | Map of response headers |
Network interceptor
A network interceptor lets you monitor and track REST requests as they flow between a client application and a server.
Default network interception
Interceptors can be used without any configuration:
URLSession
.shared
.dataTask(with: URLRequest(url: url)) { data, response, error in
// Handle response
}
.intercept()
.resume()
What data is captured
Due to Smartlook Privacy by design, not all data is captured.
The following data is captured by default:
- All basic data:
start
,duration
,url
,method
,protocol
,initiator
,status
,statusCode
,cached
The following headers are considered safe:
A-IM
,Accept
,Accept-Charset
,Accept-Datetime
,Accept-Encoding
,Accept-Language
,Access-Control-Request-Method
,Access-Control-Request-Headers
,Cache-Control
,Connection
,Content-Encoding
,Content-Length
,Content-MD5
,Content-Type
,Cookie
,Date
,Expect
,Forwarded
,From
,Host
,HTTP2-Settings
,If-Match
,If-Modified-Since
,If-None-Match
,If-Range
,If-Unmodified-Since
,Max-Forwards
,Origin
,Pragma
,Prefer
,Range
,Referrer
,TE
,Trailer
,Transfer-Encoding
,User-Agent
,Upgrade
,Via
,Warning
The following data isn't captured:
requestBody
andresponseBody
because they can contain sensitive data
Non-binary body interceptor
NonBinaryBodyInterceptor
replaces requestBody
and responseBody
with String
representations of the original bodies if the content-type
is one of the following:
text/plain
text/csv
application/xml
,text/xml
,application/json
,application/ld+json
,text/x-markdown
URLSession
.shared
.dataTask(with: URLRequest(url: url)) { data, response, error in
// Handle response
}
.intercept(interceptors: [NonBinaryBodyInterceptor()])
.resume()
Headers interceptor
You can decide which headers to capture with HeadersInterceptor
:
URLSession
.sharedh
.dataTask(with: URLRequest(url: url)) { data, response, error in
// Handle response
}
.intercept(interceptors: [HeadersInterceptor(allowedHeaders: ["Example-Header", "Accept.*"])])
.resume()
As you can see in the example, HeadersInterceptor
lets you define set
of allowed header names. Regular expressions can be used too, meaning that headers like Accept-Charset
, Accept-Encoding
, and Accept-Language
can be captured.
Mask URL interceptor
You can filter sensitive parts of URLs with MaskUrlInterceptor
:
let mask = InterceptorMask(regex: "(secret=)[^&]+(&*)", mask: "hidden")
URLSession
.shared
.dataTask(with: URLRequest(url: url)) { data, response, error in
// Handle response
}
.intercept(interceptors: [MaskUrlInterceptor(mask: mask)])
.resume()
MaskUrlInterceptor.Mask
has two parameters:
regexPattern
/regex
—Regular expression used to define parts of URL that should be masked. It can be defined as aString
pattern orRegex
.replaceWith
—AString
used to mask/replace matched URL part.
An example URL with masking using the code example:
// URL before masking
https://www.example.com/sample?secret=token&test=param
// URL after masking
https://www.example.com/sample?secret=<hidden>&test=param
Mask body interceptor
You can mask sensitive parts of the request and response bodies using MaskBodyInterceptor
:
let mask = InterceptorMask(regex: "(secret=)[^&]+(&*)", mask: "hidden")
URLSession
.shared
.dataTask(with: URLRequest(url: url)) { data, response, error in
// Handle response
}
.intercept(interceptors: [MaskBodyInterceptor(mask: mask)])
.resume()
MaskBodyInterceptor.Mask
has two parameters:
regexPattern
/regex
—Regular expression used to define parts of the body that should be masked. It can be defined as aString
pattern orRegex
.replaceWith
—AString
used to mask/replace matched body parts.
An example body with masking using the code example:
// Body before masking
{
"sample": "json",
"sensitive": "password"
}
// Body after masking
{
"sample": "json",
"sensitive": <hidden>
}
Custom network interceptor
If none of the above interceptors suit your needs, you can define a custom interceptor:
URLSession
.shared
.dataTask(with: URLRequest(url: url)) { data, response, error in
// Handle response
}
.intercept(interceptors: [CustomInterceptor()])
.resume()
class CustomInterceptor: NetworkInterceptor {
func onIntercept(task: URLSessionTask, responseData: Data?, intercepted: InterceptedRequestEvent?) -> InterceptedRequestEvent? {
// Process original intercepted or create new.
}
}
Function onIntercept
has the following parameters:
task
—URLSessionTask
contains the request and responseresponseData
—If you usedURLSessionDatatask
, this parameter contains data from the responseintercepted
—Contains models from previous interceptors. If this is first interceptor it contains the data that is intercepted by default.
Interceptor chaining
Multiple interceptors can be used to process the intercepted request:
URLSession
.shared
.dataTask(with: URLRequest(url: url)) { data, response, error in
// Handle response
}
.intercept(interceptors: [
NonBinaryBodyInterceptor(),
MaskUrlInterceptor(mask: mask),
CustomInterceptor()
])
.resume()
Interceptor order
When there are multiple interceptors, interceptors are used in the order they are entered. In the above example,
InterceptorA
is the first andInterceptorC
is the last.