https://github.com/feed0/appsettingsdemo
Swift Singleton by Karoly Nyisztor
https://github.com/feed0/appsettingsdemo
design-patterns singleton-pattern swift swiftui
Last synced: about 1 month ago
JSON representation
Swift Singleton by Karoly Nyisztor
- Host: GitHub
- URL: https://github.com/feed0/appsettingsdemo
- Owner: feed0
- Created: 2026-02-23T22:03:39.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-02-23T22:55:39.000Z (5 months ago)
- Last Synced: 2026-02-24T05:16:30.478Z (5 months ago)
- Topics: design-patterns, singleton-pattern, swift, swiftui
- Language: Swift
- Homepage:
- Size: 17.6 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# AppSettingsDemo: Singleton design pattern in Swift
AppSettings is a singleton class that offers:
- a `settings` **dictionary**,
- and **read** and **write** methods for its keys and values
- `int(forKey:) -> Int`
- `string(forKey:) -> String`
- through its `shared` instance.
## Concurrency issue
> When a thread tries to **read** some key's value using `.int(forKey:) -> Int`,
while other thread tries to **update** some key's value using `set(value:forKey:)`,
an **_EXCUTION_BAD_ACCESS_** _crash_ happens.
### Reproduce
1. Clone the repo
1. **Switch** to commit 4 _53f482271e975ee852059dfd35158ab9aadc5a38_
1. **Run** only `testConcurrentUsage()` until a crash happens
1. Take a **look** at each thread on Xcode's Debug Navigator _(cmd + 7)_
### Understand
#### The test
- A concurrent queue for writes
- A read op that might eventually happen during the write ops, resulting in a **data race**.
```swift
func testConcurrentUsage() {
let concurrentQueue = DispatchQueue(
label: "concurrentQueue",
attributes: .concurrent)
let expect = expectation(
description: "Using AppSettings.shared from multiple threads shall succeed.")
let callCount = 100
/// write ops
for callIndex in 1...callCount {
concurrentQueue.async {
AppSettings.shared.set(
value: callIndex,
forKey: String(callIndex))
}
}
/// read op
while AppSettings.shared.int(forKey: String(callCount)) != callCount {
/// nop
}
expect.fulfill()
waitForExpectations(timeout: 5) { (error) in
XCTAssertNil(error, "Test expectation failed")}
}
```
#### The crash
- Yellow dashed boxes on the left panel highlight the threads accessing with `AppSettings.int(forKey:)` and threads updating with `AppSettings.set(value:forKey:)`.
- Magenta dashed boxes on the right panel highlight one of the threads' call stack
