-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
robot-divkit
committed
Oct 23, 2023
1 parent
796c8db
commit 7ee2177
Showing
21 changed files
with
498 additions
and
93 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
public enum DivKitInfo { | ||
public static let version = "28.6.0" | ||
public static let version = "28.7.0" | ||
} |
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import Foundation | ||
|
||
import BasePublic | ||
|
||
public enum DivVariableNameTag {} | ||
public typealias DivVariableName = Tagged<DivVariableNameTag, String> | ||
|
||
public typealias DivVariables = [DivVariableName: DivVariableValue] |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import Foundation | ||
|
||
import BasePublic | ||
|
||
/// Stores variables. | ||
/// Use ``DivVariableStorage`` to provide external variables into `DivKit`. | ||
/// You can combine storages to have variables with different scopes. | ||
public final class DivVariableStorage { | ||
public struct ChangeEvent { | ||
public let changedVariables: Set<DivVariableName> | ||
public let oldValues: DivVariables | ||
} | ||
|
||
private let outerStorage: DivVariableStorage? | ||
|
||
private var values = DivVariables() | ||
private let lock = RWLock() | ||
|
||
var allValues: DivVariables { | ||
(outerStorage?.allValues ?? [:]) + values | ||
} | ||
|
||
private let changeEventsPipe = SignalPipe<ChangeEvent>() | ||
let changeEvents: Signal<ChangeEvent> | ||
|
||
/// Initializes a new instance of ``DivVariableStorage``. | ||
/// | ||
/// - Parameters: | ||
/// - outerStorage: Storage that provides outer scope variables. Outer scope variables are accessible via | ||
/// current storage (see `getValue` and `update` methods). Outer scope variables can be shadowed. | ||
public init(outerStorage: DivVariableStorage? = nil) { | ||
self.outerStorage = outerStorage | ||
|
||
if let outerStorage = outerStorage { | ||
weak var weakSelf: DivVariableStorage? | ||
let outerStorageEvents: Signal<ChangeEvent> = outerStorage.changeEvents.compactMap { | ||
guard let self = weakSelf else { | ||
return nil | ||
} | ||
return ChangeEvent( | ||
changedVariables: $0.changedVariables, | ||
oldValues: $0.oldValues + self.values | ||
) | ||
} | ||
changeEvents = Signal.merge(outerStorageEvents, changeEventsPipe.signal) | ||
weakSelf = self | ||
} else { | ||
changeEvents = changeEventsPipe.signal | ||
} | ||
} | ||
|
||
/// Gets variable value. | ||
/// If variable with the given name not exists gets value from the outer storage. | ||
public func getValue<T>(_ name: DivVariableName) -> T? { | ||
let variable = lock.read { | ||
values[name] | ||
} | ||
if let variable = variable { | ||
return variable.typedValue() | ||
} | ||
return outerStorage?.getValue(name) | ||
} | ||
|
||
/// Puts variable into the storage. | ||
/// Updates variable value if a variable with the given name already exists. | ||
/// Does not affect outer storage. | ||
public func put(name: DivVariableName, value: DivVariableValue) { | ||
lock.write { | ||
let oldValues = allValues | ||
values[name] = value | ||
notify(ChangeEvent(changedVariables: [name], oldValues: oldValues)) | ||
} | ||
} | ||
|
||
/// Puts multiple variables into the storage. | ||
/// Updates values of existing variables. | ||
/// Does not affect outer storage. | ||
public func put( | ||
_ variables: DivVariables, | ||
notifyObservers: Bool = true | ||
) { | ||
lock.write { | ||
let oldValues = allValues | ||
values = values + variables | ||
if notifyObservers { | ||
notify(ChangeEvent(changedVariables: Set(variables.map { $0.key }), oldValues: oldValues)) | ||
} | ||
} | ||
} | ||
|
||
/// Replaces all variables with new ones in single transaction. | ||
/// Does not affect outer storage. | ||
public func replaceAll( | ||
_ variables: DivVariables, | ||
notifyObservers: Bool = true | ||
) { | ||
lock.write { | ||
let oldValues = allValues | ||
values = variables | ||
if notifyObservers { | ||
let changedVariables = makeChangedVariables(old: oldValues, new: variables) | ||
if changedVariables.isEmpty { | ||
return | ||
} | ||
notify(ChangeEvent(changedVariables: changedVariables, oldValues: oldValues)) | ||
} | ||
} | ||
} | ||
|
||
/// Updates variable value. | ||
/// If variable with the given name not exists updates value in the outer storage. | ||
public func update(name: DivVariableName, value: DivVariableValue) { | ||
update(name: name, valueFactory: { _ in value }) | ||
} | ||
|
||
/// Clears the storage. | ||
/// Does not affect outer storage. | ||
public func clear() { | ||
lock.write { | ||
values = DivVariables() | ||
} | ||
} | ||
|
||
func update( | ||
name: DivVariableName, | ||
valueFactory: (DivVariableValue) -> DivVariableValue? | ||
) { | ||
let hasLocalValue = lock.write { | ||
guard let oldValue = values[name] else { | ||
return false | ||
} | ||
|
||
if let value = valueFactory(oldValue), value != oldValue { | ||
let oldValues = allValues | ||
values[name] = value | ||
notify(ChangeEvent(changedVariables: [name], oldValues: oldValues)) | ||
} | ||
|
||
return true | ||
} | ||
|
||
if hasLocalValue { | ||
return | ||
} | ||
|
||
if let outerStorage = outerStorage { | ||
outerStorage.update(name: name, valueFactory: valueFactory) | ||
} else { | ||
DivKitLogger.error("Variable is not declared: \(name)") | ||
} | ||
} | ||
|
||
private func notify(_ event: ChangeEvent) { | ||
onMainThread { [weak self] in | ||
self?.changeEventsPipe.send(event) | ||
} | ||
} | ||
} | ||
|
||
func makeChangedVariables( | ||
old: DivVariables, | ||
new: DivVariables | ||
) -> Set<DivVariableName> { | ||
var result = Set<DivVariableName>() | ||
for (name, value) in old { | ||
if new[name] != value { | ||
result.insert(name) | ||
} | ||
} | ||
return result.union(Set(new.keys).subtracting(Set(old.keys))) | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import Foundation | ||
|
||
import CommonCorePublic | ||
import BasePublic | ||
|
||
@frozen | ||
public enum DivVariableValue: Hashable { | ||
|
Oops, something went wrong.