Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Q42/template-ios

Repository files navigation

Q42 iOS template

This is a template for creating iOS projects at Q42. It has opinionated defaults and boilerplate, based on how we do iOS projects at Q42.

Using this template

  1. Click the green "Use this template" button in the Github repo to create your own repository with this code template.
  2. Clone the new repo locally on your machine.
  3. Run python3 ./scripts/rename-project.py from the project root to change the project name.
    • Delete the script afterwards: rm ./scripts/rename-project.py.
  4. Optional: Configure GitHub Actions for TestFlight and App Store builds.
    • Edit build.yml to enable the workflow trigger.
    • Set an App Store Connect API key and signing certificate in your GitHub Actions Secrets as described in this readme under CI configuration.

Features

Only basic features that almost all projects use were added to this template:

  • SwiftUI using the SwiftUI lifecycle with an AppDelegate
  • Implementation of Clean Architecture
  • Dependency injection using the library Factory
  • Unit tests and UI tests using Salad
  • GitHub Actions CI configuration that runs the tests and submits the app to TestFlight

Xcode 26 or higher is required.

Code style

The Xcode project is configured to use 4 spaces for indentation. For linting Swift source code, we use SwiftLint. A configuration for SwiftFormat is also included.

To install and run the formatter:

brew install swiftformat
swiftformat .

To install and run the linter:

brew install swiftlint
swiftlint . --fix

App architecture

Core principles

This app is built using SwiftUI and targets iOS 15 and higher. We use SwiftUI as much as possible, but fall back to UIKit views using view(controller) representables where needed.

We try to stick to the Apple conventions and write idiomatic SwiftUI code. Do things the Apple way. Lean in to the platform instead of fighting it.

Keep it simple. Less is more.

Architecture patterns

We use the Clean Architecture pattern, combined with dependency injection.

Clean Architecture layers

  • Features with SwiftUI views and view models
  • Domain for domain models, UseCases and other domain logic.
  • Data for persistent data storage and retrieval using Repositories.

Use cases

  • Use cases are single-purpose: GetUserUseCase, but also: GetUserWithArticlesUseCase.
  • Use cases can call other use cases.
  • Use cases do not have persistent state. They are instantiated, called to perform their function once or multiple times, and are then discarded.

Dependency injection

Dependency injection (DI) means that objects don't instantiate the objects or configurations that they require themselves, but they are passed in from the outside. This is useful, because it makes code easier to test and easier to change. It promotes good separation of concerns.

We use Factory as a DI container.

Modules & libraries

  • Preferably use the Swift Package Manager for dependencies. Use other package managers only if there's no other option.
  • Only extract code into a package if there are strong reasons to do so. For example:
    • It is used from at least two different targets/packages, or is a candidate to be extracted to an open-source package.
    • It is completely self-contained.

When choosing a third-party library, prefer libraries that:

  • Are written in idiomatic Swift or Objective-C that sticks to best practices.
  • Have as few dependencies of its own as possible. Preferably none.
  • Aren't too big, so as to keep compile times and bloat in check.

Testing

  • For business logic, we write unit tests.
  • For testing the user interface, we write UI tests in a behaviour-driven way using the Salad library.
  • Tests are run on CI (GitHub Actions). Tests must pass before a PR may be merged and before any sort of build is created.

Views

  • Keep views focused (single-responsibility principle from SOLID). When a view becomes large, split it up into smaller views.
  • Every view gets a UI preview if at all possible. The preview should show the view in different states using dummy data.
  • We use custom SF Symbols whenever a custom icon is needed, so that they render in a consistent manner.

Accessibility

  • Every new component or control should be audited for basic accessibility support:
    • Dynamic type size support
    • VoiceOver support
  • Also consider:
    • Bold text support
    • High contrast support
  • Use accessibilityRepresentation on custom controls to make them accessible.

Localisation

String catalogues are used to localise the project. The default languages supported are English and Dutch.

Async code

  • async/await is preferred over Combine/Promises/etc. to leverage the compiler concurrency checking.
  • Combine can be used when async/await or AsyncSequence fall short, and more complexity is needed to solve the problem at hand.

Continuous integration

GitHub Actions is used for continuous integration (CI). The CI runs the automated tests when you make a pull request. On a push to the main branch, it will also run the tests, and if they pass, a build of the app is made and uploaded to TestFlight.

CI configuration

Five environment secrets are needed for the workflow to run on GitHub Actions. You may configure these in the repository secret settings on GitHub.

  • BUILD_CERTIFICATE_BASE64 contains a base64-encoded string of the .p12 certificate bundle, used to code sign the app. This bundle needs to contain two certificates: development and distribution.
  • P12_PASSWORD contains the password of the certificate bundle.
  • APP_STORE_CONNECT_API_KEY_BASE64 contains a base64-encoded string of the .p8 App Store Connect API key.
  • APP_STORE_CONNECT_API_KEY_ID contains the key ID of the App Store Connect API key.
  • APP_STORE_CONNECT_API_KEY_ISSUER_ID contains the issuer ID of the App Store Connect API key.

To create such a certificate bundle, open Keychain Access. Unfold the entries for the development and distribution certificate. Select the certificates and their private keys using shift, then right-click and select "Export 4 items...".

You can encode a file to base64 on the command line like this: base64 -i ~/Desktop/Certificates.p12 | pbcopy. This automatically puts the result on your clipboard.

Release process

The process of releasing a new version of an app goes as follows.

1. Increment version number and commit

First, a new version number is picked for the app. The version numbering convention is up to the project team to decide. Semantic Versioning may be used. For example, given a version number MAJOR.MINOR.PATCH, increment the:

  • MAJOR version when you make changes that impact the user's workflow of the app
  • MINOR version when you add functionality
  • PATCH version when you make bug fixes

Set this version number in the Xcode project settings and commit it.

2. Create release branch

Create a branch release/<version> in which <version> is the new version number. Push the branch to GitHub and create a pull request. GitHub Actions will automatically create a release build for you and upload it to App Store Connect. You can distribute this build to TestFlight if you want.

Any last-minute changes before the release goes out may be committed to this branch.

3. Create GitHub release

In the Releases section of the project on GitHub, draft a new release. For the tag field, enter the new version number. For the target field, select the release branch that you've created earlier.

You can automatically generate the (internal) release notes. It will include a list of all the PRs that were merged.

Save the release as a draft so that GitHub doesn't add a tag for it yet.

4. Release to the App Store

Create a new version of the App in App Store Connect using the new version number. Select the build of the app that GitHub Actions has created earlier. Update the App Store metadata (screenshots, description) as needed. Enter the public release notes.

Then submit to the App Store.

5. Finalise GitHub release

In the Releases section of the project on GitHub, publish your release, checking the "Set as the latest release" checkbox. Add the public release notes to the changelog if you'd like.

Finally, merge the release branch to main.

All done! 🚀

About

A template for creating iOS projects at Q42.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •