Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7657bcc
add developer flag
netbe May 4, 2026
1c64960
add sharedebugbanner
netbe May 4, 2026
c92064d
wip first draft
netbe May 4, 2026
173e8a5
fix UI
netbe May 5, 2026
270e6e7
wip
netbe May 5, 2026
8e88362
wip
netbe May 5, 2026
5c5dba7
wip
netbe May 5, 2026
26a4756
fix copy and replace sendDebug
netbe May 5, 2026
20c7847
wip uitests
netbe May 5, 2026
420467e
add tests
netbe May 5, 2026
b22319b
format
netbe May 5, 2026
fb1b505
replaced blockViewController send debug action
netbe May 5, 2026
9317a7f
review design
netbe May 6, 2026
407f10f
code review
netbe May 6, 2026
682ca10
refactor
netbe May 6, 2026
d415271
fix ipad and ui
netbe May 6, 2026
db57a35
fix color of banner in darkmode
netbe May 6, 2026
4f0d329
allow override of snapshot mode
netbe May 6, 2026
8dbc068
re-record snaphots
netbe May 6, 2026
5c0f617
fix override of snapshots
netbe May 6, 2026
fe9fcca
fix snapshot
netbe May 6, 2026
124b388
fix failing snaphots camera
netbe May 6, 2026
1a10cab
code review
netbe May 6, 2026
8b4eb20
Revert "fix failing snaphots camera"
netbe May 6, 2026
bdf2163
code review
netbe May 6, 2026
e43b7f6
fix snapshots in wire ui
netbe May 6, 2026
50b4043
fix build issue
netbe May 6, 2026
e982621
Merge branch 'develop' into feat/add-share-debug
netbe May 6, 2026
dda7b4f
add tests cases
netbe May 6, 2026
8128d32
Merge branch 'feat/add-share-debug' of github.com:wireapp/wire-ios in…
netbe May 6, 2026
ce87cc3
swift format
netbe May 6, 2026
86bd0f7
Merge branch 'develop' into feat/add-share-debug
netbe May 6, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ public struct UITestConfig: Codable {

public var isBuildBlacklisted = false

/// When `true`, a triple-tap on the app window triggers the same action as the shake gesture.
/// On XCUITests, shake gesture is not available.
public var useTripleTapForShakeGesture = true
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.

suggestion: Have this disabled by default and only enable it in the setup of the UITests that use it.

issue(maybe): I was looking at UITestConfig yesterday and realized that we should change something... Currently if UITestConfig.environment returns a default if it is not set in the environment. I think it should return nil. I think the in the current implementation any kind of debug build will have useTripleTapForShakeGesture, not just UI tests


/// Developer flags to apply at launch, keyed by `DeveloperFlag.rawValue`.
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.

thought: I wonder if we should move DeveloperFlag to WirePrimitives so that we can pass them directly instead of their string values.

/// Overrides any flags already stored in `UserDefaults`.
public var developerFlags: [String: Bool] = [:]

// MARK: - Init

public init() {}
Expand Down
13 changes: 10 additions & 3 deletions WireFoundation/Sources/WireTesting/Utilities/SnapshotHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ public struct SnapshotHelper {

private var defaultRecordMode: SnapshotTestingConfiguration.Record? {
let ci = ProcessInfo.processInfo.environment["CI"]
return (ci == nil || ci?.isEmpty == true) ? .missing : .never
if let value = ProcessInfo.processInfo.environment["SNAPSHOT_TESTING_RECORD"],
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

now, it reads the env variable from the testplan first so if you need to record snapshots you can just modify the env value to failed and not remove the file manually

let record = SnapshotTestingConfiguration.Record(rawValue: value) {
return record
} else if ci == nil || ci?.isEmpty == true {
return .missing
} else {
return .never
}
Comment on lines +37 to +44
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: This looks super helpful.

}

public init() {}
Expand Down Expand Up @@ -360,7 +367,7 @@ public struct SnapshotHelper {
matching value: UIViewController,
size: CGSize? = nil,
named name: String? = nil,
record recording: Bool = false,
record recording: Bool? = nil,
file: StaticString = #filePath,
testName: String = #function,
safeArea: UIEdgeInsets = .zero,
Expand Down Expand Up @@ -522,7 +529,7 @@ public struct SnapshotHelper {
public func verify(
matching value: UIImage,
named name: String? = nil,
record recording: Bool = false,
record recording: Bool? = nil,
file: StaticString = #filePath,
testName: String = #function,
line: UInt = #line
Expand Down
21 changes: 6 additions & 15 deletions WireLogging/Sources/WireLogging/LogFilesProviding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,19 @@ public import Foundation
// sourcery: AutoMockable
public protocol LogFilesProviding {

/// Generates a zip file containing all log files and returns its data before removing the files
///
/// - Returns: the log files archive data
/// All log file URLs to include in the archive.
var logFileURLs: [URL] { get }

func generateLogFilesData() throws -> Data

/// Generates a zip file containing all log files
///
/// - Returns: the log files archive URL

func generateLogFilesZip() throws -> URL
/// Returns system and app info, optionally including journal entries for the given user.
func info(selfUserID: UUID?) -> String

/// Clears the logs directory.
/// Call once you are done using the URL returned by `generateLogFilesZip` to clean up.

/// Call once you are done using the URL returned by `CreateDebugReportUseCase` to clean up.
func clearLogsDirectory(fileManager: FileManager) throws

/// Clears individual log files from their source locations.

func removeLogFiles(fileManager: FileManager) throws

/// Deletes all log-related legacy archives

/// Deletes all log-related legacy archives.
func removeLegacyLogArchives() throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ public extension UITabBarController {

let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithDefaultBackground()
tabBarAppearance.backgroundColor = ColorTheme.Backgrounds.background
tabBarAppearance.backgroundColor = ColorTheme.Backgrounds.backgroundVariant
tabBarAppearance.stackedLayoutAppearance = tabBarItemAppearance

tabBar.backgroundColor = ColorTheme.Backgrounds.background
tabBar.backgroundColor = ColorTheme.Backgrounds.backgroundVariant
tabBar.unselectedItemTintColor = ColorTheme.Base.secondaryText
tabBar.standardAppearance = tabBarAppearance
}
Expand Down
10 changes: 10 additions & 0 deletions WireUI/Sources/WireDesign/Colors/ColorTheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,16 @@
public enum NotificationBadge {
public static let fill = ColorTheme.Base.error
}

// MARK: - Wire Design System

// TODO: [WPB-25347] implement the new color system

Check warning on line 207 in WireUI/Sources/WireDesign/Colors/ColorTheme.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-ios&issues=AZ39YdnJpSBVOqQ2Ewpw&open=AZ39YdnJpSBVOqQ2Ewpw&pullRequest=4677
public enum Content {
public enum Base {
public static let secondary = UIColor(light: .gray70, dark: .gray60)
}
}

}

private extension UIColor {
Expand Down
25 changes: 25 additions & 0 deletions WireUI/Sources/WireDesign/Typography/WireTextStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,43 @@

public enum WireTextStyle: String, CaseIterable, Sendable {

/// Style iOS & Figma: ?
case largeTitle

/// Style iOS & Figma: Title 3
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: Adding comments here. I guess at some point we should rename things to match Figma design system if it is up-to-date

case h1

/// Style iOS & Figma: Title 3 (bold) - Emphasized
case h2

/// Style iOS & Figma: Headline
case h3

/// Style iOS & Figma: Subheadline
case h4

/// Style iOS & Figma: Footnote
case h5

/// Style iOS & Figma: Body
case body1

/// Style iOS & Figma: Body 2 (custom)
case body2

/// Figma: Callout (bold) - Emphasized
case body3

/// Style iOS & Figma: Caption 1
case subline1

/// Style iOS & Figma: Caption 1 (bold) - Emphasized
case subline2

/// Style iOS & Figma: Button Small (custom)
case buttonSmall

/// Style iOS & Figma: Button Big (custom)
case buttonBig

}
22 changes: 22 additions & 0 deletions WireUI/Sources/WireLocators/Locators.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,28 @@ public enum Locators {

case accountCell
case optionsCell
case shareDebugBanner
}

public enum ShareDebugReportPage: String {

case actionSheet = "Having trouble?"
case shareViaWireButton = "ShareDebugReportPage.shareViaWireButton"
case sendEmailButton = "ShareDebugReportPage.sendEmailButton"
case shareButton = "ShareDebugReportPage.shareButton"
case cancelButton = "ShareDebugReportPage.cancelButton"
}

public enum ShareViaWirePage: AutoPrefixedEnum {

case sendButton
case closeButton
}

public enum ActivitySheetPage: String {

case sheet = "ActivityListView"
case saveToFiles = "Save to Files"
}

public enum AccountSettingsPage: String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,35 @@
//

import SwiftUI
import WireDesign
import WireFoundation

/// Adds an activity indicator subview to the provided `UIView` instance and disables user interaction.
public final class BlockingActivityIndicator {

public enum Style {
/// Full-screen dimmed overlay with a white spinner centered on it.
case fullScreen
/// Full-screen dimmed overlay with a centered white card containing a dark spinner and text.
case card
}

// MARK: - Private Properties

private weak var view: UIView?
private let accessibilityAnnouncement: String?
private let style: Style

// MARK: - Life Cycle

public init(
view: UIView,
accessibilityAnnouncement: String?
accessibilityAnnouncement: String?,
style: Style = .fullScreen
) {
self.view = view
self.accessibilityAnnouncement = accessibilityAnnouncement
self.style = style
}

deinit {
Expand Down Expand Up @@ -62,7 +73,7 @@ public final class BlockingActivityIndicator {
if let accessibilityAnnouncement {
UIAccessibility.post(notification: .announcement, argument: accessibilityAnnouncement)
}
view?.blockAndStartAnimating(blockingActivityIndicator: self, text: text)
view?.blockAndStartAnimating(blockingActivityIndicator: self, text: text, style: style)
}

@MainActor
Expand All @@ -76,6 +87,7 @@ public final class BlockingActivityIndicator {
private struct BlockingActivityIndicatorState {
var weakReferences = [WeakReference<BlockingActivityIndicator>]()
private(set) var activityIndicatorView = ProgressSpinner()
var blockingView: UIView?
}

// MARK: - UIView + BlockingActivityIndicators
Expand All @@ -84,37 +96,73 @@ private extension UIView {

func blockAndStartAnimating(
blockingActivityIndicator reference: BlockingActivityIndicator,
text: String
text: String,
style: BlockingActivityIndicator.Style
) {
var state: BlockingActivityIndicatorState! = blockingActivityIndicatorState

// set up subviews
if state == nil {
state = .init()

// view with dimmed background which swallows touch events
// dim overlay which swallows touch events
let blockingView = UIView()
state.blockingView = blockingView
blockingView.backgroundColor = .black.withAlphaComponent(0.5)
blockingView.isUserInteractionEnabled = true
blockingView.translatesAutoresizingMaskIntoConstraints = false
addSubview(blockingView)

// activity indicator view
state.activityIndicatorView.color = .white
state.activityIndicatorView.text = text
state.activityIndicatorView.isAnimating = true
state.activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
blockingView.addSubview(state.activityIndicatorView)

NSLayoutConstraint.activate([
blockingView.leadingAnchor.constraint(equalTo: leadingAnchor),
blockingView.topAnchor.constraint(equalTo: topAnchor),
trailingAnchor.constraint(equalTo: blockingView.trailingAnchor),
bottomAnchor.constraint(equalTo: blockingView.bottomAnchor),

state.activityIndicatorView.centerXAnchor.constraint(equalTo: blockingView.centerXAnchor),
state.activityIndicatorView.centerYAnchor.constraint(equalTo: blockingView.centerYAnchor)
bottomAnchor.constraint(equalTo: blockingView.bottomAnchor)
])

state.activityIndicatorView.text = text
state.activityIndicatorView.isAnimating = true
state.activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false

switch style {
case .fullScreen:
state.activityIndicatorView.color = .white
blockingView.addSubview(state.activityIndicatorView)
NSLayoutConstraint.activate([
state.activityIndicatorView.centerXAnchor.constraint(equalTo: blockingView.centerXAnchor),
state.activityIndicatorView.centerYAnchor.constraint(equalTo: blockingView.centerYAnchor)
])

case .card:
state.activityIndicatorView.color = .label
state.activityIndicatorView.textColor = .label

let card = UIView()
card.backgroundColor = ColorTheme.Backgrounds.surface
card.layer.cornerRadius = 16
card.layer.masksToBounds = true
card.translatesAutoresizingMaskIntoConstraints = false
blockingView.addSubview(card)
card.addSubview(state.activityIndicatorView)

// Card prefers 65% of the overlay width (wide enough for label text on iPhone)
// but is capped at 300pt so it stays compact on iPad.
// Spinner uses equalTo horizontal margins so it fills the card — this overrides
// ProgressSpinner.intrinsicContentSize (32pt) which ignores the label width.
let preferredWidth = card.widthAnchor.constraint(equalTo: blockingView.widthAnchor, multiplier: 0.65)
preferredWidth.priority = .defaultHigh
NSLayoutConstraint.activate([
card.centerXAnchor.constraint(equalTo: blockingView.centerXAnchor),
card.centerYAnchor.constraint(equalTo: blockingView.centerYAnchor),
preferredWidth,
card.widthAnchor.constraint(lessThanOrEqualToConstant: 300),

state.activityIndicatorView.topAnchor.constraint(equalTo: card.topAnchor, constant: 24),
state.activityIndicatorView.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 24),
card.trailingAnchor.constraint(equalTo: state.activityIndicatorView.trailingAnchor, constant: 24),
card.bottomAnchor.constraint(equalTo: state.activityIndicatorView.bottomAnchor, constant: 24)
])
}
}

// add the reference into the `weakReferences` array
Expand All @@ -127,7 +175,7 @@ private extension UIView {

state.weakReferences = state.weakReferences.filter { $0.reference != nil && $0.reference !== reference }
if state.weakReferences.isEmpty {
state.activityIndicatorView.superview!.removeFromSuperview()
state.blockingView?.removeFromSuperview()
blockingActivityIndicatorState = nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public final class ProgressSpinner: UIView {
didSet { updateSpinnerIcon() }
}

public var textColor: UIColor = .white {
didSet { label.textColor = textColor }
}

public var iconSize: CGFloat = 32 {
didSet { updateSpinnerIcon() }
}
Expand All @@ -37,6 +41,7 @@ public final class ProgressSpinner: UIView {
set {
label.text = newValue
label.isHidden = newValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
invalidateIntrinsicContentSize()
}
}

Expand Down Expand Up @@ -82,6 +87,7 @@ public final class ProgressSpinner: UIView {
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)

spinner.accessibilityElementsHidden = true
spinner.contentMode = .center
updateSpinnerIcon()
stackView.addArrangedSubview(spinner)
Expand Down Expand Up @@ -133,7 +139,7 @@ public final class ProgressSpinner: UIView {
}

public override var intrinsicContentSize: CGSize {
spinner.image?.size ?? super.intrinsicContentSize
stackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
}

private func startAnimationInternal() {
Expand Down
5 changes: 5 additions & 0 deletions WireUI/Tests/TestPlans/AllTests.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
{
"key" : "CI",
"value" : "${CI}"
},
{
"enabled" : false,
"key" : "SNAPSHOT_TESTING_RECORD",
"value" : "failed"
}
],
"language" : "en",
Expand Down
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.

thought (out of scope): This test looks weird. I hope our tab bar never looks like that in reality

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
Loading