Native Optional Methods in Swift Protocols

Dear Swift Evolution Community,

I am writing to formally initiate a discussion on a design enhancement proposal that concerns the current limitations surrounding optional method declarations in Swift protocols.

As previously outlined, the existing approach — which mandates the use of @objc and its associated runtime — introduces a reliance on Objective-C semantics that runs counter to Swift's guiding principles of type safety, clarity, and native expressiveness. More critically, it restricts the use of Swift-native types such as structs and enums in optional protocol methods, resulting in compiler diagnostics that obstruct certain architectural designs.

This proposal suggests two avenues for language evolution:

  1. Introducing Swift-native syntax for optional methods within protocols, e.g.:

swift

protocol MyDelegate {
    @required func didTapButton()
    @optional func didUpdate(value: Int)
}

Such a syntax would allow for clearer declaration intent, while removing the dependency on Objective-C interop for optionality. This aligns with Swift’s ongoing evolution toward a self-contained and purely Swift-native ecosystem.
2. Extending compiler support to permit optional methods with Swift-native parameter types — potentially through default implementations, compiler synthesis, or static analysis. This would preserve compile-time safety while enabling greater flexibility in protocol design.

These additions would serve to enhance the ergonomics of protocol-oriented programming in Swift, particularly in patterns such as delegation or event handling, without incurring the conceptual or technical costs of bridging to Objective-C.

I would very much welcome feedback from this community — both in terms of the merits of such a direction, and on the feasibility and scope of implementation — before pursuing a formal Swift Evolution proposal (pitch).

Thank you for your time and thoughtful consideration.

Warm regards

keisuke yamagishi

6 Likes

I've always felt that the lack of optional methods in protocols is intentional. Optional methods create a new level of difficulty when reasoning about code execution. They incentivize the special-casing approach when processing the results of these calls, which contradicts the primary purpose of protocols, which is to create a uniform approach to behavior modification without the need to modify the code on the caller side.
Additionally, this system has complex relationships with extensions. If the protocol has default implementations that are defined by extensions, entities that implement this protocol will have to have a way to 'remove' this implementation to indicate that they do not want to implement this method. Even more complexity comes with class hierarchies I think.
Thus I think Swift has enough better tools and approaches to express the behavior that is expressed in ObjC as 'optional methods', and therefore this could complicate the language unnecessarily.

23 Likes

There are a LOT of prior suggestions similar to this:

In particular, this was the SE to get rid of optional requirements in Swift (it was once a thing, very very very early on). However, I can't find the rationale behind not wanting "optional requirements" (funny name) in Swift Protocols - I only see it being stated. The SE forums thread was also rather sparse, so I'm not sure if I'm missing something.

=====

I'm personally in favour of optional methods. I have a lot of delegate-style protocols in my apps, which usually look like:

protocol CatDelegate {
    func catDidSit(_ cat: Cat)
    func catDidStand(_ cat: Cat)
    func catDidEat(_ cat: Cat)
}

protocol CatDelegate {
    /* default implementations, it's just {} for each case */
}

class CatFoodTracker: CatDelegate {
    func catDidEat(_ cat: Cat) {
        print("CAT ATE FOOD")
    }
}

However, the default implementations are a bit of a pain when I need to refer to the original functions in docc (since they have the same name and signature), and honestly just having those functions be optional would be fine. We can just

  • Bring back the optional keyword
  • Call functions using objectConformingToProtocol.myMethod?() (we use this syntax already for objc optional methods and optional closures)
  • Remove a default implementation by func myMethod?() = nil or smth like that
5 Likes

In rare cases when I need optional protocol requirements, I model them as a read-only property that returns optional closure:

protocol Foo {
    var bar: Bar { get }
    var setBar: Optional<(Bar) -> Void>
}

I’m mostly happy with this approach, except for the lack of argument labels.

If optionality of several methods need to be grouped together (all or none must be implemented), then it can be achieved by splitting protocol into two and having a property in the mandatory part to return the optional extension part:

protocol Foo {
    func a()
    vas asEx: FooEx?
}

protocol FooEx: Foo {
    func b()
    func c()
}
7 Likes

You can simulate optional methods with current swift to a some extent:

protocol OptionalPart {
    func foo() -> Int
}

protocol MandatoryPart {
    func bar() -> Float
}

extension MandatoryPart {
    func bar() -> Float { fatalError() }
    func foo() -> Int? { nil }
}

extension MandatoryPart where Self: OptionalPart {
    func foo() -> Int? { self.foo() as Int }
}

struct Foo: MandatoryPart & OptionalPart {
    func foo() -> Int { 42 }
}

struct Bar: MandatoryPart {
    //
}

print(Foo().foo())
print(Bar().foo())

The only thing that's really missing here is the ability to document in code the relationship between MandatoryPart and OptionalPart.

Considering how much less popular delegate pattern becomes with swiftUI and modern swift API's, and how little value this brings, from me that's a -1.

2 Likes

It's a pain, but you can always add them back in.

struct Whatever {
  struct LabeledClosure {
    let callAsFunction: (Int) -> Void
    func callAsFunction(label: Int) { callAsFunction(label) }
  }

  init(_ closure: ((Int) -> Void)? = nil) {
    labeledClosure = closure.map(LabeledClosure.init)
  }

  var labeledClosure: LabeledClosure?
}
Whatever { print($0 + 1) }.labeledClosure?(label: 1) // 2
4 Likes

I am deeply moved and sincerely grateful for the thoughtful and constructive solutions presented. They are immensely informative and have provided significant insight.

That said, I must still express a fundamental concern: the inability to use custom Swift types in protocol methods without resorting to @objc remains a notable shortcoming in the language’s current design. It seems to me that permitting the use of @optional independently of @objc, while enabling compatibility with fully Swift-native types, should be a clear direction for further consideration.

I respectfully urge the community to revisit this matter with the aim of advancing a more cohesive and modern Swift syntax that fully embraces the language's native paradigms.

2 Likes

+1 on allowing native non-ObjC Swift protocols to mark methods as optional. The semantics of something being marked as optional (and therefore also needing to be explicitly tested for in the caller's context) is different from what is achievable with default implementations.

2 Likes

The following protocol definition currently results in a compilation error:

swift

protocol OptionalDelegate: AnyObject {
    optional func optionalMethod(at word: String)
    // Error: 'optional' can only be applied to members of an @objc protocol
}

This error occurs because Swift restricts the use of optional to members of protocols explicitly marked with @objc. As a result, it is not currently possible to declare optional protocol methods unless the entire protocol conforms to Objective-C runtime semantics — which in turn prohibits the use of Swift-native types such as struct, enum, or non-@objc classes.

To improve ergonomics and align with Swift’s design goals, it would be highly beneficial to decouple optionality from Objective-C interoperability. One potential direction could be to allow method-level annotations of optional behavior in a Swift-native way, for example:

swift

protocol OptionalDelegate: AnyObject {
    optional func optionalMethod(at word: String) @objc protocol
}

While the above syntax is hypothetical, the core idea is to support optional protocol requirements without requiring the entire protocol to adopt @objc, thereby enabling broader use of Swift-native types and promoting more flexible architectural patterns.

I have been allured by the idea of having an optional requirement for Protocol conformances for a while. Obviously there is a reason this is discouraged in Swift.


protocol OptionalMembers {
    optional func member() {
    }

    optional func member2() -> Int  {
    }

    optional func member3() -> Int?  {
    }

}

class MyClass: OptionalMembers {

}

In this instance, none of the methods from OptionalMembers are implemented on MyClass.

It would seem simple to implement a case where an unimplemented method that returns nothing or returns an optional could be called.

So

myClass.member() // Ok because even though it is unimplemented, it doesn't return anything, it just callas a blank stub function. 
let value = myClass.member2() // Ok because it calls a default implementation that returns `nil`. 

But what happens here?

let value = myClass.member3() // What happens here? 

If one were to implement this, you'd have to put some restrictions, perhaps optional members must return either Void or an Optional?

Additionally, you may need to be able to ask a conforming type to a protocol if it implements a member.

Ex.

implemented myClass.member3()

You’d probably only be able to ask this of a concrete type.

I am not sure if this is wanted.

1 Like

In this case...

let foo = objectConformingToProtocol.optionallyGetMeAString?()

what is the type of foo? We can't make it Optional<String>, because then it would be impossible to tell the difference between an implementation of optionallyGetMeAString explicitly returning none and calling optionallyGetMeAString on an object that doesn't implement it. You'd either need a nested Optional, or a new type that wraps the value returned from an @optional protocol method.

You can already do this if you use protocol extensions to simulate optional conformances via default implementations. You can even have those methods return your own ValueReturnedByOptionalMethod<T> wrapper type to force callers to handle implemented vs not-implemented properly.

1 Like

I completely misunderstood this when I first read it. I take back some of what I said. I very much forgot that one calls optional objc protocol members using method?() syntax.

I think that it is OK if it returns an Optional<T> if there is just a way to test if a concrete type implements an optional requirement.

1 Like

Having code that checks if a method has been implemented feels very un-Swifty to me, partly because that check doesn’t feel at all at home in a statically typed language, and it’s exactly the sort of “remember to do this cos the compiler can’t spot if you forget” thing that Swift aims to avoid.

Wrapping the actual return type solves this problem. Perhaps the feature could be just that, with the compiler providing default implementations returning .unimplemented, implicit conversions of return values in implementations to .implemented(value: “Hello”), and some syntax sugar similar to Optional<T>.

2 Likes

How do you test for implementations for methods that return Void then? Also, there are cases where not providing a requirement might need to result in different logic, and then you have scenarios where a method could return nil and also be an optional requirement. How would you distinguish between the requirement being unimplemented?

If you don't like the idea of having something like X implements Y or implemented object.method()

Then maybe something similar to Optional<T>, liked

enum ImplementedOptional<T> {
    case some<T>
    case unimplemented
    case none
}
1 Like

I'm currently imagining optional methods looking exactly like optional closures, because they look identical at the call site (and optional closures can be used to emulate optional methods). Considering an optional closure like such:

let closure: (() -> Int?)? = { 42 }
let value = closure?()

The type of value is Int?, and is nil when either the "function" is nil, or returns nil. I would take this as precedence for how an optional function's return type should behave. This isn't actually ambiguous - for closures, you could simply check whether the closure itself is nil, without even re-executing it. Each case is very well defined:

if let closure, let value = closure() {
    // closure is non-nil, and returns non-nil value
}

if let closure {
    // closure is non-nil
    // value is nil when closure returns nil, and vice versa
    let value = closure()
}

if closure == nil {
    // closure not defined
}

With just a way to compare the hypothetical "optional method requirement" to nil, this double-optional is no longer a problem. It also aligns with the precedence of closures themselves.

2 Likes

If we were to add native optional methods to Swift, I think the best thing to do would be to treat it like init? requirements, and let the method on a concrete type that implements the method return a non-optional, so if you statically know that the type implements the method you don't even need to check it, but make it always an optional in a generic context.

protocol OptionalDelegate: AnyObject {
  optional func optionalMethod() -> Int
}

final class SomeDelegate: OptionalDelegate {
  func optionalMethod() -> Int {
    42
  }
}

func test1(_ delegate: some OptionalDelegate) {
  // The concrete type isn't known so we must
  // optionally call the method.
  guard let answer = delgate.optionalMethod?() else {
    print("We never got the answer.")
  }
  print("The answer is \(answer).")
}

func test2(_ delegate: SomeDelegate) {
  // The concrete type is known, so we can just call it.
  print("The answer is \(delegate.optionalMethod())")
}

And if the optional method itself returned an optional, it would probably need wrapped in another optional because the difference between this method returned nil and this method is nil is potentially important in the general case.

Although, in all honesty, I think we need to finally re-add parameter labels to closures like we intended to do back when we removed them in the first place.

1 Like

Is it important? If I need to know if the method is implemented or not I could test it:

if let method = objectConformingToProtocol.optionallyGetMeAString {
    ...
}

or with if objectConformingToProtocol.optionallyGetMeAString != nil if I only need to know whether the method is implemented or not without calling.

2 Likes

In reflecting on Swift’s current approach to optional protocol requirements, I would like to pose a question—not strictly of syntax, but of language design philosophy.

Presently, if one wishes to define an optional method in a protocol, one must resort to @objc and its associated runtime semantics:

@objc protocol TextFieldDelegate: AnyObject {
    func textFieldDidEnd(_ textField: UITextField)
    @objc optional func get(_ word: String)
}

This form, while functional, brings along legacy from Objective-C, and introduces constraints that feel orthogonal to Swift’s stated goals of type safety, clarity, and expressiveness. More concretely, it limits us from using Swift-native types like structs or enums as parameters in optional methods, and it burdens the code with both @objc annotations and interop considerations.

By contrast, imagine a future syntax:

swift

protocol TextFieldDelegate: AnyObject {
    func textFieldDidEnd(_ textField: UITextField)
    optional func get(_ word: String)
    optional func get(_ model: OriginalModel) -> MyOriginalModel
}

Here, the intention is self-evident. There is no cognitive overhead or need for runtime translation. The optionality is expressed plainly, natively, and—most importantly—in line with Swift’s modern design language. It mirrors how optional initializers (init?) are handled: statically resolvable when the concrete type is known, but treated as optional in a generic context.

From a pedagogical standpoint, I believe such a syntax would significantly improve code readability and maintainability, particularly for those new to Swift, or coming from statically typed languages. It aligns with Swift’s ethos of eliminating boilerplate in favor of clarity.

Thus, I pose to the community—might it be time for Swift to move beyond its Objective-C roots in this regard, and embrace optional protocol methods as first-class citizens of the language?

I would sincerely welcome your thoughts.

— Keisuke Yamagishi

Optional protocol requirements are an Objective-C compatibility feature. The spelling that you are pitching was removed from the language even for @objc protocols in SE-0070 to emphasize that point.

As SE-0070 described, a deliberate choice was made not to enable this design pattern for Swift-native protocols. Instead, the proposal argued that users should adopt protocol inheritance and default implementations, much like what others have suggested in this thread.

10 Likes

Abstract

Swift is a language founded on principles of clarity, safety, and expressiveness. Yet in the realm of protocol design, one expressive tool remains conspicuously restricted: optional requirements. Currently, such requirements are confined to @objc protocols for Objective-C compatibility, a constraint inherited from history rather than grounded in Swift's design philosophy. In this proposal, I argue that it is time for Swift to embrace native support for optional protocol methods.


Motivation

The current approach—requiring @objc and optional annotations—forces Swift developers to contort their designs for a language that is no longer central to Swift's future. The alternatives we are offered, such as protocol extensions with default implementations or protocol composition, are clever but verbose, cognitively taxing, and far from intuitive to newcomers.

Worse yet, they obscure the original intent: "this requirement is optional to implement."

The elegance of Swift lies in its ability to express intent concisely and safely. The inability to mark protocol methods as optional, without bridging to Objective-C, contradicts this very ideal.


Proposed Solution

We propose allowing the use of the optional keyword directly in Swift-native protocols:

protocol TextFieldDelegate: AnyObject {
    func textFieldDidEnd(_ textField: UITextField)
    optional func get(_ word: String) -> String
}

Semantics would align with Swift's existing model for optional closures and init? initializers:

  • If the method is implemented, it is called as normal.
  • If not, the method reference is nil and can be checked via optional binding or optional chaining.
  • This allows both expressive API design and safe runtime handling.

Rationale

It is not persuasive to argue that a limitation must remain merely because it was decided in the past. SE-0070 made a defensible choice in a particular context, but the language has evolved. The needs of modern Swift developers have changed. Expressiveness and clarity should not be held hostage to legacy compatibility decisions.

The argument that protocol extensions suffice fails to account for real-world ergonomic complexity. Optional methods, when natively supported, would simplify delegate-style APIs, plugin systems, and layered behaviors—common in both UI frameworks and architecture.


Conclusion

This proposal is not a repudiation of SE-0070, but a recognition that the landscape of Swift has matured. What was once avoided for compatibility's sake can now be embraced for clarity’s sake.

Let Swift protocols gain the same expressive power as Swift functions and initializers. Let us mark optional requirements as optional—not through indirection, but directly, naturally, and Swiftly.

1 Like