YapDatabaseConnectionProxy
@interface YapDatabaseConnectionProxy : NSObject
A proxy
connection is a trade-off in terms of the ACID guarantees of the database.
Under normal operations, you must execute a read-write transaction in order to modify the datatbase. If the transaction completes, then all data from the transaction has been written to the database. And further, the transaction is durable even in the event of an application or system crash. In other words, you’re guaranteed the data will be there when the app re-launches.
A proxy connection allows you to relax these constraints, which may be useful for certain subsets of your data.
Here’s how it works:
- you write collection/key/value rows to a proxy connection instance
- the value(s) are immediately readable via the proxy connection instance
- the proxy will attempt to write the changes (in batches) at some point in the near future
Thus you can read & write values as if the proxy is an in-memory dictionary. However, the proxy will transparently write the changes to the database (but without any guarantees).
** When should I use a proxy ?
You can use a proxy when:
- it’s not important that you manage an individual transaction
- it’s not important if the values don’t make it to disk
Example #1 : A download manager
Applications sometimes have download logic encapsulated in a manager
class.
The manager class typically handles things such as:
- downloaing resources on demand
- parsing the results
- providing getter methods to fetch particular items
- automatically checking expiration dates & refreshing items as needed
- automatically deleting unused expired items
You’ll notice the manager class is the ONLY class that handles reading/writing certain values in the database. And it doesn’t matter if a value doesn’t get written to disk, as it can simply be re-downloaed.
Example #2 : NSUserDefaults replacement
NSUserDefaults works similarly to a connection proxy. From the documentation for NSUserDefaults:
The synchronize method, which is automatically invoked at periodic intervals, keeps the in-memory cache in sync with a user’s defaults database.
So NSUserDefaults will eventually
write changes to disk.
Unless you invoke the synchronize method, which involves waiting for disk IO to complete.
Thus a connection proxy can easily replace your NSUserDefaults system.
It’s worth pointing out that a proxy connection doesn’t have the equivalent of a synchronize method because
you don’t need one. A connection proxy always begins an asyncReadWrite transaction once it becomes dirty
.
In case you’re wondering, Why would I use YapDatabase over NSUserDefaults ?
- NSUserDefaults is not encrypted. And you can easily encrypt a YapDatabase.
- NSUserDefaults writes ALL values to disk everytime because it uses a plist to store the values on disk. YapDatabase uses a database, so only changed values need to be re-written. Thus, YapDatabase has the potential to be faster and involve less disk IO.
- YapDatabase has a notification system that tells you exactly which key/value pairs were changed.
- YapDatabase makes it easier to sync your data using a wide variety of cloud services.
** Caveats
A connection proxy instance expects to own
a subset of the database.
That is, it expects to be the only thing reading & writing a subset of rows in the database.
If you violate this, you may get unexpected results.
Example #1 :
- You write a value using a proxy.
- You then attempt to read that value using a regular database connection.
- If the proxy hasn’t written the value to disk yet, then the regualar database connection won’t see the proper value.
Example #2 :
- You write a value using the proxy.
- You write a different value (for the same collection/key) using the regular database connection.
- The proxy later performs a readWrite, and writes its value for the collection/key, overwriting the regular database connection.
-
Initializes a new connction proxy by creating both the readOnlyConnection & readWriteConnection via [database newConnection]. Both connection’s receive the default configuration for the database.
Declaration
Objective-C
- (nonnull instancetype)initWithDatabase:(nonnull YapDatabase *)database;
Swift
init(database: YapDatabase)
-
Initializes a new connection proxy using the (optional) given connections.
Declaration
Objective-C
- (nonnull instancetype) initWithDatabase:(nonnull YapDatabase *)database readOnlyConnection:(nullable YapDatabaseConnection *)readOnlyConnection readWriteConnection:(nullable YapDatabaseConnection *)readWriteConnection;
Swift
init(database: YapDatabase, readOnlyConnection: YapDatabaseConnection?, readWrite readWriteConnection: YapDatabaseConnection?)
Parameters
database
The underlying database to use.
readOnlyConnection
You may pass a readOnlyConnection if you want to share a read-only connection amongst multiple classes. However, you must be sure to NEVER perform a write on the read-only connection.
readWriteConnection
You may pass a readWriteConnection if you want to share a read-write connection amongst multiple classes.
-
Undocumented
Declaration
Objective-C
@property (nonatomic, strong, readonly) YapDatabaseConnection *readOnlyConnection
Swift
var readOnlyConnection: YapDatabaseConnection { get }
-
Undocumented
Declaration
Objective-C
@property (nonatomic, strong, readonly) YapDatabaseConnection *readWriteConnection
Swift
var readWriteConnection: YapDatabaseConnection { get }
-
Returns the proxy’s value for the given collection/key tuple.
If this proxy instance has recently had a value set for the given collection/key, then that value is returned, even if the value has not been written to the database yet.
Declaration
Objective-C
- (nullable id)objectForKey:(nonnull NSString *)key inCollection:(nullable NSString *)collection;
Swift
func object(forKey key: String, inCollection collection: String?) -> Any?
-
Undocumented
Declaration
Objective-C
- (nullable id)metadataForKey:(NSString *)key inCollection:(nullable NSString *)collection;
Swift
func metadata(forKey key: String, inCollection collection: String?) -> Any?
-
Undocumented
Declaration
Objective-C
- (BOOL)getObject:(__nullable id * __nullable)objectPtr metadata:(__nullable id * __nullable)metadataPtr forKey:(NSString *)key inCollection:(nullable NSString *)collection;
Swift
func getObject(_ objectPtr: AutoreleasingUnsafeMutablePointer<AnyObject?>?, metadata metadataPtr: AutoreleasingUnsafeMutablePointer<AnyObject?>?, forKey key: String, inCollection collection: String?) -> Bool
-
Sets a value for the given collection/key tuple.
The proxy will attempt to write the value to the database at some point in the near future. If the application is terminated before the write completes, then the value may not make it to the database. However, the proxy will immediately begin to return the new value when queried for the same collection/key tuple.
This is the trade-off you make when using a proxy. The values written to a proxy are not guaranteed to be written to the database. However, the values are immediately available (from this proxy instance) without waiting for the database disk IO.
Declaration
Objective-C
- (void)setObject:(nullable id)object forKey:(nonnull NSString *)key inCollection:(nullable NSString *)collection;
Swift
func setObject(_ object: Any?, forKey key: String, inCollection collection: String?)
Parameters
object
The value for the collection/key tuple. If nil, this is equivalent to invoking removeObjectForKey:inCollection:
-
Undocumented
Declaration
Objective-C
- (void)setObject:(nullable id)object forKey:(NSString *)key inCollection:(nullable NSString *)collection withMetadata:(nullable id)metadata;
Swift
func setObject(_ object: Any?, forKey key: String, inCollection collection: String?, withMetadata metadata: Any?)
-
The replace methods allows you to modify the object, without modifying the metadata for the row. Or vice-versa, you can modify the metadata, without modifying the object for the row.
If there is no row in the database for the given key/collection then this method does nothing.
The proxy will attempt to write the value to the database at some point in the near future. If the application is terminated before the write completes, then the value may not make it to the database. However, the proxy will immediately begin to return the new value when queried for the same collection/key tuple.
This is the trade-off you make when using a proxy. The values written to a proxy are not guaranteed to be written to the database. However, the values are immediately available (from this proxy instance) without waiting for the database disk IO.
Declaration
Objective-C
- (void)replaceObject:(nullable id)object forKey:(nonnull NSString *)key inCollection:(nullable NSString *)collection;
Swift
func replace(_ object: Any?, forKey key: String, inCollection collection: String?)
-
Undocumented
Declaration
Objective-C
- (void)replaceMetadata:(nullable id)metadata forKey:(NSString *)key inCollection:(nullable NSString *)collection;
Swift
func replaceMetadata(_ metadata: Any?, forKey key: String, inCollection collection: String?)
-
Removes any set value for the given collection/key tuple.
The proxy will attempt to remove the value from the database at some point in the near future. If the application is terminated before the write completes, then the update may not make it to the database. However, the proxy will immediately begin to return nil when queried for the same collection/key tuple.
This is the trade-off you make when using a proxy. The values written to a proxy are not guaranteed to be written to the database. However, the values are immediately available (from this proxy instance) without waiting for the database disk IO.
Declaration
Objective-C
- (void)removeObjectForKey:(nonnull NSString *)key inCollection:(nullable NSString *)collection;
Swift
func removeObject(forKey key: String, inCollection collection: String?)
-
Removes any set value(s) for the given collection/key tuple(s).
The proxy will attempt to remove the value(s) from the database at some point in the near future. If the application is terminated before the write completes, then the update may not make it to the database. However, the proxy will immediately begin to return nil when queried for the same collection/key tuple.
This is the trade-off you make when using a proxy. The values written to a proxy are not guaranteed to be written to the database. However, the values are immediately available (from this proxy instance) without waiting for the database disk IO.
Declaration
Objective-C
- (void)removeObjectsForKeys:(nonnull NSArray<NSString *> *)keys inCollection:(nullable NSString *)collection;
Swift
func removeObjects(forKeys keys: [String], inCollection collection: String?)
-
Tells the proxy to discard pending changes (if any) for the given
tuple. That is, IF the proxy has a pending change (for the tuple) that it intends to write to the database during the next flush, it will drop the pending change (and not write it to the database).
Declaration
Objective-C
- (void)resetObjectForKey:(nonnull NSString *)key inCollection:(nullable NSString *)collection;
Swift
func resetObject(forKey key: String, inCollection collection: String?)
-
Tells the proxy to discard ALL pending changes.
That is, IF the proxy has pending changes that it intends to write to the database during the next flush, it will instead drop all those pending changes (and not write any of them).
Declaration
Objective-C
- (void)reset;
Swift
func reset()
-
The fetchedCollectionsFilter is useful when you need to delete one or more collections from the database. For example:
- you’re going to ASYNCHRONOUSLY delete the
foobar
collection from the database - you want to instruct the connectionProxy to act as if its readOnlyConnection doesn’t see any objects in this collection (even before the ASYNC cleanup transaction completes).
- when the cleanup transaction does complete, you instruct the connectionProxy to return to normal.
NSSet *set = [NSSet setWithObject:@
foobar
]; YapWhitelistBlacklist *blacklist = [[YapWhitelistBlacklist alloc] initWithBlacklist:set]; connectionProxy.fetchedCollectionsFilter = blacklist;[connectionProxy.readWriteConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction){
[transaction removeAllObjectsInCollection:@"foobar"];
} completionBlock:^{
// allow the connectionProxy to start reading the "foobar" collection again connectionProxy.fetchedCollectionsFilter = nil;
}];
Keep in mind that the fetchedCollectionsFilter only applies to how the proxy interacts with the readOnlyConnection. That is, it still allows the proxy to write values to any collection.
Declaration
Objective-C
@property (readwrite, strong, atomic, nullable) YapWhitelistBlacklist *fetchedCollectionsFilter;
Swift
var fetchedCollectionsFilter: YapWhitelistBlacklist? { get set }
- you’re going to ASYNCHRONOUSLY delete the
-
Deprecated
Replace with the following code:
// blacklist everything - act as if db is empty YapWhitelistBlacklist *whitelist = [[YapWhitelistBlacklist alloc] initWithWhitelist:nil]; connectionProxy.fetchedCollectionsFilter = whitelist; [connectionProxy reset];
// Then actually clear the db - but asynchronously [databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction){
[transaction removeAllObjectsInAllCollections];
} completionBlock:^{
// allow the connectionProxy to start reading from the db again connectionProxy.fetchedCollectionsFilter = nil;
}];
Declaration
Objective-C
- (void)abortAndReset: (nullable YapWhitelistBlacklist *)fetchedCollectionsFilter;
Swift
func abortAndReset(_ fetchedCollectionsFilter: YapWhitelistBlacklist?)