1 minute read

How to use GitHub for version control as a solo iOS developer

When an iOS project grows beyond a few screens, the Xcode folder becomes valuable quickly. Without history, a broken dependency or accidental project-setting change can erase hours. Version control gives each meaningful change an identity and a route back, whether you are polishing a Swift app for friends or preparing a commercial build.

Git is the version control system that records those changes; GitHub hosts the repository so its history can be cloned, reviewed, and shared. A commit stores a snapshot, a branch separates an experiment, and a tag marks a stable point such as a TestFlight release. Local commits also keep you working when the connection is unreliable.

The mental model is simple: keep main working, use a feature branch for unfinished ideas, and push local commits to GitHub. This does not require a team. It makes decisions visible to your future self, especially when a bug appears three weeks later.

For an Australian solo developer, remote history supports a distributed workday. If the project owner is in Melbourne while you code in Atyrau, AEST is five hours ahead, and daylight-saving changes can alter that gap. A pushed commit, tagged build, and short update let either person continue without waiting.

Why version history matters

The main advantage is selective recovery. A cloud copy may contain yesterday’s folder, but it cannot explain which edit introduced a crash or preserve several nearby versions. Git links snapshots as commits, so you can compare changes, restore an earlier state, or reverse one commit without discarding everything else.

Xcode, GitHub Desktop, and the command line operate on the same Git history; they differ mainly in visibility and control.

Method Main strength Main limitation Sensible role
Xcode source control Familiar project context and visual conflict tools Some Git details remain hidden Everyday commits and branch review
GitHub Desktop or terminal Precise control, scripting, and access to full history Requires interface or command knowledge Focused coding, automation, and recovery
Cloud folder or ZIP archive Quick whole-project copy Weak history and frequent overwrites Extra backup rather than primary history

Use one interface for routine actions and learn the underlying commands as needed. Xcode is convenient for reviewing a project diff, while Terminal commands are valuable for scripting and recovering history. Treat both as views of the same repository rather than separate copies.

Start with a private, clean repository

Create a GitHub account, sign in through Xcode’s account settings, and make the first repository private. Set your identity with git config --global user.name and git config --global user.email. A Git repository contains source files and their history; it is not the same thing as an Xcode project.

For a local app, run git init, create .gitignore and README.md, then stage and commit the important files. Rename the branch with git branch -M main, connect it using git remote add origin <repository-url>, and publish it with git push -u origin main. If Xcode creates the repository first, complete the same remote and push steps.

The ignore file should exclude generated output such as DerivedData/, build/, .DS_Store, and Xcode user state such as xcuserdata/. Keep Swift sources, asset catalogues, entitlements, and usually Package.resolved, because an app often needs those files to build consistently. Avoid ignoring a file until you know whether Xcode generates it or it carries project intent.

Make commits that explain the change

A commit should represent one coherent change, not an entire day’s output. Before committing, inspect git status, read the diff, and stage relevant files with git add. This catches an accidental signing key, a large build product, or an unrelated rename while the change is still easy to understand.

Use a short, imperative subject such as Fix Face ID fallback after cancellation. Add a body when the reason is not obvious: Use Keychain-backed device owner authentication instead of leaving the sign-in flow stuck. Good history explains why the change was made because the diff already shows which lines changed.

Keep unrelated cleanup in another commit, and commit before experiments that could destabilise the project. If a framework or Swift package changes the result, record that in the message or link the relevant issue. These habits make git log useful documentation rather than a collection of vague update entries.

Use branches without team overhead

Keep main buildable and create a branch for a feature, experiment, or bug fix. The command git switch -c feature/biometric-login creates one locally, while git push -u origin feature/biometric-login creates a matching remote branch. Short names make branches easier to scan and clarify unfinished work.

After several focused commits, open a pull request even when nobody else reviews it. The diff provides a final check, the description records the purpose, and merging creates a clear point in history. Once the work is merged and pushed, delete the branch locally with git branch -d and remove it from GitHub when it no longer has value.

For a one-line correction on a private personal app, committing directly to main can be reasonable. Branches become useful when a change affects signing, dependencies, navigation, or several screens. If a merge conflict appears, open the affected files in Xcode, choose the correct content deliberately, mark them resolved, and finish the merge rather than forcing the remote branch.

Sync Xcode, GitHub, and tested builds

Begin a coding session by pulling remote commits, then finish it by committing and pushing your own. In Xcode, use the Source Control navigator to inspect changes and the Push and Pull controls to synchronise. git pull updates from the remote, while git push publishes local commits. Pull before branching so the feature starts from the latest known state.

Authentication normally happens through Xcode, GitHub Desktop, GitHub CLI, SSH, or a credential manager. Do not paste a personal access token into a command, README, or committed configuration. If Git rejects an identity, verify the repository URL, branch name, account permissions, and stored credentials before changing source files.

Tags and GitHub Releases make delivery easier to repeat. After an Archive passes your checks, tag the exact commit with a command such as git tag -a v1.2.0 -m "Quiet Hours 1.2.0" and push the tag. Link the release to TestFlight or App Store Connect notes, but keep large binary archives in the appropriate distribution service unless Git LFS is configured.

Recover safely and protect sensitive work

Start recovery with git log --oneline and the file diff rather than deleting the project. git restore <file> can discard an uncommitted file change, while git revert <commit> creates a new commit that reverses an earlier one. The reflog can reveal local commits that appear lost after a reset, but copying the folder first makes recovery safer.

Avoid git push --force on a shared default branch. On a solo repository it may occasionally repair history, yet it can replace work that exists only on GitHub. Prefer a revert or merge. For an unused feature branch, --force-with-lease is safer because it refuses to overwrite unseen commits.

A .gitignore entry protects only files that have not been committed. If an API key, signing secret, or client document reaches history, rotate the secret first, remove unnecessary access, and then clean the repository. A private GitHub repository is not a secrets vault, and rewriting old commits does not make exposed material private again.

Australian client agreements may involve confidentiality and obligations under the Privacy Act 1988. Grant collaborators only the access they need and keep unrelated client work in separate repositories. Store production credentials through Keychain or a secure deployment process. Git LFS can handle large approved assets, but it does not replace encrypted backups or a tested account-recovery plan.

Build a dependable Australian solo workflow

Australian freelance work often requires technical evidence and clear commercial boundaries. An ABN identifies an Australian business, invoices may be issued in Australian dollars, and GST at 10% may need to be included where required. Keep financial records separate from source control, but mention the app version and commit hash in release notes so delivery can be traced.

A relaxed phrase such as “no worries” fits casual Australian workplace conversation, but it should not replace precision. A useful update says, “Pushed the sign-in fix, tested it on two devices, and commit a1b2c3d is ready for review.” It confirms the work without requiring the client to understand Git.

A dependable solo routine can stay deliberately small:

  • Keep main in a buildable state and avoid parking unfinished code there.
  • Pull before starting and push after every completed checkpoint.
  • Make one useful commit for each logical change, with a clear subject.
  • Tag tested versions and attach concise release notes.
  • Review branches, dependencies, repository access, and GitHub settings monthly.
  • Keep an encrypted backup and rehearse restoring one important project.

None of this requires a large team or an expensive hosting plan. GitHub private repositories, local clones, and release records provide a practical safety net for a solo developer. Open the current Xcode project, create a private repository, and push its present source as a commit tagged baseline.

Updated: