From 0f7029583c6899676111bd4953bf08d06933dcae Mon Sep 17 00:00:00 2001 From: Josh Palmer Date: Sun, 28 Dec 2025 16:39:40 +0100 Subject: [PATCH] macOS: load device models from bundle resources --- .../Sources/Clawdis/DeviceModelCatalog.swift | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/apps/macos/Sources/Clawdis/DeviceModelCatalog.swift b/apps/macos/Sources/Clawdis/DeviceModelCatalog.swift index 2e37088d3..3bda2e487 100644 --- a/apps/macos/Sources/Clawdis/DeviceModelCatalog.swift +++ b/apps/macos/Sources/Clawdis/DeviceModelCatalog.swift @@ -104,8 +104,8 @@ enum DeviceModelCatalog { } private static func loadMapping(resourceName: String) -> [String: String] { - guard let url = Bundle.module.url( - forResource: resourceName, + guard let url = self.resourceURL( + resourceName: resourceName, withExtension: "json", subdirectory: "DeviceModels") else { @@ -121,6 +121,37 @@ enum DeviceModelCatalog { } } + private static func resourceURL( + resourceName: String, + withExtension ext: String, + subdirectory: String + ) -> URL? { + let bundledSubdir = "Clawdis_Clawdis.bundle/\(subdirectory)" + let mainBundle = Bundle.main + + if let url = mainBundle.url(forResource: resourceName, withExtension: ext, subdirectory: bundledSubdir) + ?? mainBundle.url(forResource: resourceName, withExtension: ext, subdirectory: subdirectory) + { + return url + } + + let fallbackBases = [ + mainBundle.resourceURL, + mainBundle.bundleURL.appendingPathComponent("Contents/Resources"), + mainBundle.bundleURL.deletingLastPathComponent(), + ].compactMap { $0 } + + let fileName = "\(resourceName).\(ext)" + for base in fallbackBases { + let bundled = base.appendingPathComponent(bundledSubdir).appendingPathComponent(fileName) + if FileManager.default.fileExists(atPath: bundled.path) { return bundled } + let loose = base.appendingPathComponent(subdirectory).appendingPathComponent(fileName) + if FileManager.default.fileExists(atPath: loose.path) { return loose } + } + + return nil + } + private enum NameValue: Decodable { case string(String) case stringArray([String])