How to Declare Optional Methods in a Swift Protocol

How to Declare Optional Methods in a Swift Protocol

Written by

in

Quick answer: Pure Swift protocols cannot mark a method as optional. Give the method a default implementation in a protocol extension, or mark the protocol @objc and the method @objc optional if you need to detect whether it was implemented.

You write a protocol, add a few methods, and then realize that conforming types should only need to implement some of them. In Objective-C you would mark methods @optional. In Swift, a plain protocol has no such keyword, so writing optional func fails to compile with an error like 'optional' can only be applied to members of an @objc protocol.

There are two working approaches. Which one fits depends on whether you need structs and enums, and whether you need to know if a method was implemented.

Why this happens

A Swift protocol is a contract: every conforming type must provide every requirement. The compiler checks this statically, so a type that conforms is guaranteed to have all the methods.

Optional requirements come from Objective-C, where a message sent to an object that doesn’t implement it can be checked at runtime with respondsToSelector:. Swift only exposes that behavior for protocols that opt in to the Objective-C runtime with @objc.

For everything else, Swift’s answer is the protocol extension: a method that has a default body is effectively optional for the conforming type, because the type gets the default for free.

How to fix it

1. Use a default implementation in a protocol extension (preferred)

Declare the method in the protocol, then provide a default in an extension.

protocol Downloader {
    func start()                 // required
    func didFinish()             // effectively optional
}

extension Downloader {
    func didFinish() {
        // default: do nothing
    }
}

struct ImageDownloader: Downloader {
    func start() { print("Downloading...") }
    // didFinish() not needed, no compile error
}

A type can still override the default by implementing the method itself.

Important: declare the method in the protocol body as well as in the extension. If it exists only in the extension, it is not a protocol requirement, and calls through the protocol type will always use the extension’s version, even if a conforming type defines its own.

Benefits

  • No Objective-C runtime involved. Structs, enums and non-NSObject classes can all conform.
  • Works with generics and associated types.
  • Every conforming type is guaranteed to have an implementation, either its own or the default.

Drawbacks

  • Methods that return a value need a sensible default. If no reasonable default exists, the requirement probably should not be optional.
  • You cannot tell whether a type provided its own implementation or is using the default.

That last point matters in cases like this:

protocol ParserDelegate {
    func validate(value: Any) -> Bool
}

extension ParserDelegate {
    func validate(value: Any) -> Bool { true }
}

A parser would like to skip validation entirely, as a speed optimization, when the delegate has no validate implementation. With a default, it cannot know. Workarounds include an optional closure property, or splitting the behavior into a separate protocol.

2. Use @objc optional

If you need to check at runtime whether a method exists, mark the protocol and method as @objc.

import Foundation

@objc protocol DownloadDelegate: AnyObject {
    @objc optional func downloadDidFinish()
    func downloadDidFail()
}

final class Controller: NSObject, DownloadDelegate {
    func downloadDidFail() {}
    // downloadDidFinish() omitted, no compile error
}

Call optional methods with optional chaining:

weak var delegate: DownloadDelegate?

delegate?.downloadDidFinish?()

Or check explicitly:

if let finish = delegate?.downloadDidFinish {
    finish()
} else {
    // not implemented
}

Benefits

  • No default implementation needed.
  • You can detect whether the method was implemented.

Drawbacks

  • Only classes can conform, and they must be Objective-C compatible (in practice, inheriting from NSObject). No structs, no enums.
  • Requirements must be representable in Objective-C, which rules out things like associated types and many generic signatures.
  • Every call needs optional chaining or a check, which adds boilerplate.

3. Alternative: optional closures or split protocols

When you need pure Swift and need to know whether a callback exists, store it as a closure:

struct ParserHooks {
    var validate: ((Any) -> Bool)?
}

func parse(_ data: [Any], hooks: ParserHooks) -> [Any] {
    guard let validate = hooks.validate else { return data }
    return data.filter(validate)
}

Another option is to split one large protocol into smaller ones and have the caller check conformance with as?:

protocol Validating { func validate(value: Any) -> Bool }

if let validating = delegate as? Validating { /* validate */ }

Which one should you pick?

Need Use
Structs or enums must conform Default implementation
Generics or associated types Default implementation
Must detect “not implemented” @objc optional, closures, or split protocols
Working with UIKit/AppKit-style delegates @objc optional is common

How to prevent it

  • Start with the default-implementation pattern unless you have a specific reason to need the Objective-C runtime.
  • Always declare the method in the protocol body, not only in the extension.
  • If you can’t write a reasonable default, make the requirement mandatory.
  • Mark delegate protocols AnyObject (or @objc) and hold the delegate weak to avoid retain cycles.
  • Keep protocols small so that fewer methods need to be optional.

FAQ

Why does optional func give a compile error in my Swift protocol?

The optional modifier only works inside protocols marked @objc, and the method must also be marked @objc. In a plain Swift protocol, use a default implementation instead.

Can a struct conform to a protocol with @objc optional methods?

No. @objc protocols can only be adopted by classes, and those classes must be Objective-C compatible. Use default implementations if structs or enums need to conform.

Why is my protocol extension method called instead of the type’s own method?

The method is probably declared only in the extension and not in the protocol. Without a protocol requirement, Swift dispatches statically on the protocol type. Add the method signature to the protocol body.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.