Skip to content

Commit

Permalink
implement sheet listing all package dependencies from SPM; (StanfordS…
Browse files Browse the repository at this point in the history
…pezi#36)

<!--

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-License-Identifier: MIT

-->

# *Add an Open-Source Contributions Screen*

## ♻️ Current situation & Problem
The application currently does not provide any way to see all
open-source tools used within the app, nor does it give access to all
licenses used.
See:
[StanfordSpezi#29

## ⚙️ Release Notes 
- add a info button to the top right corner of Home view that opens the
Contributions sheet
- add Contributions sheet that lists all package dependencies used
- package dependencies are read via SwiftPackageList API (using a build
phase script, the view is always up do date with the latest
dependencies). For more info check out the repo:
[Swift-Package-List](https://github.com/FelixHerrmann/swift-package-list)
- each dependency item in the list shows the package name,
version/branch, a license label as well as a link to the GitHub repo
- currently detected licenses (shown as tags in the UI) of packages are
MIT, Apache-2.0, GPL-2.0, GPL-3.0. Could still be extended in the
future.

Here is how it looks like:
![Simulator Screenshot - iPhone 14 Pro - 2023-09-18 at 15 42
39](https://github.com/StanfordSpezi/SpeziTemplateApplication/assets/33159293/a22d225c-b70e-47c2-a6a5-dc75fcf3fa2e)
![Simulator Screenshot - iPhone 14 Pro - 2023-09-18 at 15 43
13](https://github.com/StanfordSpezi/SpeziTemplateApplication/assets/33159293/21918fcb-2646-4107-a04d-44ead92ced72)


## 📚 Documentation
TBA


## ✅ Testing
- Simple UI test added that tests the info button and the subsequent
existence of the contributions sheet

## 📝 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).

---------

Co-authored-by: Andi <[email protected]>
Co-authored-by: Paul Schmiedmayer <[email protected]>
Co-authored-by: Philipp, Zagar <[email protected]>
  • Loading branch information
4 people committed Oct 18, 2023
1 parent 5f4e95c commit cf19568
Show file tree
Hide file tree
Showing 12 changed files with 511 additions and 34 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ Template Application Contributors
* [Paul Schmiedmayer](https://github.com/PSchmiedmayer)
* [Andreas Bauer](https://github.com/Supereg)
* [Philipp Zagar](https://github.com/philippzagar)
* [Nikolai Madlener](https://github.com/NikolaiMadlener)
105 changes: 101 additions & 4 deletions TemplateApplication.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
{
"identity" : "healthkitonfhir",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordBDHG/HealthKitOnFHIR",
"location" : "https://github.com/StanfordBDHG/HealthKitOnFHIR.git",
"state" : {
"revision" : "fdf8e4543718a940643598e4bd5e750e9c4c5540",
"version" : "0.2.4"
Expand Down Expand Up @@ -140,26 +140,26 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/Spezi",
"state" : {
"revision" : "7462510badaa156c1e25efd7eabbf5b85ecb0098",
"version" : "0.7.2"
"revision" : "e75ea4d241b6eb611ca4c1c25926097cf324ce37",
"version" : "0.7.3"
}
},
{
"identity" : "speziaccount",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziAccount.git",
"state" : {
"revision" : "5937ed6e0baea9f53688c2752acfff23d45335cd",
"version" : "0.5.2"
"revision" : "1eca29373a5c27e7dfe7871be77be306ca626989",
"version" : "0.5.3"
}
},
{
"identity" : "spezicontact",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziContact.git",
"state" : {
"revision" : "2d2b8096dcea737723430a3b98ebe3c3a79f6f41",
"version" : "0.4.0"
"revision" : "9dea670bcfea51c2db04ea73b80fda923c2ebaed",
"version" : "0.4.1"
}
},
{
Expand Down Expand Up @@ -212,8 +212,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziQuestionnaire.git",
"state" : {
"revision" : "9c4edec01e786447cf37ad272ebbb7b2f12e692a",
"version" : "0.4.2"
"revision" : "849e207da973a089e7a209289217ff3efab68596",
"version" : "0.4.3"
}
},
{
Expand All @@ -230,8 +230,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziStorage.git",
"state" : {
"revision" : "739ee1eadc5f9b1b5d2e8752ba077243d42fa79f",
"version" : "0.4.2"
"revision" : "58f79a21291da6d2d2193275486fa3c69b4f31fa",
"version" : "0.4.3"
}
},
{
Expand All @@ -252,6 +252,24 @@
"version" : "1.0.5"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
"version" : "1.2.3"
}
},
{
"identity" : "swift-package-list",
"kind" : "remoteSourceControl",
"location" : "https://github.com/FelixHerrmann/swift-package-list",
"state" : {
"revision" : "374fedd183a1682f3fd5fd0c2e2676d5f3e7c352",
"version" : "3.0.9"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
Expand Down
8 changes: 7 additions & 1 deletion TemplateApplication/Account/AccountSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ struct AccountSheet: View {
NavigationStack {
ZStack {
if account.signedIn {
AccountOverview(isEditing: $overviewIsEditing)
AccountOverview(isEditing: $overviewIsEditing) {
NavigationLink {
ContributionsList()
} label: {
Text("LICENSE_INFO_TITLE")
}
}
.onDisappear {
overviewIsEditing = false
}
Expand Down
55 changes: 55 additions & 0 deletions TemplateApplication/Contributions/ContributionsList.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// This source file is part of the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//

import SwiftPackageList
import SwiftUI


struct ContributionsList: View {
var packages: [Package] = PackageHelper.getPackageList()

var body: some View {
List {
Section(footer: Text("PROJECT_LICENSE_DESCRIPTION")) {
Text("CONTRIBUTIONS_LIST_DESCRIPTION")
}
Section(
header: Text("CONTRIBUTIONS_LIST_HEADER"),
footer: Text("CONTRIBUTIONS_LIST_FOOTER")
) {
ForEach(packages.sorted(by: { $0.name < $1.name }), id: \.name) { package in
PackageCell(package: package)
}
}
}
.navigationTitle("LICENSE_INFO_TITLE")
.navigationBarTitleDisplayMode(.inline)
}
}


#if DEBUG
struct ContributionsList_Previews: PreviewProvider {
static var previews: some View {
let mockPackages = [
Package(
name: "MockPackage",
version: "1.0",
branch: nil,
revision: "0",
// We use a force unwrap in the preview as we can not recover from an error here
// and the code will never end up in a production environment.
// swiftlint:disable:next force_unwrapping
repositoryURL: URL(string: "github.com")!,
license: "MIT License"
)
]
return ContributionsList(packages: mockPackages)
}
}
#endif
104 changes: 104 additions & 0 deletions TemplateApplication/Contributions/Package+LicenseType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// This source file is part of the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//

import Foundation
import SwiftPackageList


// This section of code is based on the SwiftPackageList package:
// - Original code: https://github.com/FelixHerrmann/swift-package-list/issues/43
enum LicenseType {
case mit
case apachev2
case gplv2
case gplv3
case bsd2
case bsd3
case bsd4
case zlib

/// SPDX-License-Identifier for the UI
var spdxIdentifier: String {
switch self {
case .mit: return "MIT"
case .apachev2: return "Apache-2.0"
case .gplv2: return "GPL-2.0"
case .gplv3: return "GPL-3.0"
case .bsd2: return "BSD-2-Clause"
case .bsd3: return "BSD-3-Clause"
case .bsd4: return "BSD-4-Clause"
case .zlib: return "Zlib"
}
}

/// Initializer that scans the license document for common licenses and versions
init?(license: String) {
let license = license
.replacingOccurrences(of: "\\s+|\\n", with: " ", options: .regularExpression)

if license.contains(mitText) {
self = .mit
} else if license.contains(apacheText) && license.contains("Version 2.0") {
self = .apachev2
} else if license.contains(gnuText) && license.contains("Version 2") {
self = .gplv2
} else if license.contains(gnuText) && license.contains("Version 3") {
self = .gplv3
} else if license.contains(bsdFourClauseText) {
self = .bsd4
} else if license.range(of: bsdThreeClausePattern, options: .regularExpression) != nil {
self = .bsd3
} else if license.contains(bsdTwoClauseText) {
self = .bsd2
} else if license.range(of: zlibPattern, options: .regularExpression) != nil {
self = .zlib
} else {
return nil
}
}
}


// Constants representing typical text and regular expression patterns often found in license files.
// They are used for matching and identifying different types of licenses within text documents.
let mitText = "MIT License"
let apacheText = "Apache License"
let gnuText = "GNU GENERAL PUBLIC LICENSE"
let bsdTwoClauseText =
"""
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met
"""
let bsdThreeClausePattern =
"""
Neither the name of (.+) nor the names of (.+) may be used to endorse or promote products derived from this software \
without specific prior written permission
"""
let bsdFourClauseText =
"""
All advertising materials mentioning features or use of this software must display the following acknowledgement: \
this product includes software developed by
"""
let zlibPattern =
"""
The origin of this software must not be misrepresented; you must not claim that you wrote the original software. \
If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.(.*) \
Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.(.*) \
This notice may not be removed or altered from any source distribution.
"""


extension Package {
/// Generates the `LicenseType` from a license document of `String`
func getLicenseType(license: String?) -> LicenseType? {
if let license = license {
let licenseType = LicenseType(license: license)
return licenseType
}
return nil
}
}
74 changes: 74 additions & 0 deletions TemplateApplication/Contributions/PackageCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// This source file is part of the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//

import SwiftPackageList
import SwiftUI


struct PackageCell: View {
let package: Package

var body: some View {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(package.name).font(.headline)
HStack {
Text(getPackageDetails(package: package))
.font(.caption)
if let licenseType = package.getLicenseType(license: package.license) {
Text(licenseType.spdxIdentifier)
.font(.caption)
.fontWeight(.semibold)
.padding(2)
.background(Color(.systemGray5))
.cornerRadius(4)
}
}
}
Spacer()
Button(action: {
UIApplication.shared.open(package.repositoryURL)
}) {
Image(systemName: "safari.fill")
.imageScale(.large)
}.buttonStyle(PlainButtonStyle())
.foregroundColor(.blue)
.accessibilityLabel(Text("Repository Link"))
}
}

func getPackageDetails(package: Package) -> String {
if let branch = package.branch {
return "Branch: \(branch)"
} else if let version = package.version {
return "Version: \(version)"
} else {
return "Revision: \(package.revision)"
}
}
}


#if DEBUG
struct PackageCell_Previews: PreviewProvider {
static var previews: some View {
let mockPackage = Package(
name: "MockPackage",
version: "1.0",
branch: nil,
revision: "0",
// We use a force unwrap in the preview as we can not recover from an error here
// and the code will never end up in a production environment.
// swiftlint:disable:next force_unwrapping
repositoryURL: URL(string: "github.com")!,
license: "MIT License"
)
return PackageCell(package: mockPackage).previewLayout(.sizeThatFits)
}
}
#endif
26 changes: 26 additions & 0 deletions TemplateApplication/Contributions/PackageHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// This source file is part of the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//

import Foundation
import SwiftPackageList


enum PackageHelper {
/// Helper function that calls the corrensponding API of `SwiftPackageList`to fetch the list of packages
static func getPackageList() -> [Package] {
do {
let packages = try packageList()
return packages
} catch PackageListError.noPackageList {
print("There is no package-list file")
} catch {
print(error)
}
return []
}
}
Loading

0 comments on commit cf19568

Please sign in to comment.