{"id":21668443,"url":"https://github.com/toainguyenkotu/mockupinterview","last_synced_at":"2026-05-15T20:31:44.546Z","repository":{"id":264415700,"uuid":"893307916","full_name":"toainguyenkotu/MockupInterview","owner":"toainguyenkotu","description":"iOS-Clean-Architecture-MVVM-SwiftUI-Concurrence","archived":false,"fork":false,"pushed_at":"2024-11-24T13:52:05.000Z","size":249,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-20T07:18:30.336Z","etag":null,"topics":["clean-architecture","concurrency","ios","mvvm","protocol-oriented-programming","sdwebimage","swift","swiftformat","swiftgen","swiftlint","swiftui","unit-testing"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/toainguyenkotu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-11-24T04:43:17.000Z","updated_at":"2024-12-14T01:23:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"9c1ecfd9-1357-40d4-ba39-0788a9ba5432","html_url":"https://github.com/toainguyenkotu/MockupInterview","commit_stats":null,"previous_names":["toainguyenkotu/mockupinterview"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/toainguyenkotu/MockupInterview","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toainguyenkotu%2FMockupInterview","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toainguyenkotu%2FMockupInterview/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toainguyenkotu%2FMockupInterview/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toainguyenkotu%2FMockupInterview/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/toainguyenkotu","download_url":"https://codeload.github.com/toainguyenkotu/MockupInterview/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toainguyenkotu%2FMockupInterview/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265345229,"owners_count":23750572,"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","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":["clean-architecture","concurrency","ios","mvvm","protocol-oriented-programming","sdwebimage","swift","swiftformat","swiftgen","swiftlint","swiftui","unit-testing"],"created_at":"2024-11-25T12:15:52.716Z","updated_at":"2026-05-15T20:31:44.517Z","avatar_url":"https://github.com/toainguyenkotu.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n## MockupInter App using Clean Architecture and MVVM\nThe purpose of this project is to show the basic aspects of Clean Architecture and MVVM design pattern, protocol-oriented-programming, Concurrence, SwiftUI.\n\n## Architecture\nThe architecture is structured into three main layers:\n\n1. Data Layer: Responsible for data retrieval and manipulation: Gateway Implementations + API (Network) + Database\n2. Domain Layer: Contains business logic and use cases: Entities + Use Cases + Gateway Protocols\n3. UI/Presentation Layer: Manages user interface and user interactions: ViewModels + View\n\n## Project Structure\n\n```\n.\n├── MockupInterview\n│   ├── Application\n│   │   └── MockupInterviewApp.swift\n│   ├── Data\n│   │   ├── APIServiceBase\n│   │   │   ├── Common\n│   │   │   │   ├── APIServiceBase.swift\n│   │   │   │   ├── BaseEndpoint.swift\n│   │   │   │   └── APIErrorBase.swift\n│   │   │   ├── API\n│   │   │   │   ├── APIService\n│   │   │   │   └── APIUrls\n│   │   │   └── Endpoint\n│   │   │       ├── Endpoint.swift\n│   │   │       ├── UserEndPoint.swift\n│   │   │       └── UserDetailEndpoint.swift\n│   │   ├── Gateway \n│   │   │   ├── UserGateway.swift\n│   │   │   └── UserDetailGateway.swift\n│   │── Domain\n│   │   ├── UseCase\n│   │   │   │── UserUseCase.swift\n│   │   │   └── UserUseCase.swift\n│   │   ├── Entities\n│   │   │   ├── Common\n│   │   │   │   ├── EmptyModel.swift\n│   │   │   │   └── APIError.swift\n│   │   │   ├── User.swift\n│   │   │   └── UserDetail.swift\n│   ├── Presentation\n│   │   ├── Common\n│   │   │   ├── RefreshableListView.swift\n│   │   │   ├── SFSafiriView.swift\n│   │   │   ├── LoadingView.swift\n│   │   │   ├── BackButton.swift\n│   │   │   └── CardView.swift\n│   │   ├── Splash\n│   │   │   └── SplashView.swift\n│   │   ├── UserDetail\n│   │   │   ├── ChildView\n│   │   │   │    ├── LocationView.swift\n│   │   │   │    ├── FollowView.swift\n│   │   │   │    └── Blog.swift\n│   │   │   └── UserDetailView.swift\n│   │   │    └── UserDetailViewModel.swift\n│   │   ├── UserDetail\n│   │   │    ├── ChildView\n│   │   │    │    └── LandingPageView.swift\n│   │   │    ├── UserView.swift\n│   │   │    └── UserViewModel.swift\n│   ├── Resources\n│   │   ├── Assets.xcassets\n│   │   ├── File+\n│   │   │   ├── Util.swift\n│   │   │   └── Constants.swift\n│   │   ├── Extension\n│   │   │   ├── PreferenceKey+.swift\n│   │   │   ├── CGFloat++.swift\n│   │   │   ├── String+.swift\n│   │   │   └── ViewDidLoadModifier.swift\n│   │   ├── Helper\n│   │   │   ├── CoordinatorStack\n│   │   │   │   ├── Coordinatable.swift\n│   │   │   │   ├── Coordinator.swift\n│   │   │   │   ├── CoordinatorPages.swift\n│   │   │   │   └── CoordinatorStack.swift\n│   │   │   ├── BaseViewModel\n│   │   │   │   └── ViewModel.swift\n│   │   │   └── ThreadSafe\n│   │   │       └── ThreadSafe.swift\n│   │   └── Localizable.xcstrings\n│   ├── Generated\n│   │   ├── Assets.swift\n│   │   └── Strings.swift\n│   └── Preview Content\n│       ├── UserDetailMock.json\n│       ├── UserlMock.json\n│       └── Preview Assets.xcassets\n├── MockupInterviewTests\n│   ├── MockupInterviewTests\n│   ├── Data\n│   │   └── GatewayMock\n│   │      ├── UserGatewayMock.swift\n│   │      └── UserDetailGatewayMockup.swift\n│   ├── Data\n│   │   └── Entities\n│   │      ├── UserViewTests.swift\n│   │      └── UserDetailViewTests.swift\n│   └── Presentation\n│       ├── UserViewTests.swift\n│       └── UserDetailViewTests.swift\n└── GithubUsersUITests\n    └──  MockupInterviewUITests.swift\n```\n\n## Developer Tools\n\n- [Xcode 15.4](https://apps.apple.com/vn/app/xcode/id497799835?mt=12) or newest\n- [Mint 0.17.5](https://github.com/yonaskolb/Mint) or newest\n\n## Get Started\n\n1. Install [Mint](https://github.com/yonaskolb/Mint) via Homebrew\n   ```sh\n   brew install mint\n   ```\n2. Open Terminal and cd to the project folder and run each of the following commands:\n   ```sh\n   mint run SwiftGen\n   mint run swiftformat \"MockupInterview\"\n   mint run SwiftLint\n   ```\n3. Open `MockupInterview.xcodeproj`, choose target and build.\n\n## Schemes\n\n- MockupInterview\n- MockupInterviewTests: Unit Test\n\n## Library\n- SDWebImageSwifUI: to download image asynchronous and cache image into local device \n(https://github.com/SDWebImage/SDWebImageSwiftUI.git)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoainguyenkotu%2Fmockupinterview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftoainguyenkotu%2Fmockupinterview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoainguyenkotu%2Fmockupinterview/lists"}