Xcode 16 Retroactive Conformance Warning
When migrating projects to Xcode 16, you might encounter this new warning:
Extension declares a conformance of imported type 'Date' to imported protocol 'Identifiable'; this will not behave correctly if the owners of 'Foundation' introduce this conformance in the future
This occurs when declaring protocol conformances for types outside your control—such as extending Apple’s Date
to adopt Identifiable
:
extension Date: Identifiable {
public var id: TimeInterval { timeIntervalSince1970 } // Warning triggers here
}
The warning surfaces for many common protocols like CustomStringConvertible
, Equatable
, Hashable
, and Sendable
.
Why This Matters
This warning (implemented through Swift Evolution SE-0364) protects against ambiguous behavior when:
- Module owners add the conformance later: The compiler won't know whether to use your implementation or the module's official version.
- Multiple conflicting conformances exist: If both your code and the original module declare conformance to the same protocol, runtime behavior becomes unpredictable.
Recommended Solutions
1. Wrap External Types
Create a custom wrapper for the type instead of extending it directly. This avoids protocol conflicts entirely and gives you full control:
struct SafeDate: Identifiable {
let rawValue: Date
var id: TimeInterval { rawValue.timeIntervalSince1970 }
}
When to use: For non-Sendable
protocols in shared code. Ideal when you need control over conformance logic.
2. Use @retroactive
Attribute (Xcode 16+)
Acknowledge the risk explicitly by marking the conformance as retroactive:
extension Date: @retroactive Identifiable {
public var id: TimeInterval { timeIntervalSince1970 }
}
When to use: When you're aware of the risks and need original-type conformance. Wrap in a compiler check to maintain compatibility with older Xcode versions:
#if hasFeature(RetroactiveAttribute)
extension Date: @retroactive Identifiable {
public var id: TimeInterval { timeIntervalSince1970 }
}
#endif
3. Fully Qualify Module Names
Work across Xcode versions by explicitly declaring module origins:
extension Foundation.Date: Swift.Identifiable {
public var id: TimeInterval { timeIntervalSince1970 }
}
When to use: When @retroactive
isn't viable, and you need backwards compatibility.
4. Restructure Context-Specific Conformances
For demo/test targets: If a conformance only exists in sandboxed code (like an Example app), move both the protocol and conformance there instead of a shared module:
// Define within ExampleApp target
protocol DemoIdentifiable: Identifiable {}
extension Date: DemoIdentifiable { /* ... */ }
For libraries: Submit conformance requests to the original module owner (optimal but slow).
Key Recommendations
- Avoid unqualified extensions for public types in production shared modules.
- Use type wrappers for maximum stability.
- Reserve
@retroactive
/qualified names for cases where wrappers cause significant overhead. - Audit existing extensions—many trivial conformances (like
Hashable
for basic types) might already exist in current SDKs.
Maintenance Tip
Always specify public
access explicitly in extensions to avoid default internal
visibility surprises—especially in mixed-module projects.
By proactively structuring conformances, you maintain predictable behavior while silencing Xcode's warnings about protocol collisions.