swift-sharing: one property wrapper for shared and persisted state
A universal solution to persistence and data sharing in surprisingly little code.
At a glance
- What is it?
- swift-sharing is a Point-Free Swift library built around the @Shared property wrapper, which holds one value in several places at once and persists it to user defaults, the file system or memory. It works in SwiftUI views, @Observable models and UIKit view controllers, and it stays unit testable because each test gets a quarantined storage system.
- Who is it for?
- Adopt swift-sharing if you are building an iOS or macOS application with SwiftUI or UIKit and want one piece of state to be visible in several features and persisted across launches without hand wiring notifications or a shared store. Do not adopt it if your state is small enough for a plain property, if you need value semantics with copy on write, or if your app lives entirely on one platform where the system already provides what you need.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 16 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One wrapper, several holders, one value
swift-sharing instantly shares state among your app's features and external persistence layers, including user defaults, the file system and more. The library description calls it a universal solution to persistence and data sharing in surprisingly little code.
The mechanism is the @Shared property wrapper. Every holder of the same shared value refers to the same underlying storage, and any change through one holder is immediately visible through the others. The README's example has two separate observable models holding the same collection, each declared with the same storage key:
// MeetingsList.swift
@Observable
class MeetingsListModel {
@ObservationIgnored
@Shared(.fileStorage(.meetingsURL)) var meetings: [Meeting] = []
}
// ArchivedMeetings.swift
@Observable
class ArchivedMeetingsModel {
@ObservationIgnored
@Shared(.fileStorage(.meetingsURL)) var meetings: [Meeting] = []
}If either model changes meetings, the other sees the change instantly. The README adds a second property that matters just as much: if the file on disk changes from an external write, both instances of @Shared update to hold the freshest data. So the synchronisation runs in both directions, not only between holders inside the app.
The library came out of many episodes of the Point-Free video series, hosted by Brandon Williams and Stephen Celis, so it arrives with the design vocabulary that series uses rather than with an API shaped by a framework team.
Three persistence strategies with different jobs
The @Shared property wrapper gives a succinct and consistent way to persist data, and the library ships three strategies that decide where the bytes go.
The appStorage strategy stores small pieces of simple data in user defaults, which is the settings case:
@Shared(.appStorage("soundsOn")) var soundsOn = true
@Shared(.appStorage("hapticsOn")) var hapticsOn = true
@Shared(.appStorage("userSort")) var userSort = UserSort.nameThe fileStorage strategy is for more complex data types, serialising the data to bytes in the file system:
@Shared(.fileStorage(.meetingsURL)) var meetings: [Meeting] = []The inMemory strategy shares any kind of data globally across the app, but it is reset the next time the app is relaunched:
@Shared(.inMemory("events")) var events: [String] = []The documentation also covers creating your own strategies, so the three built-ins are a starting set rather than a closed list. That extensibility is the part that scales with an application, since a team that needs a different backing store can write a strategy rather than abandoning the wrapper.
The practical distinction to hold on to is that these are persistence mechanisms first and sharing mechanisms second. Choosing appStorage for a large array because the sharing is convenient is how an application ends up putting megabytes into user defaults.
The @ObservationIgnored requirement and the macro conflict
The README carries a note that looks like an implementation detail and is in fact a hard requirement:
Due to the fact that Swift macros do not play nicely with property wrappers, you must annotate each @Shared held by an @Observable model with @ObservationIgnored. Views will still update when shared state changes since @Shared handles its own observation.
That is worth reading twice. The @Observable macro and the property wrapper both want to own observation of the same storage, and the compiler cannot give both of them what they want, so the model opts out of macro generated observation for that property and the wrapper takes over. The result is that views still react to changes, but the model itself is no longer the observation source for that property.
The practical consequence for a team is that the annotation is not optional and it is not obvious. A developer adding a new shared property to an observable model will get no compile error telling them to add @ObservationIgnored, they will simply get behaviour that does not match the rest of the model. Knowing that rule before you write the code is the difference between a two second fix and an afternoon.
It also explains why the wrapper can be used outside of observable models at all. Observation is a property of the wrapper, not of the thing that holds it.
Using @Shared directly in a view or a view controller
The library's claim is that @Shared can be used essentially anywhere, including observable models, SwiftUI views and UIKit view controllers, and the README shows the two cases where a model would be overkill.
A view that needs access to shared state but not the full power of an observable model can hold the wrapper directly:
struct DebugMeetingsView: View {
@Shared(.fileStorage(.meetingsURL)) var meetings: [Meeting] = []
var body: some View {
ForEach(meetings) { meeting in
Text(meeting.title)
}
}
}And a legacy feature that cannot use SwiftUI yet can hold it in a view controller:
final class DebugMeetingsViewController: UIViewController {
@Shared(.fileStorage(.meetingsURL)) var meetings: [Meeting] = []
// ...
}For code that needs to react to changes in order to update something outside SwiftUI, the README points at two options: the Shared/publisher property, or the observe tool from the Swift Navigation library.
The second option is worth noting as a design decision rather than a detail. swift-sharing does not try to be the whole state management story. It gives you the storage and the sharing, and it leaves the observation plumbing for imperative code to a companion library that already has an opinion about how to structure it.
Testing shared state without touching the real storage
The testing story is the part of the design that usually forces a compromise, and this library treats it as a first class requirement.
Features using the @Shared property wrapper remain testable even though they interact with outside storage systems such as user defaults and the file system. The README explains how: each test gets a fresh storage system that is quarantined to only that test, so any changes made to it will only be seen by that test.
That is a stronger guarantee than simply swapping in an in memory implementation for the duration of a test, because the quarantine is scoped per test rather than per suite, so two tests cannot see each other's writes even when the suite runs in parallel.
The documentation for the library covers testing in more detail under its own Testing section, and it is the right place to look before designing a test suite around shared state, since the shape of the quarantine is what determines what your tests are allowed to assume.
The repository backs the claim with structure. The Examples directory contains CaseStudies, CaseStudiesTests and CaseStudiesUITests, plus API client, Firebase and WebAssembly demos, and the package itself is built from Sources/ and Tests/ with a Package.swift at the root. There are also several version specific manifests, [email protected], [email protected] and [email protected], so a project on an older toolchain can still resolve the package.
Where swift-sharing is the wrong tool
Three cases point elsewhere.
The first is a piece of state that only one feature reads. Wrapping a boolean in @Shared when nothing else observes it adds a storage strategy, a serialisation format and a synchronisation path for no benefit. The library is for state with more than one reader.
The second is when you need value semantics with copy on write. Shared state is shared state, and the wrapper is built so that every holder sees the same value. If your model needs copies that diverge, for example a value type that is passed around and edited locally before being written back, then a reference type with a store is the better shape, and forcing it into @Shared trades away a property the type system was giving you.
The third is a project outside the Apple platforms, or one where the system already provides the same thing. The package depends on Swift macros and @Observable, which is the current Apple toolchain, and the persistence strategies target user defaults and the file system.
One maintenance point to weigh as well. The release line moves regularly, with 2.9.1 dated 2026-07-01, 2.10.0 on 2026-08-28 and 2.10.1 on 2026-08-31, and the last push was on 2026-10-07. The API surface of a property wrapper with strategies is exactly the kind of thing that shifts between minor versions, so a project that pins an exact version should expect to revisit that pin. The Makefile in the tree shows the maintainer's own toolchain, Swift 6.0, with a format target using swift format and a docker-build target that runs swift build inside a container image.
Editorial conclusion
Adopt swift-sharing if you are building an iOS or macOS application with SwiftUI or UIKit and want one piece of state to be visible in several features and persisted across launches without hand wiring notifications or a shared store. Do not adopt it if your state is small enough for a plain property, if you need value semantics with copy on write, or if your app lives entirely on one platform where the system already provides what you need. Verify three things first: that you are willing to annotate every shared property of an @Observable model with @ObservationIgnored, since Swift macros and property wrappers do not mix and the documentation states this requirement plainly; which persistence strategy fits your data, because appStorage is for small simple values in user defaults while fileStorage serialises more complex types to bytes; and that the external change detection behaves the way you expect, since a write to the file from outside the app propagates into every live @Shared holder. The licence is MIT, the last push was on 2026-10-07, and the newest release is 2.10.1 dated 2026-08-31.
Frequently asked questions
What does the swift-sharing library provide?
It provides the @Shared property wrapper, which shares one value between several parts of an application and persists it at the same time. It works in SwiftUI views, @Observable models and UIKit view controllers, and it is designed to remain unit testable.
Which persistence strategies does @Shared support?
Three built-in strategies: appStorage for small simple data in user defaults, fileStorage for more complex types serialised to bytes in the file system, and inMemory for sharing data globally that is reset when the app relaunches. The documentation also covers writing your own strategies.
Why does an @Observable model need @ObservationIgnored?
The README notes that Swift macros do not play nicely with property wrappers, so each @Shared held by an @Observable model must be annotated with @ObservationIgnored. Views still update when shared state changes because @Shared handles its own observation.
How do I test code that uses @Shared?
Each test gets a fresh storage system that is quarantined to only that test, so changes are visible only within that test. The library's documentation has a dedicated Testing section with more detail.
Can I use @Shared in a UIKit view controller?
Yes. The README shows a UIViewController subclass holding @Shared(.fileStorage(.meetingsURL)) var meetings directly, for legacy features that cannot use SwiftUI yet. To react to changes outside SwiftUI you can use the Shared/publisher property or the observe tool from the Swift Navigation library.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pointfreeco-swift-sharing)Was this useful?