I'm building an API that uses ResultBuilder with structs as components and chaining methods as modifiers. Sometimes different components have the same modifier, e.g.:
var resultBuilderContent = {
Component1()
.modifier(x: 1)
Component2()
.modifier(x: 2)
}
I'd like to implement 'modifier' method in a protocol to avoid duplicating the code. But the method relies on internal properties and if I implement it like this:
protocol SameModifierProtocol{
var x: Int { get set }
}
extension SameModifierProtocol{
public func modifier(x: Int)->SameModifierProtocol{
var s = self
s.x = x
return s
}
}
public struct Component: SameModifierProtocol{
var x: Int = 0
public init(){}
}
// in another module
let c = Component().modifier(x: 1)
I get the error: "'modifier' is inaccessible due to 'internal' protection level".
If I try to differentiate access levels between two protocols like this:
protocol SameModifierProtocol{
var x: Int { get set }
}
public protocol ReceivingSameModifier{
}
extension ReceivingSameModifier where Self: SameModifierProtocol{
public func modifier(x: Int)->ReceivingSameModifier{
var s = self
s.x = x
return s
}
}
I get the following error: "Cannot declare a public instance method in an extension with internal requirements".
This is where I stuck. What are my options?
Following this discussion from Swift forum, there are two ways to solve the issue.
First one is straightforward:
/// The publically visible capabilities.
public protocol SameModifierProtocol {
func modifier(x: Int) -> SameModifierProtocol
}
/// The internal requirements on which the default implementation relies.
internal protocol SynthesizedSameModifierProtocolConformance:
SameModifierProtocol {
var x: Int { get set }
}
/// The default implementation.
extension SynthesizedSameModifierProtocolConformance {
public func modifier(x: Int) -> SameModifierProtocol{
var s = self
s.x = x
return s
}
}
/// Conforms to the public protocol
/// and requests the default implementation from the internal one.
/// Clients can only see the public protocol.
public struct Component: SynthesizedSameModifierProtocolConformance {
internal var x: Int = 0
public init() {}
}
The second involves an unofficial feature @_spi, that allows to make implementation details unexposed in a public protocol:
public protocol SameModifierProtocol {
@_spi(SameModifier) var x: Int { get set }
}
extension SameModifierProtocol {
public func modifier(x: Int) -> Self {
var s = self
s.x = x
return s
}
}
public struct Component: SameModifierProtocol {
@_spi(SameModifier) public var x: Int = 0
public init() {}
}