Last active
July 15, 2025 08:54
-
-
Save adam-zethraeus/27b97f2726a8aae1978ba2505300a5ac to your computer and use it in GitHub Desktop.
VisualHash.swift (Value -> Emoji / Pastel / Neon)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import CryptoKit | |
| import SwiftUI | |
| #if canImport(UIKit) | |
| import UIKit | |
| #elseif canImport(AppKit) | |
| import AppKit | |
| #endif | |
| public enum VisualHash { | |
| public static func pastel(from item: some Codable) -> Color { | |
| let raw = try! JSONEncoder().encode(item) | |
| let hash = SHA256.hash(data: raw) | |
| let data = Array(hash) | |
| let r = Double(data[0]) / 255.0 | |
| let g = Double(data[1]) / 255.0 | |
| let b = Double(data[2]) / 255.0 | |
| let reader = Color( | |
| red: r, | |
| green: g, | |
| blue: b | |
| ) | |
| let pastelHue = reader.hsba.hue | |
| let pastelSaturation = 0.05 + (reader.hsba.saturation * 0.35) | |
| let pastelBrightness = 0.6 + (reader.hsba.brightness * 0.4) | |
| return Color( | |
| hue: pastelHue, | |
| saturation: pastelSaturation, | |
| brightness: pastelBrightness | |
| ) | |
| } | |
| public static func neon(from item: some Codable) -> Color { | |
| let raw = try! JSONEncoder().encode(item) | |
| let hash = SHA256.hash(data: raw) | |
| let data = Array(hash) | |
| let r = Double(data[0]) / 255.0 | |
| let g = Double(data[1]) / 255.0 | |
| let b = Double(data[2]) / 255.0 | |
| let reader = Color( | |
| red: r, | |
| green: g, | |
| blue: b | |
| ) | |
| let neonHue = reader.hsba.hue | |
| let neonSaturation = 0.7 + (reader.hsba.saturation * 0.3) | |
| let neonBrightness = 0.5 + (reader.hsba.brightness * 0.5) | |
| return Color( | |
| hue: neonHue, | |
| saturation: neonSaturation, | |
| brightness: neonBrightness | |
| ) | |
| } | |
| public static func emoji(from item: some Codable) -> String { | |
| let raw = try! JSONEncoder().encode(item) | |
| let hash = SHA256.hash(data: raw).intValue | |
| let index = abs(hash) % contiguousEmoji.count | |
| return String(contiguousEmoji[index]) | |
| } | |
| } | |
| extension Digest { | |
| var bytes: [UInt8] { Array(makeIterator()) } | |
| var data: Data { Data(bytes) } | |
| var intValue: Int { | |
| Int(bytes.map { String(format: "%02X", $0) }.joined(), radix: 10) ?? -1 | |
| } | |
| } | |
| extension Color { | |
| var hsba: (hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat) { | |
| #if canImport(UIKit) | |
| typealias NativeColor = UIColor | |
| #elseif canImport(AppKit) | |
| typealias NativeColor = NSColor | |
| #endif | |
| var h: CGFloat = 0 | |
| var s: CGFloat = 0 | |
| var b: CGFloat = 0 | |
| var a: CGFloat = 0 | |
| NativeColor(self) | |
| .getHue(&h, saturation: &s, brightness: &b, alpha: &a) | |
| return (h, s, b, a) | |
| } | |
| } | |
| fileprivate let contiguousEmoji: [UnicodeScalar] = { | |
| let ranges: [ClosedRange<Int>] = [ | |
| 0x1f600...0x1f64f, | |
| 0x1f680...0x1f6c5, | |
| 0x1f6cb...0x1f6d2, | |
| 0x1f6e0...0x1f6e5, | |
| 0x1f6f3...0x1f6fa, | |
| 0x1f7e0...0x1f7eb, | |
| 0x1f90d...0x1f93a, | |
| 0x1f93c...0x1f945, | |
| 0x1f947...0x1f971, | |
| 0x1f973...0x1f976, | |
| 0x1f97a...0x1f9a2, | |
| 0x1f9a5...0x1f9aa, | |
| 0x1f9ae...0x1f9ca, | |
| 0x1f9cd...0x1f9ff, | |
| 0x1fa70...0x1fa73, | |
| 0x1fa78...0x1fa7a, | |
| 0x1fa80...0x1fa82, | |
| 0x1fa90...0x1fa95, | |
| ] | |
| return ranges.reduce([], +).map { return UnicodeScalar($0)! } | |
| }() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment