Skip to content

Implemented swift additional best practices#20

Open
dragosv wants to merge 1 commit intomainfrom
best-practices
Open

Implemented swift additional best practices#20
dragosv wants to merge 1 commit intomainfrom
best-practices

Conversation

@dragosv
Copy link
Copy Markdown
Owner

@dragosv dragosv commented Feb 21, 2026

Description

Implemented swift additional best practices

Type of Change

  • [ X] Refactoring

Continue Tasks: ❌ 3 failed — View all

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling by replacing unsafe force unwraps with proper error throwing in request encoding.
    • Enhanced type safety by replacing force casts with guarded optional casts in response decoding.
  • Improvements

    • Added concurrency safety constraints to custom wait strategy configurations.
  • Tests

    • Added documentation to container cleanup logic for improved clarity.

@dragosv dragosv requested a review from Copilot February 21, 2026 08:50
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 21, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

The changes improve error handling and concurrency safety across the codebase. Force-unwraps and force-casts are replaced with safer error-throwing patterns. A public protocol gains a Sendable constraint for concurrency safety. Test teardown blocks receive clarifying comments. One method's return value handling warrants careful review.

Changes

Cohort / File(s) Summary
DockerClientSwift Error Handling
Sources/DockerClientSwift/APIs/DockerClient.swift, Sources/DockerClientSwift/Helper/HTTPClient+Codable.swift
Replaced force-unwraps and force-casts with guarded unwraps and proper error throwing. Computes bodyData safely before use. Introduces guard-let checks with noBodyData error throws for NoBody and String decoding paths.
Testcontainers Concurrency & Inspection
Sources/Testcontainers/DockerClient.swift, Sources/Testcontainers/WaitStrategy.swift
Updated getInstance() with guarded singleton unwrap. Modified inspectContainer(id:) to use getRawContainerInspect endpoint directly (return statement removed). Added @Sendable constraint to CustomWaitStrategy closure property and initializer parameter for concurrency safety.
Test Documentation
Tests/TestcontainersTests.swift
Enhanced teardown blocks for Postgres, MySQL, Redis, and MongoDB tests with multi-line defer statements and explanatory comments. Preserves existing asynchronous stop logic and error suppression.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 With trembling paws, we unwrap with care,
No more force-casts floating in the air!
Sendable closures, safe and sound,
Error-throwing patterns abound!
Our warren hops to safer ground, 🌿✨

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete, missing a detailed explanation of changes, only stating 'Implemented swift additional best practices' without specifics about the refactoring work. Add specific details about changes: removing force unwraps in DockerClient and HTTPClient+Codable, adding Sendable constraints to wait strategies, and updating singleton handling.
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Implemented swift additional best practices' is vague and generic, using non-descriptive terms that don't convey the specific changes made (safety improvements like removing force unwraps, adding Sendable constraints). Replace with a specific title like 'Remove force unwraps and add Sendable constraints for better Swift concurrency safety' to clearly indicate the main changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch best-practices

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors Swift code to follow additional best practices, focusing on improving safety and concurrency handling. The changes replace unsafe Swift patterns (force unwrapping, force casting, force try) with safer alternatives, add concurrency annotations, and improve code clarity.

Changes:

  • Replaced force unwraps, force casts, and force try operators with safe alternatives using guard statements and proper error propagation
  • Added @Sendable annotations to closure types for Swift concurrency safety
  • Added Sendable conformance to the Decoder protocol for thread-safe usage
  • Removed redundant code in container inspection logic
  • Added explanatory comments to defer blocks in tests explaining error suppression rationale

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Tests/TestcontainersTests.swift Added explanatory comments to defer blocks clarifying why cleanup errors are intentionally suppressed
Sources/Testcontainers/WaitStrategy.swift Added @Sendable annotation to CustomWaitStrategy closure type for concurrency safety
Sources/Testcontainers/DockerClient.swift Replaced force unwrap with guard statement in singleton getInstance; removed redundant container lookup code
Sources/DockerClientSwift/Helper/HTTPClient+Codable.swift Replaced force casts with safe guard-let patterns; added Sendable conformance to Decoder protocol
Sources/DockerClientSwift/APIs/DockerClient.swift Replaced try! with proper try for error propagation in endpoint body encoding

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if T.self == NoBody.self || T.self == NoBody?.self {
return NoBody() as! T
guard let result = NoBody() as? T else {
throw BodyError.noBodyData
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

The error thrown here (BodyError.noBodyData) is misleading. This guard is protecting against a type casting failure (NoBody() as? T), not missing body data. The cast should theoretically always succeed when T.self == NoBody.self, so this represents an unexpected type system issue rather than missing data. Consider using a more appropriate error or adding a comment explaining why this cast could fail. If this truly should never fail, a fatalError with a clear message might be more appropriate than throwing noBodyData.

Suggested change
throw BodyError.noBodyData
fatalError("Internal error: Failed to cast NoBody to expected type \(T.self). This indicates an unexpected type system issue and should never occur.")

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +74
guard let result = String(data: bodyData, encoding: .utf8) as? T else {
throw BodyError.noBodyData
}
return result
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

Similar to the NoBody case above, the error thrown here (BodyError.noBodyData) is misleading. The String(data:encoding:) call already succeeded (we have bodyData from line 67-68), so this guard is protecting against a type casting failure (String as? T), not missing data or encoding issues. When T.self == String.self, this cast should always succeed. Consider using a more appropriate error message or documenting why this cast could theoretically fail.

Suggested change
guard let result = String(data: bodyData, encoding: .utf8) as? T else {
throw BodyError.noBodyData
}
return result
guard let string = String(data: bodyData, encoding: .utf8) else {
throw BodyError.noBodyData
}
return string as! T

Copilot uses AI. Check for mistakes.
Comment on lines +160 to +163
guard let result = NoBody() as? T else {
throw EventLoopFuture<HTTPClient.Response>.BodyError.noBodyData
}
return result
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

Same issue as in the EventLoopFuture variant: BodyError.noBodyData is a misleading error for a type casting failure. When T.self == NoBody.self, the cast NoBody() as? T should always succeed, so this guard represents an unexpected type system issue rather than missing body data.

Suggested change
guard let result = NoBody() as? T else {
throw EventLoopFuture<HTTPClient.Response>.BodyError.noBodyData
}
return result
// For `NoBody` (and `NoBody?`) the cast is guaranteed by the type check above.
return NoBody() as! T

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +172
guard let result = String(data: bodyData, encoding: .utf8) as? T else {
throw EventLoopFuture<HTTPClient.Response>.BodyError.noBodyData
}
return result
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

Same issue as above: when T.self == String.self and String(data:encoding:) succeeded (creating a non-nil String), the cast String as? T should always succeed. The BodyError.noBodyData error message is misleading for what is actually a type casting failure scenario.

Suggested change
guard let result = String(data: bodyData, encoding: .utf8) as? T else {
throw EventLoopFuture<HTTPClient.Response>.BodyError.noBodyData
}
return result
guard let string = String(data: bodyData, encoding: .utf8) else {
throw EventLoopFuture<HTTPClient.Response>.BodyError.noBodyData
}
return string as! T

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +65
guard let instance = _shared.value else {
// Should never happen; assignment always succeeds.
return TestcontainersDockerClient()
}
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

The fallback logic here creates a new instance instead of returning the shared singleton, defeating the purpose of the singleton pattern. If assignment succeeds (which it should), the guard will never fail. Consider simplifying this to directly return the value after the nil check and assignment, or use a fatalError if this truly should never happen. A safer approach would be:

if _shared.value == nil {
    _shared.value = TestcontainersDockerClient()
}
return _shared.value! // Safe: we just assigned above if nil

Or with a more explicit check:

_shared.value = _shared.value ?? TestcontainersDockerClient()
return _shared.value!

The fallback to creating a new instance silently creates non-singleton behavior without any indication that something went wrong.

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 21, 2026

Codecov Report

❌ Patch coverage is 48.00000% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.96%. Comparing base (bf92e17) to head (3ff686f).

Files with missing lines Patch % Lines
.../DockerClientSwift/Helper/HTTPClient+Codable.swift 16.66% 10 Missing ⚠️
Sources/Testcontainers/DockerClient.swift 75.00% 2 Missing ⚠️
Sources/Testcontainers/WaitStrategy.swift 0.00% 1 Missing ⚠️

❌ Your patch check has failed because the patch coverage (48.00%) is below the target coverage (70.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #20      +/-   ##
==========================================
- Coverage   62.36%   61.96%   -0.41%     
==========================================
  Files          40       40              
  Lines        1201     1212      +11     
==========================================
+ Hits          749      751       +2     
- Misses        452      461       +9     
Flag Coverage Δ
swift 61.96% <48.00%> (-0.41%) ⬇️
unittests 61.96% <48.00%> (-0.41%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
Sources/DockerClientSwift/APIs/DockerClient.swift 85.18% <100.00%> (+1.18%) ⬆️
Sources/Testcontainers/WaitStrategy.swift 27.04% <0.00%> (ø)
Sources/Testcontainers/DockerClient.swift 54.42% <75.00%> (-1.06%) ⬇️
.../DockerClientSwift/Helper/HTTPClient+Codable.swift 45.56% <16.66%> (-3.73%) ⬇️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update bf92e17...3ff686f. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codacy-production
Copy link
Copy Markdown

Codacy's Analysis Summary

0 new issue (≤ 0 issue)
0 new security issue
4 complexity
0 duplications

Review Pull Request in Codacy →

AI Reviewer available: add the codacy-review label to get contextual insights without leaving GitHub.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants