Cache threads, interactions, attachments. Improve model cache.
This commit is contained in:
parent
edb703a550
commit
179ecaf258
@ -86,6 +86,14 @@ public class ThreadMappingDiff: NSObject {
|
||||
@objc
|
||||
class ThreadMapping: NSObject {
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
private var threadReadCache: ThreadReadCache {
|
||||
SSKEnvironment.shared.modelReadCaches.threadReadCache
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private var threads: [TSThread] = []
|
||||
|
||||
private let kSection: Int = ConversationListViewControllerSection.conversations.rawValue
|
||||
@ -169,12 +177,76 @@ class ThreadMapping: NSObject {
|
||||
try Bench(title: "update thread mapping (\(isViewingArchive ? "archive" : "inbox"))") {
|
||||
archiveCount = try threadFinder.visibleThreadCount(isArchived: true, transaction: transaction)
|
||||
inboxCount = try threadFinder.visibleThreadCount(isArchived: false, transaction: transaction)
|
||||
threads = try self.loadThreads(isViewingArchive: isViewingArchive, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
private let shouldUseModelCache = false
|
||||
|
||||
private func loadThreads(isViewingArchive: Bool, transaction: SDSAnyReadTransaction) throws -> [TSThread] {
|
||||
|
||||
// This method is a perf hotspot. To improve perf, we try to leverage
|
||||
// the model cache. If any problems arise, we fall back to using
|
||||
// threadFinder.enumerateVisibleThreads() which is robust but expensive.
|
||||
let loadWithoutCache: () throws -> [TSThread] = {
|
||||
var newThreads: [TSThread] = []
|
||||
try threadFinder.enumerateVisibleThreads(isArchived: isViewingArchive, transaction: transaction) { thread in
|
||||
try self.threadFinder.enumerateVisibleThreads(isArchived: isViewingArchive, transaction: transaction) { thread in
|
||||
newThreads.append(thread)
|
||||
}
|
||||
threads = newThreads
|
||||
return newThreads
|
||||
}
|
||||
|
||||
guard shouldUseModelCache else {
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
|
||||
// Loading the mapping from the cache has the following steps:
|
||||
//
|
||||
// 1. Fetch the uniqueIds for the visible threads.
|
||||
let threadIds = try threadFinder.visibleThreadIds(isArchived: isViewingArchive, transaction: transaction)
|
||||
guard !threadIds.isEmpty else {
|
||||
return []
|
||||
}
|
||||
|
||||
// 2. Try to pull as many threads as possible from the cache.
|
||||
var threadIdToModelMap: [String: TSThread] = threadReadCache.getThreadsIfInCache(forUniqueIds: threadIds,
|
||||
transaction: transaction)
|
||||
var threadsToLoad = Set(threadIds)
|
||||
threadsToLoad.subtract(threadIdToModelMap.keys)
|
||||
|
||||
// 3. Bulk load any threads that are not in the cache in a
|
||||
// single query.
|
||||
//
|
||||
// NOTE: There's an upper bound on how long SQL queries should be.
|
||||
// We use kMaxIncrementalRowChanges to limit query size.
|
||||
guard threadsToLoad.count <= UIDatabaseObserver.kMaxIncrementalRowChanges else {
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
if !threadsToLoad.isEmpty {
|
||||
let loadedThreads = try threadFinder.threads(withThreadIds: threadsToLoad, transaction: transaction)
|
||||
guard loadedThreads.count == threadsToLoad.count else {
|
||||
owsFailDebug("Loading threads failed.")
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
for thread in loadedThreads {
|
||||
threadIdToModelMap[thread.uniqueId] = thread
|
||||
}
|
||||
}
|
||||
guard threadIds.count == threadIdToModelMap.count else {
|
||||
owsFailDebug("Missing threads.")
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
|
||||
// 4. Build the ordered list of threads.
|
||||
var threads = [TSThread]()
|
||||
for threadId in threadIds {
|
||||
guard let thread = threadIdToModelMap[threadId] else {
|
||||
owsFailDebug("Couldn't read thread: \(threadId)")
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
threads.append(thread)
|
||||
}
|
||||
return threads
|
||||
}
|
||||
|
||||
@objc
|
||||
@ -318,6 +390,8 @@ class ThreadMapping: NSObject {
|
||||
}
|
||||
// Once the moves are complete, the new ordering should be correct.
|
||||
guard newThreadIds == naiveThreadIdOrdering else {
|
||||
Logger.verbose("newThreadIds: \(newThreadIds)")
|
||||
Logger.verbose("naiveThreadIdOrdering: \(naiveThreadIdOrdering)")
|
||||
throw OWSAssertionError("Could not reorder contents.")
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,14 @@ import Foundation
|
||||
@objc
|
||||
public class ConversationMessageMapping: NSObject {
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
private var interactionReadCache: InteractionReadCache {
|
||||
SSKEnvironment.shared.modelReadCaches.interactionReadCache
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private let interactionFinder: InteractionFinder
|
||||
|
||||
@objc
|
||||
@ -269,12 +277,74 @@ public class ConversationMessageMapping: NSObject {
|
||||
Logger.verbose("canLoadOlder: \(canLoadOlder) canLoadNewer: \(canLoadNewer)")
|
||||
}
|
||||
|
||||
private let shouldUseModelCache = true
|
||||
|
||||
private func fetchInteractions(nsRange: NSRange, transaction: SDSAnyReadTransaction) throws -> [TSInteraction] {
|
||||
var newItems: [TSInteraction] = []
|
||||
try interactionFinder.enumerateInteractions(range: nsRange, transaction: transaction) { (interaction: TSInteraction, _) in
|
||||
newItems.append(interaction)
|
||||
|
||||
// This method is a perf hotspot. To improve perf, we try to leverage
|
||||
// the model cache. If any problems arise, we fall back to using
|
||||
// interactionFinder.enumerateInteractions() which is robust but expensive.
|
||||
let loadWithoutCache: () throws -> [TSInteraction] = {
|
||||
|
||||
var newItems: [TSInteraction] = []
|
||||
try self.interactionFinder.enumerateInteractions(range: nsRange, transaction: transaction) { (interaction: TSInteraction, _) in
|
||||
newItems.append(interaction)
|
||||
}
|
||||
return newItems
|
||||
}
|
||||
return newItems
|
||||
|
||||
guard shouldUseModelCache else {
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
|
||||
// Loading the mapping from the cache has the following steps:
|
||||
//
|
||||
// 1. Fetch the uniqueIds for the interactions in the load window/mapping.
|
||||
let interactionIds = try interactionFinder.interactionIds(inRange: nsRange, transaction: transaction)
|
||||
guard !interactionIds.isEmpty else {
|
||||
return []
|
||||
}
|
||||
|
||||
// 2. Try to pull as many interactions as possible from the cache.
|
||||
var interactionIdToModelMap: [String: TSInteraction] = interactionReadCache.getInteractionsIfInCache(forUniqueIds: interactionIds,
|
||||
transaction: transaction)
|
||||
var interactionsToLoad = Set(interactionIds)
|
||||
interactionsToLoad.subtract(interactionIdToModelMap.keys)
|
||||
let cachedCount = interactionIdToModelMap.count
|
||||
|
||||
// 3. Bulk load any interactions that are not in the cache in a
|
||||
// single query.
|
||||
//
|
||||
// NOTE: There's an upper bound on how long SQL queries should be.
|
||||
// We use kMaxIncrementalRowChanges to limit query size.
|
||||
guard interactionsToLoad.count <= UIDatabaseObserver.kMaxIncrementalRowChanges else {
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
if !interactionsToLoad.isEmpty {
|
||||
let loadedInteractions = InteractionFinder.interactions(withInteractionIds: interactionsToLoad, transaction: transaction)
|
||||
guard loadedInteractions.count == interactionsToLoad.count else {
|
||||
owsFailDebug("Loading interactions failed.")
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
for interaction in loadedInteractions {
|
||||
interactionIdToModelMap[interaction.uniqueId] = interaction
|
||||
}
|
||||
}
|
||||
guard interactionIds.count == interactionIdToModelMap.count else {
|
||||
owsFailDebug("Missing interactions.")
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
|
||||
// 4. Build the ordered list of interactions.
|
||||
var interactions = [TSInteraction]()
|
||||
for interactionId in interactionIds {
|
||||
guard let interaction = interactionIdToModelMap[interactionId] else {
|
||||
owsFailDebug("Couldn't read interaction: \(interactionId)")
|
||||
return try loadWithoutCache()
|
||||
}
|
||||
interactions.append(interaction)
|
||||
}
|
||||
return interactions
|
||||
}
|
||||
|
||||
@objc
|
||||
|
||||
@ -64,6 +64,11 @@ ConversationColorName const ConversationColorNameDefault = ConversationColorName
|
||||
return SSKEnvironment.shared.tsAccountManager;
|
||||
}
|
||||
|
||||
- (ThreadReadCache *)threadReadCache
|
||||
{
|
||||
return SSKEnvironment.shared.modelReadCaches.threadReadCache;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
+ (NSString *)collection {
|
||||
@ -196,6 +201,22 @@ ConversationColorName const ConversationColorNameDefault = ConversationColorName
|
||||
if (self.shouldThreadBeVisible && ![SSKPreferences hasSavedThreadWithTransaction:transaction]) {
|
||||
[SSKPreferences setHasSavedThread:YES transaction:transaction];
|
||||
}
|
||||
|
||||
[self.threadReadCache didInsertOrUpdateThread:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidUpdateWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidUpdateWithTransaction:transaction];
|
||||
|
||||
[self.threadReadCache didInsertOrUpdateThread:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidRemoveWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidRemoveWithTransaction:transaction];
|
||||
|
||||
[self.threadReadCache didRemoveThread:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyWillRemoveWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
|
||||
@ -30,6 +30,15 @@ NSUInteger const TSAttachmentSchemaVersion = 5;
|
||||
|
||||
@implementation TSAttachment
|
||||
|
||||
#pragma mark - Dependencies
|
||||
|
||||
- (AttachmentReadCache *)attachmentReadCache
|
||||
{
|
||||
return SSKEnvironment.shared.modelReadCaches.attachmentReadCache;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
// This constructor is used for new instances of TSAttachmentPointer,
|
||||
// i.e. undownloaded incoming attachments.
|
||||
- (instancetype)initWithServerId:(UInt64)serverId
|
||||
@ -392,6 +401,29 @@ NSUInteger const TSAttachmentSchemaVersion = 5;
|
||||
|
||||
#pragma mark - Update With...
|
||||
|
||||
- (void)anyDidInsertWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidInsertWithTransaction:transaction];
|
||||
|
||||
[self.attachmentReadCache didInsertOrUpdateAttachment:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidUpdateWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidUpdateWithTransaction:transaction];
|
||||
|
||||
[self.attachmentReadCache didInsertOrUpdateAttachment:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidRemoveWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidRemoveWithTransaction:transaction];
|
||||
|
||||
[self.attachmentReadCache didRemoveAttachment:self transaction:transaction];
|
||||
}
|
||||
|
||||
#pragma mark - Update With...
|
||||
|
||||
- (void)updateWithBlurHash:(NSString *)blurHash transaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(blurHash.length > 0);
|
||||
|
||||
@ -49,6 +49,15 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
|
||||
|
||||
@implementation TSInteraction
|
||||
|
||||
#pragma mark - Dependencies
|
||||
|
||||
- (InteractionReadCache *)interactionReadCache
|
||||
{
|
||||
return SSKEnvironment.shared.modelReadCaches.interactionReadCache;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
+ (BOOL)shouldBeIndexedForFTS
|
||||
{
|
||||
return YES;
|
||||
@ -327,6 +336,8 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
|
||||
|
||||
TSThread *fetchedThread = [self threadWithTransaction:transaction];
|
||||
[fetchedThread updateWithInsertedMessage:self transaction:transaction];
|
||||
|
||||
[self.interactionReadCache didInsertOrUpdateInteraction:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidUpdateWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
@ -335,6 +346,8 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
|
||||
|
||||
TSThread *fetchedThread = [self threadWithTransaction:transaction];
|
||||
[fetchedThread updateWithUpdatedMessage:self transaction:transaction];
|
||||
|
||||
[self.interactionReadCache didInsertOrUpdateInteraction:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidRemoveWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
@ -345,6 +358,8 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
|
||||
TSThread *fetchedThread = [self threadWithTransaction:transaction];
|
||||
[fetchedThread updateWithRemovedMessage:self transaction:transaction];
|
||||
}
|
||||
|
||||
[self.interactionReadCache didRemoveInteraction:self transaction:transaction];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSContact.h"
|
||||
#import "TSInteraction.h"
|
||||
#import "TSQuotedMessage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -12,7 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GRDBReadTransaction;
|
||||
@class MessageSticker;
|
||||
@class OWSContact;
|
||||
@class OWSLinkPreview;
|
||||
@class SDSAnyReadTransaction;
|
||||
@class SDSAnyWriteTransaction;
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
@class SDSAnyWriteTransaction;
|
||||
@class SignalServiceAddress;
|
||||
@class TSThread;
|
||||
@class UserProfileReadCache;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -102,7 +101,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (void)warmCaches;
|
||||
|
||||
@property (nonatomic, readonly) UserProfileReadCache *userProfileReadCache;
|
||||
@property (nonatomic, readonly) BOOL hasProfileName;
|
||||
|
||||
@end
|
||||
|
||||
276
SignalServiceKit/src/Storage/Database/DeepCopy.swift
Normal file
276
SignalServiceKit/src/Storage/Database/DeepCopy.swift
Normal file
@ -0,0 +1,276 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol DeepCopyable {
|
||||
func deepCopy() throws -> AnyObject
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class DeepCopies {
|
||||
|
||||
@available(*, unavailable, message:"Do not instantiate this class.")
|
||||
private init() {
|
||||
}
|
||||
|
||||
static func deepCopy<T: DeepCopyable>(_ objectToCopy: T) throws -> T {
|
||||
guard let newCopy = try objectToCopy.deepCopy() as? T else {
|
||||
Logger.verbose("Could not copy: \(objectToCopy)")
|
||||
throw OWSAssertionError("Could not copy: \(type(of: objectToCopy))")
|
||||
}
|
||||
return newCopy
|
||||
}
|
||||
|
||||
static func deepCopy<T: DeepCopyable>(_ arrayToCopy: [T]) throws -> [T] {
|
||||
return try arrayToCopy.deepCopy()
|
||||
}
|
||||
|
||||
static func deepCopy<K: DeepCopyable, V: DeepCopyable>(_ dictToCopy: [K: V]) throws -> [K: V] {
|
||||
return try dictToCopy.deepCopy()
|
||||
}
|
||||
|
||||
// NOTE: We get not compile-time type safety with NSOrderedSet.
|
||||
static func deepCopy(_ setToCopy: NSOrderedSet) throws -> NSOrderedSet {
|
||||
return try setToCopy.deepCopy()
|
||||
}
|
||||
|
||||
// Swift does not appear to offer a way to let Array conform
|
||||
// to DeepCopyable IFF Array.Element conforms to DeepCopyable.
|
||||
// This if unfortunate and poses a decision.
|
||||
//
|
||||
// We can makes Array generically conform to DeepCopyable,
|
||||
// but then we get not compile-time type checking.
|
||||
//
|
||||
// Instead we create a variety of specializations of
|
||||
// DeepCopies.deepCopy(). This is tedious but safer.
|
||||
static func deepCopy<K: DeepCopyable, V: DeepCopyable>(_ dictToCopy: [K: [V]]) throws -> [K: [V]] {
|
||||
return Dictionary(uniqueKeysWithValues: try dictToCopy.map({ (key, value) in
|
||||
let keyCopy: K = try DeepCopies.deepCopy(key)
|
||||
let valueCopy: [V] = try value.deepCopy()
|
||||
return (keyCopy, valueCopy)
|
||||
}))
|
||||
}
|
||||
|
||||
// NOTE: We get not compile-time type safety with Any.
|
||||
static func deepCopy(_ dictToCopy: [InfoMessageUserInfoKey: Any]) throws -> [InfoMessageUserInfoKey: Any] {
|
||||
return Dictionary(uniqueKeysWithValues: try dictToCopy.map({ (key, value) in
|
||||
let keyCopy: String = try DeepCopies.deepCopy(key.rawValue as String)
|
||||
let valueCopy: AnyObject
|
||||
if let objectToCopy = value as? DeepCopyable {
|
||||
valueCopy = try objectToCopy.deepCopy()
|
||||
} else {
|
||||
Logger.verbose("Could not copy: \(value)")
|
||||
throw OWSAssertionError("Could not copy: \(type(of: value))")
|
||||
}
|
||||
return (InfoMessageUserInfoKey(rawValue: keyCopy), valueCopy)
|
||||
}))
|
||||
}
|
||||
|
||||
// "Cannot explicitly specialize a generic function."
|
||||
fileprivate static func shallowCopy<T: NSObject>(_ objectToCopy: T) throws -> T {
|
||||
guard let newCopy = objectToCopy.copy() as? T else {
|
||||
Logger.verbose("Could not copy: \(objectToCopy)")
|
||||
throw OWSAssertionError("Could not copy: \(type(of: objectToCopy))")
|
||||
}
|
||||
return newCopy
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension Array where Element: DeepCopyable {
|
||||
public func deepCopy() throws -> [Element] {
|
||||
return try map { element in
|
||||
return try DeepCopies.deepCopy(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension Dictionary where Key: DeepCopyable, Value: DeepCopyable {
|
||||
public func deepCopy() throws -> [Key: Value] {
|
||||
return Dictionary(uniqueKeysWithValues: try self.map({ (key, value) in
|
||||
let keyCopy: Key = try DeepCopies.deepCopy(key)
|
||||
let valueCopy: Value = try DeepCopies.deepCopy(value)
|
||||
return (keyCopy, valueCopy)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
// NOTE: We get not compile-time type safety with NSOrderedSet.
|
||||
extension NSOrderedSet {
|
||||
public func deepCopy() throws -> NSOrderedSet {
|
||||
let copiedElements: [AnyObject] = try self.array.map { (element: Any) throws -> AnyObject in
|
||||
guard let objectToCopy = element as? DeepCopyable else {
|
||||
Logger.verbose("Could not copy: \(element)")
|
||||
throw OWSAssertionError("Could not copy: \(type(of: element))")
|
||||
}
|
||||
return try objectToCopy.deepCopy()
|
||||
}
|
||||
return NSOrderedSet(array: copiedElements)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension NSNumber: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// No need to copy; NSNumber is immutable.
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension String: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self as NSString)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension StickerInfo: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension OWSLinkPreview: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension SignalServiceAddress: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension MessageSticker: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension TSQuotedMessage: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension OWSContact: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension StickerPackInfo: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension StickerPackItem: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension Contact: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension OWSAES256Key: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension TSGroupModel: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension PreKeyBundle: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension TSOutgoingMessageRecipientState: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
extension DisappearingMessageToken: DeepCopyable {
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
// This class can use shallow copies.
|
||||
return try DeepCopies.shallowCopy(self)
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,8 @@ protocol InteractionFinderAdapter {
|
||||
|
||||
static func enumerateMessagesWhichFailedToStartExpiring(transaction: ReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer<ObjCBool>) -> Void)
|
||||
|
||||
static func interactions(withInteractionIds interactionIds: Set<String>, transaction: ReadTransaction) -> Set<TSInteraction>
|
||||
|
||||
// MARK: - instance methods
|
||||
|
||||
func mostRecentInteractionForInbox(transaction: ReadTransaction) -> TSInteraction?
|
||||
@ -38,6 +40,7 @@ protocol InteractionFinderAdapter {
|
||||
func enumerateInteractionIds(transaction: ReadTransaction, block: @escaping (String, UnsafeMutablePointer<ObjCBool>) throws -> Void) throws
|
||||
func enumerateRecentInteractions(transaction: ReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer<ObjCBool>) -> Void) throws
|
||||
func enumerateInteractions(range: NSRange, transaction: ReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer<ObjCBool>) -> Void) throws
|
||||
func interactionIds(inRange range: NSRange, transaction: ReadTransaction) throws -> [String]
|
||||
func existsOutgoingMessage(transaction: ReadTransaction) -> Bool
|
||||
func outgoingMessageCount(transaction: ReadTransaction) -> UInt
|
||||
|
||||
@ -179,6 +182,16 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public class func interactions(withInteractionIds interactionIds: Set<String>, transaction: SDSAnyReadTransaction) -> Set<TSInteraction> {
|
||||
switch transaction.readTransaction {
|
||||
case .yapRead(let yapRead):
|
||||
return YAPDBInteractionFinderAdapter.interactions(withInteractionIds: interactionIds, transaction: yapRead)
|
||||
case .grdbRead(let grdbRead):
|
||||
return GRDBInteractionFinder.interactions(withInteractionIds: interactionIds, transaction: grdbRead)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public class func findMessage(
|
||||
withTimestamp timestamp: UInt64,
|
||||
@ -340,6 +353,16 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
public func interactionIds(inRange range: NSRange, transaction: SDSAnyReadTransaction) throws -> [String] {
|
||||
switch transaction.readTransaction {
|
||||
case .yapRead(let yapRead):
|
||||
owsFailDebug("Invalid transaction.")
|
||||
return try yapAdapter.interactionIds(inRange: range, transaction: yapRead)
|
||||
case .grdbRead(let grdbRead):
|
||||
return try grdbAdapter.interactionIds(inRange: range, transaction: grdbRead)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all the unread interactions in this thread
|
||||
@objc
|
||||
public func allUnreadMessages(transaction: GRDBReadTransaction) -> [OWSReadTracking] {
|
||||
@ -583,6 +606,10 @@ struct YAPDBInteractionFinderAdapter: InteractionFinderAdapter {
|
||||
OWSDisappearingMessagesFinder.ydb_enumerateMessagesWhichFailedToStartExpiring(block, transaction: transaction)
|
||||
}
|
||||
|
||||
static func interactions(withInteractionIds interactionIds: Set<String>, transaction: YapDatabaseReadTransaction) -> Set<TSInteraction> {
|
||||
owsFail("Not implemented.")
|
||||
}
|
||||
|
||||
// MARK: - instance methods
|
||||
|
||||
func mostRecentInteractionForInbox(transaction: YapDatabaseReadTransaction) -> TSInteraction? {
|
||||
@ -691,6 +718,11 @@ struct YAPDBInteractionFinderAdapter: InteractionFinderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
public func interactionIds(inRange range: NSRange, transaction: YapDatabaseReadTransaction) throws -> [String] {
|
||||
owsFailDebug("Invalid transaction.")
|
||||
return []
|
||||
}
|
||||
|
||||
func interaction(at index: UInt, transaction: YapDatabaseReadTransaction) -> TSInteraction? {
|
||||
guard let view = interactionExt(transaction) else {
|
||||
return nil
|
||||
@ -929,6 +961,28 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
static func interactions(withInteractionIds interactionIds: Set<String>, transaction: GRDBReadTransaction) -> Set<TSInteraction> {
|
||||
guard !interactionIds.isEmpty else {
|
||||
return []
|
||||
}
|
||||
|
||||
let sql = """
|
||||
SELECT * FROM \(InteractionRecord.databaseTableName)
|
||||
WHERE \(interactionColumn: .uniqueId) IN (\(interactionIds.map { "\'\($0)'" }.joined(separator: ",")))
|
||||
"""
|
||||
let arguments: StatementArguments = []
|
||||
let cursor = TSInteraction.grdbFetchCursor(sql: sql, arguments: arguments, transaction: transaction)
|
||||
var interactions = Set<TSInteraction>()
|
||||
do {
|
||||
while let interaction = try cursor.next() {
|
||||
interactions.insert(interaction)
|
||||
}
|
||||
} catch {
|
||||
owsFailDebug("unexpected error \(error)")
|
||||
}
|
||||
return interactions
|
||||
}
|
||||
|
||||
// MARK: - instance methods
|
||||
|
||||
func mostRecentInteractionForInbox(transaction: GRDBReadTransaction) -> TSInteraction? {
|
||||
@ -1056,6 +1110,21 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
func interactionIds(inRange range: NSRange, transaction: GRDBReadTransaction) throws -> [String] {
|
||||
let sql = """
|
||||
SELECT \(interactionColumn: .uniqueId)
|
||||
FROM \(InteractionRecord.databaseTableName)
|
||||
WHERE \(interactionColumn: .threadUniqueId) = ?
|
||||
ORDER BY \(interactionColumn: .id)
|
||||
LIMIT \(range.length)
|
||||
OFFSET \(range.location)
|
||||
"""
|
||||
let arguments: StatementArguments = [threadUniqueId]
|
||||
return try String.fetchAll(transaction.database,
|
||||
sql: sql,
|
||||
arguments: arguments)
|
||||
}
|
||||
|
||||
@objc
|
||||
func enumerateMessagesWithAttachments(transaction: GRDBReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
|
||||
|
||||
|
||||
@ -10,7 +10,9 @@ public protocol ThreadFinder {
|
||||
|
||||
func visibleThreadCount(isArchived: Bool, transaction: ReadTransaction) throws -> UInt
|
||||
func enumerateVisibleThreads(isArchived: Bool, transaction: ReadTransaction, block: @escaping (TSThread) -> Void) throws
|
||||
func visibleThreadIds(isArchived: Bool, transaction: ReadTransaction) throws -> [String]
|
||||
func sortIndex(thread: TSThread, transaction: ReadTransaction) throws -> UInt?
|
||||
func threads(withThreadIds threadIds: Set<String>, transaction: ReadTransaction) throws -> Set<TSThread>
|
||||
}
|
||||
|
||||
@objc
|
||||
@ -39,6 +41,16 @@ public class AnyThreadFinder: NSObject, ThreadFinder {
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public func visibleThreadIds(isArchived: Bool, transaction: SDSAnyReadTransaction) throws -> [String] {
|
||||
switch transaction.readTransaction {
|
||||
case .grdbRead(let grdb):
|
||||
return try grdbAdapter.visibleThreadIds(isArchived: isArchived, transaction: grdb)
|
||||
case .yapRead(let yap):
|
||||
return try yapAdapter.visibleThreadIds(isArchived: isArchived, transaction: yap)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public func sortIndexObjc(thread: TSThread, transaction: ReadTransaction) -> NSNumber? {
|
||||
do {
|
||||
@ -60,6 +72,15 @@ public class AnyThreadFinder: NSObject, ThreadFinder {
|
||||
return yapAdapter.sortIndex(thread: thread, transaction: yap)
|
||||
}
|
||||
}
|
||||
|
||||
public func threads(withThreadIds threadIds: Set<String>, transaction: SDSAnyReadTransaction) throws -> Set<TSThread> {
|
||||
switch transaction.readTransaction {
|
||||
case .grdbRead(let grdb):
|
||||
return try grdbAdapter.threads(withThreadIds: threadIds, transaction: grdb)
|
||||
case .yapRead(let yap):
|
||||
return try yapAdapter.threads(withThreadIds: threadIds, transaction: yap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct YAPDBThreadFinder: ThreadFinder {
|
||||
@ -87,6 +108,10 @@ struct YAPDBThreadFinder: ThreadFinder {
|
||||
}
|
||||
}
|
||||
|
||||
func visibleThreadIds(isArchived: Bool, transaction: YapDatabaseReadTransaction) throws -> [String] {
|
||||
throw OWSAssertionError("Not implemented.")
|
||||
}
|
||||
|
||||
func sortIndex(thread: TSThread, transaction: YapDatabaseReadTransaction) -> UInt? {
|
||||
guard let view = ext(transaction) else {
|
||||
owsFailDebug("view was unexpectedly nil")
|
||||
@ -117,6 +142,10 @@ struct YAPDBThreadFinder: ThreadFinder {
|
||||
}
|
||||
}
|
||||
|
||||
func threads(withThreadIds threadIds: Set<String>, transaction: YapDatabaseReadTransaction) throws -> Set<TSThread> {
|
||||
throw OWSAssertionError("Not implemented.")
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private static let extensionName: String = TSThreadDatabaseViewExtensionName
|
||||
@ -170,6 +199,21 @@ public class GRDBThreadFinder: NSObject, ThreadFinder {
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public func visibleThreadIds(isArchived: Bool, transaction: GRDBReadTransaction) throws -> [String] {
|
||||
let sql = """
|
||||
SELECT \(threadColumn: .uniqueId)
|
||||
FROM \(ThreadRecord.databaseTableName)
|
||||
WHERE \(threadColumn: .shouldThreadBeVisible) = 1
|
||||
AND \(threadColumn: .isArchived) = ?
|
||||
ORDER BY \(threadColumn: .lastInteractionRowId) DESC
|
||||
"""
|
||||
let arguments: StatementArguments = [isArchived]
|
||||
return try String.fetchAll(transaction.database,
|
||||
sql: sql,
|
||||
arguments: arguments)
|
||||
}
|
||||
|
||||
public func sortIndex(thread: TSThread, transaction: GRDBReadTransaction) throws -> UInt? {
|
||||
let sql = """
|
||||
SELECT sortIndex
|
||||
@ -279,4 +323,23 @@ public class GRDBThreadFinder: NSObject, ThreadFinder {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc
|
||||
public func threads(withThreadIds threadIds: Set<String>, transaction: GRDBReadTransaction) throws -> Set<TSThread> {
|
||||
guard !threadIds.isEmpty else {
|
||||
return []
|
||||
}
|
||||
|
||||
let sql = """
|
||||
SELECT * FROM \(ThreadRecord.databaseTableName)
|
||||
WHERE \(threadColumn: .uniqueId) IN (\(threadIds.map { "\'\($0)'" }.joined(separator: ",")))
|
||||
"""
|
||||
let arguments: StatementArguments = []
|
||||
let cursor = TSThread.grdbFetchCursor(sql: sql, arguments: arguments, transaction: transaction)
|
||||
var threads = Set<TSThread>()
|
||||
while let thread = try cursor.next() {
|
||||
threads.insert(thread)
|
||||
}
|
||||
return threads
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +32,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@implementation OWSFakeProfileManager
|
||||
|
||||
@synthesize localProfileKey = _localProfileKey;
|
||||
@synthesize userProfileReadCache = _userProfileReadCache;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
@ -44,7 +43,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
_profileKeys = [NSMutableDictionary new];
|
||||
_recipientWhitelist = [NSMutableSet new];
|
||||
_threadWhitelist = [NSMutableSet new];
|
||||
_userProfileReadCache = [UserProfileReadCache new];
|
||||
_stubbedUuidCapabilitiesMap = [NSMutableDictionary new];
|
||||
|
||||
return self;
|
||||
|
||||
@ -736,6 +736,201 @@ public class SignalRecipientReadCache: NSObject {
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public class ThreadReadCache: NSObject {
|
||||
typealias KeyType = NSString
|
||||
typealias ValueType = TSThread
|
||||
|
||||
private class Adapter: ModelCacheAdapter<KeyType, ValueType> {
|
||||
override func read(key: KeyType, transaction: SDSAnyReadTransaction) -> ValueType? {
|
||||
return TSThread.anyFetch(uniqueId: key as String,
|
||||
transaction: transaction,
|
||||
ignoreCache: true)
|
||||
}
|
||||
|
||||
override func deriveKey(fromValue value: ValueType) -> KeyType {
|
||||
value.uniqueId as NSString
|
||||
}
|
||||
|
||||
override func copy(value: ValueType) throws -> ValueType {
|
||||
return try DeepCopies.deepCopy(value)
|
||||
}
|
||||
|
||||
override func uiReadEvacuation(databaseChanges: UIDatabaseChanges,
|
||||
nsCache: NSCache<KeyType, ModelCacheValueBox<ValueType>>) {
|
||||
// Only evacuate modified models.
|
||||
for uniqueId: String in databaseChanges.threadUniqueIds {
|
||||
nsCache.removeObject(forKey: uniqueId as NSString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let cache: ModelReadCacheWrapper<KeyType, ValueType>
|
||||
|
||||
@objc
|
||||
public override init() {
|
||||
cache = ModelReadCacheWrapper(cacheName: "TSThread", adapter: Adapter())
|
||||
}
|
||||
|
||||
@objc(getThreadForUniqueId:transaction:)
|
||||
public func getThread(uniqueId: String, transaction: SDSAnyReadTransaction) -> TSThread? {
|
||||
return cache.getValue(for: uniqueId as NSString, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc(getThreadsIfInCacheForUniqueIds:transaction:)
|
||||
public func getThreadsIfInCache(forUniqueIds uniqueIds: [String], transaction: SDSAnyReadTransaction) -> [String: TSThread] {
|
||||
let keys: [NSString] = uniqueIds.map { $0 as NSString }
|
||||
let result: [NSString: TSThread] = cache.getValuesIfInCache(for: keys, transaction: transaction)
|
||||
return Dictionary(uniqueKeysWithValues: result.map({ (key, value) in
|
||||
return (key as String, value)
|
||||
}))
|
||||
}
|
||||
|
||||
@objc(didRemoveThread:transaction:)
|
||||
public func didRemove(thread: TSThread, transaction: SDSAnyWriteTransaction) {
|
||||
cache.didRemove(value: thread, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc(didInsertOrUpdateThread:transaction:)
|
||||
public func didInsertOrUpdate(thread: TSThread, transaction: SDSAnyWriteTransaction) {
|
||||
cache.didInsertOrUpdate(value: thread, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func didReadThread(_ thread: TSThread, transaction: SDSAnyReadTransaction) {
|
||||
cache.didRead(value: thread, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public class InteractionReadCache: NSObject {
|
||||
typealias KeyType = NSString
|
||||
typealias ValueType = TSInteraction
|
||||
|
||||
private class Adapter: ModelCacheAdapter<KeyType, ValueType> {
|
||||
override func read(key: KeyType, transaction: SDSAnyReadTransaction) -> ValueType? {
|
||||
return TSInteraction.anyFetch(uniqueId: key as String,
|
||||
transaction: transaction,
|
||||
ignoreCache: true)
|
||||
}
|
||||
|
||||
override func deriveKey(fromValue value: ValueType) -> KeyType {
|
||||
value.uniqueId as NSString
|
||||
}
|
||||
|
||||
override func copy(value: ValueType) throws -> ValueType {
|
||||
return try DeepCopies.deepCopy(value)
|
||||
}
|
||||
|
||||
override func uiReadEvacuation(databaseChanges: UIDatabaseChanges,
|
||||
nsCache: NSCache<KeyType, ModelCacheValueBox<ValueType>>) {
|
||||
// Only evacuate modified models.
|
||||
for uniqueId: String in databaseChanges.interactionUniqueIds {
|
||||
nsCache.removeObject(forKey: uniqueId as NSString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let cache: ModelReadCacheWrapper<KeyType, ValueType>
|
||||
|
||||
@objc
|
||||
public override init() {
|
||||
cache = ModelReadCacheWrapper(cacheName: "TSInteraction", adapter: Adapter())
|
||||
}
|
||||
|
||||
@objc(getInteractionForUniqueId:transaction:)
|
||||
public func getInteraction(uniqueId: String, transaction: SDSAnyReadTransaction) -> TSInteraction? {
|
||||
return cache.getValue(for: uniqueId as NSString, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc(getInteractionsIfInCacheForUniqueIds:transaction:)
|
||||
public func getInteractionsIfInCache(forUniqueIds uniqueIds: [String], transaction: SDSAnyReadTransaction) -> [String: TSInteraction] {
|
||||
let keys: [NSString] = uniqueIds.map { $0 as NSString }
|
||||
let result: [NSString: TSInteraction] = cache.getValuesIfInCache(for: keys, transaction: transaction)
|
||||
return Dictionary(uniqueKeysWithValues: result.map({ (key, value) in
|
||||
return (key as String, value)
|
||||
}))
|
||||
}
|
||||
|
||||
@objc(didRemoveInteraction:transaction:)
|
||||
public func didRemove(interaction: TSInteraction, transaction: SDSAnyWriteTransaction) {
|
||||
cache.didRemove(value: interaction, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc(didInsertOrUpdateInteraction:transaction:)
|
||||
public func didInsertOrUpdate(interaction: TSInteraction, transaction: SDSAnyWriteTransaction) {
|
||||
cache.didInsertOrUpdate(value: interaction, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func didReadInteraction(_ interaction: TSInteraction, transaction: SDSAnyReadTransaction) {
|
||||
cache.didRead(value: interaction, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public class AttachmentReadCache: NSObject {
|
||||
typealias KeyType = NSString
|
||||
typealias ValueType = TSAttachment
|
||||
|
||||
private class Adapter: ModelCacheAdapter<KeyType, ValueType> {
|
||||
override func read(key: KeyType, transaction: SDSAnyReadTransaction) -> ValueType? {
|
||||
return TSAttachment.anyFetch(uniqueId: key as String,
|
||||
transaction: transaction,
|
||||
ignoreCache: true)
|
||||
}
|
||||
|
||||
override func deriveKey(fromValue value: ValueType) -> KeyType {
|
||||
value.uniqueId as NSString
|
||||
}
|
||||
|
||||
override func copy(value: ValueType) throws -> ValueType {
|
||||
return try DeepCopies.deepCopy(value)
|
||||
}
|
||||
|
||||
override func uiReadEvacuation(databaseChanges: UIDatabaseChanges,
|
||||
nsCache: NSCache<KeyType, ModelCacheValueBox<ValueType>>) {
|
||||
// Only evacuate modified models.
|
||||
for uniqueId: String in databaseChanges.attachmentUniqueIds {
|
||||
nsCache.removeObject(forKey: uniqueId as NSString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let cache: ModelReadCacheWrapper<KeyType, ValueType>
|
||||
|
||||
@objc
|
||||
public override init() {
|
||||
cache = ModelReadCacheWrapper(cacheName: "TSAttachment", adapter: Adapter())
|
||||
}
|
||||
|
||||
@objc(getAttachmentForUniqueId:transaction:)
|
||||
public func getAttachment(uniqueId: String, transaction: SDSAnyReadTransaction) -> TSAttachment? {
|
||||
return cache.getValue(for: uniqueId as NSString, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc(didRemoveAttachment:transaction:)
|
||||
public func didRemove(attachment: TSAttachment, transaction: SDSAnyWriteTransaction) {
|
||||
cache.didRemove(value: attachment, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc(didInsertOrUpdateAttachment:transaction:)
|
||||
public func didInsertOrUpdate(attachment: TSAttachment, transaction: SDSAnyWriteTransaction) {
|
||||
cache.didInsertOrUpdate(value: attachment, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func didReadAttachment(_ attachment: TSAttachment, transaction: SDSAnyReadTransaction) {
|
||||
cache.didRead(value: attachment, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public class ModelReadCaches: NSObject {
|
||||
@objc
|
||||
@ -744,4 +939,10 @@ public class ModelReadCaches: NSObject {
|
||||
public let signalAccountReadCache = SignalAccountReadCache()
|
||||
@objc
|
||||
public let signalRecipientReadCache = SignalRecipientReadCache()
|
||||
@objc
|
||||
public let threadReadCache = ThreadReadCache()
|
||||
@objc
|
||||
public let interactionReadCache = InteractionReadCache()
|
||||
@objc
|
||||
public let attachmentReadCache = AttachmentReadCache()
|
||||
}
|
||||
|
||||
@ -69,6 +69,11 @@ NSUInteger const kUserProfileSchemaVersion = 1;
|
||||
return SSKEnvironment.shared.storageServiceManager;
|
||||
}
|
||||
|
||||
- (UserProfileReadCache *)userProfileReadCache
|
||||
{
|
||||
return SSKEnvironment.shared.modelReadCaches.userProfileReadCache;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@synthesize avatarUrlPath = _avatarUrlPath;
|
||||
@ -691,21 +696,21 @@ NSUInteger const kUserProfileSchemaVersion = 1;
|
||||
{
|
||||
[super anyDidInsertWithTransaction:transaction];
|
||||
|
||||
[self.profileManager.userProfileReadCache didInsertOrUpdateUserProfile:self transaction:transaction];
|
||||
[self.userProfileReadCache didInsertOrUpdateUserProfile:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidUpdateWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidUpdateWithTransaction:transaction];
|
||||
|
||||
[self.profileManager.userProfileReadCache didInsertOrUpdateUserProfile:self transaction:transaction];
|
||||
[self.userProfileReadCache didInsertOrUpdateUserProfile:self transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)anyDidRemoveWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
[super anyDidRemoveWithTransaction:transaction];
|
||||
|
||||
[self.profileManager.userProfileReadCache didRemoveUserProfile:self transaction:transaction];
|
||||
[self.userProfileReadCache didRemoveUserProfile:self transaction:transaction];
|
||||
}
|
||||
|
||||
+ (void)mergeUserProfilesIfNecessaryForAddress:(SignalServiceAddress *)address
|
||||
|
||||
@ -29,6 +29,7 @@ public enum GroupV2Access: UInt, Codable {
|
||||
|
||||
// MARK: -
|
||||
|
||||
// This class is immutable.
|
||||
@objc
|
||||
public class GroupAccess: MTLModel {
|
||||
@objc
|
||||
|
||||
Loading…
Reference in New Issue
Block a user