Skip to content

Commit

Permalink
Add macOS support (#30)
Browse files Browse the repository at this point in the history
# Add macOS support

## ♻️ Current situation & Problem

This PR updates the UITests and TestApp to run in macOS. Sadly we
currently can't really run UI tests on macOS without the requirement to
sign the application.
Additionally, this PR upgrade the XCTestExtensions version with updated
support for visionOS and macOS.


## ⚙️ Release Notes


* Updated support for macOS and visionOS

## 📚 Documentation
--


## ✅ Testing
We restructured to CI configuration to align better with the current
styling guides.

## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
Supereg authored Feb 19, 2024
1 parent 7210f72 commit d49f716
Show file tree
Hide file tree
Showing 21 changed files with 282 additions and 130 deletions.
61 changes: 36 additions & 25 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#
# This source file is part of the Stanford Spezi open-source project
#
# SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
# SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
#
# SPDX-License-Identifier: MIT
#
Expand All @@ -16,71 +16,82 @@ on:
workflow_dispatch:

jobs:
buildandtest:
buildandtest_ios:
name: Build and Test Swift Package iOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: SpeziViews-Package.xcresult
runsonlabels: '["macOS", "self-hosted"]'
scheme: SpeziViews-Package
buildandtestwatchos:
resultBundle: SpeziViews-iOS.xcresult
artifactname: SpeziViews-iOS.xcresult
buildandtest_watchos:
name: Build and Test Swift Package watchOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: SpeziViews-Package-watchOS.xcresult
resultBundle: SpeziViews-Package-watchOS.xcresult
runsonlabels: '["macOS", "self-hosted"]'
scheme: SpeziViews-Package
destination: 'platform=watchOS Simulator,name=Apple Watch Series 9 (45mm)'
buildandtestvisionos:
resultBundle: SpeziViews-watchOS.xcresult
artifactname: SpeziViews-watchOS.xcresult
buildandtest_visionos:
name: Build and Test Swift Package visionOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: SpeziViews-Package-visionOS.xcresult
resultBundle: SpeziViews-Package-visionOS.xcresult
runsonlabels: '["macOS", "self-hosted"]'
scheme: SpeziViews-Package
destination: 'platform=visionOS Simulator,name=Apple Vision Pro'
buildandtesttvos:
resultBundle: SpeziViews-visionOS.xcresult
artifactname: SpeziViews-visionOS.xcresult
buildandtest_tvos:
name: Build and Test Swift Package tvOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: SpeziViews-Package-tvOS.xcresult
resultBundle: SpeziViews-Package-tvOS.xcresult
runsonlabels: '["macOS", "self-hosted"]'
scheme: SpeziViews-Package
destination: 'platform=tvOS Simulator,name=Apple TV 4K (3rd generation)'
buildandtestuitests:
resultBundle: SpeziViews-tvOS.xcresult
artifactname: SpeziViews-tvOS.xcresult
buildandtest_macos:
name: Build and Test Swift Package macOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
runsonlabels: '["macOS", "self-hosted"]'
scheme: SpeziViews-Package
destination: 'platform=macOS,arch=arm64'
resultBundle: SpeziViews-macOS.xcresult
artifactname: SpeziViews-macOS.xcresult
buildandtestuitests_ios:
name: Build and Test UI Tests iOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: TestApp.xcresult
runsonlabels: '["macOS", "self-hosted"]'
path: 'Tests/UITests'
path: Tests/UITests
scheme: TestApp
buildandtestuitestsipad:
resultBundle: TestApp-iOS.xcresult
artifactname: TestApp-iOS.xcresult
buildandtestuitests_ipad:
name: Build and Test UI Tests iPadOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: TestApp-iPad.xcresult
resultBundle: TestApp-iPad.xcresult
runsonlabels: '["macOS", "self-hosted"]'
path: 'Tests/UITests'
path: Tests/UITests
scheme: TestApp
destination: 'platform=iOS Simulator,name=iPad Air (5th generation)'
buildandtestuitestsvisionos:
resultBundle: TestApp-iPad.xcresult
artifactname: TestApp-iPad.xcresult
buildandtestuitests_visionos:
name: Build and Test UI Tests visionOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
artifactname: TestApp-visionOS.xcresult
resultBundle: TestApp-visionOS.xcresult
runsonlabels: '["macOS", "self-hosted"]'
path: 'Tests/UITests'
path: Tests/UITests
scheme: TestApp
destination: 'platform=visionOS Simulator,name=Apple Vision Pro'
resultBundle: TestApp-visionOS.xcresult
artifactname: TestApp-visionOS.xcresult
uploadcoveragereport:
name: Upload Coverage Report
needs: [buildandtest, buildandtestwatchos, buildandtestvisionos, buildandtesttvos, buildandtestuitests, buildandtestuitestsipad, buildandtestuitestsvisionos]
needs: [buildandtest_ios, buildandtest_visionos, buildandtest_watchos, buildandtest_tvos, buildandtest_macos, buildandtestuitests_ios, buildandtestuitests_ipad, buildandtestuitests_visionos]
uses: StanfordSpezi/.github/.github/workflows/create-and-upload-coverage-report.yml@v2
with:
coveragereports: SpeziViews-Package.xcresult SpeziViews-Package-watchOS.xcresult SpeziViews-Package-visionOS.xcresult SpeziViews-Package-tvOS.xcresult TestApp.xcresult TestApp-iPad.xcresult TestApp-visionOS.xcresult
coveragereports: SpeziViews-iOS.xcresult SpeziViews-watchOS.xcresult SpeziViews-visionOS.xcresult SpeziViews-tvOS.xcresult TestApp-iOS.xcresult TestApp-iPad.xcresult TestApp-visionOS.xcresult
21 changes: 16 additions & 5 deletions Sources/SpeziPersonalInfo/Fields/NameFieldRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,22 @@ public struct NameFieldRow<Description: View, Label: View>: View {


public var body: some View {
DescriptionGridRow {
description
} content: {
NameTextField(name: $name, for: component) {
label
#if os(macOS)
let isMacOS = true
#else
let isMacOS = false
#endif
if isMacOS, let label = label as? Text {
NameTextField(name: $name, for: component, prompt: label) {
description
}
} else {
DescriptionGridRow {
description
} content: {
NameTextField(name: $name, for: component) {
label
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/SpeziViews/Views/Layout/DescriptionGridRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ struct DescriptionGridRow_Previews: PreviewProvider {
}
}
.padding(32)
#if os(iOS)
.background(Color(.systemGroupedBackground))
#endif
}
}
#endif
1 change: 1 addition & 0 deletions Tests/SpeziViewsTests/SnapshotTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ final class SnapshotTests: XCTestCase {
}
}


let largeRow = row
.dynamicTypeSize(.accessibility3)

Expand Down
2 changes: 2 additions & 0 deletions Tests/UITests/TestApp/Examples/NameFieldsExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ struct NameFieldsExample: View {
}
}
.navigationTitle("Enter your details")
#if !os(macOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.navigationBarBackButtonHidden(hideBackButton)
.toolbar {
ToolbarItem(placement: .primaryAction) {
Expand Down
4 changes: 4 additions & 0 deletions Tests/UITests/TestApp/Examples/ValidationExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ struct ValidationExample: View {
VerifiableTextField("Password", text: $password, type: .secure)
.validate(input: password, rules: .minimalPassword)
}
#if !os(macOS)
.textInputAutocapitalization(.never)
#endif
.autocorrectionDisabled(true)

Section {
Expand All @@ -40,7 +42,9 @@ struct ValidationExample: View {
}
}
.navigationTitle("Signup")
#if !os(macOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.navigationBarBackButtonHidden(backButtonHidden)
.receiveValidation(in: $validation)
.toolbar {
Expand Down
6 changes: 5 additions & 1 deletion Tests/UITests/TestApp/Examples/ViewStateExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ struct ViewStateExample: View {
.padding()
}
.navigationTitle("Reset Password")
#if !os(macOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.viewStateAlert(state: $viewState)
.frame(maxWidth: .infinity, minHeight: proxy.size.height)
}
Expand All @@ -54,9 +56,11 @@ struct ViewStateExample: View {
.validate(input: emailAddress, rules: .minimalEmail)
.textFieldStyle(.roundedBorder)
.autocorrectionDisabled(true)
#if !os(macOS)
.textInputAutocapitalization(.never)
.textContentType(.username)
.keyboardType(.emailAddress)
#endif
.textContentType(.username)
.font(.title3)

Spacer()
Expand Down
16 changes: 16 additions & 0 deletions Tests/UITests/TestApp/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@
},
"Action executed" : {

},
"Canvas Size: none" : {

},
"Canvas Size: width %lf, height %lf" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Canvas Size: width %1$lf, height %2$lf"
}
}
}
},
"CanvasTest" : {

},
"Credentials" : {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ struct NameFieldsTestView: View {
nameFields
}
}
#if !os(macOS)
.navigationBarTitleDisplayMode(.inline)
#endif
}


Expand Down
38 changes: 35 additions & 3 deletions Tests/UITests/TestApp/SpeziViewsTargetsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ struct SpeziViewsTargetsTests: View {
@State var presentingSpeziPersonalInfo = false
@State var presentingSpeziValidation = false

#if os(macOS)
@MainActor
private var idealWidth: CGFloat {
guard let width = NSApp.keyWindow?.contentView?.bounds.width else {
return 500
}
return max(width - 100, 300)
}

@MainActor
private var idealHeight: CGFloat {
guard let height = NSApp.keyWindow?.contentView?.bounds.height else {
return 400
}
return max(height - 50, 250)
}
#endif


var body: some View {
NavigationStack {
Expand All @@ -28,6 +46,11 @@ struct SpeziViewsTargetsTests: View {
Button("SpeziValidation") {
presentingSpeziValidation = true
}
#if canImport(PencilKit) && !os(macOS)
NavigationLink("CanvasTest") {
CanvasTestView()
}
#endif

Section {
NavigationLink("ViewState") {
Expand All @@ -48,13 +71,22 @@ struct SpeziViewsTargetsTests: View {
.navigationTitle("Targets")
}
.sheet(isPresented: $presentingSpeziViews) {
TestAppTestsView<SpeziViewsTests>()
TestAppTestsView<SpeziViewsTests>(showCloseButton: true)
#if os(macOS)
.frame(minWidth: idealWidth, minHeight: idealHeight)
#endif
}
.sheet(isPresented: $presentingSpeziPersonalInfo) {
TestAppTestsView<SpeziPersonalInfoTests>()
TestAppTestsView<SpeziPersonalInfoTests>(showCloseButton: true)
#if os(macOS)
.frame(minWidth: idealWidth, minHeight: idealHeight)
#endif
}
.sheet(isPresented: $presentingSpeziValidation) {
TestAppTestsView<SpeziValidationTests>()
TestAppTestsView<SpeziValidationTests>(showCloseButton: true)
#if os(macOS)
.frame(minWidth: idealWidth, minHeight: idealHeight)
#endif
}
}
}
Expand Down
10 changes: 0 additions & 10 deletions Tests/UITests/TestApp/TestApp.entitlements

This file was deleted.

38 changes: 25 additions & 13 deletions Tests/UITests/TestApp/ViewsTests/CanvasTestView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

#if canImport(PencilKit) && !os(macOS)
import PencilKit
import SpeziViews
import SwiftUI
Expand All @@ -16,28 +17,38 @@ struct CanvasTestView: View {
@State var didDrawAnything = false
@State var showToolPicker = false
@State var drawing = PKDrawing()

@State var receivedSize: CGSize?

var body: some View {
VStack {
Text("Did Draw Anything: \(didDrawAnything.description)")
Button("Show Tool Picker") {
showToolPicker.toggle()
ZStack {
VStack {
Text("Did Draw Anything: \(didDrawAnything.description)")
if let receivedSize {
Text("Canvas Size: width \(receivedSize.width), height \(receivedSize.height)")
} else {
Text("Canvas Size: none")
}
Button("Show Tool Picker") {
showToolPicker.toggle()
}
CanvasView(
drawing: $drawing,
isDrawing: $isDrawing,
tool: .init(.pencil, color: .red, width: 10),
drawingPolicy: .anyInput,
showToolPicker: $showToolPicker
)
}
CanvasView(
drawing: $drawing,
isDrawing: $isDrawing,
tool: .init(.pencil, color: .red, width: 10),
drawingPolicy: .anyInput,
showToolPicker: $showToolPicker
)
}
.onChange(of: isDrawing) {
if isDrawing {
didDrawAnything = true
}
}
.navigationBarTitleDisplayMode(.inline)
.onPreferenceChange(CanvasView.CanvasSizePreferenceKey.self) { size in
self.receivedSize = size
}
}
}

Expand All @@ -49,3 +60,4 @@ struct CanvasTestView_Previews: PreviewProvider {
}
}
#endif
#endif
2 changes: 2 additions & 0 deletions Tests/UITests/TestApp/ViewsTests/GeometryReaderTestView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ struct GeometryReaderTestView: View {
.frame(width: 300)
.border(.blue)
}
#if !os(macOS)
.navigationBarTitleDisplayMode(.inline)
#endif
}
}

Expand Down
Loading

0 comments on commit d49f716

Please sign in to comment.