~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atirc-ios-client

0
1
main
289 lines · 7.7 KB Raw History
# Contributing to AT-IRC

AT-IRC is a SwiftUI iOS client with a small embedded Go library (`GoLib`). The
whole build/test/run loop works from the command line; Xcode is optional.

All commands below are run from the repository root.

## Prerequisites

- macOS with Xcode 27.2 or newer and an iOS 27.2 simulator runtime.
- Xcode's command-line tools selected:

  ```bash
  xcode-select -p   # should point into /Applications/Xcode.app
  xcodebuild -version
  xcrun simctl list runtimes
  ```

- Go 1.27.3+ if you will change `GoLib`:

  ```bash
  go version
  go install golang.org/x/mobile/cmd/gomobile@latest
  go install golang.org/x/mobile/cmd/gobind@latest
  ```

  Install **both** tools. `gomobile bind` shells out to `gobind`; installing only
  `gomobile` fails with `gobind was not found`.

The generated Go framework is not checked in. On a fresh clone, build it once
before building the app:

```bash
./GoLib/build.sh
```

This writes `GoLib/AtircGo.xcframework`, which is gitignored. Skipping this step
makes the app fail to compile with `No such module 'AtircGo'` or fail to link
with `framework not found AtircGo`.

## Layout

| Path | Contents |
|---|---|
| `AT-IRC/` | SwiftUI app sources |
| `AT-IRCTests/` | Swift Testing unit tests |
| `AT-IRCUITests/` | XCUITest tests |
| `GoLib/` | Go module, `build.sh`, and the generated xcframework |
| `docs/golang/` | Notes on the Go bridge |

There is one shared scheme, `AT-IRC`.

## Set up the shell

The examples use these variables. Run this once in the shell you are working in;
change `DEVICE` to any available simulator.

```bash
DEVICE="iPhone 17"
DEST="platform=iOS Simulator,name=$DEVICE"
BUNDLE_ID="com.fedfork.irc.AT-IRC"
APP=".build/Build/Products/Debug-iphonesimulator/AT-IRC.app"
```

Using `-derivedDataPath .build` keeps build products inside the repository at a
predictable path. `.build/` is gitignored.

List the available simulators and destinations with:

```bash
xcrun simctl list devices available
xcodebuild -showdestinations -project AT-IRC.xcodeproj -scheme AT-IRC
```

## Build from the command line

Debug build for a simulator:

```bash
xcodebuild build \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -configuration Debug \
  -destination "$DEST" \
  -derivedDataPath .build
```

For a CI build that does not need a specific simulator, use the generic
destination:

```bash
xcodebuild build \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -configuration Debug \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath .build
```

The app is always at:

```text
.build/Build/Products/Debug-iphonesimulator/AT-IRC.app
```

Use `-configuration Release` for a release build.

## Test from the command line

Fast loop: run only the unit tests.

```bash
xcodebuild test \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -destination "$DEST" \
  -derivedDataPath .build \
  -only-testing:AT-IRCTests
```

Run only UI tests:

```bash
xcodebuild test \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -destination "$DEST" \
  -derivedDataPath .build \
  -only-testing:AT-IRCUITests
```

Run everything (unit + UI):

```bash
xcodebuild test \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -destination "$DEST" \
  -derivedDataPath .build
```

Run a single Swift Testing test. The parentheses are part of the identifier, so
quote the whole value:

```bash
xcodebuild test \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -destination "$DEST" \
  -derivedDataPath .build \
  -only-testing:'AT-IRCTests/AT_IRCTests/nickHashMatchesWeeChat()'
```

To keep a machine-readable result bundle, add `-resultBundlePath` (the path must
not already exist):

```bash
rm -rf .build/TestResults.xcresult

xcodebuild test \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -destination "$DEST" \
  -derivedDataPath .build \
  -resultBundlePath .build/TestResults.xcresult

xcrun xcresulttool get test-results summary \
  --path .build/TestResults.xcresult
```

The UI suite includes a launch-performance test and takes about a minute. The
unit suite is the fast pre-commit check.

## Run and iterate in the simulator

Boot the simulator (and optionally open the Simulator UI):

```bash
xcrun simctl bootstatus "$DEVICE" -b
open -a Simulator
```

Build, install, and relaunch:

```bash
xcodebuild build \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -configuration Debug \
  -destination "$DEST" \
  -derivedDataPath .build

xcrun simctl install "$DEVICE" "$APP"
xcrun simctl launch --terminate-running-process "$DEVICE" "$BUNDLE_ID"
```

`--terminate-running-process` replaces the already-running copy, so the same
two `simctl` commands work for every iteration.

Useful companion commands:

```bash
# Stream this app's unified log
xcrun simctl spawn "$DEVICE" log stream --style compact --level debug \
  --predicate 'process == "AT-IRC"'

# Show recent log entries once
xcrun simctl spawn "$DEVICE" log show --last 2m --style compact \
  --predicate 'process == "AT-IRC"'

# Blocking stdout/stderr console (Ctrl-C to stop)
xcrun simctl launch --console-pty --terminate-running-process \
  "$DEVICE" "$BUNDLE_ID"

# Screenshot
xcrun simctl io "$DEVICE" screenshot /tmp/atirc.png
```

Reset the app without wiping the simulator:

```bash
xcrun simctl terminate "$DEVICE" "$BUNDLE_ID" 2>/dev/null || true
xcrun simctl uninstall "$DEVICE" "$BUNDLE_ID"
```

To clear Keychain and all other simulator state (this erases the whole
simulator, so shut it down first):

```bash
xcrun simctl shutdown "$DEVICE"
xcrun simctl erase "$DEVICE"
```

## Changing the Go library

Xcode does not track the Go sources and will not regenerate the framework
automatically. After editing anything under `GoLib/`:

```bash
./GoLib/build.sh
```

Then rebuild the app. If you skip this, the app keeps using the previous Go
binary, and new exported symbols will not exist in Swift.

`gomobile bind` generates Objective-C/Swift bindings prefixed with `Golib`.
Interface names that collide with generated class names get a `Protocol` suffix
in Swift (for example `GolibCallbackProtocol`). Bridge details and type limits
are documented in [docs/golang/0001-notes.md](docs/golang/0001-notes.md).

## Before you commit

1. If you changed `GoLib/`, run `./GoLib/build.sh`.
2. Run the unit tests:

   ```bash
   xcodebuild test \
     -project AT-IRC.xcodeproj \
     -scheme AT-IRC \
     -destination "$DEST" \
     -derivedDataPath .build \
     -only-testing:AT-IRCTests
   ```

3. Run the full suite if you changed UI code or UI tests.
4. Do not commit `.build/` or `GoLib/AtircGo.xcframework/`; both are generated.

## Troubleshooting

| Symptom | Fix |
|---|---|
| `No such module 'AtircGo'` or `framework not found AtircGo` | Run `./GoLib/build.sh`. Confirm `GoLib/AtircGo.xcframework` exists. |
| `gobind was not found` | Install `gomobile` and `gobind`; `build.sh` adds `$(go env GOPATH)/bin` to `PATH` for you. |
| `Unable to find a destination matching ...` | Run `xcrun simctl list devices available` and `xcodebuild -showdestinations`; use the exact device name, or install the iOS 27.2 runtime. |
| Tests cannot launch the app | Boot the simulator explicitly with `xcrun simctl bootstatus "$DEVICE" -b` and retry. |
| App still shows old Go behavior | Rerun `./GoLib/build.sh`, then rebuild. Do a clean build (`rm -rf .build`) if the stale binary persists. |
| Installed app does not reflect a new build | Reinstall, then launch with `--terminate-running-process`; uninstall first if needed. |
| Code-signing error | Simulator builds need no team. Device builds need your own signing team in the target settings. |

For device runs, use the device destination and a configured signing team:

```bash
xcodebuild test \
  -project AT-IRC.xcodeproj \
  -scheme AT-IRC \
  -destination 'platform=iOS,id=<device-udid>' \
  -derivedDataPath .build \
  -only-testing:AT-IRCTests
```