Skip to content

Commit 83c2306

Browse files
Update AlamofireWrapper to use request IDs and NSHTTPURLResponse
Agent-Logs-Url: https://github.com/nativescript-community/https/sessions/b7e39983-090a-48a9-b8d2-1f08682e72e0 Co-authored-by: farfromrefug <655344+farfromrefug@users.noreply.github.com>
1 parent 9948008 commit 83c2306

1 file changed

Lines changed: 128 additions & 51 deletions

File tree

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

Lines changed: 128 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ public class AlamofireWrapper: NSObject {
1111
private var securityPolicy: SecurityPolicyWrapper?
1212
private var cacheResponseHandler: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
1313

14+
// Store active requests by ID for cancellation
15+
private var activeRequests: [String: Request] = [:]
16+
private let requestsLock = NSLock()
17+
18+
// Interceptors
19+
private var requestInterceptors: [RequestInterceptor] = []
20+
private var eventMonitors: [EventMonitor] = []
21+
1422
@objc public static let shared = AlamofireWrapper()
1523

1624
@objc public override init() {
@@ -78,14 +86,52 @@ public class AlamofireWrapper: NSObject {
7886
// Use allHostsMustBeEvaluated: false to allow default trust evaluation for non-pinned hosts
7987
let serverTrustManager = ServerTrustManager(allHostsMustBeEvaluated: false, evaluators: [:])
8088

81-
// Create new session with server trust manager
89+
// Create new session with server trust manager and interceptors
8290
// Keep the session alive by replacing it atomically
8391
session = Session(
8492
configuration: configuration,
85-
serverTrustManager: serverTrustManager
93+
serverTrustManager: serverTrustManager,
94+
eventMonitors: eventMonitors
8695
)
8796
}
8897

98+
// MARK: - Interceptors
99+
100+
/// Add a request interceptor (for request/response modification)
101+
@objc public func addInterceptor(_ interceptor: RequestInterceptor) {
102+
requestInterceptors.append(interceptor)
103+
}
104+
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+
}
110+
111+
// MARK: - Request Management
112+
113+
/// Store a request by ID
114+
private func storeRequest(_ request: Request, id: String) {
115+
requestsLock.lock()
116+
defer { requestsLock.unlock() }
117+
activeRequests[id] = request
118+
}
119+
120+
/// Remove a request by ID
121+
private func removeRequest(id: String) {
122+
requestsLock.lock()
123+
defer { requestsLock.unlock() }
124+
activeRequests.removeValue(forKey: id)
125+
}
126+
127+
/// Cancel a request by ID
128+
@objc public func cancelRequest(id: String) {
129+
requestsLock.lock()
130+
let request = activeRequests[id]
131+
requestsLock.unlock()
132+
request?.cancel()
133+
}
134+
89135
/// Get dispatch queue for responses
90136
private func responseQueue(mainThread: Bool?) -> DispatchQueue {
91137
// Default to main thread if not specified (matches Android behavior)
@@ -131,16 +177,18 @@ public class AlamofireWrapper: NSObject {
131177
_ urlString: String,
132178
_ parameters: NSDictionary?,
133179
_ headers: NSDictionary?,
180+
_ requestId: String,
134181
_ uploadProgress: ((Progress) -> Void)?,
135182
_ downloadProgress: ((Progress) -> Void)?,
136-
_ success: @escaping (URLSessionTask?, Any?) -> Void,
137-
_ failure: @escaping (URLSessionTask?, Error) -> Void
138-
) -> URLSessionTask? {
139-
return requestWithThreading(
183+
_ success: @escaping (NSHTTPURLResponse?, Any?) -> Void,
184+
_ failure: @escaping (NSHTTPURLResponse?, Error) -> Void
185+
) {
186+
requestWithThreading(
140187
method,
141188
urlString,
142189
parameters,
143190
headers,
191+
requestId,
144192
nil, // responseOnMainThread - defaults to true
145193
nil, // progressOnMainThread - defaults to responseOnMainThread
146194
uploadProgress,
@@ -156,18 +204,19 @@ public class AlamofireWrapper: NSObject {
156204
_ urlString: String,
157205
_ parameters: NSDictionary?,
158206
_ headers: NSDictionary?,
207+
_ requestId: String,
159208
_ responseOnMainThread: NSNumber?, // NSNumber wrapper for optional Bool
160209
_ progressOnMainThread: NSNumber?, // NSNumber wrapper for optional Bool
161210
_ uploadProgress: ((Progress) -> Void)?,
162211
_ downloadProgress: ((Progress) -> Void)?,
163-
_ success: @escaping (URLSessionTask?, Any?) -> Void,
164-
_ failure: @escaping (URLSessionTask?, Error) -> Void
165-
) -> URLSessionTask? {
212+
_ success: @escaping (NSHTTPURLResponse?, Any?) -> Void,
213+
_ failure: @escaping (NSHTTPURLResponse?, Error) -> Void
214+
) {
166215

167216
guard let url = URL(string: urlString) else {
168217
let error = NSError(domain: "AlamofireWrapper", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
169218
failure(nil, error)
170-
return nil
219+
return
171220
}
172221

173222
var request: URLRequest
@@ -182,11 +231,19 @@ public class AlamofireWrapper: NSObject {
182231
try requestSerializer.encodeParameters(parameters, into: &request, method: HTTPMethod(rawValue: method.uppercased()))
183232
} catch {
184233
failure(nil, error)
185-
return nil
234+
return
186235
}
187236

188237
var afRequest: DataRequest = session.request(request)
189238

239+
// Store request for cancellation
240+
storeRequest(afRequest, id: requestId)
241+
242+
// Apply interceptors
243+
for interceptor in requestInterceptors {
244+
afRequest = afRequest.interceptor(interceptor) as! DataRequest
245+
}
246+
190247
// Apply server trust evaluation if security policy is set
191248
if let host = url.host {
192249
afRequest = applyServerTrustValidation(afRequest, host: host)
@@ -216,26 +273,25 @@ public class AlamofireWrapper: NSObject {
216273
afRequest.response(queue: respQueue) { [weak self] response in
217274
guard let self = self else { return }
218275

219-
// Get the actual task from the DataRequest (available after request started)
220-
let task = response.request?.task
276+
// Remove request from active list
277+
self.removeRequest(id: requestId)
278+
279+
// Get the HTTP response
280+
let httpResponse = response.response as? NSHTTPURLResponse
221281

222282
if let error = response.error {
223283
let nsError = self.createNSError(from: error, response: response.response, data: response.data)
224-
failure(task, nsError)
284+
failure(httpResponse, nsError)
225285
return
226286
}
227287

228288
// Deserialize response based on responseSerializer
229289
if let data = response.data {
230-
// let result = self.responseSerializer.deserialize(data: data, response: response.response)
231-
success(task, data)
290+
success(httpResponse, data)
232291
} else {
233-
success(task, nil)
292+
success(httpResponse, nil)
234293
}
235294
}
236-
237-
// Return the task (it will be available after request starts)
238-
return afRequest.task
239295
}
240296

241297
// MARK: - Multipart Form Data
@@ -244,14 +300,16 @@ public class AlamofireWrapper: NSObject {
244300
@objc public func uploadMultipart(
245301
_ urlString: String,
246302
_ headers: NSDictionary?,
303+
_ requestId: String,
247304
_ constructingBodyWithBlock: @escaping (MultipartFormDataWrapper) -> Void,
248305
_ progress: ((Progress) -> Void)?,
249-
_ success: @escaping (URLSessionTask?, Any?) -> Void,
250-
_ failure: @escaping (URLSessionTask?, Error) -> Void
251-
) -> URLSessionTask? {
252-
return uploadMultipartWithThreading(
306+
_ success: @escaping (NSHTTPURLResponse?, Any?) -> Void,
307+
_ failure: @escaping (NSHTTPURLResponse?, Error) -> Void
308+
) {
309+
uploadMultipartWithThreading(
253310
urlString,
254311
headers,
312+
requestId,
255313
nil, // responseOnMainThread
256314
nil, // progressOnMainThread
257315
constructingBodyWithBlock,
@@ -265,18 +323,19 @@ public class AlamofireWrapper: NSObject {
265323
@objc public func uploadMultipartWithThreading(
266324
_ urlString: String,
267325
_ headers: NSDictionary?,
326+
_ requestId: String,
268327
_ responseOnMainThread: NSNumber?,
269328
_ progressOnMainThread: NSNumber?,
270329
_ constructingBodyWithBlock: @escaping (MultipartFormDataWrapper) -> Void,
271330
_ progress: ((Progress) -> Void)?,
272-
_ success: @escaping (URLSessionTask?, Any?) -> Void,
273-
_ failure: @escaping (URLSessionTask?, Error) -> Void
274-
) -> URLSessionTask? {
331+
_ success: @escaping (NSHTTPURLResponse?, Any?) -> Void,
332+
_ failure: @escaping (NSHTTPURLResponse?, Error) -> Void
333+
) {
275334

276335
guard let url = URL(string: urlString) else {
277336
let error = NSError(domain: "AlamofireWrapper", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
278337
failure(nil, error)
279-
return nil
338+
return
280339
}
281340

282341
let wrapper = MultipartFormDataWrapper()
@@ -292,13 +351,21 @@ public class AlamofireWrapper: NSObject {
292351
)
293352
} catch {
294353
failure(nil, error)
295-
return nil
354+
return
296355
}
297356

298357
var afRequest = session.upload(multipartFormData: { multipartFormData in
299358
wrapper.apply(to: multipartFormData)
300359
}, with: request)
301360

361+
// Store request for cancellation
362+
storeRequest(afRequest, id: requestId)
363+
364+
// Apply interceptors
365+
for interceptor in requestInterceptors {
366+
afRequest = afRequest.interceptor(interceptor) as! UploadRequest
367+
}
368+
302369
// Apply server trust evaluation if security policy is set
303370
if let host = url.host {
304371
afRequest = applyServerTrustValidation(afRequest, host: host)
@@ -321,25 +388,25 @@ public class AlamofireWrapper: NSObject {
321388
afRequest.response(queue: respQueue) { [weak self] response in
322389
guard let self = self else { return }
323390

324-
// Get the actual task from the DataRequest (available after request started)
325-
let task = response.request?.task
391+
// Remove request from active list
392+
self.removeRequest(id: requestId)
393+
394+
// Get the HTTP response
395+
let httpResponse = response.response as? NSHTTPURLResponse
326396

327397
if let error = response.error {
328398
let nsError = self.createNSError(from: error, response: response.response, data: response.data)
329-
failure(task, nsError)
399+
failure(httpResponse, nsError)
330400
return
331401
}
332402

333403
// Deserialize response based on responseSerializer
334404
if let data = response.data {
335-
// let result = self.responseSerializer.deserialize(data: data, response: response.response)
336-
success(task, data)
405+
success(httpResponse, data)
337406
} else {
338-
success(task, nil)
407+
success(httpResponse, nil)
339408
}
340409
}
341-
342-
return afRequest.task
343410
}
344411

345412
// MARK: - Upload Tasks
@@ -689,16 +756,17 @@ public class AlamofireWrapper: NSObject {
689756
_ urlString: String,
690757
_ parameters: NSDictionary?,
691758
_ headers: NSDictionary?,
759+
_ requestId: String,
692760
_ sizeThreshold: Int64,
693761
_ progress: ((Progress) -> Void)?,
694-
_ success: @escaping (URLSessionTask?, Any?, String?) -> Void,
695-
_ failure: @escaping (URLSessionTask?, Error) -> Void
696-
) -> URLSessionTask? {
762+
_ success: @escaping (NSHTTPURLResponse?, Any?, String?) -> Void,
763+
_ failure: @escaping (NSHTTPURLResponse?, Error) -> Void
764+
) {
697765

698766
guard let url = URL(string: urlString) else {
699767
let error = NSError(domain: "AlamofireWrapper", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
700768
failure(nil, error)
701-
return nil
769+
return
702770
}
703771

704772
var request: URLRequest
@@ -713,12 +781,20 @@ public class AlamofireWrapper: NSObject {
713781
try requestSerializer.encodeParameters(parameters, into: &request, method: HTTPMethod(rawValue: method.uppercased()))
714782
} catch {
715783
failure(nil, error)
716-
return nil
784+
return
717785
}
718786

719787
// Start as data request to get headers quickly
720788
var afRequest: DataRequest = session.request(request)
721789

790+
// Store request for cancellation
791+
storeRequest(afRequest, id: requestId)
792+
793+
// Apply interceptors
794+
for interceptor in requestInterceptors {
795+
afRequest = afRequest.interceptor(interceptor) as! DataRequest
796+
}
797+
722798
// Apply server trust evaluation if security policy is set
723799
if let host = url.host {
724800
afRequest = applyServerTrustValidation(afRequest, host: host)
@@ -735,12 +811,15 @@ public class AlamofireWrapper: NSObject {
735811
afRequest.response(queue: .main) { [weak self] response in
736812
guard let self = self else { return }
737813

738-
// Get the actual task from the DataRequest (available after request started)
739-
let task = response.request?.task
814+
// Remove request from active list
815+
self.removeRequest(id: requestId)
816+
817+
// Get the HTTP response
818+
let httpResponse = response.response as? NSHTTPURLResponse
740819

741820
if let error = response.error {
742821
let nsError = self.createNSError(from: error, response: response.response, data: response.data)
743-
failure(task, nsError)
822+
failure(httpResponse, nsError)
744823
return
745824
}
746825

@@ -762,23 +841,21 @@ public class AlamofireWrapper: NSObject {
762841
do {
763842
try data.write(to: tempFileURL)
764843
// Return with temp file path
765-
success(task, nil, tempFileURL.path)
844+
success(httpResponse, nil, tempFileURL.path)
766845
} catch {
767846
// Failed to write, just return data in memory
768847
let result = self.responseSerializer.deserialize(data: data, response: response.response)
769-
success(task, result, nil)
848+
success(httpResponse, result, nil)
770849
}
771850
} else {
772851
// Small response or threshold not set, return data in memory
773852
let result = self.responseSerializer.deserialize(data: data, response: response.response)
774-
success(task, result, nil)
853+
success(httpResponse, result, nil)
775854
}
776855
} else {
777-
success(task, nil, nil)
856+
success(httpResponse, nil, nil)
778857
}
779858
}
780-
781-
return task
782859
}
783860

784861
// MARK: - Helper Methods

0 commit comments

Comments
 (0)