// // StatusCache.swift // Tusker // // Created by Shadowfacts on 9/17/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import Foundation import Combine import Pachyderm class MastodonCache { private var statuses = CachedDictionary(name: "Statuses") private var accounts = CachedDictionary(name: "Accounts") private var relationships = CachedDictionary(name: "Relationships") private var notifications = CachedDictionary(name: "Notifications") let statusSubject = PassthroughSubject() let accountSubject = PassthroughSubject() weak var mastodonController: MastodonController? init(mastodonController: MastodonController) { self.mastodonController = mastodonController } // MARK: - Statuses func status(for id: String) -> Status? { return statuses[id] } func set(status: Status, for id: String) { statuses[id] = status add(account: status.account) if let reblog = status.reblog { add(status: reblog) add(account: reblog.account) } statusSubject.send(status) } func status(for id: String, completion: @escaping (Status?) -> Void) { guard let mastodonController = mastodonController else { fatalError("The MastodonController for this cache has been deinitialized, so this cache should no longer exist. Are you storing a strong reference to it?") } let request = Client.getStatus(id: id) mastodonController.run(request) { response in guard case let .success(status, _) = response else { completion(nil) return } self.set(status: status, for: id) completion(status) } } func add(status: Status) { set(status: status, for: status.id) } func addAll(statuses: [Status]) { statuses.forEach(add) } // MARK: - Accounts func account(for id: String) -> Account? { return accounts[id] } func set(account: Account, for id: String) { accounts[id] = account accountSubject.send(account) } func account(for id: String, completion: @escaping (Account?) -> Void) { guard let mastodonController = mastodonController else { fatalError("The MastodonController for this cache has been deinitialized, so this cache should no longer exist. Are you storing a strong reference to it?") } let request = Client.getAccount(id: id) mastodonController.run(request) { response in guard case let .success(account, _) = response else { completion(nil) return } self.set(account: account, for: account.id) completion(account) } } func add(account: Account) { set(account: account, for: account.id) } func addAll(accounts: [Account]) { accounts.forEach(add) } // MARK: - Relationships func relationship(for id: String) -> Relationship? { return relationships[id] } func set(relationship: Relationship, id: String) { relationships[id] = relationship } func relationship(for id: String, completion: @escaping (Relationship?) -> Void) { guard let mastodonController = mastodonController else { fatalError("The MastodonController for this cache has been deinitialized, so this cache should no longer exist. Are you storing a strong reference to it?") } let request = Client.getRelationships(accounts: [id]) mastodonController.run(request) { response in guard case let .success(relationships, _) = response, let relationship = relationships.first else { completion(nil) return } self.set(relationship: relationship, id: relationship.id) completion(relationship) } } func add(relationship: Relationship) { set(relationship: relationship, id: relationship.id) } func addAll(relationships: [Relationship]) { relationships.forEach(add) } // MARK: - Notifications func notification(for id: String) -> Pachyderm.Notification? { return notifications[id] } func set(notification: Pachyderm.Notification, id: String) { notifications[id] = notification } func add(notification: Pachyderm.Notification) { set(notification: notification, id: notification.id) } func addAll(notifications: [Pachyderm.Notification]) { notifications.forEach(add) } } class CachedDictionary { private let name: String private var dict = [String: Value]() private let queue: DispatchQueue init(name: String) { self.name = name self.queue = DispatchQueue(label: "CachedDictionary (\(name)) Coordinator", attributes: .concurrent) } subscript(key: String) -> Value? { get { var result: Value? = nil queue.sync { result = dict[key] } return result } set(value) { queue.async(flags: .barrier) { self.dict[key] = value } } } }