Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# AGENTS.md
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Finally we have a shared AGENTS.md


This file provides guidance to AI agents when working with code in this repository.

## Project Overview

Wire iOS is a secure messaging app built as a monorepo (`wire-ios-mono.xcworkspace`). The codebase combines modern Swift Package Manager modules with legacy Carthage/Xcode framework projects. The primary workspace file is `wire-ios-mono.xcworkspace`.

## Prerequisites & Setup

- **Xcode**: version specified in `.xcode-version`
- **Carthage**: 0.39.1+
- **Ruby**: version in `.ruby-version`, managed via `rbenv`
- **Git LFS**: required for binary files

Initial setup:
```sh
./setup.sh
```

To update or rebuild dependencies, just re-run the `setup.sh` script when needed.

## Build

Open `wire-ios-mono.xcworkspace` in Xcode and select the `Wire-iOS` scheme.

## Testing

All the commands for testing are used via fastlane. This is what the CI executes. See `fastlane/README.md` for the common commands.

Note that the simulator model and OS version can be read in `fastlane/.env`

Run security tests from the command line:
```sh
xcodebuild test \
-workspace wire-ios-mono.xcworkspace \
-scheme Wire-iOS \
-testPlan SecurityTests \
-destination 'platform=iOS Simulator,name=iPhone 14,OS=26.0.1'
```

Available test plans in `wire-ios/Tests/TestPlans/`:
- `AllTests.xctestplan` — all tests
- `UnitTests.xctestplan` — unit tests only
- `UITests.xctestplan` — UI tests only
- `SecurityTests.xctestplan` — security-specific tests
- `GermanLocaleTests.xctestplan` — German locale tests

The same `-testPlan` flag applies to the `WireSyncEngine` and `WireDataModel` schemes.

UI test methods **must** include a Testiny TC ID suffix, e.g. `testLogin_TC_1234`.
UITests require a special env, that is filled by 1password command. See `wire-ios/WireUITests/README` for more information
## Linting & Formatting

Both tools are distributed as SPM binary targets in `scripts/Package.swift`.

```sh
./scripts/run-swiftlint.sh # lint
./scripts/run-swiftformat.sh # format
```

Key rules enforced:
- **SwiftLint**: TODOs must reference a JIRA ticket — `// TODO: [WPB-123] description`
- **SwiftFormat**: max line width 120 chars, 4-space indent, imports sorted (`testable-last`)
- Every Swift file must begin with the GPL v3 license header (year 2026)

## Architecture

The app is layered. Data flows upward:

```
UI Layer WireUI (SwiftUI) — WireDesign, WireConversationListUI, WireMainNavigationUI, etc.
Domain Layer WireDomain — business logic, use cases, entity models
Sync Engine wire-ios-sync-engine — network sync, push notifications, client-side logic
Data/Model wire-ios-data-model (Core Data) + WireData (modern SPM layer)
Transport/Network wire-ios-transport + wire-ios-request-strategy + WireNetwork
```

Supporting modules (all SPM packages unless noted):
- **WireAuthentication** — login flows, SSO
- **WireCalling** — VoIP via AVS (open-source audio/video signaling)
- **WireCoreCrypto** — Proteus/MLS end-to-end encryption
- **WireMessaging** — messaging logic
- **WireBackup** — import/export
- **WireAnalytics** — analytics
- **WireLogging** — structured logging
- **WireFoundation** — shared utilities and test helpers
- **WireDebug** — debug-only tooling

Legacy Xcode projects (in their own subdirectories): `wire-ios-sync-engine/`, `wire-ios-data-model/`, `wire-ios-request-strategy/`, `wire-ios-transport/`, `wire-ios-system/`, `wire-ios-utilities/`, `wire-ios-testing/`, `wire-ios-images/`, `wire-ios-share-engine/`, `wire-ios-link-preview/`, `wire-ios-ziphy/`, `wire-ios-canvas/`.

Code generation: Sourcery generates mocks (`AutoMockable`), SwiftGen generates string/asset accessors. Run via `scripts/run-sourcery.sh` and `scripts/run-swiftgen.sh`.

## Conventions

- **Commits and PR titles** must reference a JIRA issue: `fix: description - WPB-XXXXX` - see [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
- Code review uses [conventional comments](https://conventionalcomments.org/)
- Cherry-picked commits are marked with 🍒
- All colors in UI code must come from `WireDesign.ColorTheme` or `WireDesign.BaseColorPalette`
- New UI elements require `accessibilityLabel` / `accessibilityIdentifier` strings for VoiceOver
- UI elements that display text must use the API that supports dynamic type / large fonts
- Push notifications only work with the App Store-signed build (Apple security restriction)
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Please refer to AGENTS.md for full instructions
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ To rebuild a dependency and use it in the umbrella project, run:
carthage bootstrap --platform ios --use-xcframeworks
```

To run tests, make sure to use a simulated iPhone 17 with iOS 26.1, as reference screenshot tests rely on that layout.

### Known Limitations

Notifications sent through the Apple Push Notification service can only be received by the App Store Wire client, which is code-signed with Wire's own certificate. This is a security feature enforced by Apple, as documented in Apple's [Local and Remote Notification Programming Guide](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/). Any client built from source will not be able to receive notifications.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions wire-ios/Wire-iOS/Generated/Strings+Generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5011,13 +5011,15 @@ internal enum L10n {
/// Guest
internal static let guest = L10n.tr("Localizable", "profile.details.guest", fallback: "Guest")
/// Get certainty about %@’s identity before connecting.
/// Wire’s Support team will never reach out to you in the app.
internal static func identityWarning(_ p1: Any) -> String {
return L10n.tr("Localizable", "profile.details.identity_warning", String(describing: p1), fallback: "Get certainty about %@’s identity before connecting.")
return L10n.tr("Localizable", "profile.details.identity_warning", String(describing: p1), fallback: "Get certainty about %@’s identity before connecting.\nWire’s Support team will never reach out to you in the app.")
}
/// external
internal static let partner = L10n.tr("Localizable", "profile.details.partner", fallback: "external")
/// Please verify the person's identity before accepting the connection request.
internal static let requestedIdentityWarning = L10n.tr("Localizable", "profile.details.requested_identity_warning", fallback: "Please verify the person's identity before accepting the connection request.")
/// Wire’s Support team will never reach out to you in the app.
internal static let requestedIdentityWarning = L10n.tr("Localizable", "profile.details.requested_identity_warning", fallback: "Please verify the person's identity before accepting the connection request.\nWire’s Support team will never reach out to you in the app.")
/// Details
internal static let title = L10n.tr("Localizable", "profile.details.title", fallback: "Details")
internal enum Title {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1063,8 +1063,8 @@
"profile.details.partner" = "external";
"profile.details.federated" = "Federated";
"profile.details.blocking_reason" = "This user is blocked due to legal hold. [LEARN MORE](%@)";
"profile.details.identity_warning" = "Get certainty about %@’s identity before connecting.";
"profile.details.requested_identity_warning" = "Please verify the person's identity before accepting the connection request.";
"profile.details.identity_warning" = "Get certainty about %@’s identity before connecting.\nWire’s Support team will never reach out to you in the app.";
"profile.details.requested_identity_warning" = "Please verify the person's identity before accepting the connection request.\nWire’s Support team will never reach out to you in the app.";
"profile.details.title.unavailable" = "Name not available";

// Device list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@
import WireDesign

final class WarningLabelView: UIView {
private let stackView = UIStackView(axis: .horizontal)
private let imageView = UIImageView(image: UIImage(named: "Info"))
private let stackView = UIStackView(axis: .vertical)

private let label = DynamicFontLabel(
style: .h5,
color: SemanticColors.Label.textErrorDefault
)

private static let paragraphStyle: NSParagraphStyle = {
let style = NSMutableParagraphStyle()
style.paragraphSpacing = 8
style.alignment = .center
return style
}()

// MARK: - Setup

init() {
Expand All @@ -45,22 +51,16 @@
private func setupViews() {
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
stackView.alignment = .top
stackView.alignment = .center
stackView.spacing = 10
imageView.tintColor = SemanticColors.Icon.foregroundDefaultRed
stackView.addArrangedSubview(imageView)
label.numberOfLines = 0
stackView.addArrangedSubview(label)
NSLayoutConstraint.activate(
[
imageView.widthAnchor.constraint(equalToConstant: 16.0),
imageView.heightAnchor.constraint(equalToConstant: 16.0)
] +
NSLayoutConstraint.forView(
NSLayoutConstraint.forView(
view: stackView,

Check warning on line 60 in wire-ios/Wire-iOS/Sources/UserInterface/SelfProfile/WarningLabelView.swift

View workflow job for this annotation

GitHub Actions / SwiftFormat

Indent code in accordance with the scope level. (indent)
inContainer: self,

Check warning on line 61 in wire-ios/Wire-iOS/Sources/UserInterface/SelfProfile/WarningLabelView.swift

View workflow job for this annotation

GitHub Actions / SwiftFormat

Indent code in accordance with the scope level. (indent)
withInsets: .zero

Check warning on line 62 in wire-ios/Wire-iOS/Sources/UserInterface/SelfProfile/WarningLabelView.swift

View workflow job for this annotation

GitHub Actions / SwiftFormat

Indent code in accordance with the scope level. (indent)
)

Check warning on line 63 in wire-ios/Wire-iOS/Sources/UserInterface/SelfProfile/WarningLabelView.swift

View workflow job for this annotation

GitHub Actions / SwiftFormat

Indent code in accordance with the scope level. (indent)
)
}

Expand All @@ -68,14 +68,17 @@
typealias profileDetails = L10n.Localizable.Profile.Details
if user.isPendingApprovalBySelfUser {
isHidden = false
label.text = profileDetails.requestedIdentityWarning
label.attributedText = attributedWarning(profileDetails.requestedIdentityWarning)
}
guard let name = user.name else {
isHidden = true
return
}
isHidden = user.isConnected || user.isTeamMember || user.isSelfUser
label.text = profileDetails.identityWarning(name)
label.attributedText = attributedWarning(profileDetails.identityWarning(name))
}

private func attributedWarning(_ text: String) -> NSAttributedString {
NSAttributedString(string: text, attributes: [.paragraphStyle: Self.paragraphStyle])
}
}
Loading