# 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=' \ -derivedDataPath .build \ -only-testing:AT-IRCTests ```