A native iOS GitLab client built with SwiftUI. Grit aims to provide a way to browse repositories, review merge requests, track pipelines, and monitor your GitLab workflow from an iPhone. Issues can be created and commented on as well, bringing together a collective set of management features to help in a pinch for when you're out and about.
Just some of the features of this application, with more to come as the project matures.
- Browse all your personal and group repositories with group filtering and three sort orders (Recently Edited, Alphabetical, Newest First)
- Star / unstar repositories directly from the list or detail view
- Switch branches and view the default-branch pipeline status at a glance
- Copy clone URLs, open repos in Safari, or jump to group namespaces
- Navigate directory trees and view file contents with syntax-aware display
- Full commit history per branch with author, timestamp, and short message
- Commit detail view with unified diff and metadata
- List all branches; protected and default branches are visually distinguished
- Navigate directly into a branch detail view to observe commit history for commits
- Embedded MR list inside the repository detail view, plus a standalone sheet
- MR detail with description, labels, assignees, and milestone
- Pipeline browser — all pipelines triggered for the MR, each tappable to open a full pipeline detail sheet
- Per-job status with stage grouping
- Live status badges (Passed, Failed, Running, Pending, Canceled, etc.)
- Pipeline source labels (Push, Schedule, API, Web IDE, DAST Scan, etc.)
- Background polling refreshes pipeline status while the view is open
The public test flight for this app can be joined at this link: https://testflight.apple.com/join/4VWmabkT
Grit follows MVVM with a unidirectional data flow:
Views ──▶ ViewModels ──▶ Services ──▶ GitLab API / Cache
▲ │
└───────────────┘ (@Published / ObservableObject)
Grit/
├── App/
│ ├── GritApp.swift # @main entry point, background task registration
│ └── AppNavigationState.swift # Global navigation state (current repo, branch)
├── Models/
│ ├── Repository.swift
│ └── ...
├── ViewModels/
│ ├── RepositoryViewModel.swift # List VM + Detail VM
│ └── ...
├── Views/
│ ├── Repositories/
│ ├── Files/
│ ├── Commits/
│ ├── Branches/
│ ├── MergeRequests/
│ ├── Pipelines/
│ ├── Issues/
│ ├── Forks/
│ ├── Search/
│ ├── Settings/
│ └── Components/
├── Services/
└── Resources/
| Requirement | Version |
|---|---|
| iOS | 26.0+ |
| Xcode | 16.3+ |
| XcodeGen | 2.x (brew install xcodegen) |
| GitLab | Any self-hosted or GitLab.com instance with API v4 |
git clone https://gitlab.com/stoicswe/grit.git
cd gritxcodegen generateopen Grit.xcodeprojIn Xcode, select the Grit target → Signing & Capabilities → set your Team and Bundle Identifier.
- In your GitLab instance go to User Settings → Applications.
- Create a new application with the redirect URI
grit://oauth/callback. - Grant scopes:
read_api,read_user,read_repository. - Copy the Application ID and Secret.
- Add them to
Grit/Resources/Config.xcconfig(or the appropriate secrets file in your setup).
Select a simulator or device and press ⌘R.
Grit uses the Xcode 15+ String Catalog approach (Localizable.xcstrings):
- SwiftUI
Text("literal")calls are automatically localised — no code changes needed. - Non-UI strings (model labels, error descriptions) use
String(localized: "…", comment: "…"). SWIFT_EMIT_LOC_STRINGS = YESis set in the build configuration; the Swift compiler extracts all localisable strings into the catalog automatically on each build.- To add a new language: open
Localizable.xcstringsin Xcode, click +, select the language, and provide translations — no code changes required.
Currently ships with English. The infrastructure is fully ready for additional languages.
- Fork the repository on GitLab.
- Create a feature branch:
git checkout -b feature/my-feature. - Commit your changes following the existing code style.
- Open a Merge Request against
main.
Note: There are multiple Swift format checks made against the PRs in this repository. Please make note of suggested changes and warnings generated by the tooling to ensure that we can keep the source code maintainable.
Please ensure:
- New UI strings use
String(localized:comment:)orText("literal")(not string variables). - New API calls are added to
GitLabAPIServiceand areasync throws. - New model types conform to
CodableandIdentifiable. - Cache keys for new data types are added to
RepoCacheStore.CacheKey.
This project uses a dual-license model:
| Component | License | File |
|---|---|---|
| Source code (Swift, logic, services) | MIT | LICENSE |
| App & UI design (visual design, assets, UI components) | Apache 2.0 | APP_LICENSE |
In short: you can freely use and build on the code under MIT terms, but the app's visual design and UI are covered by Apache 2.0, which requires attribution when redistributed.
Original Author: Nathaniel Knudsen (@stoicswe)
For additional authors, please look at the list of contributors that GitLab generates.