Skip to content

v1.5.0

Choose a tag to compare

@2dubu2dubu released this 05 May 16:50
· 19 commits to main since this release
cc47975

PaletteKit v1.5.0 Release Notes

Highlights

  • Async-loading SwiftUI + UIKit views (AsyncPaletteGraphic, AsyncPaletteGraphicView) — pass an image source, the view extracts the palette internally before rendering. Eliminates the @State + .task boilerplate for the common case.
  • Public PaletteCache — process-wide .shared singleton with DI-friendly named instances. URL sources cache automatically; .data / .cgImage opt in via cacheKey:. Sign-out / theme reset clear the store.
  • Two SwiftUI init styles: simple convenience (auto-renders PaletteGraphic with placeholder slot) and phase content closure mirroring Apple's AsyncImage(url:content:) for telemetry / secondary UI composition / custom error rendering.

Library changes

  • Sources/PaletteKit/Graphic/Async/:
    • AsyncPaletteGraphic (SwiftUI View) — generic over Content: View, two public inits.
    • AsyncPaletteGraphicView (UIKit UIView subclass) — pair of PaletteGraphicView. Public imageSource, extractionOptions, configuration, swatchStrategy, cache, cacheKey, placeholderView, transition, onSuccess, onFailure, reload(), cancel().
    • AsyncPaletteGraphicLoader — internal @MainActor ObservableObject shared by both views. Owns one Task per resolution, integrates with PaletteCache, drives the 4-state machine.
    • PaletteCache — public NSCache wrapper, countLimit = 100, String-keyed (debuggable, no hash collision), DI via cache: parameter.
    • AsyncPaletteGraphicTransition + View.asyncPaletteGraphicTransition(_:) modifier — env-driven cross-fade with .normal (0.20s) / .slow (0.35s) / .extraSlow (0.50s) / .custom(_:duration:) presets. Cache hits skip the transition (sync resolution → no animation).
    • AsyncPaletteGraphicPhase — public enum (.empty / .loading / .success(palette:swatches:fromCache:) / .failure(any Error)).
  • SwatchStrategy extracted to its own file (still public via Configuration.swatchStrategy).
  • Folder renames for naming consistency: Card/Graphic/, Support/Logging/. No public API impact from these renames.
  • All observable ExtractionOptions fields are folded into the cache key (caller changing quality, whiteThreshold, fallbackStrategy, etc. invalidates correctly — no stale palettes).

Breaking changes

  • CardPalette public type removed. The resolved center/edge logic is now an internal PaletteGraphicRenderer.resolveAnchors helper. Most users never touched CardPalette directly — PaletteGraphic/AsyncPaletteGraphic resolve it internally. Demo lab includes a local ResolvedColors struct as a reference implementation for callers wanting the same 4-color (center/edge/background/accent) resolution pattern.
  • AsyncPaletteGraphic is now generic over Content: View (was Placeholder: View). The convenience placeholder init is unchanged ergonomically but its generic shape changed via where Content == AnyView. Callers using the default placeholder (Color.clear) or trailing-closure placeholder are unaffected; explicit type annotations like AsyncPaletteGraphic<MyView> need updating.
  • Convenience init's swatchStrategy: top-level parameter dropped (was duplicating configuration.swatchStrategy). Set via configuration: .init(swatchStrategy: .contrast).

Demo app

  • Demo continues to expose the Card lab UI for tweaking direction / colorCount / strategy / grain / axis / shape and exporting share assets. Async-loading-from-URL is documented in DocC + README rather than a parallel demo tab — the wrapper API is simple enough (single line) that runtime exploration adds little value beyond the canonical examples.

Docs

  • New DocC article Loading palettes asynchronously covering both init styles, caching, transitions, and error handling.
  • Tutorials/Card.md gains an ## Async loading section linking to the article.
  • PaletteKit.md Topics catalog adds ### Async loading subsection.
  • README: ### Load asynchronously block next to ### Generate a graphic, install snippet bumped to 1.5.0, Roadmap updated (v1.5 ✅, v1.6 PaletteMeshGraphic listed, v2.0 unchanged).

Install

.package(url: "https://github.com/2dubu/PaletteKit", from: "1.5.0")

Try it on your device

make demo-app

Open the Card lab tab to tune configuration knobs and export share images.

Audit notes

  • Cache architecture follows Lottie's lineage (memory-only NSCache, no disk persistence) — palette is a small derived value (~1KB per entry) and the original image source is the canonical store. Disk persistence is a v1.5.x candidate if real demand surfaces.
  • Singleton + per-call DI mirrors Kingfisher / SDWebImage / Nuke convention (immutable .shared + cache: parameter).
  • SwiftUI phase content closure mirrors Apple's AsyncImage precedent rather than imperative callbacks. UIKit twin retains onSuccess / onFailure callbacks because UIKit convention favors callbacks.
  • KarrotUIKit/AsyncImageProviding/ provided the architectural blueprint for the 2-View-1-Loader pair pattern + transition env value, with Combine swapped for Swift Concurrency.

Roadmap

  • v1.6 — PaletteMeshGraphic: iOS 18+ multi-color mesh primitive (separate type, narrative-decoupled from PaletteGraphic).
  • v2.0 — observe() (live video / camera) + PaletteKitInsights (FoundationModels captions, color naming, custom instructions on iOS 26+).

Acknowledgements
Thanks to color-thief (MIT) for the algorithmic lineage and to KarrotUIKit/AsyncImageProviding/ for the proven SwiftUI+UIKit pair pattern that informed v1.5's architecture. v1.5 ships no extraction algorithm changes from v1.4 — the addition is the async wrapper layer + cache, not the core extractor.