Skip to content

Commit 2d4e144

Browse files
Refactor settings manager
1 parent 27afe5c commit 2d4e144

19 files changed

+385
-293
lines changed

Sources/DolbyIORTSCore/Manager/SettingsManager.swift

-120
This file was deleted.

Sources/DolbyIORTSCore/Model/StreamSettings.swift

-43
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// SettingsManager.swift
3+
//
4+
5+
import Combine
6+
import DolbyIORTSCore
7+
import Foundation
8+
9+
public enum SettingsMode: Equatable {
10+
case global
11+
case stream(streamName: String, accountID: String)
12+
13+
// Key against which `global settings` is stored in the `SettingsDictionary`
14+
fileprivate static let keyGlobalStreamSettings = "global"
15+
16+
fileprivate var storageKey: String {
17+
switch self {
18+
case .global:
19+
return Self.keyGlobalStreamSettings
20+
case let .stream(streamName: streamName, accountID: accountID):
21+
return "\(streamName)-\(accountID)"
22+
}
23+
}
24+
}
25+
26+
final public class SettingsManager {
27+
28+
private enum Constants {
29+
static let keyStreamSettingsDictionary = "stream-settings"
30+
}
31+
32+
private typealias SettingsDictionary = [String: StreamSettings]
33+
34+
@UserDefaultsCodableBacked(key: Constants.keyStreamSettingsDictionary, defaultValue: [SettingsMode.keyGlobalStreamSettings: StreamSettings.default])
35+
private var settingsDictionary: SettingsDictionary
36+
37+
public static let shared = SettingsManager()
38+
39+
public func publisher(for mode: SettingsMode) -> AnyPublisher<StreamSettings, Never> {
40+
$settingsDictionary
41+
.map {
42+
$0[mode.storageKey] ?? StreamSettings.default
43+
}
44+
.removeDuplicates()
45+
.eraseToAnyPublisher()
46+
}
47+
48+
public func removeSettings(for mode: SettingsMode) {
49+
settingsDictionary.removeValue(forKey: mode.storageKey)
50+
}
51+
52+
func update(settings: StreamSettings, for mode: SettingsMode) {
53+
settingsDictionary[mode.storageKey] = settings
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// StreamSettings.swift
3+
//
4+
5+
import Foundation
6+
import SwiftUI
7+
8+
public struct StreamSettings: Codable, Equatable {
9+
10+
public enum MultiviewLayout: String, Codable, Equatable {
11+
case list
12+
case single
13+
case grid
14+
}
15+
16+
public enum StreamSortOrder: String, Codable, Equatable {
17+
case connectionOrder
18+
case alphaNumeric
19+
}
20+
21+
public enum AudioSelection: Codable, Equatable {
22+
case firstSource
23+
case followVideo
24+
case mainSource
25+
case source(sourceId: String)
26+
}
27+
28+
public var showSourceLabels: Bool
29+
public var multiviewLayout: MultiviewLayout
30+
public var streamSortOrder: StreamSortOrder
31+
public var audioSelection: AudioSelection
32+
public var audioSources: [String] = []
33+
}
34+
35+
extension StreamSettings {
36+
public static let `default`: StreamSettings = .init(
37+
showSourceLabels: true,
38+
multiviewLayout: .list,
39+
streamSortOrder: .connectionOrder,
40+
audioSelection: .firstSource
41+
)
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// UserDefaults+Codable.swift
3+
//
4+
5+
import Foundation
6+
7+
extension UserDefaults {
8+
func registerCodableDefaults<T: Codable>(_ defaults: T, forKey defaultName: String) {
9+
let encoded = try? JSONEncoder().encode(defaults)
10+
register(defaults: [defaultName: (encoded ?? nil) as Any])
11+
}
12+
13+
func setCodableValue<T: Codable>(_ data: T?, forKey defaultName: String) {
14+
let encoded = try? JSONEncoder().encode(data)
15+
set(encoded, forKey: defaultName)
16+
}
17+
18+
func getCodableValue<T: Codable>(dataType: T.Type, key: String) -> T? {
19+
guard let userDefaultData = data(forKey: key) else {
20+
return nil
21+
}
22+
return try? JSONDecoder().decode(T.self, from: userDefaultData)
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//
2+
// UserDefaultsBacked.swift
3+
//
4+
5+
import Combine
6+
import Foundation
7+
8+
@propertyWrapper
9+
struct UserDefaultsBacked<T> {
10+
let key: String
11+
let defaultValue: T
12+
let userDefaults: UserDefaults
13+
14+
private let publisher: CurrentValueSubject<T, Never>
15+
16+
init(key: String, defaultValue: T, container: UserDefaults = .standard) {
17+
self.key = key
18+
self.defaultValue = defaultValue
19+
self.userDefaults = container
20+
self.publisher = .init(defaultValue)
21+
22+
// Register default value
23+
userDefaults.register(defaults: [key: defaultValue])
24+
25+
defer {
26+
self.publisher.send(wrappedValue)
27+
}
28+
}
29+
30+
var wrappedValue: T {
31+
get {
32+
return userDefaults.object(forKey: key) as? T ?? defaultValue
33+
}
34+
set {
35+
if let optional = newValue as? AnyOptional, optional.isNil {
36+
userDefaults.removeObject(forKey: key)
37+
} else {
38+
userDefaults.set(newValue, forKey: key)
39+
}
40+
publisher.send(newValue)
41+
}
42+
}
43+
44+
var projectedValue: AnyPublisher<T, Never> {
45+
return publisher.eraseToAnyPublisher()
46+
}
47+
}
48+
49+
@propertyWrapper
50+
struct UserDefaultsCodableBacked<T: Codable> {
51+
let key: String
52+
let defaultValue: T
53+
let userDefaults: UserDefaults
54+
55+
private let publisher: CurrentValueSubject<T, Never>
56+
57+
init(key: String, defaultValue: T, container: UserDefaults = .standard) {
58+
self.key = key
59+
self.defaultValue = defaultValue
60+
self.userDefaults = container
61+
self.publisher = .init(defaultValue)
62+
63+
// Register default value
64+
userDefaults.registerCodableDefaults(defaultValue, forKey: key)
65+
66+
defer {
67+
self.publisher.send(wrappedValue)
68+
}
69+
}
70+
71+
var wrappedValue: T {
72+
get {
73+
return userDefaults.getCodableValue(dataType: T.self, key: key) ?? defaultValue
74+
}
75+
set {
76+
if let optional = newValue as? AnyOptional, optional.isNil {
77+
userDefaults.removeObject(forKey: key)
78+
} else {
79+
userDefaults.setCodableValue(newValue, forKey: key)
80+
}
81+
publisher.send(newValue)
82+
}
83+
}
84+
85+
var projectedValue: AnyPublisher<T, Never> {
86+
return publisher.eraseToAnyPublisher()
87+
}
88+
}
89+
90+
protocol AnyOptional {
91+
var isNil: Bool { get }
92+
}
93+
94+
extension Optional: AnyOptional {
95+
var isNil: Bool { self == nil }
96+
}
97+

0 commit comments

Comments
 (0)