First off, thank you for considering contributing to Snacker! It's people like you that make Snacker such a great tool for the iOS community.
- Code of Conduct
- Getting Started
- Development Setup
- Project Structure
- How Can I Contribute?
- Reporting Bugs
- Suggesting Features
- Improving Documentation
- Submitting Code
- Development Workflow
- Branching Strategy
- Commit Guidelines
- Pull Request Process
- Coding Standards
- Swift Style Guide
- Code Quality
- Testing Requirements
- 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/snacker.git
cd snacker- 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.swiftBefore 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
- Screenshots/Video - Visual proof of UI glitches (highly recommended for Snacker)
Example:
**Title:** Snackbar ignores safe area on iPhone 15 Pro
**Steps to reproduce:**
1. Call Snacker.shared.action with `.bottom` alignment
2. Rotate device to landscape
3. Observe snackbar overlapping the home indicator
**Expected:** Snackbar should respect safe area insets
**Actual:** Snackbar is clipped by the screen edge/home indicator
**Environment:**
- iOS 17.0
- Xcode 16.0
- Swift 6.0
**Code:**
\`\`\`swift
Snacker.shared.action(
.snack(view: myView, data: .init(snackbarAlignment: .bottom(spacing: 0))),
container: window
)
\`\`\`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
- Visuals - Mockups or sketches if applicable
Example:
**Feature:** Swipe to dismiss
**Problem:** Users cannot manually dismiss a snackbar before the duration expires.
**Solution:** Add gesture recognizer to the container view to allow swiping the snackbar away.
**API:**
\`\`\`swift
let data = SnackbarData(
snackbarAlignment: .top,
interaction: .swipeToDismiss(direction: .up) // New parameter
)
\`\`\`
**Use case:** Error messages that block content which the user has already read.Documentation improvements are always welcome:
- Code comments - Add/improve inline documentation
- DocC documentation - Enhance documentation articles
- README - Fix typos, add visual examples
- Guides - Write tutorials on creating custom views
- 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 (logic & UI snapshots if possible)
- 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/swipe-to-dismiss
fix/safe-area-calculation
docs/add-custom-view-example
refactor/animation-coordinator
test/add-queue-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 tasks
Scopes:
core- Core logic (Manager)ui- View componentsanimation- Transitions and animationslayout- Constraints and positioningdeps- Dependencies
Examples:
feat(animation): add spring configuration support
Allow users to customize spring damping and velocity for
smoother snackbar appearance.
Closes #12
---
fix(layout): correct bottom spacing on iPad
iPad multitasking mode was causing incorrect width calculation.
Now uses safeAreaLayoutGuide correctly relative to container.
Fixes #23
---
docs(readme): add screenshot for custom view
Update README to show how a custom success toast looks
with the new styling options.
---
test(core): ensure queue order is preserved
Add tests to verify that multiple snackbar requests are
shown sequentially and not overlapping.
- 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- Push to your fork
git push origin feature/your-feature- Create pull request
- Target the
mainbranch - Provide clear description
- Link related issues
- Attach screen recordings for UI changes
- Request review from maintainers
We follow the Swift API Design Guidelines and Ray Wenderlich Swift Style Guide.
Key points:
- Naming
// ✅ Good
func show(view: UIView, data: SnackbarData)
let animationDuration: TimeInterval
// ❌ Bad
func doSnack(_ v: UIView, _ d: SnackbarData)
let dur: Double- Protocols
// ✅ Good - Use "I" prefix for protocols (Project Convention)
protocol ISnackbarManager {
func schedule(_ action: SnackAction)
}
// ❌ Bad
protocol SnackbarManagerProtocol { }- Access Control
// ✅ Good - Explicit access control
public final class Snacker {
public static let shared = Snacker()
private let queue: DispatchQueue
private init() {
self.queue = DispatchQueue(label: "com.snacker.queue")
}
}- Documentation
/// Displays a snackbar with custom configuration.
///
/// Use this method to present a transient notification view on top of
/// the current context. The view will be automatically dismissed
/// after the duration specified in `data`.
///
/// - Parameters:
/// - action: The action containing the view and configuration data.
/// - container: The window or view controller view to host the snackbar.
///
/// - Example:
/// ```swift
/// Snacker.shared.action(.snack(view: v, data: d), container: window)
/// ```
public func action(_ action: SnackAction, container: UIView) {
// Implementation
}- No force unwrapping - Use optional binding or guards
- No force casting - Use conditional casting
- UI on Main Thread - Ensure all UI updates happen on
@MainActor - Memory Management - Watch out for retain cycles in closures (animations)
- SOLID principles - Follow SOLID design
Example:
// ✅ Good
@MainActor
func present(_ view: UIView) {
guard let superview = container else { return }
superview.addSubview(view)
}
// ❌ Bad
func present(_ view: UIView) {
// Might crash if called from background
container!.addSubview(view)
}All code changes must include tests:
- Unit tests - Test queue logic and data models
- Snapshot tests - (Optional but recommended) Verify UI rendering
- Integration tests - Test interaction between Manager and Views
Coverage requirements:
- New code: minimum 80% coverage
- Modified code: maintain or improve existing coverage
Test structure:
import XCTest
@testable import Snacker
final class SnackerManagerTests: XCTestCase {
var sut: Snacker!
override func setUp() {
super.setUp()
sut = Snacker()
}
override func tearDown() {
sut = nil
super.tearDown()
}
// MARK: - Queue Tests
func testAction_WhenQueueEmpty_ShowsImmediately() {
// Given
let view = UIView()
let expectation = expectation(description: "View shown")
// When
sut.action(.snack(view: view, data: .default), container: UIView())
// Then
// Verify state change...
expectation.fulfill()
wait(for: [expectation], timeout: 1.0)
}
}- 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 Snacker! 🎉
Your efforts help make this project better for everyone.