First off, thank you for considering contributing to Atomic! It's people like you that make Atomic such a great tool.
- Code of Conduct
- Getting Started
- How Can I Contribute?
- Development Workflow
- Coding Standards
- Community
This project and everyone participating in it is governed by our Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to nv3212@gmail.com.
-
Fork the repository
# Click the "Fork" button on GitHub -
Clone your fork
git clone https://github.com/YOUR_USERNAME/atomic.git cd atomic -
Set up the development environment
# Bootstrap the project make bootstrap -
Create a feature branch
git checkout -b feature/your-feature-name
-
Open the project in Xcode
open Package.swift
Before creating a bug report, please check the existing issues to avoid duplicates.
When creating a bug report, include:
- Clear title - Describe the issue concisely
- Reproduction steps - Detailed steps to reproduce the bug
- Expected behavior - What you expected to happen
- Actual behavior - What actually happened
- Environment - OS, Xcode version, Swift version
- Code samples - Minimal reproducible example
- Error messages - Complete error output if applicable
Example:
**Title:** Race condition in write method under high concurrency
**Steps to reproduce:**
1. Create @Atomic var counter = 0
2. Execute 10,000 concurrent writes using DispatchQueue.concurrentPerform
3. Read final value
**Expected:** Counter should equal 10,000
**Actual:** Counter shows random value less than 10,000
**Environment:**
- iOS 16.0
- Xcode 15.3
- Swift 5.10
**Code:**
\`\`\`swift
@Atomic var counter = 0
DispatchQueue.concurrentPerform(iterations: 10000) { _ in
_counter.write { $0 += 1 }
}
print(counter) // Expected: 10000, Actual: varies
\`\`\`We love feature suggestions! When proposing a new feature, include:
- Problem statement - What problem does this solve?
- Proposed solution - How should it work?
- Alternatives - What alternatives did you consider?
- Use cases - Real-world scenarios
- API design - Example code showing usage
- Breaking changes - Will this break existing code?
Example:
**Feature:** Add async/await support for Atomic operations
**Problem:** Current API is synchronous. Async operations need to await within Atomic context.
**Solution:** Add async versions of read/write methods.
**API:**
\`\`\`swift
extension Atomic {
func read<U>(_ closure: (Value) async throws -> U) async rethrows -> U
func write<U>(_ closure: (inout Value) async throws -> U) async rethrows -> U
}
\`\`\`
**Use case:**
\`\`\`swift
@Atomic var cache: [String: Data] = [:]
await _cache.write { cache in
let data = await fetchData()
cache["key"] = data
}
\`\`\`Documentation improvements are always welcome:
- Code comments - Add/improve inline documentation
- DocC documentation - Enhance documentation articles
- README - Fix typos, add examples
- Guides - Write tutorials or how-to guides
- API documentation - Document public APIs
- Check existing work - Look for related issues or PRs
- Discuss major changes - Open an issue for large features
- Follow coding standards - See Coding Standards
- Write tests - All code changes require tests
- Update documentation - Keep docs in sync with code
- Create a pull request - Use clear description
We use a simplified branching model:
main- Main development branch (all PRs target this)feature/*- New featuresfix/*- Bug fixesdocs/*- Documentation updatesrefactor/*- Code refactoringtest/*- Test improvements
Branch naming examples:
feature/async-support
fix/race-condition-in-write
docs/add-concurrency-guide
refactor/simplify-lock-implementation
test/add-stress-testsWe use Conventional Commits for clear, structured commit history.
Format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat- New featurefix- Bug fixdocs- Documentation changesstyle- Code style (formatting, no logic changes)refactor- Code refactoringtest- Adding or updating testschore- Maintenance tasksperf- Performance improvements
Scopes:
core- Core atomic logiclock- Lock implementationsapi- Public APItests- Test infrastructuredeps- Dependencies
Examples:
feat(api): add async/await support for read and write methods
Implement async versions of read() and write() to support
asynchronous operations within atomic context. Maintains
thread-safety guarantees while allowing await calls.
Closes #23
---
fix(lock): prevent deadlock in nested write operations
UnfairLock was not reentrant, causing deadlock when write()
was called from within another write(). Now uses NSRecursiveLock
for write operations to support nesting.
Fixes #45
---
docs(readme): add dynamic member lookup examples
Add examples showing how to use dynamic member lookup for
direct property access. Includes thread-safety explanation
and common pitfalls.
---
test(core): add comprehensive concurrency tests
Add tests for:
- 10,000 concurrent reads
- 10,000 concurrent writes
- Mixed read/write operations
- Nested write operationsCommit message rules:
- Use imperative mood ("add" not "added")
- Don't capitalize first letter
- No period at the end
- Keep subject line under 72 characters
- Separate subject from body with blank line
- Reference issues in footer
-
Update your branch
git checkout main git pull upstream main git checkout feature/your-feature git rebase main
-
Run tests and checks
# Run all tests swift test # Check test coverage swift test --enable-code-coverage # Run SwiftLint (if configured) swiftlint
-
Push to your fork
git push origin feature/your-feature
-
Create pull request
- Target the
mainbranch - Provide clear description
- Link related issues
- Include examples if applicable
- Request review from maintainers
- Target the
-
Review process
- Address review comments
- Keep PR up to date with main
- Squash commits if requested
- Wait for CI to pass
-
After merge
# Clean up local branch git checkout main git pull upstream main git branch -d feature/your-feature # Clean up remote branch git push origin --delete feature/your-feature
We follow the Swift API Design Guidelines and Ray Wenderlich Swift Style Guide.
Key points:
-
Naming
// ✅ Good func read<U>(_ closure: (Value) throws -> U) rethrows -> U func write<U>(_ closure: (inout Value) throws -> U) rethrows -> U // ❌ Bad func get(_ c: (Value) throws -> Any) rethrows -> Any func set(_ c: (inout Value) throws -> Any) rethrows -> Any
-
Protocols
// ✅ Good - Use "I" prefix for protocols protocol ILock { func lock() func unlock() } // ❌ Bad protocol Lock { }
-
Access Control
// ✅ Good - Explicit access control @propertyWrapper public struct Atomic<Value> { private var value: Value private let lock: ILock public init(wrappedValue: Value) { self.value = wrappedValue self.lock = UnfairLock() } public var wrappedValue: Value { get { lock.around { value } } set { lock.around { value = newValue } } } }
-
Documentation
/// A property wrapper that provides thread-safe access to a value. /// /// `Atomic` ensures that all reads and writes to the wrapped value are /// synchronized, preventing race conditions in concurrent environments. /// /// - Note: Access the wrapper using `_propertyName` to use read/write methods. /// /// - Example: /// ```swift /// @Atomic var counter = 0 /// /// // Thread-safe increment /// _counter.write { $0 += 1 } /// /// // Thread-safe read /// let value = _counter.read { $0 } /// ``` @propertyWrapper public struct Atomic<Value> { // Implementation }
- No force unwrapping - Use optional binding or guards
- No force casting - Use conditional casting
- No magic numbers - Use named constants
- Single responsibility - One class, one purpose
- DRY principle - Don't repeat yourself
- Thread-safety first - All operations must be thread-safe
Example:
// ✅ Good
public func read<U>(_ closure: (Value) throws -> U) rethrows -> U {
try lock.around { try closure(self.value) }
}
public func write<U>(_ closure: (inout Value) throws -> U) rethrows -> U {
try lock.around { try closure(&self.value) }
}
// ❌ Bad
public func read<U>(_ closure: (Value) throws -> U) rethrows -> U {
lock.lock()
defer { lock.unlock() } // Should use lock.around()
return try closure(self.value)
}All code changes must include tests:
- Unit tests - Test individual components
- Concurrency tests - Test thread-safety with high iteration counts
- Edge cases - Test boundary conditions
- Error handling - Test throwing closures
- Performance tests - Test lock contention
Coverage requirements:
- New code: minimum 80% coverage
- Modified code: maintain or improve existing coverage
- Critical paths: 100% coverage
Test structure:
import XCTest
@testable import Atomic
final class AtomicTests: XCTestCase, @unchecked Sendable {
@Atomic private var counter = 0
override func setUp() {
super.setUp()
_counter.write(0)
}
// MARK: - Basic Operations
func test_thatReadReturnsCurrentValue() {
// Given
_counter.write(42)
// When
let result = _counter.read { $0 }
// Then
XCTAssertEqual(result, 42)
}
func test_thatWriteModifiesValue() {
// Given
_counter.write(0)
// When
_counter.write { $0 += 10 }
// Then
XCTAssertEqual(_counter.read { $0 }, 10)
}
// MARK: - Thread Safety
func test_thatConcurrentWritesAreSafe() {
// Given
_counter.write(0)
let iterations = 10_000
// When
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
_counter.write { $0 += 1 }
}
// Then
XCTAssertEqual(_counter.read { $0 }, iterations)
}
// MARK: - Dynamic Member Lookup
func test_thatDynamicMemberLookupWorks() {
// Given
struct Config {
var timeout: Double = 30.0
}
let config = Atomic(wrappedValue: Config())
// When
config.timeout = 60.0
// Then
XCTAssertEqual(config.timeout, 60.0)
}
// MARK: - Error Handling
func test_thatThrowingClosuresWork() throws {
// Given
enum TestError: Error { case test }
_counter.write(10)
// When/Then
XCTAssertThrowsError(
try _counter.write { value -> Int in
throw TestError.test
}
)
// Value remains unchanged
XCTAssertEqual(_counter.read { $0 }, 10)
}
}Concurrency test requirements:
- Use at least 10,000 iterations for stress tests
- Test reads, writes, and mixed operations
- Verify final state matches expected result
- Test under various dispatch queue configurations
- Discussions - Join GitHub Discussions
- Issues - Track open issues
- Pull Requests - Review open PRs
Contributors are recognized in:
- GitHub contributors page
- Release notes
- Project README (for significant contributions)
- Check existing issues
- Search discussions
- Ask in Q&A discussions
- Email the maintainer: nv3212@gmail.com
Thank you for contributing to Atomic! 🎉
Your efforts help make this project better for everyone.