Skip to content

Commit

Permalink
Enable RemoteLoggerURLProtocol automatically when using URLSessionProxy
Browse files Browse the repository at this point in the history
  • Loading branch information
kean committed Aug 31, 2024
1 parent fcfd94d commit 4b57cae
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 19 deletions.
41 changes: 32 additions & 9 deletions Sources/Pulse/NetworkLogger/URLSessionProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,22 @@ public final class URLSessionProxy {
self.logger = logger
}

/// Enables automatic `URLSession` logging.
/// Enables automatic logging and remote debugging of network requests.
///
/// - warning: This method of logging relies heavily on swizzling and might
/// stop working in the future versions of the native SDKs. If you are looking
/// for a more stable solution, consider using ``URLSessionProxyDelegate`` or
/// manually logging the requests using ``NetworkLogger``.
///
/// - parameter logger: The network logger to be used for recording the requests.
public static func enable(with logger: NetworkLogger = .init()) {
guard URLSessionProxy.proxy == nil else {
NSLog("Error: Pulse.URLSessionProxy already enabled")
return
}
guard sharedNetworkLogger == nil else {
NSLog("Error: Pulse network request logging is already enabled")
return
}
guard !isAutomaticNetworkLoggingEnabled else { return }

let proxy = URLSessionProxy(logger: logger)
proxy.enable()
URLSessionProxy.proxy = proxy

RemoteLoggerURLProtocol.enableAutomaticRegistration()
}

func enable() {
Expand Down Expand Up @@ -120,6 +121,28 @@ public final class URLSessionProxy {
}
}

// MARK: - RemoteLoggerURLProtocol (Automatic Regisration)

extension RemoteLoggerURLProtocol {
@MainActor
static func enableAutomaticRegistration() {
if let lhs = class_getClassMethod(URLSession.self, #selector(URLSession.init(configuration:delegate:delegateQueue:))),
let rhs = class_getClassMethod(URLSession.self, #selector(URLSession.pulse_init2(configuration:delegate:delegateQueue:))) {
method_exchangeImplementations(lhs, rhs)
}
}
}

private extension URLSession {
@objc class func pulse_init2(configuration: URLSessionConfiguration, delegate: URLSessionDelegate?, delegateQueue: OperationQueue?) -> URLSession {
guard isConfiguringSessionSafe(delegate: delegate) else {
return self.pulse_init2(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue)
}
configuration.protocolClasses = [RemoteLoggerURLProtocol.self] + (configuration.protocolClasses ?? [])
return self.pulse_init2(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue)
}
}

// MARK: - Experimental (Deprecated)

@available(*, deprecated, message: "Experimental.URLSessionProxy is replaced with a reworked URLSessionProxy")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@
import Foundation

extension URLSessionProxyDelegate {
/// Enables automatic registration of `URLSessionProxyDelegate`. After calling this method, every time
/// you initialize a `URLSession` using `init(configuration:delegate:delegateQueue:))` method, the
/// delegate will automatically get replaced with a `URLSessionProxyDelegate` that logs all the
/// needed events and forwards the methods to your original delegate.
/// Enables automatic logging and remote debugging of network requests using
/// `URLSessionProxyDelegate`.
///
/// - note: This method works by swizzling `URLSession` init and adding
/// `URLSessionProxyDelegate` to the delegate chain and adding
/// `RemoteLoggerURLProtocol` to the list of session protocol classes.
///
/// - warning: This logging method works only with delegate-based `URLSession`
/// instances. If it doesn't work for you, consider using ``URLSessionProxy``
/// for automatic logging or manually logging the requests using ``NetworkLogger``.
///
/// - parameter logger: The network logger to be used for recording the requests.
@MainActor
public static func enableAutomaticRegistration(logger: NetworkLogger = .init()) {
guard sharedNetworkLogger == nil else {
NSLog("Error: Puls network request logging is already enabled")
return
}
guard !isAutomaticNetworkLoggingEnabled else { return }

sharedNetworkLogger = logger
if let lhs = class_getClassMethod(URLSession.self, #selector(URLSession.init(configuration:delegate:delegateQueue:))),
let rhs = class_getClassMethod(URLSession.self, #selector(URLSession.pulse_init(configuration:delegate:delegateQueue:))) {
Expand All @@ -23,15 +29,37 @@ extension URLSessionProxyDelegate {
}
}

var sharedNetworkLogger: NetworkLogger? {
/// Returns `true` if automatic logging was already enabled using one of the
/// existing mechanisms provided by Pulse.
@MainActor
var isAutomaticNetworkLoggingEnabled: Bool {
guard URLSessionProxy.proxy == nil else {
NSLog("Error: Pulse.URLSessionProxy already enabled")
return true
}
guard sharedNetworkLogger == nil else {
NSLog("Error: Pulse network request logging is already enabled")
return true
}
return false
}

func isConfiguringSessionSafe(delegate: URLSessionDelegate?) -> Bool {
if String(describing: delegate).contains("GTMSessionFetcher") {
return false
}
return true
}

private var sharedNetworkLogger: NetworkLogger? {
get { _sharedLogger.value }
set { _sharedLogger.value = newValue }
}
private let _sharedLogger = Mutex<NetworkLogger?>(nil)

private extension URLSession {
@objc class func pulse_init(configuration: URLSessionConfiguration, delegate: URLSessionDelegate?, delegateQueue: OperationQueue?) -> URLSession {
guard !String(describing: delegate).contains("GTMSessionFetcher") else {
guard isConfiguringSessionSafe(delegate: delegate) else {
return self.pulse_init(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue)
}
configuration.protocolClasses = [RemoteLoggerURLProtocol.self] + (configuration.protocolClasses ?? [])
Expand Down

0 comments on commit 4b57cae

Please sign in to comment.