{"id":30344431,"url":"https://github.com/protonpass/ios-authenticator","last_synced_at":"2025-08-18T12:40:01.198Z","repository":{"id":308162637,"uuid":"1022048613","full_name":"protonpass/ios-authenticator","owner":"protonpass","description":"iOS client for Proton Authenticator","archived":false,"fork":false,"pushed_at":"2025-08-04T12:24:15.000Z","size":99407,"stargazers_count":33,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-04T16:48:48.525Z","etag":null,"topics":["authenticator","ios","proton","swift","swiftui"],"latest_commit_sha":null,"homepage":"https://apps.apple.com/us/app/proton-authenticator/id6741758667","language":"Swift","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/protonpass.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-18T11:14:53.000Z","updated_at":"2025-08-04T14:11:39.000Z","dependencies_parsed_at":"2025-08-04T16:59:12.935Z","dependency_job_id":null,"html_url":"https://github.com/protonpass/ios-authenticator","commit_stats":null,"previous_names":["protonpass/ios-authenticator"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/protonpass/ios-authenticator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protonpass%2Fios-authenticator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protonpass%2Fios-authenticator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protonpass%2Fios-authenticator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protonpass%2Fios-authenticator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/protonpass","download_url":"https://codeload.github.com/protonpass/ios-authenticator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protonpass%2Fios-authenticator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270996021,"owners_count":24681931,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authenticator","ios","proton","swift","swiftui"],"created_at":"2025-08-18T12:39:57.930Z","updated_at":"2025-08-18T12:40:01.187Z","avatar_url":"https://github.com/protonpass.png","language":"Swift","readme":"# Proton Authenticator\nThis repository contains the source code for the Proton Authenticator application. \n\n* [Installation](#installation)\n* [Technical Choices](#technical-choices)\n    * [Project Architecture Overview](#project-architecture-overview)\n    * [Architecture Diagram](#architecture-diagram)\n    * [Dependency manager](#dependency-manager)\n    * [Modularization](#modularization)\n* [Debug](#debug)\n    * [Debug network traffic](#debug-network-traffic)\n    * [Debug Sentry activities](#debug-sentry-activities)\n* [Tools](#tools)\n\t* [Dependency injection](#dependency-injection) \n\t* [SwiftLint](#swiftlint)\n\t* [SwiftFormat](#swiftformat)\n    * [Sourcery](#sourcery)\n* [Changelog](#changelog)\n* [Contributing](#contributing)\n* [License](#license)\n\n# Installation\n\nThe app targets iOS 18 and above. Make sure you have Xcode 16+ installed, check out the repo and open `Authenticator.xcodeproj` to run the project.\n\n# Technical Choices\n\n## Project Architecture Overview\n\nThis document outlines our project structure based on a layered (Clean Architecture) approach. \nThe code is organized into local Swift packages to enforce separation of concerns and improve maintainability. \n\nThe main layers are:\n    •    Entities: Core domain models (data structures).\n    •    Domain: Business logic including use cases and repository protocols.\n    •    Data: Concrete implementations for data operations (networking, persistence).\n    •    Presentation: UI components, views, and view models.\n    \n## Architecture Diagram\n\nBelow is a Mermaid diagram that visualizes the dependency flow between the layers:\n\n```mermaid\ngraph TD\n    %% Define Layers\n    subgraph Models [Models Package]\n      E[Models]\n    end\n\n    subgraph Domain [Domain Package]\n      D1[Repository Protocols]\n      D2[Use Cases / Interactors]\n    end\n\n    subgraph Data [Data Package]\n      DA[Repository Implementations]\n      DB[Networking / Persistence]\n    end\n\n    subgraph Presentation [Presentation Package]\n      P1[Views]\n      P2[View Models]\n    end\n    \n    subgraph Shared [Shared Modules]\n      S1[Common Utilities]\n    end\n\n    %% Dependencies between layers\n    D1 --\u003e E         \n    %% Domain uses Models\n    D2 --\u003e D1        \n    %% Use Cases depend on Repository Protocols\n    DA --\u003e D1        \n    %% Data layer implements Domain protocols\n    DA --\u003e DB       \n    %% Data layer handles external data (API, DB)\n    P2 --\u003e D2        \n    %% Presentation layer (View Models) uses Use Cases\n    P1 --\u003e P2        \n    %% Views bind to View Models\n    \n    %% Shared modules dependencies\n    D1 \u0026 D2 \u0026 DA \u0026 P1 \u0026 P2 --\u003e S1  \n    %% All layers can use common utilities\n```\n\n## Package Functions\n\n### Models Package\n\n- Purpose:\nContains the fundamental data models (the “entities”) that represent your domain objects. \nThese should be simple, framework-agnostic, and ideally immutable value types (structs).\n\n- Examples:\n    \n```swift\n// User.swift\npublic struct User {\n    public let id: String\n    public let name: String\n}\n```\n### Domain Layer (Domain Package)\n\n- Purpose:\nHolds the core business logic of your application. This layer is independent of any external frameworks or data sources, making it highly testable and reusable. It defines what the app does, not how it does it.\n\n- What to Include:\n    •    Use Cases: These encapsulate specific business operations (e.g., “Log in a user”, “Fetch articles”) and orchestrate the work of various repositories or domain services.\n    •    Repository Protocols: Define the interfaces for data operations without committing to an implementation. The domain layer shouldn’t know about networking, persistence, etc.\n    •    Domain Services: If you have business rules or operations that don’t naturally fit into a single entity, they can go here.\n    •    (Optional) Domain Models: Sometimes you may have a distinction between raw data models (entities) and enriched domain models, though in many cases your entities package can serve both purposes.\n- Example:\n\n```swift\n// UserRepository.swift (protocol)\npublic protocol UserRepository {\n    func fetchUser(withID id: String, completion: @escaping (Result\u003cUser, Error\u003e) -\u003e Void)\n}\n\n// LoginUseCase.swift\npublic struct LoginUseCase {\n    private let userRepository: UserRepository\n    \n    public init(userRepository: UserRepository) {\n        self.userRepository = userRepository\n    }\n    \n    public func execute(login: String, password: String, completion: @escaping (Result\u003cUser, Error\u003e) -\u003e Void) {\n        // Business logic can be inserted here, e.g. validation, transformation, etc.\n        userRepository.fetchUser(withID: login, completion: completion)\n    }\n}\n```\n\n### Data Layer (Data Package)\n- Purpose:\nContains the concrete implementations for data operations. \nThis layer is responsible for how data is fetched, stored, or updated. It might interact with REST APIs, databases, or other external services.\n\n- What to Include:\n    •    Repository Implementations: Classes or structs that conform to the protocols defined in the Domain layer.\nFor example, a UserRepositoryImpl that uses URLSession to fetch user data from a server.\n    •    Network Clients \u0026 API Services: All the code related to making HTTP requests, handling responses, and mapping raw data into your domain models.\n    •    Persistence Solutions: Code that deals with local storage, caching, or database operations.\n    •    Data Mappers: If your external data formats differ from your domain models, include logic here to translate between them.\n\n- Example:\n\n```swift\n// UserRepositoryImpl.swift\npublic class UserRepositoryImpl: UserRepository {\n    private let apiClient: APIClient  // Assume APIClient is a networking abstraction\n    \n    public init(apiClient: APIClient) {\n        self.apiClient = apiClient\n    }\n    \n    public func fetchUser(withID id: String, completion: @escaping (Result\u003cUser, Error\u003e) -\u003e Void) {\n        let endpoint = \"https://api.example.com/users/\\(id)\"\n        apiClient.get(url: endpoint) { result in\n            switch result {\n            case .success(let data):\n                // Map data to User (consider using a dedicated mapper)\n                do {\n                    let user = try JSONDecoder().decode(User.self, from: data)\n                    completion(.success(user))\n                } catch {\n                    completion(.failure(error))\n                }\n            case .failure(let error):\n                completion(.failure(error))\n            }\n        }\n    }\n}\n```\n\n### Presentation Layer (Screens Package)\n- Purpose:\nManages everything related to the UI—views, view models, and any presentation logic. \nThis layer depends on the Domain layer (via use cases) to drive its data and actions.\n\n- What to Include:\n    •    Views: SwiftUI views or UIKit view controllers.\n    •    View Models: Components that bind the UI to the underlying domain logic. They often call the use cases from the Domain layer and handle state updates for the view.\n\n### Common Utilities\n\n- Purpose:\nProvides cross-cutting extensions and utility functions (e.g., extensions on String, Date, etc.) that are useful across multiple packages.\nHolds configuration files (e.g., JSON, plist, or YAML) and loader logic that provide environment-specific settings such as API endpoints or feature toggles.\n\n## Dependency manager\nSwift Package Manager\n\n# Debug\n\u003c!----\u003e\n\u003c!--## Debug network traffic--\u003e\n\u003c!--You can print to the console information related to requests (HTTP method, path, headers, \u0026 parameters) and responses (HTTP method, status code, url, headers \u0026 result) by activating `me.proton.pass.NetworkDebug` environment variable in the scheme configuration. This is disabled by default.--\u003e\n\n\u003c!--## Debug Sentry activities--\u003e\n\u003c!--You can print to the console Sentry activities by activating `me.proton.pass.SentryDebug` environment variable in the scheme configuration. This is disabled by default.--\u003e\n\n# Tools\n\n## Dependency injection\n\nThe main DI tool used is [Factory](https://github.com/hmlongco/Factory). It is very light but yet very powerful.\n\n## SwiftLint\n\nThis is the main linter for the project.\nTo install run the following [Homebrew](https://brew.sh/) command:\n\n```bash\nbrew install swiftlint\n```\n\nIf you don't have this tool installed please refer to the following link to set it up: [SwiftLint](https://github.com/realm/SwiftLint)\nThe configuration for this tool can be found in the `.swiftlint.yml` file.\n\n\n## SwiftFormat\n\nThis is the main code reformatting tool for the project.\nTo install run the following [Homebrew](https://brew.sh/) command:\n\n```bash\nbrew install swiftformat\n```\n\nIf you don't have this tool installed please refer to the following link to set it up: [SwiftFormat](https://github.com/nicklockwood/SwiftFormat)\nThe configuration for this tool can be found in the `.swiftformat` file\n\n## Periphery\n\nThis is the main tool to detect and remove unused code in the project.\nTo install run the following [Homebrew](https://brew.sh/) command:\n\n```bash\nbrew install periphery\n```\n\nIf you don't have this tool installed please refer to the following link to set it up: [Periphery](https://github.com/peripheryapp/periphery)\nThe configuration for this tool can be found in the `.periphery.yml` file.\n\nTo scan and detect unused code just execute the following CLI command:\n\n```bash\nperiphery scan\n```\n\nYou can now remove all the unused code.\n\n## Pre-commit\n\nMake sure codes are properly linted and formatted before committing.\n\nFirst install pre-commit\n```bash\nbrew install pre-commit\n```\n\nThen create a pre-commit hook\n```bash\npre-commit install --hook-type pre-commit\n```\n\nAfter initializing pre-commit for the repo, the next first commit will take a bit of time because pre-commit needs to download and compile necessary tools configured in `.pre-commit-config.yaml` (swiftlint, swiftformat...)\n\n# Changelog\nFor a detailed list of changes in each version of the project, please refer to the [CHANGELOG](CHANGELOG.md) file.\n\n\n\u003c!--# Contributing--\u003e\n\u003c!----\u003e\n\u003c!--We value and welcome contributions from the community to help make this project better.--\u003e\n\u003c!----\u003e\n\u003c!--Please note that while we encourage contributions, we have a specific process in place for handling pull requests (PR) and merging changes. To ensure a smooth workflow, we manage contributions internally on our GitLab repository rather than directly on GitHub.--\u003e\n\u003c!----\u003e\n\u003c!--Here's how you can contribute:--\u003e\n\u003c!----\u003e\n\u003c!--1. **Fork the Repository**: Start by forking this repository to your own GitHub account.--\u003e\n\u003c!----\u003e\n\u003c!--2. **Make Your Changes**: Create a new branch from `main` in your forked repository and make the necessary changes.--\u003e\n\u003c!----\u003e\n\u003c!--3. **Test Your Changes**: Ensure that all tests, swiftlint check and swiftformat check are passing.--\u003e\n\u003c!----\u003e\n\u003c!--To do a swiftlint check, run this command:--\u003e\n\u003c!----\u003e\n\u003c!--```bash--\u003e\n\u003c!--\u003e swiftlint--\u003e\n\u003c!--```--\u003e\n\u003c!----\u003e\n\u003c!--To do a swiftformat check, run this command:--\u003e\n\u003c!----\u003e\n\u003c!--```bash--\u003e\n\u003c!--\u003e swiftformat --lint .--\u003e\n\u003c!--```--\u003e\n\u003c!----\u003e\n\u003c!--To let swiftformat format your code, run this command:--\u003e\n\u003c!----\u003e\n\u003c!--```bash--\u003e\n\u003c!--\u003e swiftformat .--\u003e\n\u003c!--```--\u003e\n\u003c!----\u003e\n\u003c!--4. **Submit a PR**: Once your changes are ready for review, you can submit a PR on this repository. Our team will review your PR, provide feedback, and collaborate with you if any adjustments are needed.--\u003e\n\u003c!----\u003e\n\u003c!--5. **Collaborate**: Feel free to engage in discussions and address any feedback or questions related to your PR. Collaboration is key to delivering high-quality contributions.--\u003e\n\u003c!----\u003e\n\u003c!--6. **Finalization**: Once the PR is approved and meets our criteria, it will be merged into our internal Gitlab repository. Subsequently, your PR will be closed, and your changes will be incorporated when we periodically synchronize updates to GitHub.--\u003e\n\n\n# License\nThe code and data files in this distribution are licensed under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See \u003chttps://www.gnu.org/licenses/\u003e for a copy of this license.\n\nSee [LICENSE](LICENSE) file\n","funding_links":[],"categories":["Swift"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprotonpass%2Fios-authenticator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprotonpass%2Fios-authenticator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprotonpass%2Fios-authenticator/lists"}