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.

5 (1) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Logging Basic Data

In addition to network logs, Pulse lets you take advantage of SwiftLog to log any data you need outside of networking-related concerns. You’ll view these logs alongside your network logs.

Setting Up Pulse Logs

First, initialize and configure the Pulse logging backend. Open AppMain.swift. At the top of the file, add an import for Pulse and Logging under the other imports:

import Pulse
import Logging

In init(), add the following code under setupNavbarAppearance():

LoggingSystem.bootstrap(PersistentLogHandler.init)

This code configures SwiftLog to use Pulse’s PersistentLogHandler.

Next, you’ll log some information using SwiftLog, the underlying subsystem Pulse uses.

Introducing SwiftLog

SwiftLog is an open-source logging implementation by Apple. SwiftLog statements have three components: Log Levels, Message and MetadataValue.

Log Levels

SwiftLog provides seven built-in log levels:

  • trace
  • debug
  • info
  • notice
  • warning
  • error
  • critical

You’ll use these levels as part of each log statement based on the severity.

Message

The message is the primary piece of information sent to a log statement. You can make use of interpolated strings in the message to add dynamic data at runtime in your logs.

Metadata Value

This optional parameter helps you attach more data to the log statement. It can be a String, Array or Dictionary. You won’t use this value in your log statements here, but if you ever need to attach more context to your logs, this is the place to do so.

Are you ready to add your first log using SwiftLog API? Here you go! :]

Logging With SwiftLog

Open MovieListViewModel.swift. Then import Logging under the other imports:

import Logging

Under the declaration of the networkService property, add:

let logger = Logger(label: "com.razeware.moviesearch")

This line creates a SwiftLog Logger instance you can use to log events. You’ve given it a label that will separate these log messages from any others.

Next, you’ll log an event when the user performs a search.

Add the following code inside the search() function right before the loading = true:

logger.info("Performing search with term: \(self.searchText)")

So you’ve added a log. But you have no way to view the logs! In the next section, you’ll remedy that.

Using PulseUI to View Logs

Pulse comes with a built-in viewer for logs available on macOS, iOS, iPadOS and tvOS. You can use it anywhere in your UI where you want to see what’s happening in your network stack.

In this tutorial, you’ll set up the viewer as a modal view that displays via a toolbar button. You might want to make this view only available in debug builds or hide it deeper in the UI in a production app. Pulse is an excellent debugging tool, but you wouldn’t want it front and center in your app’s UI!

Press Shift-Command-O and open ContentView.swift. At the top, right under the SwiftUI import, add an import for PulseUI:

import PulseUI

Next, under the declaration of var viewModel, add a state variable to control sheet visibility:

@State
private var showingSheet = false

This state variable controls when the PulseUI log view renders as a modal.

Next, right under the .searchable modifier, add a sheet modifier:

.sheet(isPresented: $showingSheet) {
  MainView()
}

PulseUI provides MainView. It’s an easy-to-use view that displays a lot of information about your network and SwiftLog data.

You added the ability to display the log view in a sheet, but you haven’t created a way to trigger the display yet. You’ll handle that now via a toolbar button.

Under the .onSubmit action, add a toolbar modifier:

// 1
.toolbar {
  // 2
  ToolbarItem(placement: .navigationBarTrailing) {
    // 3
    Button {
      // 4
      showingSheet = true
    } label: {
      // 5
      Image(systemName: "wifi")
    }
  }
}

This code:

  1. Adds a toolbar to the navigation bar.
  2. Creates a ToolbarItem aligned to the right.
  3. Adds a Button as content to the ToolbarItem.
  4. When a user taps the button, sets the showingSheet variable to true.
  5. Uses the SFSymbol for WiFi as an icon for the button.

That’s all you need to display your log UI. Build and run.

When the app launches, you’ll see your new toolbar item on the right side of the navigation bar:

Simulator with wifi icon and search bar

Perform a couple of searches, then click the new toolbar button. You’ll see your search logs:

Log results

You won’t see much on the other tabs yet. You’ll explore them after you start capturing network traffic.

Capturing Network Traffic

Pulse has two methods for logging and capturing network traffic. You can use a combination of URLSessionTaskDelegate and URLSessionDataDelegate, or set up automated logging when your app launches.

The recommended approach is to use delegation to log your network requests. The automated approach uses an Objective-C runtime feature called swizzling to inject Pulse into the depths of URLSession. This allows Pulse to capture all requests regardless of origin. While this may be desirable in some instances, it is something that can be dangerous. So you’ll follow the recommended approach here.

Next, you’ll update the networking stack to use delegation as a way to insert Pulse into the mix.

Adding a Logger Instance

Open NetworkService.swift. Then add an import for PulseCore at the top of the file under the other imports:

import PulseCore

Under the declaration of urlSession, create a logger:

private let logger = NetworkLogger()

NetworkLogger is the class Pulse provides to perform all your logging functions. You’ll use this instance in the next step to log network activity.

Implementing URLSession Delegates

First, you need to change the network interaction to enable Pulse logging. You need to hook up Pulse to various different parts of the network request. This means you will switch from handling the search network request with an inline completion block, to a cached block that will execute as part of one of the URLSession delegate callback.

Update Search to Work with a Delegate

In NetworkService.swift, add the following property underneath the declaration of logger:

var searchCompletion: ((Result<[Movie], NetworkError>) -> Void)?

This property has the same signature as the completion block for search(for:).

Now, update the search(for:) function to remove parsing, error and completion handling. All of that responsibility will shift to the delegate.

Replace the search(for:) function with the following code:

@discardableResult
// 1
func search(for searchTerm: String) -> URLSessionDataTask? {
  // 2
  guard let url = try? url(for: searchTerm) else {
    searchCompletion?(.failure(NetworkError.invalidURL))
    return nil
  }

  // 3
  let task = urlSession.dataTask(with: url)
  // 4
  task.delegate = self
  // 5
  task.resume()
  // 6
  return task
}

This code:

  1. Removes the completion argument and passes the search term entered in the search bar.
  2. Then checks for a valid URL. If the URL is not valid then it calls the completion handler, if it exists.
  3. Creates a data task from the URL.
  4. Then sets the task’s delegate to self.
  5. Calls resume() to start the request.
  6. Finally, returns the task so you can cancel it if needed.

You’ll see a warning, but don’t worry. You’ll fix it soon.