Skip to content

Feat/upload ios#35

Merged
patrickkabwe merged 15 commits intomainfrom
feat/upload-ios
May 24, 2025
Merged

Feat/upload ios#35
patrickkabwe merged 15 commits intomainfrom
feat/upload-ios

Conversation

@patrickkabwe
Copy link
Copy Markdown
Owner

@patrickkabwe patrickkabwe commented May 24, 2025

Summary by CodeRabbit

  • New Features

    • Added the ability to generate large files of a specified size for testing purposes.
  • Bug Fixes

    • Improved error handling and standardized error messages for file and network operations.
  • Refactor

    • Simplified and unified error types across file and network operations.
    • Streamlined file download and upload APIs by removing the separate filename parameter from download methods and updating related documentation and usage examples.
    • Enhanced multipart file upload with streaming, progress reporting, and robust error handling.
    • Removed support declaration for visionOS platform in compatibility settings.
  • Documentation

    • Updated usage examples to reflect changes in the file download method signature.

@patrickkabwe patrickkabwe self-assigned this May 24, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2025

"""

Walkthrough

The changes update the file download and upload flows, error handling, and platform support. The downloadFile API is simplified by removing the filename parameter, and error enums are consolidated for consistency. The iOS uploader is refactored to stream multipart uploads from disk, and platform support for visionOS is removed from the podspec.

Changes

File(s) Change Summary
NitroFS.podspec Removed visionOS platform support; now only declares iOS compatibility.
example/App.tsx Updated download URLs to include filename; removed filename from downloadFile call; minor formatting adjustment.
ios/HybridNitroFs.swift, ios/NitroFSImpl.swift, src/specs/nitro-fs.nitro.ts, android/src/main/java/com/nitrofs/HybridNitroFS.kt, android/src/main/java/com/nitrofs/NitroFSImpl.kt Removed filename parameter from downloadFile methods and interface; updated error handling for consistency; adjusted return types.
ios/NitroFSFileDownloader.swift Removed filename parameter from downloadFile and makeRequest; standardized error types/messages; added session invalidation.
ios/NitroFSFileUploader.swift Refactored to stream multipart uploads from disk; added helper methods; improved error handling and progress reporting; moved delegate conformance to extension.
ios/Utils.swift Consolidated NitroFSError enum; removed NetworkError; added generateLargeFile function.
android/src/main/java/com/nitrofs/FileDownloader.kt Removed filename parameter from downloadFile; now returns NitroFile; uses full URL directly in HTTP request.

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant NitroFS (JS/TS)
    participant HybridNitroFS (Swift)
    participant NitroFSImpl
    participant NitroFSFileDownloader

    App->>NitroFS: downloadFile(serverUrl, destinationPath, onProgress)
    NitroFS->>HybridNitroFS: downloadFile(serverUrl, destinationPath, onProgress)
    HybridNitroFS->>NitroFSImpl: downloadFile(serverUrl, destinationPath, onProgress)
    NitroFSImpl->>NitroFSFileDownloader: downloadFile(serverUrl, destinationPath, onProgress)
    NitroFSFileDownloader-->>NitroFSImpl: NitroFile (or error)
    NitroFSImpl-->>HybridNitroFS: NitroFile (or error)
    HybridNitroFS-->>NitroFS: NitroFile (or error)
    NitroFS-->>App: NitroFile (or error)
Loading
sequenceDiagram
    participant App
    participant NitroFS (JS/TS)
    participant HybridNitroFS (Swift)
    participant NitroFSImpl
    participant NitroFSFileUploader

    App->>NitroFS: uploadFile(file, options, onProgress)
    NitroFS->>HybridNitroFS: uploadFile(file, options, onProgress)
    HybridNitroFS->>NitroFSImpl: uploadFile(file, options, onProgress)
    NitroFSImpl->>NitroFSFileUploader: uploadFile(file, options, onProgress)
    NitroFSFileUploader->>NitroFSFileUploader: createMultipartBodyFile(file, ...)
    NitroFSFileUploader->>Server: Upload multipart file
    NitroFSFileUploader-->>NitroFSImpl: Success or error
    NitroFSImpl-->>HybridNitroFS: Success or error
    HybridNitroFS-->>NitroFS: Success or error
    NitroFS-->>App: Success or error
Loading

Possibly related PRs

  • Feat/download file #30: Refactors the downloadFile method signatures across multiple platform implementations by removing the fileName parameter, closely related to this PR’s API simplification.

Suggested labels

released

Poem

A bunny hopped through code today,
Tidying errors along the way.
No more visionOS in sight—
Just iOS, clean and bright!
Downloads now need less to tell,
Uploads stream and work so well.
🐇✨ Debugging’s fun—can’t you tell?
"""

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef95465 and bfd8c71.

📒 Files selected for processing (1)
  • ios/NitroFSFileUploader.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ios/NitroFSFileUploader.swift
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Build iOS Example App (new architecture)
  • GitHub Check: Build iOS Example App (old architecture)
  • GitHub Check: Build Android Example App (new architecture)
  • GitHub Check: Build Android Example App (old architecture)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
ios/NitroFSImpl.swift (1)

115-116: Misleading error message for downloadFile unavailability

The thrown message references “stat file”, which is unrelated to the downloadFile operation and will confuse consumers troubleshooting download failures.

-            throw NitroFSError.unavailable(message: "Failed to stat file. fileDownloader is unavailable")
+            throw NitroFSError.unavailable(message: "Failed to download file. fileDownloader is unavailable")
ios/Utils.swift (1)

24-28: Consider surfacing localizedDescription for unified error reporting

Each case already carries a message. Conforming NitroFSError to LocalizedError (or CustomStringConvertible) would let callers access error.localizedDescription instead of manually switching on the enum.

extension NitroFSError: LocalizedError {
    var errorDescription: String? {
        switch self { case let .unavailable(msg),
                           .fileError(msg),
                           .networkError(msg),
                           .encodingError(msg):
            return msg
        }
    }
}
ios/NitroFSFileDownloader.swift (2)

56-65: Validate & percent-encode the URL

URL(string:) assumes the input is already percent-encoded. If serverUrl comes from user input it may contain spaces or other illegal characters and the request will fail at runtime.

Consider:

guard let encoded = serverUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
      let url = URL(string: encoded) else {
    throw URLError(.badURL)
}

73-82: Differentiate network vs server-side errors

The same networkError case is used for both transport failures and non-2xx HTTP codes. Distinguishing them (transportError vs httpError(code:)) can simplify client-side handling (e.g., retry policy vs user-visible error).

ios/NitroFSFileUploader.swift (2)

135-140: SwiftLint: replace unused closure parameters with “_”

session, task, and bytesSent are unused, triggering the SwiftLint warning reported by static analysis.

-    func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64,
-                    totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
+    func urlSession(_ _: URLSession, task _: URLSessionTask, didSendBodyData _: Int64,
+                    totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {

72-104: createMultipartBodyFile marked async but performs no asynchronous work

Removing async simplifies call-sites and avoids unnecessary suspension points.

-    private func createMultipartBodyFile(... ) async throws -> URL {
+    private func createMultipartBodyFile(... ) throws -> URL {

-        let multipartFile = try await createMultipartBodyFile(
+        let multipartFile = try createMultipartBodyFile(
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 958ce38 and f0277bc.

⛔ Files ignored due to path filters (8)
  • example/ios/Podfile.lock is excluded by !**/*.lock
  • nitrogen/generated/android/c++/JHybridNitroFSSpec.cpp is excluded by !**/generated/**
  • nitrogen/generated/android/c++/JHybridNitroFSSpec.hpp is excluded by !**/generated/**
  • nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofs/HybridNitroFSSpec.kt is excluded by !**/generated/**
  • nitrogen/generated/ios/c++/HybridNitroFSSpecSwift.hpp is excluded by !**/generated/**
  • nitrogen/generated/ios/swift/HybridNitroFSSpec.swift is excluded by !**/generated/**
  • nitrogen/generated/ios/swift/HybridNitroFSSpec_cxx.swift is excluded by !**/generated/**
  • nitrogen/generated/shared/c++/HybridNitroFSSpec.hpp is excluded by !**/generated/**
📒 Files selected for processing (8)
  • NitroFS.podspec (1 hunks)
  • example/App.tsx (2 hunks)
  • ios/HybridNitroFs.swift (6 hunks)
  • ios/NitroFSFileDownloader.swift (3 hunks)
  • ios/NitroFSFileUploader.swift (1 hunks)
  • ios/NitroFSImpl.swift (4 hunks)
  • ios/Utils.swift (1 hunks)
  • src/specs/nitro-fs.nitro.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
ios/NitroFSFileDownloader.swift (2)
ios/NitroFSImpl.swift (1)
  • downloadFile (109-122)
ios/HybridNitroFs.swift (1)
  • downloadFile (126-139)
🪛 SwiftLint (0.57.0)
ios/NitroFSFileUploader.swift

[Warning] 48-48: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build Android Example App (new architecture)
  • GitHub Check: Build iOS Example App (old architecture)
  • GitHub Check: Build iOS Example App (new architecture)
🔇 Additional comments (8)
NitroFS.podspec (1)

13-13: Verify the intentional removal of visionOS support.

This change removes visionOS platform support, which is a breaking change for any existing users targeting visionOS. Please ensure this removal is intentional and consider documenting this breaking change in release notes.

example/App.tsx (2)

12-14: LGTM! URL structure updated to include filename.

The download URLs now properly include the filename as part of the path, which aligns with the simplified downloadFile API that no longer requires a separate filename parameter.


122-122: LGTM! Minor formatting improvement.

Adding the space before the url key improves code readability and maintains consistency.

src/specs/nitro-fs.nitro.ts (2)

118-118: LGTM! Documentation updated to reflect new API.

The example code correctly demonstrates the simplified downloadFile method without the separate filename parameter.


124-124: LGTM! Method signature simplified effectively.

Removing the fileName parameter simplifies the API by embedding the filename directly in the serverUrl. This reduces complexity and makes the interface more intuitive.

ios/HybridNitroFs.swift (3)

33-33: LGTM! Error handling consolidated for consistency.

The consolidation of specific error types into more general NitroFSError.fileError improves consistency across the codebase, despite the message issues noted separately.

Also applies to: 44-44, 55-55, 66-66


102-102: LGTM! Appropriate error type for stat operation.

Using NitroFSError.encodingError for stat operation failures is more semantically appropriate than the previous error type.


126-126: LGTM! Method signature updated to match TypeScript interface.

The downloadFile method signature correctly removes the fileName parameter, aligning with the simplified API design across the codebase.

Comment thread ios/HybridNitroFs.swift Outdated
Comment thread ios/HybridNitroFs.swift
} catch {
os_log("Failed to read file content: \(error.localizedDescription)")
throw NitroFSError.nitroFSWriteFailed(message: "Failed to write file content to disk")
throw NitroFSError.fileError(message: "Failed to write file content to disk")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect error message in readFile method.

The error message says "write" but this is in the readFile method, so it should say "read".

-                throw  NitroFSError.fileError(message: "Failed to write file content to disk")
+                throw  NitroFSError.fileError(message: "Failed to read file content from disk")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
throw NitroFSError.fileError(message: "Failed to write file content to disk")
throw NitroFSError.fileError(message: "Failed to read file content from disk")
🤖 Prompt for AI Agents
In ios/HybridNitroFs.swift at line 44, the error message in the readFile method
incorrectly states "Failed to write file content to disk." Change the message to
"Failed to read file content from disk" to accurately reflect the read operation
failure.

Comment thread ios/Utils.swift
Comment thread ios/NitroFSFileUploader.swift Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
android/src/main/java/com/nitrofs/NitroFSImpl.kt (1)

93-108: API refactoring looks good with a minor improvement suggestion.

The method signature changes align well with the cross-platform API simplification. The null checking logic is appropriate, and the return type change improves the API consistency.

Consider using a more specific exception type for better error handling:

-        } else {
-            throw Error("Error downloading file")
-        }
+        } else {
+            throw RuntimeException("Failed to download file from: $serverUrl")
+        }
android/src/main/java/com/nitrofs/FileDownloader.kt (1)

43-54: Excellent content-Type extraction and NitroFile construction.

The implementation correctly extracts the MIME type from the HTTP response with a sensible fallback, and constructs the NitroFile object with all required fields from the TypeScript interface.

Consider adding HTTP status code validation to handle error responses:

            }.execute { response ->
+                if (!response.status.isSuccess()) {
+                    throw RuntimeException("HTTP ${response.status.value}: Failed to download file")
+                }
                contentType = response.headers["Content-Type"] ?: "application/octet-stream"
                val channel: ByteReadChannel = response.body()
                channel.copyAndClose(outputFile.writeChannel())
            }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0277bc and 2633c2b.

📒 Files selected for processing (3)
  • android/src/main/java/com/nitrofs/FileDownloader.kt (3 hunks)
  • android/src/main/java/com/nitrofs/HybridNitroFS.kt (1 hunks)
  • android/src/main/java/com/nitrofs/NitroFSImpl.kt (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
android/src/main/java/com/nitrofs/FileDownloader.kt (1)
src/type.ts (1)
  • NitroFile (36-40)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Build iOS Example App (old architecture)
  • GitHub Check: Build iOS Example App (new architecture)
  • GitHub Check: Build Android Example App (new architecture)
  • GitHub Check: Build Android Example App (old architecture)
🔇 Additional comments (4)
android/src/main/java/com/nitrofs/FileDownloader.kt (3)

3-3: Good addition of NitroFile import.

The import is correctly added to support the new return type.


18-26: Method signature update aligns with API simplification.

The removal of the fileName parameter and addition of NitroFile? return type correctly implements the new API design.


31-31: Correct use of full serverUrl.

Using the full serverUrl directly in the HTTP request is the right approach for the simplified API.

android/src/main/java/com/nitrofs/HybridNitroFS.kt (1)

135-135: Correct API signature update.

The method call correctly matches the updated nitroFsImpl.downloadFile signature by removing the fileName parameter while preserving all other functionality.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
ios/Utils.swift (1)

43-67: Partial writes not handled correctly - same issue as previous review.

The generateLargeFile function has the same issue identified in previous reviews: OutputStream.write can legitimately return fewer bytes than requested without signifying an error, but the current implementation treats partial writes as complete success, potentially resulting in incomplete file generation.

The fix remains the same - implement a retry loop for partial writes:

-        let written = outputStream.write(buffer, maxLength: toWrite)
-        if written <= 0 {
-            throw NSError(domain: "FileError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to write data"])
-        }
-        bytesWritten += written
+        var totalWritten = 0
+        while totalWritten < toWrite {
+            let written = outputStream.write(buffer.advanced(by: totalWritten),
+                                             maxLength: toWrite - totalWritten)
+            if written <= 0 {
+                throw NSError(domain: "FileError",
+                              code: 2,
+                              userInfo: [NSLocalizedDescriptionKey: "Failed to write data"])
+            }
+            totalWritten += written
+        }
+        bytesWritten += totalWritten
ios/NitroFSFileUploader.swift (1)

45-66: Session leakage and retain cycle risk - same issue as previous review.

The issue identified in previous reviews persists: the URLSession retains its delegate (self), and without proper session invalidation, resources may leak. The [weak self] capture helps but doesn't fully resolve the session retention issue.

Add session invalidation:

 let task = session.uploadTask(with: request, fromFile: multipartFile) { data, response, error in
     try? FileManager.default.removeItem(at: multipartFile)
+    session.finishTasksAndInvalidate()

     if let error = error {
+        session.invalidateAndCancel()
         continuation.resume(throwing: error)
         return
     }
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 48-48: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

🧹 Nitpick comments (1)
ios/NitroFSFileUploader.swift (1)

48-48: Remove unused parameter to address static analysis warning.

The data parameter in the upload task completion handler is not used.

Apply this diff to fix the warning:

-let task = session.uploadTask(with: request, fromFile: multipartFile) { data, response, error in
+let task = session.uploadTask(with: request, fromFile: multipartFile) { _, response, error in
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 48-48: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2633c2b and ce42c2d.

📒 Files selected for processing (7)
  • android/src/main/java/com/nitrofs/FileDownloader.kt (3 hunks)
  • android/src/main/java/com/nitrofs/NitroFSImpl.kt (1 hunks)
  • ios/HybridNitroFs.swift (6 hunks)
  • ios/NitroFSFileDownloader.swift (3 hunks)
  • ios/NitroFSFileUploader.swift (1 hunks)
  • ios/NitroFSImpl.swift (4 hunks)
  • ios/Utils.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • android/src/main/java/com/nitrofs/NitroFSImpl.kt
  • ios/HybridNitroFs.swift
  • ios/NitroFSFileDownloader.swift
  • android/src/main/java/com/nitrofs/FileDownloader.kt
  • ios/NitroFSImpl.swift
🧰 Additional context used
🪛 SwiftLint (0.57.0)
ios/NitroFSFileUploader.swift

[Warning] 48-48: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Build iOS Example App (new architecture)
  • GitHub Check: Build iOS Example App (old architecture)
  • GitHub Check: Build Android Example App (new architecture)
  • GitHub Check: Build Android Example App (old architecture)
🔇 Additional comments (3)
ios/Utils.swift (1)

23-40: LGTM! Well-designed error consolidation.

The refactoring of NitroFSError with associated values provides better error messaging flexibility, and the LocalizedError conformance enhances user experience with proper error descriptions.

ios/NitroFSFileUploader.swift (2)

73-104: Well-implemented multipart body creation.

The createMultipartBodyFile method properly handles resource management with defer statements and creates correctly formatted multipart data with CRLF line endings.


145-169: Excellent partial write handling in OutputStream extension.

The OutputStream.write(_:String) extension properly handles partial writes with a retry loop, which is the correct approach for robust stream writing.

Comment on lines +120 to +124
if bytesRead > 0 {
let bytesWritten = output.write(buffer, maxLength: bytesRead)
if bytesWritten != bytesRead {
throw NitroFSError.encodingError(message: "Failed to write file data to output stream")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle partial writes correctly in file streaming.

Similar to the issue in generateLargeFile, the comparison bytesWritten != bytesRead doesn't handle partial writes correctly. OutputStream.write can legitimately return fewer bytes than requested.

Apply this fix to handle partial writes:

-            if bytesRead > 0 {
-                let bytesWritten = output.write(buffer, maxLength: bytesRead)
-                if bytesWritten != bytesRead {
-                    throw NitroFSError.encodingError(message: "Failed to write file data to output stream")
-                }
-            }
+            if bytesRead > 0 {
+                var totalWritten = 0
+                while totalWritten < bytesRead {
+                    let written = output.write(buffer.advanced(by: totalWritten), maxLength: bytesRead - totalWritten)
+                    if written <= 0 {
+                        throw NitroFSError.encodingError(message: "Failed to write file data to output stream")
+                    }
+                    totalWritten += written
+                }
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if bytesRead > 0 {
let bytesWritten = output.write(buffer, maxLength: bytesRead)
if bytesWritten != bytesRead {
throw NitroFSError.encodingError(message: "Failed to write file data to output stream")
}
if bytesRead > 0 {
var totalWritten = 0
while totalWritten < bytesRead {
let written = output.write(buffer.advanced(by: totalWritten), maxLength: bytesRead - totalWritten)
if written <= 0 {
throw NitroFSError.encodingError(message: "Failed to write file data to output stream")
}
totalWritten += written
}
}
🤖 Prompt for AI Agents
In ios/NitroFSFileUploader.swift around lines 120 to 124, the code incorrectly
treats any partial write to the output stream as an error by comparing
bytesWritten directly to bytesRead. To fix this, implement a loop that continues
writing the remaining bytes until all bytesRead are written or an error occurs,
properly handling partial writes by adjusting the buffer pointer and remaining
length accordingly.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
ios/NitroFSFileUploader.swift (1)

123-127: ⚠️ Potential issue

Critical: Partial write handling still not addressed

This is the same issue flagged in previous reviews. The comparison bytesWritten != bytesRead doesn't handle partial writes correctly, as OutputStream.write can legitimately write fewer bytes than requested.

Apply this fix to handle partial writes correctly:

-            if bytesRead > 0 {
-                let bytesWritten = output.write(buffer, maxLength: bytesRead)
-                if bytesWritten != bytesRead {
-                    throw NitroFSError.encodingError(message: "Failed to write file data to output stream")
-                }
-            }
+            if bytesRead > 0 {
+                var totalWritten = 0
+                while totalWritten < bytesRead {
+                    let written = output.write(buffer.advanced(by: totalWritten), maxLength: bytesRead - totalWritten)
+                    if written <= 0 {
+                        throw NitroFSError.encodingError(message: "Failed to write file data to output stream")
+                    }
+                    totalWritten += written
+                }
+            }
🧹 Nitpick comments (1)
ios/NitroFSFileUploader.swift (1)

19-70: Address unused parameter warning and improve code clarity

The implementation looks solid overall with proper async/await handling, session management, and cleanup. However, there's an unused parameter in the completion closure.

Apply this diff to address the SwiftLint warning:

-            let task = session.uploadTask(with: request, fromFile: multipartFile) { data, response, error in
+            let task = session.uploadTask(with: request, fromFile: multipartFile) { _, response, error in
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 48-48: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce42c2d and ef95465.

📒 Files selected for processing (2)
  • ios/NitroFSFileDownloader.swift (5 hunks)
  • ios/NitroFSFileUploader.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ios/NitroFSFileDownloader.swift
🧰 Additional context used
🪛 SwiftLint (0.57.0)
ios/NitroFSFileUploader.swift

[Warning] 48-48: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

🔇 Additional comments (4)
ios/NitroFSFileUploader.swift (4)

10-10: LGTM: Protocol conformance update

Good change to implement URLSessionDataDelegate to support upload progress reporting.


76-107: LGTM: Multipart body creation

The multipart/form-data format implementation is correct with proper headers, CRLF line endings, and boundary formatting. The temporary file approach efficiently handles large uploads without loading everything into memory.


138-143: LGTM: Progress reporting implementation

The progress callback properly dispatches to the main queue and provides both current and total bytes for upload progress tracking.


148-172: LGTM: Robust OutputStream extension

Excellent implementation that properly handles partial writes for string data. The use of withUnsafeBytes and the loop to handle remaining bytes ensures all data is written correctly.

@patrickkabwe patrickkabwe merged commit 60ab2b1 into main May 24, 2025
5 checks passed
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version 0.5.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@coderabbitai coderabbitai Bot mentioned this pull request Nov 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant