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
61 changes: 61 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Builds the DocC documentation and publishes it to GitHub Pages.
#
# One-time setup: in the repository settings, set Pages β†’ Build and deployment β†’ Source to
# "GitHub Actions". The site is then served at https://fridaytechnologies.github.io/PrototypeKit/.

name: Docs

on:
push:
branches: [ "master" ]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# Allow one concurrent deployment; don't cancel an in-progress one.
concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: macos-14
steps:
- uses: actions/checkout@v4

- name: Build DocC archive
run: >
xcodebuild docbuild
-scheme PrototypeKit
-destination 'generic/platform=iOS'
-derivedDataPath .derivedData

- name: Transform for static hosting
run: |
ARCHIVE=$(find .derivedData -type d -name 'PrototypeKit.doccarchive' | head -n 1)
if [ -z "$ARCHIVE" ]; then
echo "::error::PrototypeKit.doccarchive not found under .derivedData"
exit 1
fi
"$(xcrun --find docc)" process-archive transform-for-static-hosting "$ARCHIVE" \
--output-path _site \
--hosting-base-path PrototypeKit

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: _site

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ once tagged releases begin.
these failures β€” show a message, offer a retry β€” instead of only reading the log. The parameter
defaults to `nil`, so existing call sites are unaffected, and errors are still logged as before.
Transient per-frame Vision errors remain log-only by design.
- **Documentation & examples.** Enriched the DocC catalog and added a `Docs` workflow that builds
and publishes it to GitHub Pages; a runnable `Examples/` gallery covering every feature; and a
`SECURITY.md` vulnerability-reporting policy.
- **Localization.** The user-facing camera placeholder messages now go through `NSLocalizedString`
with an `en` string table (`defaultLocalization: "en"`), so they can be translated.

## [0.1.0] - 2026-07-02

Expand Down
181 changes: 181 additions & 0 deletions Examples/PrototypeKitExamples.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
//
// PrototypeKitExamples.swift
//
// Reference examples for PrototypeKit. Copy this file into your own SwiftUI app target.
// This file is intentionally NOT part of the Swift package (it lives under Examples/), so it is
// never compiled by the library β€” it's a copy-paste starting point.
//
// Set `ExampleGallery()` as your app's root view. See Examples/README.md for the Info.plist keys
// each feature needs (camera / microphone).
//

#if os(iOS)
import SwiftUI
import PrototypeKit

/// A menu linking to a demo for each PrototypeKit feature.
struct ExampleGallery: View {
var body: some View {
NavigationStack {
List {
Section("Camera + Vision") {
NavigationLink("Live text", destination: LiveTextExample())
NavigationLink("Live barcodes", destination: LiveBarcodeExample())
NavigationLink("Face detection", destination: FaceCountExample())
NavigationLink("Image classification", destination: ImageClassifierExample())
}
Section("Sound") {
NavigationLink("Sound recognition", destination: SoundExample())
}
Section("Motion") {
NavigationLink("Activity classification", destination: ActivityExample())
}
Section("Natural Language") {
NavigationLink("Sentiment", destination: SentimentExample())
}
}
.navigationTitle("PrototypeKit")
}
}
}

// MARK: - Camera + Vision (no model required)

/// Recognizes text in the live camera feed and lists it.
struct LiveTextExample: View {
@State private var detectedText: [String] = []

var body: some View {
ZStack(alignment: .bottom) {
LiveTextRecognizerView(detectedText: $detectedText)
Text(detectedText.joined(separator: "\n"))
.padding()
.frame(maxWidth: .infinity)
.background(.thinMaterial)
}
.ignoresSafeArea()
}
}

/// Decodes barcodes/QR codes in the live camera feed.
struct LiveBarcodeExample: View {
@State private var barcodes: [String] = []

var body: some View {
ZStack(alignment: .bottom) {
LiveBarcodeRecognizerView(detectedBarcodes: $barcodes)
Text(barcodes.first ?? "Point the camera at a barcode")
.padding()
.frame(maxWidth: .infinity)
.background(.thinMaterial)
}
.ignoresSafeArea()
}
}

/// Counts faces in the live camera feed.
struct FaceCountExample: View {
@State private var faceCount = 0

var body: some View {
ZStack(alignment: .top) {
LiveFaceDetectorView(faceCount: $faceCount)
Text("Faces: \(faceCount)")
.font(.title2.bold())
.padding()
.background(.thinMaterial, in: Capsule())
.padding(.top, 60)
}
.ignoresSafeArea()
}
}

// MARK: - Camera + Core ML (bring your own Create ML model)

/// Classifies each camera frame with a Create ML image classifier.
///
/// Replace `modelURL` with your own model, e.g. `FruitClassifier.urlOfModelInThisBundle`.
struct ImageClassifierExample: View {
@State private var prediction = ""
@State private var errorMessage: String?

// Point this at your Create ML model's `urlOfModelInThisBundle`.
private let modelURL = URL(fileURLWithPath: "/path/to/YourModel.mlmodelc")

var body: some View {
ZStack(alignment: .bottom) {
ImageClassifierView(modelURL: modelURL,
latestPrediction: $prediction) { error in
// React to a model-load failure instead of showing a silent, prediction-less feed.
errorMessage = error.localizedDescription
}
Text(errorMessage ?? (prediction.isEmpty ? "Classifying…" : prediction))
.padding()
.frame(maxWidth: .infinity)
.background(.thinMaterial)
}
.ignoresSafeArea()
}
}

// MARK: - Sound

/// Recognizes sounds from the microphone using the built-in classifier.
struct SoundExample: View {
@State private var recognizedSound: String?
@State private var errorMessage: String?

var body: some View {
VStack(spacing: 16) {
Text(recognizedSound ?? "Listening…")
.font(.title)
if let errorMessage = errorMessage {
Text(errorMessage).foregroundStyle(.red).font(.footnote)
}
}
.recognizeSounds(recognizedSound: $recognizedSound) { error in
errorMessage = error.localizedDescription
}
}
}

// MARK: - Motion

/// Classifies device activity with a Create ML activity classifier.
///
/// Replace `modelURL` with your own model, e.g. `ActivityClassifier.urlOfModelInThisBundle`.
struct ActivityExample: View {
@State private var activity: String?
@State private var errorMessage: String?

private let modelURL = URL(fileURLWithPath: "/path/to/YourActivityModel.mlmodelc")

var body: some View {
VStack(spacing: 16) {
Text(activity ?? "Detecting…").font(.title)
if let errorMessage = errorMessage {
Text(errorMessage).foregroundStyle(.red).font(.footnote)
}
}
.classifyActivity(modelURL: modelURL, latestActivity: $activity) { error in
errorMessage = error.localizedDescription
}
}
}

// MARK: - Natural Language (ships with the OS, no camera/mic/model)

/// Scores the sentiment of some text as you type.
struct SentimentExample: View {
@State private var text = "I love prototyping with this!"
@State private var score: Double = 0

var body: some View {
Form {
TextField("Type something", text: $text, axis: .vertical)
Text("Sentiment: \(score, specifier: "%.2f")")
.analyzeSentiment(text: text, score: $score)
}
}
}
#endif
32 changes: 32 additions & 0 deletions Examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# PrototypeKit Examples

Reference SwiftUI code showing how to use each PrototypeKit feature. These files live outside the
Swift package's `Sources/` directory, so they are **not** compiled as part of the library β€” they're
meant to be copied into your own app.

## Running the examples

1. Create a new iOS app in Xcode (**File β†’ New β†’ Project β†’ App**, SwiftUI lifecycle).
2. Add PrototypeKit as a package dependency (see the root [README](../README.md#installation-)).
3. Copy [`PrototypeKitExamples.swift`](PrototypeKitExamples.swift) into your app target.
4. Set `ExampleGallery()` as your root view (or present it).
5. Add the Info keys the features you try need:
- **Camera** views: `Privacy - Camera Usage Description` (`NSCameraUsageDescription`).
- **Sound** recognition: `Privacy - Microphone Usage Description` (`NSMicrophoneUsageDescription`).
- **Activity** classification reads Core Motion, which needs no usage string.

## What's covered

| Feature | Needs a model? | Info key |
| --- | --- | --- |
| Live text / barcode / face / body / rectangle / animal | No | Camera |
| Image classification, hand-pose classification | Yes (Create ML) | Camera |
| Sound recognition | Optional custom model | Microphone |
| Activity classification | Yes (Create ML) | β€” |
| Sentiment / language / entities | No (ships with the OS) | β€” |

The model-backed demos show the call site with a placeholder β€” drop in your own Create ML model and
point `modelURL` at `YourModel.urlOfModelInThisBundle`. Each model-backed example also shows the
`onError` handler so you can surface a load failure in the UI.

> **Note:** The live camera and microphone don't run in the iOS Simulator β€” try these on a device.
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PackageDescription

let package = Package(
name: "PrototypeKit",
defaultLocalization: "en",
platforms: [
.iOS(.v14),
.macOS(.v13)
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ dependencies: [
> `.upToNextMinor(from: "0.1.0")` if you need stricter guarantees. See the
> [CHANGELOG](CHANGELOG.md) for what's in each release.

## Documentation & examples πŸ“š

- **API reference** β€” DocC documentation is published to GitHub Pages:
<https://fridaytechnologies.github.io/PrototypeKit/documentation/prototypekit>
- **Sample code** β€” the [`Examples/`](Examples) directory contains a runnable SwiftUI gallery
covering every feature; copy [`PrototypeKitExamples.swift`](Examples/PrototypeKitExamples.swift)
into an app to try them.

# Examples

Here are a few basic examples you can use today.
Expand Down
37 changes: 37 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Security Policy

## Scope

PrototypeKit is a SwiftUI toolkit for prototyping on-device machine-learning features. All inference
runs on-device using Apple's frameworks (Vision, Core ML, Sound Analysis, Natural Language, Core
Motion); PrototypeKit does not collect, store, or transmit user data, and it ships a
`PrivacyInfo.xcprivacy` manifest declaring no tracking and no data collection.

Because PrototypeKit is intended for prototyping and idea validation, please review it before relying
on it in a production, safety-critical, or sensitive context.

## Supported versions

PrototypeKit is pre-1.0. Security fixes are made against the latest `master`; there is no long-term
support branch yet. Pin a released `0.x` version and update promptly when a fix ships.

## Reporting a vulnerability

Please **do not** open a public issue for security-sensitive reports.

Instead, use GitHub's private vulnerability reporting:

1. Go to the repository's **Security** tab β†’ **Report a vulnerability**
(<https://github.com/FridayTechnologies/PrototypeKit/security/advisories/new>).
2. Describe the issue, the affected version/commit, and reproduction steps.

If private reporting is unavailable, contact the maintainers privately rather than filing a public
issue.

### What to expect

- We aim to acknowledge a report within a few business days.
- We'll work with you on a fix and coordinate a disclosure timeline.
- With your permission, we're happy to credit you in the release notes.

Thank you for helping keep PrototypeKit and the apps built with it safe.
12 changes: 8 additions & 4 deletions Sources/PrototypeKit/Camera/CameraViewController-iOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ class CameraViewController: UIViewController {
guard checkDeveloperHasConfiguredInfoPlist() else {
presentCenteredMessage(
icon: "⚠️",
text: "Camera unavailable: add the \"Privacy - Camera Usage Description\" "
+ "(NSCameraUsageDescription) key to your app's Info settings. "
+ "See the PrototypeKit setup guide.")
text: NSLocalizedString("camera.missingUsageDescription",
bundle: .module,
comment: "Shown on the camera preview when NSCameraUsageDescription is missing."))
return
}
#if targetEnvironment(simulator)
Expand Down Expand Up @@ -70,7 +70,11 @@ class CameraViewController: UIViewController {

func checkAndHandleSimulator() {
#if targetEnvironment(simulator)
presentCenteredMessage(icon: "πŸ“Έ", text: "Live camera is not supported in previews.")
presentCenteredMessage(
icon: "πŸ“Έ",
text: NSLocalizedString("camera.unavailableInSimulator",
bundle: .module,
comment: "Shown in place of the camera preview in the Simulator and previews."))
#endif
}

Expand Down
Loading
Loading