Pulse SDK Integration Tutorial for iOS: Network Logger
Learn how to set up network logger for your app using Pulse SDK. Pulse framework provides you a UI to display the logs in your debug app and also persist the logs that can be exported anytime. By Mark Struzinski.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Pulse SDK Integration Tutorial for iOS: Network Logger
30 mins
- Getting Started
- Introducing Pulse
- PulseCore
- PulseUI
- Document-Based Pulse Apps
- Pulse Integration
- Logging Basic Data
- Setting Up Pulse Logs
- Introducing SwiftLog
- Log Levels
- Message
- Metadata Value
- Logging With SwiftLog
- Using PulseUI to View Logs
- Capturing Network Traffic
- Adding a Logger Instance
- Implementing URLSession Delegates
- Update Search to Work with a Delegate
- Update NetworkService with URLSessionTaskDelegate
- Update NetworkService with URLSessionDataDelegate
- Update the List View Model
- Capturing Image Requests
- Set up ImageDownloader
- Update MovieDetailViewModel
- Where to Go From Here?
Set up ImageDownloader
In the Xcode Project Navigator, open MovieSearch ▸ Source ▸ Network ▸ Service. Right-click the Service group and select New File ….
Then select Swift File and click Next. Name the new file ImageDownloader.swift and click Create.
Remove import Foundation
and replace with this code:
// 1
import UIKit
import PulseCore
// 2
class ImageDownloader: NSObject {
private let imageBaseURLString = "https://image.tmdb.org"
let urlSession = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
// 3
let logger = NetworkLogger()
// 4
var imageDownloadCompletion: ((Result<UIImage, NetworkError>) -> Void)?
// 5
func downloadImage(for imageType: ImageType, at path: String) {
guard let url = try? url(for: imageType, at: path) else {
return
}
let task = urlSession.dataTask(with: url)
task.delegate = self
task.resume()
}
// 6
private func url(for imageType: ImageType, at path: String) throws -> URL {
let imagePathParam = imageType.pathParameter()
guard let baseURL = URL(string: imageBaseURLString),
var urlComponents = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) else {
throw NetworkError.invalidURL
}
urlComponents.path = "/t/p/\(imagePathParam)\(path)"
let queryItems: [URLQueryItem] = [
URLQueryItem(name: "api_key", value: APIKey.value)
]
urlComponents.queryItems = queryItems
guard let url = urlComponents.url else {
throw NetworkError.invalidURL
}
return url
}
}
This code is an implementation of a simple download utility for images. As a quick summary, this code:
- Imports
UIKit
instead ofFoundation
because you’ll work withUIImage
. It also importsPulseCore
for logging. - Makes the implementation a class. You’ll implement the same delegation pattern as in
NetworkService
, soImageDownloader
needs to be a class that inherits fromNSObject
. - Creates a
NetworkLogger
instance just like you did before. - Uses a property to hold a completion handler like you did before.
- Creates a function to trigger image downloads.
- Generates an image URL from the image type, detail or list, and the poster path retrieved from the API response for each movie.
Next, implement Pulse logging in an extension in ImageDownloader.swift. Under the closing brace of ImageDownloader
, add:
extension ImageDownloader: URLSessionTaskDelegate, URLSessionDataDelegate {
func urlSession(
_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void
) {
logger.logDataTask(dataTask, didReceive: response)
if let response = response as? HTTPURLResponse,
response.statusCode != 200 {
imageDownloadCompletion?(.failure(.invalidResponseType))
}
completionHandler(.allow)
}
func urlSession(
_ session: URLSession,
task: URLSessionTask,
didCompleteWithError error: Error?
) {
logger.logTask(task, didCompleteWithError: error)
imageDownloadCompletion?(.failure(NetworkError.invalidResponseType))
}
func urlSession(
_ session: URLSession,
task: URLSessionTask,
didFinishCollecting metrics: URLSessionTaskMetrics
) {
logger.logTask(task, didFinishCollecting: metrics)
}
func urlSession(
_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive data: Data
) {
logger.logDataTask(dataTask, didReceive: data)
guard let image = UIImage(data: data) else {
imageDownloadCompletion?(.failure(.invalidParse))
return
}
imageDownloadCompletion?(.success(image))
}
}
Much of this is like the NetworkService
extension. Note the transformation of Data
to UIImage
in the urlSession(_:dataTask:didReceive:)
function. Aside from that key difference, all the patterns and log statements are like NetworkService
.
Update MovieDetailViewModel
Now, you’ll switch to ImageDownloader
for all image downloads. Open MovieDetailViewModel.swift. At the top of the class declaration, replace the networkService
property with an ImageDownloader
:
private let imageDownloader = ImageDownloader()
Next, under fetchImage(for:)
, add this helper function:
private func processImageResponse(result: Result<UIImage, NetworkError>) {
// 1
guard let image = try? result.get() else {
return
}
// 2
DispatchQueue.main.async {
self.posterImage = image
}
}
This helper:
- Checks whether the network request returned an image. Otherwise, it returns early.
- Sets the
posterImage
on the main queue, triggering a UI update.
Now, replace fetchImage(for:)
with:
func fetchImage(for movie: Movie, imageType: ImageType) {
guard let posterPath = movie.posterPath else {
return
}
imageDownloader.imageDownloadCompletion = processImageResponse(result:)
imageDownloader.downloadImage(for: .list, at: posterPath)
}
This code switches the download image implementation to the new class. It follows the delegation pattern required to use Pulse logging.
Next, open NetworkService.swift and remove the downloadImage(for:at:completion:)
and url(for:at:)
functions because you no longer need them. That completes your swap of the image download implementation.
Build and run to check your work. Perform a search and you’ll see image request results showing in your logs alongside the search results:
Tap an image request. Then tap again into Response Body and you’ll see the image as part of the response:
And that’s it! You updated your network implementation to add Pulse logging.
Pulse gives you some excellent functionality for in-app debugging out of the box. You’ll find the visually-oriented interface, ability to review results in-app and ability to share Pulse files helpful when debugging tricky network conditions.
Where to Go From Here?
Download the completed project by clicking Download Materials at the top or bottom of the tutorial.
Pulse has many more features you can explore. Pulse Pro adds the ability to livestream logs.
If you have specific needs, you can develop your own viewer front-end that works with the Pulse data and is implemented via a Core Data based API, so iOS and macOS developers should feel at home.
Finally, if you want to explore more about network proxy tools, check out our tutorials on Charles and Proxyman, which offer a different take on viewing and debugging network conditions.
If you have any comments or questions, please join the discussion below.