mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			127 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			127 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Swift
		
	
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
 | 
						|
 | 
						|
import Foundation
 | 
						|
 | 
						|
// MARK: - CacheType
 | 
						|
 | 
						|
public protocol MutableCacheType {}
 | 
						|
public protocol ImmutableCacheType {}
 | 
						|
 | 
						|
// MARK: - Cache
 | 
						|
 | 
						|
public class Cache {}
 | 
						|
 | 
						|
// MARK: - CacheInfo
 | 
						|
 | 
						|
public enum CacheInfo {
 | 
						|
    public class Config<M, I>: Cache {
 | 
						|
        public let key: Int
 | 
						|
        public let createInstance: () -> M
 | 
						|
        public let mutableInstance: (M) -> MutableCacheType
 | 
						|
        public let immutableInstance: (M) -> I
 | 
						|
        
 | 
						|
        fileprivate init(
 | 
						|
            createInstance: @escaping () -> M,
 | 
						|
            mutableInstance: @escaping (M) -> MutableCacheType,
 | 
						|
            immutableInstance: @escaping (M) -> I
 | 
						|
        ) {
 | 
						|
            self.key = ObjectIdentifier(M.self).hashValue
 | 
						|
            self.createInstance = createInstance
 | 
						|
            self.mutableInstance = mutableInstance
 | 
						|
            self.immutableInstance = immutableInstance
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
public extension CacheInfo {
 | 
						|
    static func create<M, I>(
 | 
						|
        createInstance: @escaping () -> M,
 | 
						|
        mutableInstance: @escaping (M) -> MutableCacheType,
 | 
						|
        immutableInstance: @escaping (M) -> I
 | 
						|
    ) -> CacheInfo.Config<M, I> {
 | 
						|
        return CacheInfo.Config(
 | 
						|
            createInstance: createInstance,
 | 
						|
            mutableInstance: mutableInstance,
 | 
						|
            immutableInstance: immutableInstance
 | 
						|
        )
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
public protocol CacheType: MutableCacheType {
 | 
						|
    associatedtype ImmutableCache = ImmutableCacheType
 | 
						|
    associatedtype MutableCache: MutableCacheType
 | 
						|
    
 | 
						|
    init()
 | 
						|
    func mutableInstance() -> MutableCache
 | 
						|
    func immutableInstance() -> ImmutableCache
 | 
						|
}
 | 
						|
 | 
						|
public extension CacheType where MutableCache == Self {
 | 
						|
    func mutableInstance() -> Self { return self }
 | 
						|
}
 | 
						|
 | 
						|
public protocol CachesType {
 | 
						|
    subscript<M, I>(cache: CacheInfo.Config<M, I>) -> I { get }
 | 
						|
    
 | 
						|
    @discardableResult func mutate<M, I, R>(
 | 
						|
        cache: CacheInfo.Config<M, I>,
 | 
						|
        _ mutation: (inout M) -> R
 | 
						|
    ) -> R
 | 
						|
    @discardableResult func mutate<M, I, R>(
 | 
						|
        cache: CacheInfo.Config<M, I>,
 | 
						|
        _ mutation: (inout M) throws -> R
 | 
						|
    ) throws -> R
 | 
						|
}
 | 
						|
 | 
						|
// MARK: - Caches Logic
 | 
						|
 | 
						|
public extension Dependencies {
 | 
						|
    class Caches: CachesType {
 | 
						|
        /// The caches need to be accessed as singleton instances so we store them in a static variable in the `Caches` type
 | 
						|
        private static var cacheInstances: Atomic<[Int: MutableCacheType]> = Atomic([:])
 | 
						|
        
 | 
						|
        // MARK: - Initialization
 | 
						|
        
 | 
						|
        public init() {}
 | 
						|
        
 | 
						|
        // MARK: - Immutable Access
 | 
						|
        
 | 
						|
        public subscript<M, I>(cache: CacheInfo.Config<M, I>) -> I {
 | 
						|
            get { Caches.getValueSettingIfNull(cache: cache, &Caches.cacheInstances) }
 | 
						|
        }
 | 
						|
        
 | 
						|
        // MARK: - Mutable Access
 | 
						|
        
 | 
						|
        @discardableResult public func mutate<M, I, R>(cache: CacheInfo.Config<M, I>, _ mutation: (inout M) -> R) -> R {
 | 
						|
            return Caches.cacheInstances.mutate { caches in
 | 
						|
                var value: M = ((caches[cache.key] as? M) ?? cache.createInstance())
 | 
						|
                return mutation(&value)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        @discardableResult public func mutate<M, I, R>(cache: CacheInfo.Config<M, I>, _ mutation: (inout M) throws -> R) throws -> R {
 | 
						|
            return try Caches.cacheInstances.mutate { caches in
 | 
						|
                var value: M = ((caches[cache.key] as? M) ?? cache.createInstance())
 | 
						|
                return try mutation(&value)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        // MARK: - Convenience
 | 
						|
        
 | 
						|
        @discardableResult private static func getValueSettingIfNull<M, I>(
 | 
						|
            cache: CacheInfo.Config<M, I>,
 | 
						|
            _ store: inout Atomic<[Int: MutableCacheType]>
 | 
						|
        ) -> I {
 | 
						|
            guard let value: M = (store.wrappedValue[cache.key] as? M) else {
 | 
						|
                let value: M = cache.createInstance()
 | 
						|
                let mutableInstance: MutableCacheType = cache.mutableInstance(value)
 | 
						|
                store.mutate { $0[cache.key] = mutableInstance }
 | 
						|
                return cache.immutableInstance(value)
 | 
						|
            }
 | 
						|
            
 | 
						|
            return cache.immutableInstance(value)
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |