<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/how-to-add-cases-to-a-public-enum-without-breaking-your-api-sggn1t5m2" -->

---
title: How to add cases to a public enum without breaking your API
description: Adding a case to a public enum in a Swift package used to be a source-breaking change, forcing awkward workarounds like avoiding enums for errors or bolting on...
canonical: https://daily.dev/posts/how-to-add-cases-to-a-public-enum-without-breaking-your-api-sggn1t5m2
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: How to add cases to a public enum without breaking your API | daily.dev
og:description: Adding a case to a public enum in a Swift package used to be a source-breaking change, forcing awkward workarounds like avoiding enums for errors or bolting on...
og:url: https://daily.dev/posts/how-to-add-cases-to-a-public-enum-without-breaking-your-api-sggn1t5m2
og:image: https://api.daily.dev/og/posts/sGgN1T5M2.png
og:image:alt: How to add cases to a public enum without breaking your API
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How to add cases to a public enum without breaking your API

**[Sarunw](https://daily.dev/sources/sarunw)** · [@sarunw](https://daily.dev/sarunw) · 6 min read · 1 upvotes · 0 comments

## Summary

Adding a case to a public enum in a Swift package used to be a source-breaking change, forcing awkward workarounds like avoiding enums for errors or bolting on .custom cases. Swift Evolution proposal SE-0487 introduces a @nonexhaustive attribute (landed in Swift 6.2.3) that lets ordinary packages opt into the non-exhaustive enum behavior previously exclusive to resilient libraries built with -enable-library-evolution. Clients in other modules must add @unknown default to their switches; within the same module or package, exhaustive matching still applies. The attribute can't combine with @frozen, and adopting @nonexhaustive itself is a one-time breaking change - though @nonexhaustive(warn) lets authors warn clients before breaking them, and swift package diagnose-api-breaking-changes understands the attribute.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://sarunw.com/posts/nonexhaustive-enums-in-swift>

## Questions this post answers

### How can I add a case to a public enum in my Swift package without breaking clients?

Mark the enum with the new @nonexhaustive attribute introduced in Swift Evolution proposal SE-0487, which landed in Swift 6.2.3. Clients in other modules then must add @unknown default to their switch statements, after which new cases can be added freely without breaking their code. Within the same module or package, the enum still requires exhaustive matching.

_Track Swift language changes like this as you plan library API compatibility, on daily.dev._

### What is the difference between @frozen and @nonexhaustive in Swift?

@frozen promises that no new cases will ever be added to an enum, while @nonexhaustive allows new cases to be added later without breaking clients who use @unknown default. The two are mutually exclusive - marking an enum both @frozen and @nonexhaustive is a compiler error, since they represent opposite guarantees about future changes.

_Developers weighing enum API stability trade-offs can follow Swift evolution updates on daily.dev._

### Is adding the @nonexhaustive attribute to an existing Swift enum a breaking change?

Yes, adding @nonexhaustive to a public enum in a non-resilient Swift library is itself an API breaking change, because it forces every existing exhaustive switch in client code to add @unknown default before it compiles. Removing the attribute later is not breaking. To ease the transition, @nonexhaustive(warn) issues a warning instead of a hard error before the eventual case addition.

_Package maintainers staging API changes can keep tabs on Swift tooling updates via daily.dev._

## Similar posts on daily.dev

- [Preparing for Change: Safe Switching over Sealed APIs](https://daily.dev/posts/preparing-for-change-safe-switching-over-sealed-apis-unrzsaifk) · Inside Java · 1 upvotes · 0 comments
- [Never Miss a Switch Case Again](https://daily.dev/posts/never-miss-a-switch-case-again-zuurg14ld) · TypeScript.TV · 29 upvotes · 3 comments
- [Memberwise Initializer in Swift explained with Code Examples](https://daily.dev/posts/memberwise-initializer-in-swift-explained-with-code-examples-oywf9uwcf) · SwiftLee · 1 upvotes · 0 comments
- [New macros for SwiftNavigation](https://daily.dev/posts/new-macros-for-swiftnavigation-huoybt7rw) · Point-Free Pointers · 0 upvotes · 0 comments

---

Tags: [#architecture](https://daily.dev/tags/architecture), [#swift](https://daily.dev/tags/swift)

[View this post on daily.dev](https://daily.dev/posts/how-to-add-cases-to-a-public-enum-without-breaking-your-api-sggn1t5m2)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"How to add cases to a public enum without breaking your API","url":"https://daily.dev/posts/how-to-add-cases-to-a-public-enum-without-breaking-your-api-sggn1t5m2","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/how-to-add-cases-to-a-public-enum-without-breaking-your-api-sggn1t5m2"},"datePublished":"2026-09-01T11:58:29.936Z","dateModified":"2026-09-02T08:00:27.544Z","description":"Adding a case to a public enum in a Swift package used to be a source-breaking change, forcing awkward workarounds like avoiding enums for errors or bolting on...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/36b438466e9a55ea9a733c5ae839bb9a?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/36b438466e9a55ea9a733c5ae839bb9a?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Sarunw","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Person","name":"Sarun Wongpatcharapakorn","url":"https://daily.dev/sarunw","image":"https://avatars.githubusercontent.com/u/795368?v=4","interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"EndorseAction"},"userInteractionCount":20}},"commentCount":0,"discussionUrl":"https://daily.dev/posts/how-to-add-cases-to-a-public-enum-without-breaking-your-api-sggn1t5m2","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"architecture,swift","timeRequired":"PT6M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Sarunw","item":"https://daily.dev/sources/sarunw"},{"@type":"ListItem","position":3,"name":"How to add cases to a public enum without breaking your API"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/how-to-add-cases-to-a-public-enum-without-breaking-your-api-sggn1t5m2#faq","mainEntity":[{"@type":"Question","name":"How can I add a case to a public enum in my Swift package without breaking clients?","acceptedAnswer":{"@type":"Answer","text":"Mark the enum with the new @nonexhaustive attribute introduced in Swift Evolution proposal SE-0487, which landed in Swift 6.2.3. Clients in other modules then must add @unknown default to their switch statements, after which new cases can be added freely without breaking their code. Within the same module or package, the enum still requires exhaustive matching. Track Swift language changes like this as you plan library API compatibility, on daily.dev."}},{"@type":"Question","name":"What is the difference between @frozen and @nonexhaustive in Swift?","acceptedAnswer":{"@type":"Answer","text":"@frozen promises that no new cases will ever be added to an enum, while @nonexhaustive allows new cases to be added later without breaking clients who use @unknown default. The two are mutually exclusive - marking an enum both @frozen and @nonexhaustive is a compiler error, since they represent opposite guarantees about future changes. Developers weighing enum API stability trade-offs can follow Swift evolution updates on daily.dev."}},{"@type":"Question","name":"Is adding the @nonexhaustive attribute to an existing Swift enum a breaking change?","acceptedAnswer":{"@type":"Answer","text":"Yes, adding @nonexhaustive to a public enum in a non-resilient Swift library is itself an API breaking change, because it forces every existing exhaustive switch in client code to add @unknown default before it compiles. Removing the attribute later is not breaking. To ease the transition, @nonexhaustive(warn) issues a warning instead of a hard error before the eventual case addition. Package maintainers staging API changes can keep tabs on Swift tooling updates via daily.dev."}}]}
```

