Skip to content

Commit e8fd3c5

Browse files
committed
add linux supoprt
1 parent 0c30209 commit e8fd3c5

File tree

15 files changed

+166
-73
lines changed

15 files changed

+166
-73
lines changed

.github/workflows/build-and-test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,21 @@ jobs:
6262
destination: ${{ matrix.platform.destination }}
6363
resultBundle: ${{ format('TestApp-{0}-{1}.xcresult', matrix.platform.name, matrix.config) }}
6464
artifactname: ${{ format('TestApp-{0}-{1}.xcresult', matrix.platform.name, matrix.config) }}
65+
package_tests_linux:
66+
name: Build and Test Swift Package Linux (${{ matrix.config }})
67+
uses: StanfordBDHG/.github/.github/workflows/swift-test.yml@v2
68+
strategy:
69+
matrix:
70+
config: [Debug, Release]
71+
fail-fast: false
72+
with:
73+
artifact_name: ${{ format('SpeziStudy-Package-Linux-{0}.lcov', matrix.config) }}
6574
uploadcoveragereport:
6675
name: Upload Coverage Report
6776
needs: [package_tests, ui_tests]
6877
uses: StanfordBDHG/.github/.github/workflows/create-and-upload-coverage-report.yml@v2
6978
with:
7079
coveragereports: SpeziStudy-*.xcresult TestApp-*.xcresult
80+
coveragereports_lcov: SpeziStudy-Package-Linux-*.lcov
7181
secrets:
7282
token: ${{ secrets.CODECOV_TOKEN }}

Package.swift

Lines changed: 87 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -21,65 +21,100 @@ let package = Package(
2121
.watchOS(.v11),
2222
.visionOS(.v2)
2323
],
24-
products: [
25-
.library(name: "SpeziStudy", targets: ["SpeziStudy"]),
24+
products: products(),
25+
dependencies: dependencies() + swiftLintPackage(),
26+
targets: targets()
27+
)
28+
29+
30+
func products() -> [Product] {
31+
var products: [Product] = [
2632
.library(name: "SpeziStudyDefinition", targets: ["SpeziStudyDefinition"])
27-
],
28-
dependencies: [
33+
]
34+
#if canImport(Darwin)
35+
products.append(.library(name: "SpeziStudy", targets: ["SpeziStudy"]))
36+
#endif
37+
return products
38+
}
39+
40+
func dependencies() -> [PackageDescription.Package.Dependency] {
41+
var dependencies: [PackageDescription.Package.Dependency] = [
2942
.package(url: "https://github.com/apple/FHIRModels.git", from: "0.8.0"),
30-
.package(url: "https://github.com/StanfordSpezi/Spezi.git", from: "1.10.1"),
3143
.package(url: "https://github.com/StanfordSpezi/SpeziFoundation.git", branch: "localizations-dictionary"), // TODO
32-
.package(url: "https://github.com/StanfordSpezi/SpeziHealthKit.git", branch: "lukas/linux-support"), // TODO
44+
.package(url: "https://github.com/StanfordSpezi/SpeziHealthKit.git", revision: "22da093bad44ec9c13ad1953f1506bd677f65b96"), // TODO
3345
.package(url: "https://github.com/StanfordSpezi/SpeziScheduler.git", from: "1.2.18"),
34-
.package(url: "https://github.com/StanfordSpezi/SpeziStorage.git", from: "2.1.0"),
3546
.package(url: "https://github.com/apple/swift-collections.git", from: "1.1.4"),
47+
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.2")
48+
]
49+
#if canImport(Darwin)
50+
dependencies.append(contentsOf: [
51+
.package(url: "https://github.com/StanfordSpezi/Spezi.git", from: "1.10.1"),
52+
.package(url: "https://github.com/StanfordSpezi/SpeziStorage.git", from: "2.1.0"),
3653
.package(url: "https://github.com/apple/swift-algorithms.git", from: "1.2.1")
37-
] + swiftLintPackage(),
38-
targets: [
39-
.target(
40-
name: "SpeziStudyDefinition",
41-
dependencies: [
42-
.product(name: "ModelsR4", package: "FHIRModels"),
43-
.product(name: "SpeziHealthKit", package: "SpeziHealthKit"),
44-
.product(name: "SpeziHealthKitBulkExport", package: "SpeziHealthKit"),
45-
.product(name: "SpeziFoundation", package: "SpeziFoundation"),
46-
.product(name: "SpeziLocalization", package: "SpeziFoundation"),
47-
.product(name: "SpeziScheduler", package: "SpeziScheduler"),
48-
.product(name: "DequeModule", package: "swift-collections")
49-
],
50-
resources: [.process("Resources")],
51-
swiftSettings: [.enableUpcomingFeature("ExistentialAny")],
52-
plugins: [] + swiftLintPlugin()
53-
),
54-
.target(
55-
name: "SpeziStudy",
56-
dependencies: [
57-
.target(name: "SpeziStudyDefinition"),
58-
.product(name: "Spezi", package: "Spezi"),
59-
.product(name: "ModelsR4", package: "FHIRModels"),
60-
.product(name: "SpeziHealthKit", package: "SpeziHealthKit"),
61-
.product(name: "SpeziLocalStorage", package: "SpeziStorage"),
62-
.product(name: "SpeziScheduler", package: "SpeziScheduler"),
63-
.product(name: "SpeziSchedulerUI", package: "SpeziScheduler"),
64-
.product(name: "Algorithms", package: "swift-algorithms")
65-
],
66-
swiftSettings: [.enableUpcomingFeature("ExistentialAny")],
67-
plugins: [] + swiftLintPlugin()
68-
),
69-
.testTarget(
70-
name: "SpeziStudyTests",
71-
dependencies: [
72-
.target(name: "SpeziStudy"),
73-
.target(name: "SpeziStudyDefinition"),
74-
.product(name: "SpeziTesting", package: "Spezi"),
75-
.product(name: "ModelsR4", package: "FHIRModels")
76-
],
77-
resources: [.process("Resources/questionnaires"), .copy("Resources/assets")],
78-
swiftSettings: [.enableUpcomingFeature("ExistentialAny")],
79-
plugins: [] + swiftLintPlugin()
80-
)
54+
])
55+
#endif
56+
return dependencies
57+
}
58+
59+
func targets() -> [Target] {
60+
var targets: [Target] = []
61+
62+
var speziStudyDefinitionDependencies: [Target.Dependency] = [
63+
.product(name: "ModelsR4", package: "FHIRModels"),
64+
.product(name: "SpeziHealthKit", package: "SpeziHealthKit"),
65+
.product(name: "SpeziHealthKitBulkExport", package: "SpeziHealthKit"),
66+
.product(name: "SpeziFoundation", package: "SpeziFoundation"),
67+
.product(name: "SpeziLocalization", package: "SpeziFoundation"),
68+
.product(name: "SpeziScheduler", package: "SpeziScheduler"),
69+
.product(name: "DequeModule", package: "swift-collections"),
70+
.product(name: "Logging", package: "swift-log")
8171
]
82-
)
72+
targets.append(.target(
73+
name: "SpeziStudyDefinition",
74+
dependencies: speziStudyDefinitionDependencies,
75+
resources: [.process("Resources")],
76+
swiftSettings: [.enableUpcomingFeature("ExistentialAny")],
77+
plugins: [] + swiftLintPlugin()
78+
))
79+
80+
#if canImport(Darwin)
81+
targets.append(.target(
82+
name: "SpeziStudy",
83+
dependencies: [
84+
.target(name: "SpeziStudyDefinition"),
85+
.product(name: "Spezi", package: "Spezi"),
86+
.product(name: "ModelsR4", package: "FHIRModels"),
87+
.product(name: "SpeziHealthKit", package: "SpeziHealthKit"),
88+
.product(name: "SpeziLocalStorage", package: "SpeziStorage"),
89+
.product(name: "SpeziScheduler", package: "SpeziScheduler"),
90+
.product(name: "SpeziSchedulerUI", package: "SpeziScheduler"),
91+
.product(name: "Algorithms", package: "swift-algorithms")
92+
],
93+
swiftSettings: [.enableUpcomingFeature("ExistentialAny")],
94+
plugins: [] + swiftLintPlugin()
95+
))
96+
#endif
97+
98+
var speziStudyTestsDependencies: [Target.Dependency] = [
99+
.target(name: "SpeziStudyDefinition"),
100+
.product(name: "ModelsR4", package: "FHIRModels")
101+
]
102+
#if canImport(Darwin)
103+
speziStudyTestsDependencies.append(contentsOf: [
104+
.target(name: "SpeziStudy"),
105+
.product(name: "SpeziTesting", package: "Spezi")
106+
])
107+
#endif
108+
targets.append(.testTarget(
109+
name: "SpeziStudyTests",
110+
dependencies: speziStudyTestsDependencies,
111+
resources: [.process("Resources/questionnaires"), .copy("Resources/assets")],
112+
swiftSettings: [.enableUpcomingFeature("ExistentialAny")],
113+
plugins: [] + swiftLintPlugin()
114+
))
115+
116+
return targets
117+
}
83118

84119

85120
func swiftLintPlugin() -> [Target.PluginUsage] {

Sources/SpeziStudyDefinition/Study Definition Components/CustomActiveTaskComponent.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// SPDX-License-Identifier: MIT
77
//
88

9+
#if canImport(Darwin)
910
import Foundation
1011

1112

@@ -42,3 +43,4 @@ extension StudyDefinition.CustomActiveTaskComponent {
4243
}
4344
}
4445
}
46+
#endif

Sources/SpeziStudyDefinition/Study Definition Components/TimedWalkingTestComponent.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ extension StudyDefinition {
5555
}
5656

5757

58+
#if canImport(Darwin)
5859
extension TimedWalkingTestConfiguration {
5960
private static let spellOutNumberFormatter: NumberFormatter = {
6061
let fmt = NumberFormatter()
@@ -86,3 +87,4 @@ extension TimedWalkingTestConfiguration {
8687
}
8788
}
8889
}
90+
#endif

Sources/SpeziStudyDefinition/StudyBundle/StudyBundle+Write.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,12 @@ extension StudyBundle {
8282
try fileManager.createDirectory(at: bundleUrl, withIntermediateDirectories: true)
8383
do {
8484
let data = try JSONEncoder().encode(definition)
85-
try data.write(to: bundleUrl.appendingPathComponent("definition", conformingTo: .json))
85+
#if canImport(UniformTypeIdentifiers)
86+
let definitionUrl = bundleUrl.appendingPathComponent("definition", conformingTo: .json)
87+
#else
88+
let definitionUrl = bundleUrl.appendingPathComponent("definition.json")
89+
#endif
90+
try data.write(to: definitionUrl)
8691
}
8792
for input in files {
8893
let url: URL

Sources/SpeziStudyDefinition/StudyBundle/StudyBundle.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@
1010

1111
import Foundation
1212
import class ModelsR4.Questionnaire
13+
#if canImport(OSLog)
1314
import OSLog
15+
#else
16+
import Logging
17+
#endif
1418
import SpeziFoundation
1519
import SpeziLocalization
20+
#if canImport(UniformTypeIdentifiers)
1621
import UniformTypeIdentifiers
1722

1823

@@ -21,6 +26,7 @@ extension UTType {
2126
public static let speziStudyBundle = UTType(filenameExtension: "spezistudybundle", conformingTo: .package)!
2227
// swiftlint:disable:previous force_unwrapping
2328
}
29+
#endif
2430

2531

2632
/// A handle for working with a Study Definition bundle.
@@ -77,7 +83,12 @@ public struct StudyBundle: Identifiable, Sendable {
7783
try Self.assertIsStudyBundleUrl(bundleUrl)
7884
self.bundleUrl = bundleUrl
7985
do {
80-
let data = try Data(contentsOf: bundleUrl.appendingPathComponent("definition", conformingTo: .json))
86+
#if canImport(UniformTypeIdentifiers)
87+
let definitionUrl = bundleUrl.appendingPathComponent("definition", conformingTo: .json)
88+
#else
89+
let definitionUrl = bundleUrl.appendingPathComponent("definition.json")
90+
#endif
91+
let data = try Data(contentsOf: definitionUrl)
8192
self.studyDefinition = try JSONDecoder().decode(
8293
StudyDefinition.self,
8394
from: data,

Sources/SpeziStudyDefinition/StudyBundle/Validation/StudyDefinition+Validation.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,12 @@ extension StudyDefinition {
107107
fileRefToCheck = component.fileRef
108108
case .questionnaire(let component):
109109
fileRefToCheck = component.fileRef
110-
case .healthDataCollection, .timedWalkingTest, .customActiveTask:
110+
case .healthDataCollection, .timedWalkingTest:
111111
fileRefToCheck = nil
112+
#if canImport(Darwin)
113+
case .customActiveTask:
114+
fileRefToCheck = nil
115+
#endif
112116
}
113117
if let fileRefToCheck, studyBundle.resolve(fileRefToCheck, in: Locale(identifier: "en_US")) == nil {
114118
result.issues.insert(.unableToFindFileRef(fileRefToCheck, component))

Sources/SpeziStudyDefinition/StudyDefinition+Components.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ extension StudyDefinition {
4545
case healthDataCollection(HealthDataCollectionComponent)
4646
/// A component that prompts the participant to perform a Timed Walking Test.
4747
case timedWalkingTest(TimedWalkingTestComponent)
48+
#if canImport(Darwin)
4849
/// A component that prompts the participant to perform a custom Active Task.
4950
case customActiveTask(CustomActiveTaskComponent)
51+
#endif
5052

5153
/// The components `id`, uniquely identifying it within the ``StudyDefinition``.
5254
public var id: UUID {
@@ -59,16 +61,22 @@ extension StudyDefinition {
5961
component.id
6062
case .timedWalkingTest(let component):
6163
component.id
64+
#if canImport(Darwin)
6265
case .customActiveTask(let component):
6366
component.id
67+
#endif
6468
}
6569
}
6670

6771
/// The Component's kind
6872
public var kind: Kind {
6973
switch self {
70-
case .informational, .questionnaire, .timedWalkingTest, .customActiveTask:
74+
case .informational, .questionnaire, .timedWalkingTest:
7175
.userInteractive
76+
#if canImport(Darwin)
77+
case .customActiveTask:
78+
.userInteractive
79+
#endif
7280
case .healthDataCollection:
7381
.internal
7482
}

Sources/SpeziStudyDefinition/StudyDefinition.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,12 @@ extension StudyDefinition {
121121
switch component {
122122
case .healthDataCollection(let component):
123123
component
124-
case .informational, .questionnaire, .timedWalkingTest, .customActiveTask:
124+
case .informational, .questionnaire, .timedWalkingTest:
125125
nil
126+
#if canImport(Darwin)
127+
case .customActiveTask:
128+
nil
129+
#endif
126130
}
127131
}
128132
}
@@ -164,9 +168,15 @@ extension StudyBundle {
164168
using: localeMatchingBehaviour
165169
)?.title?.value?.string
166170
case .timedWalkingTest(let component):
171+
#if canImport(Darwin)
167172
String(localized: component.test.displayTitle)
173+
#else
174+
nil
175+
#endif
176+
#if canImport(Darwin)
168177
case .customActiveTask(let component):
169178
String(localized: component.activeTask.title)
179+
#endif
170180
case .healthDataCollection:
171181
nil
172182
}
@@ -192,8 +202,10 @@ extension StudyBundle {
192202
nil
193203
case .healthDataCollection:
194204
nil
205+
#if canImport(Darwin)
195206
case .customActiveTask(let component):
196207
component.activeTask.subtitle.map { String(localized: $0) }
208+
#endif
197209
}
198210
}
199211

Tests/SpeziStudyTests/Resources/questionnaires/Invalid5+en-UK.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"title": "test survey with non-matching question input bounds",
33
"resourceType": "Questionnaire",
4-
"language": "en-UK",
4+
"language": "en-GB",
55
"status": "draft",
66
"id": "423EFEF6-7FC4-41B2-8211-668E90150E13",
77
"meta": {
@@ -11,7 +11,7 @@
1111
"tag": [
1212
{
1313
"system": "urn:ietf:bcp:47",
14-
"code": "en-UK",
14+
"code": "en-GB",
1515
"display": "English (United Kingdom)"
1616
}
1717
]

0 commit comments

Comments
 (0)