Observe generic values with Combine - swift

Take this case of a type constrained class Parameter, wrapping a value of given type.
Parameter conforms to the AnyParameter so it can be passed anywhere in the app without knowing the type. Parameters can be displayed in value cells AnyValueCell
How would you do to observe the change without having to know the underlying value type? It would be nice to avoid the code repetition in the value cell updateObserver function
Could AnyPublisher can be used here and how?
import UIKit
import Combine
print("Hello Playground")
protocol AnyParameter {
var anyValue: Any { get }
func set(value: Any)
}
protocol ParameterProtocol: AnyParameter {
associatedtype ValueType
var value: ValueType { get }
func set(value: ValueType)
}
public class Parameter<T>: ParameterProtocol {
typealias ValueType = T
#Published var value: T
var anyValue: Any { value }
init(value: T) {
self.value = value
}
func set(value: Any) {
guard let value = value as? T else { return }
set(value: value)
}
func set(value: T) {
self.value = value
}
}
public class AnyValueCell {
var parameter: AnyParameter {
didSet {
updateObserver()
}
}
var observer: AnyCancellable?
init(parameter: AnyParameter) {
self.parameter = parameter
updateObserver()
}
func updateObserver() {
observer?.cancel()
// This is the point of the question - How to make this generic?
// ---->
if let p = parameter as? Parameter<Int> {
observer = p.$value.sink() { value in
print("Update Cell -> \(value)")
}
return
}
if let p = parameter as? Parameter<Double> {
observer = p.$value.sink() { value in
print("Update Cell -> \(value)")
}
return
}
if let p = parameter as? Parameter<Bool> {
observer = p.$value.sink() { value in
print("Update Cell -> \(value)")
}
return
}
// <----
print("Wrong param type")
}
}
let intParam = Parameter<Int>(value: 42)
let doubleParam = Parameter<Double>(value: 3.14)
let boolParam = Parameter<Bool>(value: false)
var params: [AnyParameter] = [intParam, doubleParam, boolParam]
print ("--> Init Cells")
let cells: [AnyValueCell] = params.map { AnyValueCell(parameter: $0) }
print ("--> Change values")
intParam.set(value: 21)
doubleParam.set(value: 1.618)
boolParam.set(value: true)
Result, as expected:
Hello Playground
--> Init Cells
Update Cell -> 42
Update Cell -> 3.14
Update Cell -> false
--> Change values
Update Cell -> 21
Update Cell -> 1.618
Update Cell -> true

Add an anyValuePublisher property. You can (and maybe should) add it to AnyParameter, or you can define it in a separate protocol like this:
protocol AnyParameterPublishing: AnyParameter {
var anyValuePublisher: AnyPublisher<Any, Never> { get }
}
extension Parameter: AnyParameterPublishing {
var anyValuePublisher: AnyPublisher<Any, Never> {
return $value.map { $0 as Any }.eraseToAnyPublisher()
}
}
Then you can use it like this:
class AnyValueCell {
// ...
func updateObserver() {
guard let publishing = (parameter as? AnyParameterPublishing) else {
print("Wrong param type")
return
}
observer = publishing.anyValuePublisher
.sink { print("Update Cell -> \($0)") }
}
}

Related

How to declare generic method with parameter of protocol array or protocol?

The result I want is:
The generic type can be an array or a separate protocol,
The return value is also judged based on the generic type
This code is not compiled:
protocol HandyJSON {
}
extension HandyJSON {
static func deserialize(from dic: [String:Any]?) -> HandyJSON? {
//return HandyJSON?
}
}
extension Array where Element: HandyJSON {
static func deserialize(from arr: [Any]?) -> [Element?]? {
//return [HandyJSON?]
}
}
class Person : HandyJSON {
var name : String?
var age : Int?
}
struct Requests {
// T can be a protocal, and [object]
static func post<T>(decodeType:T.Type) where T : HandyJSON, T : [HandyJSON] -> T {
//send reques...
//let data = resp.data
var respData : Any?
// if let data = respData as? [String : Any] {
//
// }
//
// if let data = respData as? [Any] {
//
// }
//T is HandyJSON
if decodeType is HandyJSON.Type {
return T.deserialize(from: (respData as? [String : Any]))
//or? -> HandyJSON
//return decodeType.deserialize(from: NSDictionary?)
}
//T is [HandyJSON]
if decodeType is [HandyJSON].Type {
return T.deserialize(from: (respData as? [Any]))
//or?
//return decodeType.deserialize(from: [Any]?)
}
}
}
Error: Type 'T' constrained to non-protocol, non-class type '([HandyJSON]) -> T'
Whether generic can be defined as Array, Some object Array
If these methods are implemented, I can use it like this
class vc : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let somebody : Person? = Requests.post(decodeType:Person.Type) // Person Object
let arr : [Person?]? = Requests.post(decodeType:[Person].Type)
}
}
Or describe like this
T can be a object or array that implement HandyJSON protocol
func post<T:HandyJSON | [HandyJSON]>(arg:T.Type) -> T {
//
}

Init a FloatingPoint from a string

I am trying to convert a 2d array of Strings into my custom generic type Matrix:
func convert(_ arr: [[String]]) -> Matrix<Element> {
var m: Matrix<Element> = Matrix()
for row in arr {
var v: [Element] = []
for e in row {
let convE: Element = Element(string: e) // right here I'd like to implement something like that: Element(string: e)
v.append(convE)
}
m.vectors.append(Vector(v))
}
return m
}
The Matrix.Element does conform to the FloatingPoint protocol. Please tell me if you wish to see the Matrix struct but I think I haven't implemented anything that's important for this question other than that the generic Element type of Matrix does conform to the FloatingPoint protocol.
My problem is I want Element to be something like Float, Double... (any of the FloatingPoint types) but how can I initialize a FloatingPoint from a string? I tried:
extension FloatingPoint {
init(string: String) {
self.init(Int(string)!)
}
}
which obviously only works for strings like "1", "2"... and not "1.2", "3.541" and so on which I want.
Edit:(#Leo Dabus)
protocol DArray: Sequence {
associatedtype Component: FloatingPoint
}
extension DArray {
static func * <T: DArray>(lhs: Self, rhs: T) -> Vector<Component> {
let v = lhs as? Vector<Component> ?? rhs as! Vector<Component>
let m = lhs as? Matrix<Component> ?? rhs as! Matrix<Component>
return Vector(m.map { zip(v, $0).map(*).reduce(0, +) })
}
static func / <T: DArray>(lhs: Self, rhs: T) -> Vector<Component> {
let v = lhs as? Vector<Component> ?? lhs as! Vector<Component>
let m = lhs as? Matrix<Component> ?? lhs as! Matrix<Component>
return Vector(m.map { zip(v, $0).map(/).reduce(0, +) })
}
}
struct Vector<Component: FloatingPoint>: DArray {
var components: [Component]
var count: Int {
return components.count
}
init(_ Components: [Component] = []) {
self.components = Components
}
subscript(i: Int) -> Component {
get {
return components[i]
} set {
components[i] = newValue
}
}
static func + (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(+))
}
static func - (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(-))
}
static func * (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(*))
}
static func / (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(/))
}
func empty(of length: Int) -> Self {
return Vector(Array(repeating: 0, count: length))
}
}
struct Matrix<Component: FloatingPoint>: DArray {
var vectors: [Vector<Component>]
var nRows: Int {
return vectors.count
}
var nColumns: Int {
guard !vectors.isEmpty else { return 0 }
return vectors[0].count
}
var count: Int {
return vectors.count
}
init(_ vectors: [Vector<Component>] = []) {
self.vectors = vectors
}
subscript(r: Int) -> Vector<Component> {
get {
return vectors[r]
}
set {
vectors[r] = newValue
}
}
subscript(r: Int, c: Int) -> Component {
get {
return vectors[r][c]
}
set {
vectors[r][c] = newValue
}
}
}
Additionally I have my two structs conform to the Sequence protocol.
(Note: I am the OP)
What I came up with now is:
extension FloatingPoint {
public init?(string: String) {
if Self.self == Double.self {
self = Double(string) as! Self
} else if Self.self == Float.self {
self = Float(string) as! Self
} else if Self.self == Float80.self {
self = Float80(string) as! Self
} else {
return nil
}
}
}
It works for my use case but I was wondering whether it is a good way of achieving what I am looking for. So I'd be happy for someone to evaluate my solution. (#Leo Dabus)
You can extend FloatingPoint protocol and constrain the generic type to LosslessStringConvertible:
extension StringProtocol {
func floatingPoint<T: FloatingPoint>() -> T? where T: LosslessStringConvertible {
T(String(self))
}
}
Note that CGFloat does NOT conform to LosslessStringConvertible so you would need to implement a custom String initializer:
extension CGFloat: LosslessStringConvertible {
private static let formatter = NumberFormatter()
public init?(_ description: String) {
guard let number = CGFloat.formatter.number(from: description) as? CGFloat else { return nil }
self = number
}
}
let double: Double? = "2.7".floatingPoint() // 2.7
let float: Float? = "2.7".floatingPoint() // 2.7
let float80: Float80? = "2.7".floatingPoint() // 2.7
let cgfloat: CGFloat? = "2.7".floatingPoint() // 2.7
There is already an initializer for FloatingPoint types but to make your code work you need to conform your Matrix Component to LosslessStringConvertible.
Can you try something like this:
extension FloatingPoint where Self == Double {
init(string: String) {
self.init(Double(string)!)
}
}
extension FloatingPoint where Self == Float {
init(string: String) {
self.init(Float(string)!)
}
}
this should work as well
extension FloatingPoint {
init(string: String) {
self.init(Self(string)!)
}
}

Custom Property Wrapper that Updates View Swift

Xcode 11.3, Swift 5.1.3
I am trying currently to create a custom property wrapper that allows me to link variables to a Firebase database. When doing this, to make it update the view, I at first tried to use the #ObservedObject #Bar var foo = []. But I get an error that multiple property wrappers are not supported. Next thing I tried to do, which would honestly be ideal, was try to make my custom property wrapper update the view itself upon being changed, just like #State and #ObservedObject. This both avoids needing to go down two layers to access the underlying values and avoid the use of nesting property wrappers. To do this, I checked the SwiftUI documentation and found out that they both implement the DynamicProperty protocol. I tried to use this too but failed because I need to be able to update the view (call update()) from within my Firebase database observers, which I cannot do since .update() is mutating.
Here is my current attempt at this:
import SwiftUI
import Firebase
import CodableFirebase
import Combine
#propertyWrapper
final class DatabaseBackedArray<Element>: ObservableObject where Element: Codable & Identifiable {
typealias ObserverHandle = UInt
typealias Action = RealtimeDatabase.Action
typealias Event = RealtimeDatabase.Event
private(set) var reference: DatabaseReference
private var currentValue: [Element]
private var childAddedObserverHandle: ObserverHandle?
private var childChangedObserverHandle: ObserverHandle?
private var childRemovedObserverHandle: ObserverHandle?
private var childAddedActions: [Action<[Element]>] = []
private var childChangedActions: [Action<[Element]>] = []
private var childRemovedActions: [Action<[Element]>] = []
init(wrappedValue: [Element], _ path: KeyPath<RealtimeDatabase, RealtimeDatabase>, events: Event = .all,
actions: [Action<[Element]>] = []) {
currentValue = wrappedValue
reference = RealtimeDatabase()[keyPath: path].reference
for action in actions {
if action.event.contains(.childAdded) {
childAddedActions.append(action)
}
if action.event.contains(.childChanged) {
childChangedActions.append(action)
}
if action.event.contains(.childRemoved) {
childRemovedActions.append(action)
}
}
if events.contains(.childAdded) {
childAddedObserverHandle = reference.observe(.childAdded) { snapshot in
guard let value = snapshot.value, let decodedValue = try? FirebaseDecoder().decode(Element.self, from: value) else {
fatalError("Could not decode value from Firebase.")
}
self.objectWillChange.send()
self.currentValue.append(decodedValue)
self.childAddedActions.forEach { $0.action(&self.currentValue) }
}
}
if events.contains(.childChanged) {
childChangedObserverHandle = reference.observe(.childChanged) { snapshot in
guard let value = snapshot.value, let decodedValue = try? FirebaseDecoder().decode(Element.self, from: value) else {
fatalError("Could not decode value from Firebase.")
}
guard let changeIndex = self.currentValue.firstIndex(where: { $0.id == decodedValue.id }) else {
return
}
self.objectWillChange.send()
self.currentValue[changeIndex] = decodedValue
self.childChangedActions.forEach { $0.action(&self.currentValue) }
}
}
if events.contains(.childRemoved) {
childRemovedObserverHandle = reference.observe(.childRemoved) { snapshot in
guard let value = snapshot.value, let decodedValue = try? FirebaseDecoder().decode(Element.self, from: value) else {
fatalError("Could not decode value from Firebase.")
}
self.objectWillChange.send()
self.currentValue.removeAll { $0.id == decodedValue.id }
self.childRemovedActions.forEach { $0.action(&self.currentValue) }
}
}
}
private func setValue(to value: [Element]) {
guard let encodedValue = try? FirebaseEncoder().encode(currentValue) else {
fatalError("Could not encode value to Firebase.")
}
reference.setValue(encodedValue)
}
var wrappedValue: [Element] {
get {
return currentValue
}
set {
self.objectWillChange.send()
setValue(to: newValue)
}
}
var projectedValue: Binding<[Element]> {
return Binding(get: {
return self.wrappedValue
}) { newValue in
self.wrappedValue = newValue
}
}
var hasActiveObserver: Bool {
return childAddedObserverHandle != nil || childChangedObserverHandle != nil || childRemovedObserverHandle != nil
}
var hasChildAddedObserver: Bool {
return childAddedObserverHandle != nil
}
var hasChildChangedObserver: Bool {
return childChangedObserverHandle != nil
}
var hasChildRemovedObserver: Bool {
return childRemovedObserverHandle != nil
}
func connectObservers(for event: Event) {
if event.contains(.childAdded) && childAddedObserverHandle == nil {
childAddedObserverHandle = reference.observe(.childAdded) { snapshot in
guard let value = snapshot.value, let decodedValue = try? FirebaseDecoder().decode(Element.self, from: value) else {
fatalError("Could not decode value from Firebase.")
}
self.objectWillChange.send()
self.currentValue.append(decodedValue)
self.childAddedActions.forEach { $0.action(&self.currentValue) }
}
}
if event.contains(.childChanged) && childChangedObserverHandle == nil {
childChangedObserverHandle = reference.observe(.childChanged) { snapshot in
guard let value = snapshot.value, let decodedValue = try? FirebaseDecoder().decode(Element.self, from: value) else {
fatalError("Could not decode value from Firebase.")
}
guard let changeIndex = self.currentValue.firstIndex(where: { $0.id == decodedValue.id }) else {
return
}
self.objectWillChange.send()
self.currentValue[changeIndex] = decodedValue
self.childChangedActions.forEach { $0.action(&self.currentValue) }
}
}
if event.contains(.childRemoved) && childRemovedObserverHandle == nil {
childRemovedObserverHandle = reference.observe(.childRemoved) { snapshot in
guard let value = snapshot.value, let decodedValue = try? FirebaseDecoder().decode(Element.self, from: value) else {
fatalError("Could not decode value from Firebase.")
}
self.objectWillChange.send()
self.currentValue.removeAll { $0.id == decodedValue.id }
self.childRemovedActions.forEach { $0.action(&self.currentValue) }
}
}
}
func removeObserver(for event: Event) {
if event.contains(.childAdded), let handle = childAddedObserverHandle {
reference.removeObserver(withHandle: handle)
self.childAddedObserverHandle = nil
}
if event.contains(.childChanged), let handle = childChangedObserverHandle {
reference.removeObserver(withHandle: handle)
self.childChangedObserverHandle = nil
}
if event.contains(.childRemoved), let handle = childRemovedObserverHandle {
reference.removeObserver(withHandle: handle)
self.childRemovedObserverHandle = nil
}
}
func removeAction(_ action: Action<[Element]>) {
if action.event.contains(.childAdded) {
childAddedActions.removeAll { $0.id == action.id }
}
if action.event.contains(.childChanged) {
childChangedActions.removeAll { $0.id == action.id }
}
if action.event.contains(.childRemoved) {
childRemovedActions.removeAll { $0.id == action.id }
}
}
func removeAllActions(for event: Event) {
if event.contains(.childAdded) {
childAddedActions = []
}
if event.contains(.childChanged) {
childChangedActions = []
}
if event.contains(.childRemoved) {
childRemovedActions = []
}
}
}
struct School: Codable, Identifiable {
/// The unique id of the school.
var id: String
/// The name of the school.
var name: String
/// The city of the school.
var city: String
/// The province of the school.
var province: String
/// Email domains for student emails from the school.
var domains: [String]
}
#dynamicMemberLookup
struct RealtimeDatabase {
private var path: [String]
var reference: DatabaseReference {
var ref = Database.database().reference()
for component in path {
ref = ref.child(component)
}
return ref
}
init(previous: Self? = nil, child: String? = nil) {
if let previous = previous {
path = previous.path
} else {
path = []
}
if let child = child {
path.append(child)
}
}
static subscript(dynamicMember member: String) -> Self {
return Self(child: member)
}
subscript(dynamicMember member: String) -> Self {
return Self(child: member)
}
static subscript(dynamicMember keyPath: KeyPath<Self, Self>) -> Self {
return Self()[keyPath: keyPath]
}
static let reference = Database.database().reference()
struct Event: OptionSet, Hashable {
let rawValue: UInt
static let childAdded = Event(rawValue: 1 << 0)
static let childChanged = Event(rawValue: 1 << 1)
static let childRemoved = Event(rawValue: 1 << 2)
static let all: Event = [.childAdded, .childChanged, .childRemoved]
static let constructive: Event = [.childAdded, .childChanged]
static let destructive: Event = .childRemoved
}
struct Action<Value>: Identifiable {
let id = UUID()
let event: Event
let action: (inout Value) -> Void
private init(on event: Event, perform action: #escaping (inout Value) -> Void) {
self.event = event
self.action = action
}
static func on<Value>(_ event: RealtimeDatabase.Event, perform action: #escaping (inout Value) -> Void) -> Action<Value> {
return Action<Value>(on: event, perform: action)
}
}
}
Usage example:
struct ContentView: View {
#DatabaseBackedArray(\.schools, events: .all, actions: [.on(.constructive) { $0.sort { $0.name < $1.name } }])
var schools: [School] = []
var body: some View {
Text("School: ").bold() +
Text(schools.isEmpty ? "Loading..." : schools.first!.name)
}
}
When I try to use this though, the view never updates with the value from Firebase even though I am positive that the .childAdded observer is being called.
One of my attempts at fixing this was to store all of these variables in a singleton that itself conforms to ObservableObject. This solution is also ideal as it allows the variables being observed to be shared throughout my application, preventing multiples instances of the same date and allowing for a single source of truth. Unfortunately, this too did not update the view with the fetched value of currentValue.
class Session: ObservableObject {
#DatabaseBackedArray(\.schools, events: .all, actions: [.on(.constructive) { $0.sort { $0.name < $1.name } }])
var schools: [School] = []
private init() {
//Send `objectWillChange` when `schools` property changes
_schools.objectWillChange.sink {
self.objectWillChange.send()
}
}
static let current = Session()
}
struct ContentView: View {
#ObservedObject
var session = Session.current
var body: some View {
Text("School: ").bold() +
Text(session.schools.isEmpty ? "Loading..." : session.schools.first!.name)
}
}
Is there any way to make a custom property wrapper that also updates a view in SwiftUI?
Making use of the DynamicProperty protocol we can easily trigger view updates by making use of SwiftUI's existing property wrappers. (DynamicProperty tells SwiftUI to look for these within our type)
#propertyWrapper
struct OurPropertyWrapper: DynamicProperty {
// A state object that we notify of updates
#StateObject private var updater = Updater()
var wrappedValue: T {
get {
// Your getter code here
}
nonmutating set {
// Tell SwiftUI we're going to change something
updater.notifyUpdate()
// Your setter code here
}
}
class Updater: ObservableObject {
func notifyUpdate() {
objectWillChange.send()
}
}
}
The solution to this is to make a minor tweak to the solution of the singleton. Credits to #user1046037 for pointing this out to me. The problem with the singleton fix mentioned in the original post, is that it does not retain the canceller for the sink in the initializer. Here is the correct code:
class Session: ObservableObject {
#DatabaseBackedArray(\.schools, events: .all, actions: [.on(.constructive) { $0.sort { $0.name < $1.name } }])
var schools: [School] = []
private var cancellers = [AnyCancellable]()
private init() {
_schools.objectWillChange.sink {
self.objectWillChange.send()
}.assign(to: &cancellers)
}
static let current = Session()
}

How to overcome the error of "Generic parameter 'T' is not used in function signature"?

I'm trying to convert the following to be generic.
extension RLMOrganization: DataProvider {
func getLastSyncToken() -> String {
let lastUpdated: RLMOrganization? = self.findAll(sortedBy: "syncToken").last
if let syncToken = lastUpdated?.syncToken {
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
}
And have tried this:
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll<T: Object>(sortedBy key: String) -> [T]
}
extension DataProvider {
func findAll<T: Object>(sortedBy key: String) -> [T] {
let database = self.getDatabase()
if let allObjects = database?.objects(T.self) {
let results = allObjects.sorted(byKeyPath: key, ascending: true)
return Array(results)
}
return []
}
func getLastSyncToken<T: Object>() -> String {
let lastUpdated = self.findAll(sortedBy: "syncToken").last as? T
if let value = lastUpdated?.value(forKey: "syncToken") { // get value from object by string name
let syncToken = value as! String
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
...
But can't seem to overcome the error of:
Generic parameter 'T' is not used in function signature
I would think the compiler has everything it needs to determine type usage.
Below works for me, I don't know how findAll is defined but the problem is the reference to self as I see it so you need to define T there using associatedtype.
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll(sortedBy: String) -> T?
}

Insertion-Order Dictionary (like Java's LinkedHashMap) in Swift?

Is there a standard swift class that is a Dictionary, but keeps keys in insertion-order like Java's LinkedHashMap? If not, how would one be implemented?
Didn't know of one and it was an interesting problem to solve (already put it in my standard library of stuff) Mostly it's just a matter of maintaining a dictionary and an array of the keys side-by-side. But standard operations like for (key, value) in od and for key in od.keys will iterate in insertion order rather than a semi random fashion.
// OrderedDictionary behaves like a Dictionary except that it maintains
// the insertion order of the keys, so iteration order matches insertion
// order.
struct OrderedDictionary<KeyType:Hashable, ValueType> {
private var _dictionary:Dictionary<KeyType, ValueType>
private var _keys:Array<KeyType>
init() {
_dictionary = [:]
_keys = []
}
init(minimumCapacity:Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity)
_keys = Array<KeyType>()
}
init(_ dictionary:Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = map(dictionary.keys) { $0 }
}
subscript(key:KeyType) -> ValueType? {
get {
return _dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key)
}
else {
self.updateValue(newValue!, forKey: key)
}
}
}
mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
mutating func removeValueForKey(key:KeyType) {
_keys = _keys.filter { $0 != key }
_dictionary.removeValueForKey(key)
}
mutating func removeAll(keepCapacity:Int) {
_keys = []
_dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity)
}
var count: Int { get { return _dictionary.count } }
// keys isn't lazy evaluated because it's just an array anyway
var keys:[KeyType] { get { return _keys } }
// values is lazy evaluated because of the dictionary lookup and creating a new array
var values:GeneratorOf<ValueType> {
get {
var index = 0
return GeneratorOf<ValueType> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return self._dictionary[key]
}
}
}
}
}
extension OrderedDictionary : SequenceType {
func generate() -> GeneratorOf<(KeyType, ValueType)> {
var index = 0
return GeneratorOf<(KeyType, ValueType)> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return (key, self._dictionary[key]!)
}
}
}
}
func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}
Swift 5 version:
// OrderedDictionary behaves like a Dictionary except that it maintains
// the insertion order of the keys, so iteration order matches insertion
// order.
struct OrderedDictionary<KeyType: Hashable, ValueType> {
private var _dictionary: Dictionary<KeyType, ValueType>
private var _keys: Array<KeyType>
init() {
_dictionary = [:]
_keys = []
}
init(minimumCapacity: Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: minimumCapacity)
_keys = Array<KeyType>()
}
init(_ dictionary: Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = dictionary.keys.map { $0 }
}
subscript(key: KeyType) -> ValueType? {
get {
_dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key: key)
} else {
_ = self.updateValue(value: newValue!, forKey: key)
}
}
}
mutating func updateValue(value: ValueType, forKey key: KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
mutating func removeValueForKey(key: KeyType) {
_keys = _keys.filter {
$0 != key
}
_dictionary.removeValue(forKey: key)
}
mutating func removeAll(keepCapacity: Int) {
_keys = []
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: keepCapacity)
}
var count: Int {
get {
_dictionary.count
}
}
// keys isn't lazy evaluated because it's just an array anyway
var keys: [KeyType] {
get {
_keys
}
}
var values: Array<ValueType> {
get {
_keys.map { _dictionary[$0]! }
}
}
static func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
static func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}
}
extension OrderedDictionary: Sequence {
public func makeIterator() -> OrderedDictionaryIterator<KeyType, ValueType> {
OrderedDictionaryIterator<KeyType, ValueType>(sequence: _dictionary, keys: _keys, current: 0)
}
}
struct OrderedDictionaryIterator<KeyType: Hashable, ValueType>: IteratorProtocol {
let sequence: Dictionary<KeyType, ValueType>
let keys: Array<KeyType>
var current = 0
mutating func next() -> (KeyType, ValueType)? {
defer { current += 1 }
guard sequence.count > current else {
return nil
}
let key = keys[current]
guard let value = sequence[key] else {
return nil
}
return (key, value)
}
}
I didn't found way to make values 'lazy'.. need more research