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
2 changes: 1 addition & 1 deletion Example/Example/SymbolVariableValueModeSetting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ enum SymbolVariableValueModeSetting: String, CaseIterable, Identifiable {
}
}

@available(macOS 26.0, *)
@available(iOS 26.0, macOS 26.0, *)
extension SymbolVariableValueMode {
init(_ setting: SymbolVariableValueModeSetting) {
switch setting {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ private extension CategoryFilterPicker {
case .dark:
.white.opacity(0.15)
case .light:
// swiftlint:disable:next fallthrough
fallthrough
@unknown default:
.black.opacity(0.1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Foundation

final class BundleCoreGlyphsDataProvider: CoreGlyphsDataProvider {
enum ReadError: LocalizedError, CustomDebugStringConvertible {
case plistNotFound(String)
case failedReadingData(Error)

var errorDescription: String? {
debugDescription
}

var debugDescription: String {
switch self {
case .plistNotFound(let filename):
"The property list '\(filename)' was not found in the bundle."
case .failedReadingData(let error):
"Failed reading plist data: \(error.localizedDescription)"
}
}
}

func data(forPlistNamed filename: String) async throws -> Data {
let bundle = try await CoreGlyphsBundleLoader.load()
guard let filePath = bundle.path(forResource: filename, ofType: "plist") else {
throw ReadError.plistNotFound(filename)
}
do {
return try Data(contentsOf: URL(filePath: filePath))
} catch {
throw ReadError.failedReadingData(error)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import Foundation
import OSLog

final class CachingCoreGlyphsDataProvider: CoreGlyphsDataProvider {
enum ReadError: LocalizedError, CustomDebugStringConvertible {
case cacheUnavailable(underlyingError: Error)

var errorDescription: String? {
debugDescription
}

var debugDescription: String {
switch self {
case .cacheUnavailable(let underlyingError):
"""
Failed loading from bundle and cache is unavailable. \
Original error: \(underlyingError.localizedDescription)
"""
}
}
}

private let dataProvider: CoreGlyphsDataProvider
private let cacheStorage: CacheStorage

init(wrapping dataProvider: CoreGlyphsDataProvider, cacheDirectory: URL) {
self.dataProvider = dataProvider
self.cacheStorage = CacheStorage(directory: cacheDirectory)
}

func data(forPlistNamed filename: String) async throws -> Data {
do {
let data = try await dataProvider.data(forPlistNamed: filename)
Task { await cacheStorage.write(data, filename: filename) }
return data
} catch {
return try await cacheStorage.read(filename: filename, underlyingError: error)
}
}
}

private actor CacheStorage {
private let logger = Logger(subsystem: "SFSymbols", category: "CacheStorage")
private let directory: URL
private var directoryCreated = false

init(directory: URL) {
self.directory = directory
}

func write(_ data: Data, filename: String) {
do {
try createDirectoryIfNeeded()
let url = fileURL(for: filename)
try data.write(to: url, options: .atomic)
#if os(iOS)
try FileManager.default.setAttributes(
[.protectionKey: FileProtectionType.none],
ofItemAtPath: url.path()
)
#endif
} catch {
// Caching failures are non-fatal. We still have the data from the bundle.
}
}

func read(filename: String, underlyingError: Error) throws -> Data {
do {
return try Data(contentsOf: fileURL(for: filename))
} catch {
logger.error(
"""
Failed reading '\(filename)'. \
Bundle error: \(underlyingError.localizedDescription). \
Cache error: \(error.localizedDescription)
"""
)
throw CachingCoreGlyphsDataProvider.ReadError.cacheUnavailable(underlyingError: underlyingError)
}
}

private func createDirectoryIfNeeded() throws {
guard !directoryCreated else {
return
}
#if os(iOS)
try FileManager.default.createDirectory(
at: directory,
withIntermediateDirectories: true,
attributes: [.protectionKey: FileProtectionType.none]
)
#else
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
#endif
directoryCreated = true
}

private func fileURL(for filename: String) -> URL {
directory.appendingPathComponent("\(filename).plist")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Foundation

protocol CoreGlyphsDataProvider: Sendable {
func data(forPlistNamed filename: String) async throws -> Data
}

extension CoreGlyphsDataProvider where Self == CachingCoreGlyphsDataProvider {
static func `default`() -> CoreGlyphsDataProvider {
CachingCoreGlyphsDataProvider(
wrapping: BundleCoreGlyphsDataProvider(),
cacheDirectory: defaultCacheDirectory()
)
}

private static func defaultCacheDirectory() -> URL {
let applicationSupport = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask
).first!
return applicationSupport
.appendingPathComponent("SFSymbols", isDirectory: true)
.appendingPathComponent("CoreGlyphs", isDirectory: true)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Foundation

struct CoreGlyphsPlistReader: Sendable {
enum ReadError: LocalizedError, CustomDebugStringConvertible {
case decodingError(DecodingError)
case unknownError(Error)

var errorDescription: String? {
debugDescription
}

var debugDescription: String {
switch self {
case .decodingError(let error):
error.localizedDescription
case .unknownError(let error):
error.localizedDescription
}
}
}

let dataProvider: CoreGlyphsDataProvider

init(dataProvider: CoreGlyphsDataProvider = .default()) {
self.dataProvider = dataProvider
}

func read<T: Decodable>(plistNamed filename: String, as type: T.Type) async throws -> T {
let data = try await dataProvider.data(forPlistNamed: filename)
do {
let decoder = PropertyListDecoder()
return try decoder.decode(type, from: data)
} catch let error as DecodingError {
throw ReadError.decodingError(error)
} catch {
throw ReadError.unknownError(error)
}
}
}
4 changes: 0 additions & 4 deletions Sources/SFSymbols/Internal/Plists/CategoriesPlist.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ struct CategoriesPlist: Decodable {
let container = try decoder.singleValueContainer()
categories = try container.decode([Category].self)
}

static func load(from bundle: Bundle) throws(CoreGlyphsPlistFileReader.ReadError) -> Self {
try CoreGlyphsPlistFileReader.readFile(named: "categories", in: bundle, decoding: Self.self)
}
}

extension CategoriesPlist: CustomDebugStringConvertible {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,6 @@ struct NameAvailabilityPlist: Decodable {
return symbols.arrayValue.filter { availableSymbolVersions.contains($0.symbolsVersion) }
}

static func load(from bundle: Bundle) throws(CoreGlyphsPlistFileReader.ReadError) -> Self {
try CoreGlyphsPlistFileReader.readFile(named: "name_availability", in: bundle, decoding: Self.self)
}
}

extension NameAvailabilityPlist: CustomDebugStringConvertible {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ struct SymbolCategoriesPlist: Decodable {
let container = try decoder.singleValueContainer()
symbols = try container.decode([String: [String]].self)
}

static func load(from bundle: Bundle) throws(CoreGlyphsPlistFileReader.ReadError) -> Self {
try CoreGlyphsPlistFileReader.readFile(named: "symbol_categories", in: bundle, decoding: Self.self)
}
}

extension SymbolCategoriesPlist: CustomDebugStringConvertible {
Expand Down
4 changes: 0 additions & 4 deletions Sources/SFSymbols/Internal/Plists/SymbolOrderPlist.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ struct SymbolOrderPlist: Decodable {
let container = try decoder.singleValueContainer()
names = try container.decode([String].self)
}

static func load(from bundle: Bundle) throws(CoreGlyphsPlistFileReader.ReadError) -> Self {
try CoreGlyphsPlistFileReader.readFile(named: "symbol_order", in: bundle, decoding: Self.self)
}
}

extension SymbolOrderPlist: CustomDebugStringConvertible {
Expand Down
4 changes: 0 additions & 4 deletions Sources/SFSymbols/Internal/Plists/SymbolSearchPlist.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ struct SymbolSearchPlist: Decodable {
let container = try decoder.singleValueContainer()
symbols = try container.decode([String: [String]].self)
}

static func load(from bundle: Bundle) throws(CoreGlyphsPlistFileReader.ReadError) -> Self {
try CoreGlyphsPlistFileReader.readFile(named: "symbol_search", in: bundle, decoding: Self.self)
}
}

extension SymbolSearchPlist: CustomDebugStringConvertible {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ private struct SymbolPickerRenderingModeEnvironmentKey: EnvironmentKey {
static let defaultValue: SymbolRenderingMode = .monochrome
}

// swiftlint:disable:next type_name
private struct SymbolColorRenderingModeSettingEnvironmentKey: EnvironmentKey {
static let defaultValue: SymbolColorRenderingModeSetting = .flat
}
Expand All @@ -20,14 +21,17 @@ private struct SymbolPickerVariableValueEnvironmentKey: EnvironmentKey {
static let defaultValue: Double = 1
}

// swiftlint:disable:next type_name
private struct SymbolPickerVariableValueModeEnvironmentKey: EnvironmentKey {
static let defaultValue: SymbolVariableValueModeSetting = .color
}

// swiftlint:disable:next type_name
private struct SymbolPickerPreviewUsesRenderingModeEnvironmentKey: EnvironmentKey {
static let defaultValue = false
}

// swiftlint:disable:next type_name
private struct SymbolPickerPreviewUsesVariableValueEnvironmentKey: EnvironmentKey {
static let defaultValue = false
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/SFSymbols/SFSymbolPicker+Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ public extension View {
func sfSymbolPickerForegroundStyle(_ primary: Color, _ secondary: Color) -> some View {
environment(
\.symbolColorsSetting,
SymbolColorsSetting(primaryColor: primary, secondaryColor: secondary)
SymbolColorsSetting(primaryColor: primary, secondaryColor: secondary)
)
}

@ViewBuilder
func sfSymbolPickerForegroundStyle(_ primary: Color, _ secondary: Color, _ tertiary: Color) -> some View {
environment(
\.symbolColorsSetting,
SymbolColorsSetting(primaryColor: primary, secondaryColor: secondary, tertiaryColor: tertiary)
SymbolColorsSetting(primaryColor: primary, secondaryColor: secondary, tertiaryColor: tertiary)
)
}

Expand Down
Loading