Skip to content
Merged
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
138 changes: 138 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,144 @@ On iOS, `HapticFeedback` uses a `CHHapticEngine` instance that is created on fir

On Android, patterns are converted to a `VibrationEffect.Composition` with the corresponding `PRIMITIVE_*` constants and played through the system `Vibrator` obtained from `VibratorManager`. For more details, see Android's [VibrationEffect documentation](https://developer.android.com/reference/android/os/VibrationEffect) and the guide on [custom haptic effects](https://developer.android.com/develop/ui/views/haptics/custom-haptic-effects).

## SBOMView

`SBOMView` is a SwiftUI view that renders a "Software Bill of Materials" (SBOM)
screen for your app. It reads SPDX 2.3 JSON files generated by the
`skip sbom create` command and presents the bundled software dependencies as a
navigable list, with a detail view for each dependency that shows its license,
version, supplier, checksums, and any further packages it pulls in.

It is designed to be dropped straight into a Settings or Preferences screen via
a `NavigationLink`:

```swift
import SwiftUI
import SkipKit

struct SettingsView: View {
var body: some View {
Form {
// …other settings…
NavigationLink("Bill of Materials") {
SBOMView(bundle: Bundle.module)
}
}
}
}
```

### Generating SBOM resources

Use the `skip sbom create` command to produce the SPDX files and place them
inside the resources of the module that owns the SBOM screen (typically the
same module that exposes your app's settings UI):

```
skip sbom create
```

The command produces both `sbom-darwin-ios.spdx.json` and
`sbom-linux-android.spdx.json`. Drop them into your module's `Resources/`
directory and make sure your `Package.swift` target processes that
resources folder:

```swift
.target(
name: "MyAppUI",
dependencies: [.product(name: "SkipKit", package: "skip-kit")],
resources: [.process("Resources")]
)
```

### Display modes

`SBOMView` supports two display modes via the `SBOMDisplayMode` enum:

- `.hierarchy` *(default)* — Shows only the **top-level** dependencies (the
packages your app depends on directly), and lets the user drill into each
one to see its transitive dependencies. The hierarchy is reconstructed from
the `DEPENDS_ON` relationships in the SPDX document, so the screen mirrors
the actual dependency tree of your app.
- `.flat` — Shows **every** package in the SBOM as a single alphabetised
list, regardless of where it sits in the dependency tree. This is useful
for audits, search-and-find workflows, or for showing a complete catalogue
of every binary that ends up in your app.

```swift
// Hierarchy view (the default): top-level deps only, drill in for transitives
NavigationLink("Bill of Materials") {
SBOMView(bundle: Bundle.module)
}

// Flat view: every package, sorted alphabetically
NavigationLink("All Dependencies") {
SBOMView(bundle: Bundle.module, displayMode: .flat)
}
```

In both modes the dependency lists are sorted alphabetically by package name
(case-insensitive) for predictable presentation.

### What the user sees

The summary list shows each dependency's name, supplier, version, and
declared license. Tapping a row pushes a detail view that includes:

- **Package** — name, version, supplier, originator, purpose, SPDX ID
- **License** — declared and concluded licenses, copyright text, and a
*"View License on spdx.org"* button that opens the corresponding page on
[spdx.org/licenses](https://spdx.org/licenses/) (e.g. tapping `EUPL-1.2`
opens `https://spdx.org/licenses/EUPL-1.2.html`) inside an in-app browser
(`SFSafariViewController` on iOS, Chrome Custom Tabs on Android). For
custom licenses identified by `LicenseRef-…`, the full license text and
any "see also" links are displayed inline from the SBOM's
`hasExtractedLicensingInfos` section.
- **Dependencies** — the direct sub-dependencies of this package, each as a
navigation link to its own detail view. This is what makes the hierarchy
view navigable: you start at your top-level deps and walk down through the
tree.
- **Source** — download location, homepage, description, summary
- **External References** — `purl`, `swiftpm`, and other SPDX external refs
- **Checksums** — SHA-1 / SHA-256 / etc. for each package archive

The summary list also includes a *"Share Bill of Materials"* button that
brings up the system share sheet so users (or auditors) can export the raw
SPDX JSON for offline review.

### Parsing SBOMs programmatically

The same SPDX model used by `SBOMView` is also exposed for direct use, which
is handy for tests, custom UI, or build-time tooling:

```swift
if let document = try SBOMDocument.load(from: Bundle.module) {
for package in document.topLevelPackages {
print("\(package.name ?? "?") \(package.versionInfo ?? "")")
print(" License: \(package.licenseDeclared ?? "NOASSERTION")")
for dep in document.directDependencies(of: package) {
print(" └── \(dep.name ?? "?")")
}
}
}
```

The `SPDXLicense` helper provides utilities for working with SPDX license
identifiers, including extracting a canonical id from compound expressions
like `LGPL-3.0-only WITH LGPL-3.0-linking-exception` and constructing the
corresponding `https://spdx.org/licenses/<id>.html` URL:

```swift
SPDXLicense.licensePageURL(for: "EUPL-1.2")
// → https://spdx.org/licenses/EUPL-1.2.html

SPDXLicense.canonicalIdentifier("MIT OR Apache-2.0")
// → "MIT"

SPDXLicense.isUnknown("NOASSERTION")
// → true
```

## Building

This project is a Swift Package Manager module that uses the
Expand Down
115 changes: 114 additions & 1 deletion Sources/SkipKit/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"sourceLanguage" : "en",
"strings" : {
"About this Bill of Materials" : {

},
"Appearance" : {
"extractionState" : "stale",
"localizations" : {
Expand Down Expand Up @@ -29,6 +32,24 @@
}
}
}
},
"Bill of Materials" : {

},
"Checksums" : {

},
"Comments" : {

},
"Concluded" : {

},
"Copyright" : {

},
"Created" : {

},
"Dark" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -58,6 +79,33 @@
}
}
}
},
"Data License" : {

},
"Declared" : {

},
"Dependencies (%lld)" : {

},
"Description" : {

},
"Display Mode" : {

},
"Download" : {

},
"External References" : {

},
"Flattened" : {

},
"Generator" : {

},
"Hello %@!" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -87,6 +135,9 @@
}
}
}
},
"Hierarchical" : {

},
"Home" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -116,6 +167,9 @@
}
}
}
},
"Homepage" : {

},
"Item %lld" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -145,6 +199,15 @@
}
}
}
},
"License" : {

},
"License Name" : {

},
"License Text" : {

},
"Light" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -176,7 +239,6 @@
}
},
"Name" : {
"extractionState" : "stale",
"localizations" : {
"es" : {
"stringUnit" : {
Expand All @@ -203,6 +265,12 @@
}
}
}
},
"Originator" : {

},
"Package" : {

},
"Powered by %@" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -232,6 +300,9 @@
}
}
}
},
"Purpose" : {

},
"Screen %lld" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -261,6 +332,9 @@
}
}
}
},
"See Also" : {

},
"Settings" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -290,6 +364,27 @@
}
}
}
},
"Share Bill of Materials" : {

},
"Software Bill of Materials" : {

},
"Source" : {

},
"SPDX ID" : {

},
"SPDX Version" : {

},
"Summary" : {

},
"Supplier" : {

},
"System" : {
"extractionState" : "stale",
Expand Down Expand Up @@ -319,6 +414,24 @@
}
}
}
},
"Tap “View License on spdx.org” to read the full text of recognised open-source licenses." : {

},
"These packages are pulled in transitively by %@." : {

},
"This app includes the open-source software listed above. Tap a dependency to view its license and version information." : {

},
"This app includes the open-source software listed above. Tap a dependency to view its license, version information, and any further dependencies it brings in." : {

},
"Version" : {

},
"View License on spdx.org" : {

},
"Welcome" : {
"extractionState" : "stale",
Expand Down
Loading