Skip to content

Commit b321446

Browse files
Copilotfarfromrefug
andcommitted
Add early resolution and conditional streaming support
Agent-Logs-Url: https://github.com/nativescript-community/https/sessions/7bc451f5-53da-42f8-b904-b8680baa893e Co-authored-by: farfromrefug <655344+farfromrefug@users.noreply.github.com>
1 parent 07806a8 commit b321446

File tree

4 files changed

+303
-56
lines changed

4 files changed

+303
-56
lines changed

lerna.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"packages/*"
66
],
77
"npmClient": "yarn",
8-
"useWorkspaces": true,
98
"command": {
109
"publish": {
1110
"cleanupTempFiles": true

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

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,115 @@ public class AlamofireWrapper: NSObject {
513513
return downloadRequest.task as? URLSessionDownloadTask
514514
}
515515

516+
// MARK: - Early Resolution Support
517+
518+
/**
519+
* Download to temp file with early resolution on headers received.
520+
* Calls headersCallback as soon as headers are available (before download completes).
521+
* Calls completionHandler when download finishes with temp file path.
522+
* This allows inspecting status/headers early and cancelling before full download.
523+
*/
524+
@objc public func downloadToTempWithEarlyHeaders(
525+
_ method: String,
526+
_ urlString: String,
527+
_ parameters: NSDictionary?,
528+
_ headers: NSDictionary?,
529+
_ sizeThreshold: Int64,
530+
_ progress: ((Progress) -> Void)?,
531+
_ headersCallback: @escaping (URLResponse?, Int64) -> Void,
532+
_ completionHandler: @escaping (URLResponse?, String?, Error?) -> Void
533+
) -> URLSessionDownloadTask? {
534+
535+
guard let url = URL(string: urlString) else {
536+
let error = NSError(domain: "AlamofireWrapper", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
537+
completionHandler(nil, nil, error)
538+
return nil
539+
}
540+
541+
var request: URLRequest
542+
do {
543+
request = try requestSerializer.createRequest(
544+
url: url,
545+
method: HTTPMethod(rawValue: method.uppercased()),
546+
parameters: nil,
547+
headers: headers
548+
)
549+
// Encode parameters into the request
550+
try requestSerializer.encodeParameters(parameters, into: &request, method: HTTPMethod(rawValue: method.uppercased()))
551+
} catch {
552+
completionHandler(nil, nil, error)
553+
return nil
554+
}
555+
556+
// Track whether we've already called headersCallback
557+
var headersCallbackCalled = false
558+
let headersCallbackLock = NSLock()
559+
560+
// Create destination closure that saves to a temp file
561+
let destination: DownloadRequest.Destination = { temporaryURL, response in
562+
// Create a unique temp file path
563+
let tempDir = FileManager.default.temporaryDirectory
564+
let tempFileName = UUID().uuidString
565+
let tempFileURL = tempDir.appendingPathComponent(tempFileName)
566+
567+
// Call headersCallback on first response (only once)
568+
headersCallbackLock.lock()
569+
if !headersCallbackCalled {
570+
headersCallbackCalled = true
571+
headersCallbackLock.unlock()
572+
573+
let contentLength = response.expectedContentLength
574+
headersCallback(response, contentLength)
575+
} else {
576+
headersCallbackLock.unlock()
577+
}
578+
579+
return (tempFileURL, [.removePreviousFile, .createIntermediateDirectories])
580+
}
581+
582+
var downloadRequest = session.download(request, to: destination)
583+
584+
// Apply server trust evaluation if security policy is set
585+
if let secPolicy = securityPolicy, let host = url.host {
586+
downloadRequest = downloadRequest.validate { _, response, _ in
587+
guard let serverTrust = response.serverTrust else {
588+
return .failure(AFError.serverTrustEvaluationFailed(reason: .noServerTrust))
589+
}
590+
do {
591+
try secPolicy.evaluate(serverTrust, forHost: host)
592+
return .success(Void())
593+
} catch {
594+
return .failure(error)
595+
}
596+
}
597+
}
598+
599+
// Download progress
600+
if let progress = progress {
601+
downloadRequest = downloadRequest.downloadProgress { progressInfo in
602+
progress(progressInfo)
603+
}
604+
}
605+
606+
// Response handling (fires when download completes)
607+
downloadRequest.response(queue: .main) { response in
608+
if let error = response.error {
609+
completionHandler(response.response, nil, error)
610+
return
611+
}
612+
613+
// Return the temp file path on success
614+
if let tempFileURL = response.fileURL {
615+
completionHandler(response.response, tempFileURL.path, nil)
616+
} else {
617+
let error = NSError(domain: "AlamofireWrapper", code: -1, userInfo: [NSLocalizedDescriptionKey: "No file URL in download response"])
618+
completionHandler(response.response, nil, error)
619+
}
620+
}
621+
622+
return downloadRequest.task as? URLSessionDownloadTask
623+
}
624+
516625
// MARK: - Helper Methods
517626

518627
private func createNSError(from error: Error, response: HTTPURLResponse?, data: Data?) -> NSError {

src/https/request.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,22 @@ export interface HttpsRequestOptions extends HttpRequestOptions {
7171
* default to true. Android and iOS only store cookies in memory! it will be cleared after an app restart
7272
*/
7373
cookiesEnabled?: boolean;
74+
75+
/**
76+
* iOS only: Resolve request promise as soon as headers are received, before download completes.
77+
* This allows inspecting status/headers and cancelling before full download.
78+
* When true, toFile()/toJSON()/etc. will wait for download completion.
79+
* Default: false (waits for full download before resolving)
80+
*/
81+
earlyResolve?: boolean;
82+
83+
/**
84+
* iOS only: Response size threshold (in bytes) for using file download vs memory loading.
85+
* Responses larger than this will be downloaded to temp file (memory efficient).
86+
* Responses smaller will be loaded into memory (faster for small responses).
87+
* Default: 1048576 (1 MB). Set to 0 to always use memory, -1 to always use file download.
88+
*/
89+
downloadSizeThreshold?: number;
7490
}
7591

7692
export interface HttpsResponse<T = any> {

0 commit comments

Comments
 (0)