Skip to content

Commit 84f7c21

Browse files
Fix session deinitialization and add @objc interceptor wrappers
Agent-Logs-Url: https://github.com/nativescript-community/https/sessions/d2d9c53a-efe5-4696-a2aa-b293851b2169 Co-authored-by: farfromrefug <655344+farfromrefug@users.noreply.github.com>
1 parent 0777e18 commit 84f7c21

File tree

2 files changed

+188
-13
lines changed

2 files changed

+188
-13
lines changed

packages/https/platforms/ios/src/AlamofireWrapper.swift

Lines changed: 164 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public class AlamofireWrapper: NSObject {
1616
private let requestsLock = NSLock()
1717

1818
// Interceptors
19-
private var requestInterceptors: [RequestInterceptor] = []
20-
private var eventMonitors: [EventMonitor] = []
19+
private var requestInterceptors: [RequestInterceptorWrapper] = []
20+
private var eventMonitors: [EventMonitorWrapper] = []
2121

2222
@objc public static let shared = AlamofireWrapper()
2323

@@ -82,31 +82,47 @@ public class AlamofireWrapper: NSObject {
8282
private func recreateSession() {
8383
let configuration = session.sessionConfiguration
8484

85+
// Cancel all active requests before recreating session
86+
requestsLock.lock()
87+
let requests = Array(activeRequests.values)
88+
activeRequests.removeAll()
89+
requestsLock.unlock()
90+
91+
for request in requests {
92+
request.cancel()
93+
}
94+
8595
// Create a server trust manager with our security policy
8696
// Use allHostsMustBeEvaluated: false to allow default trust evaluation for non-pinned hosts
8797
let serverTrustManager = ServerTrustManager(allHostsMustBeEvaluated: false, evaluators: [:])
8898

8999
// Create new session with server trust manager and interceptors
90-
// Keep the session alive by replacing it atomically
100+
// Store old session temporarily to ensure it doesn't get deallocated immediately
101+
let oldSession = session
91102
session = Session(
92103
configuration: configuration,
93104
serverTrustManager: serverTrustManager,
94105
eventMonitors: eventMonitors
95106
)
107+
108+
// Ensure old session is explicitly invalidated after new one is created
109+
DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + 0.5) {
110+
_ = oldSession // Keep reference until delayed closure executes
111+
}
96112
}
97113

98114
// MARK: - Interceptors
99115

100116
/// Add a request interceptor (for request/response modification)
101-
// @objc public func addInterceptor(_ interceptor: RequestInterceptor) {
102-
// requestInterceptors.append(interceptor)
103-
// }
117+
@objc public func addInterceptor(_ interceptor: RequestInterceptorWrapper) {
118+
requestInterceptors.append(interceptor)
119+
}
104120

105-
// /// Add an event monitor (for network-level events like Android's network interceptor)
106-
// @objc public func addEventMonitor(_ monitor: EventMonitor) {
107-
// eventMonitors.append(monitor)
108-
// recreateSession() // Recreate session to apply new event monitors
109-
// }
121+
/// Add an event monitor (for network-level events like Android's network interceptor)
122+
@objc public func addEventMonitor(_ monitor: EventMonitorWrapper) {
123+
eventMonitors.append(monitor)
124+
recreateSession() // Recreate session to apply new event monitors
125+
}
110126

111127
// MARK: - Request Management
112128

@@ -1048,3 +1064,140 @@ public class ResponseSerializer: NSObject {
10481064
}
10491065
}
10501066
}
1067+
1068+
// MARK: - Event Monitor Wrapper
1069+
1070+
/// Wrapper around Alamofire's EventMonitor protocol to make it accessible from Objective-C/NativeScript
1071+
@objc(EventMonitorWrapper)
1072+
@objcMembers
1073+
public class EventMonitorWrapper: NSObject, EventMonitor {
1074+
1075+
// Callbacks that can be set from TypeScript
1076+
public var requestDidResumeCallback: ((URLRequest) -> Void)?
1077+
public var requestDidSuspendCallback: ((URLRequest) -> Void)?
1078+
public var requestDidCancelCallback: ((URLRequest) -> Void)?
1079+
public var requestDidFinishCallback: ((URLRequest) -> Void)?
1080+
public var requestDidCompleteCallback: ((URLRequest, HTTPURLResponse?, Error?) -> Void)?
1081+
public var dataTaskDidReceiveDataCallback: ((URLRequest, Data) -> Void)?
1082+
1083+
@objc public override init() {
1084+
super.init()
1085+
}
1086+
1087+
// EventMonitor protocol implementation
1088+
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
1089+
// No-op for now, can be added if needed
1090+
}
1091+
1092+
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
1093+
if let urlRequest = task.originalRequest {
1094+
requestDidCompleteCallback?(urlRequest, task.response as? HTTPURLResponse, error)
1095+
}
1096+
}
1097+
1098+
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
1099+
if let urlRequest = dataTask.originalRequest {
1100+
dataTaskDidReceiveDataCallback?(urlRequest, data)
1101+
}
1102+
}
1103+
1104+
public func request(_ request: Request, didResumeTask task: URLSessionTask) {
1105+
if let urlRequest = task.originalRequest {
1106+
requestDidResumeCallback?(urlRequest)
1107+
}
1108+
}
1109+
1110+
public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
1111+
if let urlRequest = task.originalRequest {
1112+
requestDidSuspendCallback?(urlRequest)
1113+
}
1114+
}
1115+
1116+
public func request(_ request: Request, didCancelTask task: URLSessionTask) {
1117+
if let urlRequest = task.originalRequest {
1118+
requestDidCancelCallback?(urlRequest)
1119+
}
1120+
}
1121+
1122+
public func request(_ request: Request, didFinishTask task: URLSessionTask, with error: AFError?) {
1123+
if let urlRequest = task.originalRequest {
1124+
requestDidFinishCallback?(urlRequest)
1125+
}
1126+
}
1127+
1128+
// Setter methods for callbacks (called from TypeScript)
1129+
@objc public func setRequestDidResume(_ callback: @escaping (URLRequest) -> Void) {
1130+
requestDidResumeCallback = callback
1131+
}
1132+
1133+
@objc public func setRequestDidSuspend(_ callback: @escaping (URLRequest) -> Void) {
1134+
requestDidSuspendCallback = callback
1135+
}
1136+
1137+
@objc public func setRequestDidCancel(_ callback: @escaping (URLRequest) -> Void) {
1138+
requestDidCancelCallback = callback
1139+
}
1140+
1141+
@objc public func setRequestDidFinish(_ callback: @escaping (URLRequest) -> Void) {
1142+
requestDidFinishCallback = callback
1143+
}
1144+
1145+
@objc public func setRequestDidComplete(_ callback: @escaping (URLRequest, HTTPURLResponse?, Error?) -> Void) {
1146+
requestDidCompleteCallback = callback
1147+
}
1148+
1149+
@objc public func setDataTaskDidReceiveData(_ callback: @escaping (URLRequest, Data) -> Void) {
1150+
dataTaskDidReceiveDataCallback = callback
1151+
}
1152+
}
1153+
1154+
// MARK: - Request Interceptor Wrapper
1155+
1156+
/// Wrapper around Alamofire's RequestInterceptor protocol to make it accessible from Objective-C/NativeScript
1157+
@objc(RequestInterceptorWrapper)
1158+
@objcMembers
1159+
public class RequestInterceptorWrapper: NSObject, RequestInterceptor {
1160+
1161+
// Callbacks that can be set from TypeScript
1162+
public var adaptCallback: ((URLRequest) -> URLRequest)?
1163+
public var retryCallback: ((URLRequest, Error, Int) -> Bool)?
1164+
1165+
@objc public override init() {
1166+
super.init()
1167+
}
1168+
1169+
// RequestAdapter protocol
1170+
public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
1171+
if let adaptCallback = adaptCallback {
1172+
let adapted = adaptCallback(urlRequest)
1173+
completion(.success(adapted))
1174+
} else {
1175+
completion(.success(urlRequest))
1176+
}
1177+
}
1178+
1179+
// RequestRetrier protocol
1180+
public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
1181+
guard let urlRequest = request.request else {
1182+
completion(.doNotRetry)
1183+
return
1184+
}
1185+
1186+
if let retryCallback = retryCallback {
1187+
let retryCount = request.retryCount
1188+
let shouldRetry = retryCallback(urlRequest, error, retryCount)
1189+
completion(shouldRetry ? .retry : .doNotRetry)
1190+
} else {
1191+
completion(.doNotRetry)
1192+
}
1193+
}
1194+
1195+
// Setter methods for callbacks (called from TypeScript)
1196+
@objc public func setAdapt(_ callback: @escaping (URLRequest) -> URLRequest) {
1197+
adaptCallback = callback
1198+
}
1199+
1200+
@objc public func setRetry(_ callback: @escaping (URLRequest, Error, Int) -> Bool) {
1201+
retryCallback = callback
1202+
}
1203+
}

src/https/typings/objc!AlamofireWrapper.d.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ declare class AlamofireWrapper extends NSObject {
1414

1515
// Request management
1616
cancelRequest(id: string): void;
17-
addInterceptor(interceptor: any): void;
18-
addEventMonitor(monitor: any): void;
17+
addInterceptor(interceptor: RequestInterceptorWrapper): void;
18+
addEventMonitor(monitor: EventMonitorWrapper): void;
1919

2020
// New clean API methods - using request IDs and NSHTTPURLResponse callbacks
2121
request(
@@ -193,3 +193,25 @@ declare const enum AFSSLPinningMode {
193193
PublicKey = 1,
194194
Certificate = 2
195195
}
196+
197+
declare class EventMonitorWrapper extends NSObject {
198+
static alloc(): EventMonitorWrapper;
199+
init(): EventMonitorWrapper;
200+
201+
// Callback setters
202+
setRequestDidResume(callback: (request: NSURLRequest) => void): void;
203+
setRequestDidSuspend(callback: (request: NSURLRequest) => void): void;
204+
setRequestDidCancel(callback: (request: NSURLRequest) => void): void;
205+
setRequestDidFinish(callback: (request: NSURLRequest) => void): void;
206+
setRequestDidComplete(callback: (request: NSURLRequest, response: NSHTTPURLResponse, error: NSError) => void): void;
207+
setDataTaskDidReceiveData(callback: (request: NSURLRequest, data: NSData) => void): void;
208+
}
209+
210+
declare class RequestInterceptorWrapper extends NSObject {
211+
static alloc(): RequestInterceptorWrapper;
212+
init(): RequestInterceptorWrapper;
213+
214+
// Callback setters
215+
setAdapt(callback: (request: NSURLRequest) => NSURLRequest): void;
216+
setRetry(callback: (request: NSURLRequest, error: NSError, retryCount: number) => boolean): void;
217+
}

0 commit comments

Comments
 (0)