I understand that Swift protocols are meant to define a public blueprint for types, and currently, access control modifiers like private are not allowed in protocol property declarations.
For example, this gives a compiler error:
protocol IAccount {
func deposit(amount: Decimal)
func withdraw(amount: Decimal)
func transfer(to: IAccount, amount: Decimal)
func getAccountNumber() -> Int
private var balance: Decimal { get } // ❌ Error
}
From what I know, protocols cannot have private properties because everything defined in a protocol must be accessible to all conforming types. That makes sense, since the purpose of a protocol is to define what a conforming type must implement publicly.
However, wouldn't it be useful if Swift supported something like an AccessControl protocol or keyword, allowing a protocol to specify the intended visibility that conforming types should apply?
(Everything could still remain public as before, but the keyword would simply indicate the intended access level for the conforming type. For example, you could choose whether or not to implement the private member—it would just serve as a preference or guideline)
For example:
AccessControl protocol IAccount {
func deposit(amount: Decimal)
func withdraw(amount: Decimal)
func transfer(to: IAccount, amount: Decimal)
func getAccountNumber() -> Int
private var balance: Decimal { get }
}
In this hypothetical example, the protocol still exposes its full interface to the outside, but conforming types could be required (or allowed) to apply specific access levels—like requiring balance to be implemented as private.
My questions:
Why doesn't Swift support private properties in protocols?
Is there any language design reason Swift couldn’t allow visibility annotations in protocols (perhaps just as a suggestion or requirement for the conforming type)?
Is there a known design pattern in Swift that achieves something similar?
Why this could be implemented:
Improved encapsulation: It allows protocol designers to express intent about implementation details.
Stronger API contracts: Helps enforce consistency in how conforming types manage internal state.
Optional enforcement: Swift could treat it as an advisory level, allowing flexibility without breaking current designs.
Why this might not be implemented:
Breaks protocol abstraction: Protocols are meant to describe external interfaces, not internal implementation.
Inconsistent access control: Allowing private members in protocols could confuse access rules and violate the protocol’s role.
So, the way I see it, keywords could be used to change the behavior or meaning of a protocol. Without a keyword, a protocol behaves as it does today. But with specific keywords, it could gain additional functionality to support different use cases.
A superficial example:
protocol {
// Standard protocol behavior
}
keyword protocol {
// Enhanced or altered behavior
}
// Or even something like:
abstract protocol {
// Similar to an abstract class: disable extensions and allow implementation inside
}
The idea is that you could add—or even remove—functionality from a protocol. In this way, protocols could serve as a central concept that takes on different roles depending on the keyword used.
Maybe if this approach doesn’t align with Swift’s design philosophy, it could be implemented in a way similar to a @propertyWrapper ?
A small example of this idea could be seen in the Facade pattern. You might have a protocol with a specific keyword designed for subsystems, and then have the Facade class also conform to that protocol.
The idea is that the keyword helps indicate the protocol's purpose—for example, that it’s intended for client-facing interaction. This could make it easier to manage subsystem implementations while providing a clear interface for any code that needs to interact with them.
Can you give some concrete examples of how you would use this feature? I'm not sure I'm understanding it correctly.
Is this effectively a defensive programming tactic, attempting to prevent conformances from accidentally or deliberately exposing a balance property to the world?
Okay, if I understand this correctly, you're basically providing a "thought piece" with this thread, right? In that case, here's my 2 cents:
I don't really believe it's useful to have a protocol define access control for its requirements, least of all private. Considering how they tie in with generics (and existentials, if you will), their entire point so far is to specify visible behavior. [1]
This enables meta-programming as with generics, you can express stuff like "this function takes any type that supports this behavior".
Adding to this makes stuff more complex, but what is the gains exactly?
You list "improved encapsulation" and "stringer API contracts", but I fail to see that, personally. I'd argue that enforcing specific private implementation details actually increases coupling, so encapsulation becomes worse. The same is basically true for API contracts, IMO. An API is specifically only about what is "visible from the outside", I'd say. You might argue that public, fileprivate, etc. can be useful then, but I think that's more of a thing for the protocol as a whole and less for individual parts of it.
The only thing potentially useful for a protocol might be defining requirements for a type's memory footprint or storage layout, I guess, but that might be better modeled as something completely new, perhaps... hm...
In short: I don't see a good use-case for expanding on protocol definitions here. However, I very much like the new SE-0470 evolution we get in Swift 6.2. It doesn't change a protocol's definition, but the mechanics of how it is adopted (namely, on a specific global actor).
I do think there might be more/future concepts that tackle how a protocol is adopted.
Lastly, I wish we could somehow express/define how protocols can be adopted by their existential containers, that sometimes pops up from time to time. There might be dragons behind that one, though...
Even a (non-private) property is, in essence, more behavior and less "layout" or storage footprint or whatever else you might call it. After all, a property is basically just a shorthand for (private) storage, i.e. a member variable, and a getter & (optionally) setter. ↩︎
Swift was designed with safety in mind. Even when converting from custom types to built-in types (or vice versa), everything could be predefined in the protocol for such usage—making it different from a basic protocol approach.
Yes, I see what you're saying. One way to think about it—using the private example—is that this keyword, property wrapper, or whatever mechanism is used, would essentially add or remove functionality.
Take, for instance, a class that uses both static and class methods. These two behave differently, but they can coexist in a class. In a protocol, however, they might not be allowed to coexist depending on the context or what you're trying to achieve. That's why introducing something like a keyword would allow you to explicitly add or remove certain features.
Overall, this kind of functionality could be very useful in the core parts of a program.
Also, just to be clear and avoid confusion—this wouldn't be a single keyword that applies to everything. Rather, it would be a specific keyword used for a specific approach.
Sorry,I can't follow... You can define static requirements in a protocol?
I think in general you describe conditional conformances, basically. That's part of the generics system, so I don't think further expanding protocol syntax and semantics into that area would be wise.
There was a discussion about allowing @optional requirements for non-@objc protocols, that's a bit like what you describe, I think. I don't know how that discussion went, but personally I would be opposed to that. I think it's usually cleaner to instead define multiple protocols and then write extensions on various types (or protocols themselves) that conform conditionally.
What’s the point of allowing private (or basically any other) access modifier in protocol members? What is the usability of the feature? I don’t get that at all.
I agree with you about defining multiple protocols, but this approach could offer a way to avoid conditional conformances and allow protocols to be implemented in a lightweight and safe manner—or even in a more robust and structured way, depending on the use case.
I understand your point—why not just use generics or something more appropriate, sometimes I prefer an object-oriented approach because it better fits my needs.
This is what I'm confused about. Private methods aren't visible to clients/consumers of a protocol, or anything outside the implementation itself, so what is the point of making them part of the protocol definition? Are you just trying to constrain implementations for defensive coding reasons?
// Internal
keyword protocol IAccount {
func ... {}
func ... {}
var ... { get }
// could be a method to match the outside example and so on
private var x: ... { get }
}
class Checking: IAccount { ... }
class Saving: IAccount { ... }
class Investment: IAccount { ... }
// Outside
class BankService: IAccount {
// ...
private var internalThing: IAccount
type(of: internalThing).x
// or
internalThing.x
}
It depends on how you structure your code and define your logic.
As the implementer, you define code for others to use directly through the protocol. Some methods might be hidden from local implementation, others from external access—you choose which methods are accessible and where.
So from what I gather, you're looking for something like a protocolprivate access modifier, to declare a field that's private to the world, but still accessible from other implementer of the protocol. Is that right? (See fileprivate as a point of comparison)
I think that's effectively the same as public, because anybody could conform to your protocol and leak the protocolprivate stuff:
struct BalanceExposer {
var target: any IAccount
public var leakedBalance: Decimal { self.target.balance }
}
class X: ThatProtocolWithKeywordThing {
private var w: Int
func x() -> Int {
return w // error: Instance member 'w' cannot be used on public method of type 'X'
}
}
I think you are misunderstanding protocols system in Swift.
If you came from C++ language, and is looking for something like protected, then Swift doesn’t have it and IIRC this is commonly rejected addition.
protocol is Swift is different from virtual abstract classes in C++. And different from interface in Java as well. Swift tries to discourage chains of inheritance in favor of other practices.
I recommend this series of articles Doug's Compiler Corner, where many things including protocols are explained and show in contrast with C++ behavior.
Let's assume the class is final or you just do not need methods access from that point on.
The way I was thinking about it at first was similar to this. If you choose a specific keyword, it would essentially generate the appropriate version of each method and property based on the intended behavior.
// Inside your code
keyword protocol X {
func a()
func b()
func c()
}
class W: X {
// you implement only this methods
func a()
func b()
func c()
}
// ----------------------------------------------------------------------
// Outside: you have the option of private, and file private, etc,
// based on what is decided in the built-in phase.
class W: X {
private func a()
private func b()
private func c()
// or
fileprivate func a()
fileprivate func b()
fileprivate func c()
// or whatever
private func a()
fileprivate func b()
private func c()
}
In my external class, I would end up duplicating those methods anyway. So why not just conform to the protocol and follow the access control the original designer intended for the code.