Reputation: 5720
I've created an abstract base class-like structure in Swift, using protocol extensions, as per this answer. This is a simplified example:
protocol AbstractBase {
var _constant: Int { get }
func _operation(_ val: Int) -> Int
}
public class ConcreteSub: AbstractBase {
let _constant: Int = 42
func _operation(_ val: Int) -> Int {
return val + 2
}
}
extension AbstractBase {
func mainOperation(_ val: Int) -> Int {
return _operation(val + _constant)
}
}
So basically, ConcreteSub
provides the implementation details needed by AbstractBase
, namely _constant
and _operation
.
I would like to hide those details from clients, and only expose mainOperation
. However, Swift does not allow me to make the members fileprivate on the protocol -- if I do the following
protocol AbstractBase {
fileprivate var _constant: Int { get }
// etc
I get "error: 'fileprivate' modifier cannot be used in protocols".
Nor can I apply the modifier on the subclass -- when I try
public class ConcreteSub: AbstractBase {
fileprivate let _constant: Int = 42
// etc
I get "error: property '_constant' must be declared internal because it matches a requirement in internal protocol 'AbstractBase'".
Lastly, when I make the whole protocol fileprivate, I get no compile errors, but I consistently run into Linking errors, which I guess is because the protocol is private, but the subclass is public.
Is there another way I'm missing?
Upvotes: 3
Views: 777
Reputation: 4865
I actually just ran into this issue. As of Swift 5.1, you can do this instead:
protocol MyProtocol {
var someVisibleVar: String { get }
func someVisibleFunc()
}
fileprivate extension MyProtocol {
var someFilePrivateVar: String {
"whatever"
}
func someFilePrivateFunc() {
print("someFilePrivateFunc() was called with \(someVisibleVar)")
}
}
class SomeClass: MyProtocol {
var someVisibleVar: String { "whatever" }
func someVisibleFunc() {
if someFilePrivateVar == someVisibleVar {
someFilePrivateFunc()
}
}
}
class SomeOtherClass: MyProtocol {
var someVisibleVar: String { "something else" }
func someVisibleFunc() {
if someFilePrivateVar == someVisibleVar {
someFilePrivateFunc()
}
}
}
Upvotes: 0
Reputation: 6204
When I need an abstract base with some properties/functions hidden I use class with some additional fatalErrors
and asserts
to crash whenever someone is trying to use Base instead of implementation.
public class AbstractBase {
init() {
assert(type(of: self) != AbstractBase.self, "Abstract class")
}
fileprivate var _constant: Int {
fatalError("Abstract class")
}
fileprivate func _operation(_ val: Int) -> Int {
fatalError("Abstract class")
}
func mainOperation(_ val: Int) -> Int {
return _operation(val + _constant)
}
}
public class ConcreteSub: AbstractBase {
fileprivate override var _constant: Int {
return 42
}
fileprivate override func _operation(_ val: Int) -> Int {
return val + 2
}
}
Upvotes: 1