| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- import Foundation
- import LoopKit
- import LoopKitUI
- import Swinject
- protocol PluginManager {
- var availablePumpManagers: [PumpManagerDescriptor] { get }
- var availableCGMManagers: [CGMManagerDescriptor] { get }
- var availableServices: [ServiceDescriptor] { get }
- func getPumpManagerTypeByIdentifier(_ identifier: String) -> PumpManagerUI.Type?
- func getCGMManagerTypeByIdentifier(_ identifier: String) -> CGMManagerUI.Type?
- func getServiceTypeByIdentifier(_ identifier: String) -> ServiceUI.Type?
- }
- class BasePluginManager: Injectable, PluginManager {
- let pluginBundles: [Bundle]
- init(resolver: Resolver) {
- let pluginsURL: URL? = Bundle.main.privateFrameworksURL
- var bundles = [Bundle]()
- if let pluginsURL = pluginsURL {
- do {
- for pluginURL in try FileManager.default.contentsOfDirectory(at: pluginsURL, includingPropertiesForKeys: nil)
- .filter({ $0.path.hasSuffix(".framework") })
- {
- if let bundle = Bundle(url: pluginURL) {
- if let bname = bundle.object(forInfoDictionaryKey: "CFBundleName") as? String {
- debug(.deviceManager, "bundle name2:\(bname)")
- }
- if let bcgm = bundle.object(forInfoDictionaryKey: "com.loopkit.Loop.CGMManagerIdentifier") as? String {
- debug(.deviceManager, "bundle is CGM")
- }
- if bundle.isLoopPlugin {
- debug(.deviceManager, "Found loop plugin:\(pluginURL.absoluteString)")
- bundles.append(bundle)
- }
- }
- }
- } catch {
- debug(.deviceManager, "Error loading plugin: \(error)")
- }
- }
- pluginBundles = bundles
- injectServices(resolver)
- }
- func getPumpManagerTypeByIdentifier(_ identifier: String) -> PumpManagerUI.Type? {
- for bundle in pluginBundles {
- if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.pumpManagerIdentifier.rawValue) as? String,
- name == identifier
- {
- do {
- try bundle.loadAndReturnError()
- if let principalClass = bundle.principalClass as? NSObject.Type {
- if let plugin = principalClass.init() as? PumpManagerUIPlugin {
- return plugin.pumpManagerType
- } else {
- fatalError("PrincipalClass does not conform to PumpManagerUIPlugin")
- }
- } else {
- fatalError("PrincipalClass not found")
- }
- } catch {
- debug(.deviceManager, "Error loading plugin: \(error)")
- }
- }
- }
- return nil
- }
- var availablePumpManagers: [PumpManagerDescriptor] {
- pluginBundles.compactMap({ (bundle) -> PumpManagerDescriptor? in
- guard let title = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.pumpManagerDisplayName.rawValue) as? String,
- let identifier = bundle
- .object(forInfoDictionaryKey: LoopPluginBundleKey.pumpManagerIdentifier.rawValue) as? String
- else {
- return nil
- }
- return PumpManagerDescriptor(identifier: identifier, localizedTitle: title)
- })
- }
- func getCGMManagerTypeByIdentifier(_ identifier: String) -> CGMManagerUI.Type? {
- for bundle in pluginBundles {
- if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.cgmManagerIdentifier.rawValue) as? String,
- name == identifier
- {
- do {
- try bundle.loadAndReturnError()
- if let principalClass = bundle.principalClass as? NSObject.Type {
- if let plugin = principalClass.init() as? CGMManagerUIPlugin {
- return plugin.cgmManagerType
- } else {
- fatalError("PrincipalClass does not conform to CGMManagerUIPlugin")
- }
- } else {
- fatalError("PrincipalClass not found")
- }
- } catch {
- debug(.deviceManager, "Error loading plugin: \(error)")
- }
- }
- }
- return nil
- }
- var availableCGMManagers: [CGMManagerDescriptor] {
- pluginBundles.compactMap({ (bundle) -> CGMManagerDescriptor? in
- guard let title = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.cgmManagerDisplayName.rawValue) as? String,
- let identifier = bundle
- .object(forInfoDictionaryKey: LoopPluginBundleKey.cgmManagerIdentifier.rawValue) as? String
- else {
- return nil
- }
- return CGMManagerDescriptor(identifier: identifier, localizedTitle: title)
- })
- }
- func getServiceTypeByIdentifier(_ identifier: String) -> ServiceUI.Type? {
- for bundle in pluginBundles {
- if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.serviceIdentifier.rawValue) as? String,
- name == identifier
- {
- do {
- try bundle.loadAndReturnError()
- if let principalClass = bundle.principalClass as? NSObject.Type {
- if let plugin = principalClass.init() as? ServiceUIPlugin {
- return plugin.serviceType
- } else {
- fatalError("PrincipalClass does not conform to ServiceUIPlugin")
- }
- } else {
- fatalError("PrincipalClass not found")
- }
- } catch {
- debug(.deviceManager, "Error loading plugin: \(error)")
- }
- }
- }
- return nil
- }
- var availableServices: [ServiceDescriptor] {
- pluginBundles.compactMap({ (bundle) -> ServiceDescriptor? in
- guard let title = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.serviceDisplayName.rawValue) as? String,
- let identifier = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.serviceIdentifier.rawValue) as? String
- else {
- return nil
- }
- return ServiceDescriptor(identifier: identifier, localizedTitle: title)
- })
- }
- func getStatefulPluginTypeByIdentifier(_ identifier: String) -> StatefulPluggable.Type? {
- for bundle in pluginBundles {
- if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.statefulPluginIdentifier.rawValue) as? String,
- name == identifier
- {
- do {
- try bundle.loadAndReturnError()
- if let principalClass = bundle.principalClass as? NSObject.Type {
- if let plugin = principalClass.init() as? StatefulPlugin {
- return plugin.pluginType
- } else {
- fatalError("PrincipalClass does not conform to StatefulPlugin")
- }
- } else {
- fatalError("PrincipalClass not found")
- }
- } catch {
- debug(.deviceManager, "Error loading plugin: \(error)")
- }
- }
- }
- return nil
- }
- var availableStatefulPluginIdentifiers: [String] {
- pluginBundles.compactMap({ (bundle) -> String? in
- bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.statefulPluginIdentifier.rawValue) as? String
- })
- }
- func getOnboardingTypeByIdentifier(_ identifier: String) -> OnboardingUI.Type? {
- for bundle in pluginBundles {
- if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.onboardingIdentifier.rawValue) as? String,
- name == identifier
- {
- do {
- try bundle.loadAndReturnError()
- if let principalClass = bundle.principalClass as? NSObject.Type {
- if let plugin = principalClass.init() as? OnboardingUIPlugin {
- return plugin.onboardingType
- } else {
- fatalError("PrincipalClass does not conform to OnboardingUIPlugin")
- }
- } else {
- fatalError("PrincipalClass not found")
- }
- } catch {
- debug(.deviceManager, "Error loading plugin: \(error)")
- }
- }
- }
- return nil
- }
- var availableOnboardingIdentifiers: [String] {
- pluginBundles.compactMap({ (bundle) -> String? in
- bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.onboardingIdentifier.rawValue) as? String
- })
- }
- func getSupportUITypeByIdentifier(_ identifier: String) -> SupportUI.Type? {
- for bundle in pluginBundles {
- if let name = bundle.object(forInfoDictionaryKey: LoopPluginBundleKey.supportIdentifier.rawValue) as? String,
- name == identifier
- {
- do {
- try bundle.loadAndReturnError()
- if let principalClass = bundle.principalClass as? NSObject.Type {
- if let plugin = principalClass.init() as? SupportUIPlugin {
- return type(of: plugin.support)
- } else {
- fatalError("PrincipalClass does not conform to SupportUIPlugin")
- }
- } else {
- fatalError("PrincipalClass not found")
- }
- } catch {
- debug(.deviceManager, "Error loading plugin: \(error)")
- }
- }
- }
- return nil
- }
- }
- extension Bundle {
- var isPumpManagerPlugin: Bool {
- object(forInfoDictionaryKey: LoopPluginBundleKey.pumpManagerIdentifier.rawValue) as? String != nil }
- var isCGMManagerPlugin: Bool {
- object(forInfoDictionaryKey: LoopPluginBundleKey.cgmManagerIdentifier.rawValue) as? String != nil }
- var isStatefulPlugin: Bool {
- object(forInfoDictionaryKey: LoopPluginBundleKey.statefulPluginIdentifier.rawValue) as? String != nil }
- var isServicePlugin: Bool { object(forInfoDictionaryKey: LoopPluginBundleKey.serviceIdentifier.rawValue) as? String != nil }
- var isOnboardingPlugin: Bool {
- object(forInfoDictionaryKey: LoopPluginBundleKey.onboardingIdentifier.rawValue) as? String != nil }
- var isSupportPlugin: Bool { object(forInfoDictionaryKey: LoopPluginBundleKey.supportIdentifier.rawValue) as? String != nil }
- var isLoopPlugin: Bool {
- isPumpManagerPlugin || isCGMManagerPlugin || isStatefulPlugin || isServicePlugin || isOnboardingPlugin || isSupportPlugin
- }
- var isLoopExtension: Bool { object(forInfoDictionaryKey: LoopPluginBundleKey.extensionIdentifier.rawValue) as? String != nil }
- var isSimulator: Bool { object(forInfoDictionaryKey: LoopPluginBundleKey.pluginIsSimulator.rawValue) as? Bool == true }
- }
|