iOS
Caching Data Basic

File caching: Basic

This page shows you how to build a simple cache that stores a user's most recent channels and messages. A cache can be used to load data when an offline user attempts to view a list of channels, or enters a channel to view their chat history. Implementing just a basic cache like the following can improve user experience, as users get to see empty lists of channels or messages due to their unstable connection.

In the steps described below, we create a file per channel in your client app's cache directory, write serialized data into the file to store a set amount of recent messages, configure the app to first load messages from the cache, and then finally replace them when the newest results are successfully fetched from SendBird server.


Serialize and deserialize SendBird objects

In order to enable you to store SendBird objects such as messages, channels, and users in a local storage, we provide serialization and deserialization methods through our SDK.

By using the serialize method to convert a SendBird object to binary data like the following, you can store the binary data natively in a file.

Objective-C
Swift
Light Color Skin
Copy
// In SBDBaseMessage.h
- (nullable NSData *)serialize;
+ (nullable instancetype)buildFromSerializedData:(NSData * _Nonnull)data;
Light Color Skin
Copy
// In SBDBaseMessage.h
func serialize() -> Data?
class func build(fromSerializedData data: Data) -> Self?

Save serialized messages

With serialization, you can store a channel and its most recent messages in a file. In this case, we are encoding the binary serialized data into a Base64 string. then storing each item in a new line. Normally, save data when onStop() is called in your user's chat screen.

Note: Because messages in a channel can be updated or deleted, to keep your cache synced with data in SendBird server, your client app should check changes to the messages regularly and apply the changes to the cache. You can retrieve change logs of messages in a channel by using the getMessageChangeLogsWithToken:completionHandler: or getMessageChangeLogsByTimestamp:completionHandler: method, with which you can manage cache updates.

Objective-C
Swift
Light Color Skin
Copy
// Saving a serialized message.
+ (void)saveMessage:(SBDBaseMessage *)message {
    // Check if the message is nil
    if (message == nil) {
        return;
    }

    // A value of 0 indicates that the message has not sent to SendBird server yet. 
    if (message.messageId == 0) {
        return;     // The message of which ID is 0 should be excluded from caching.
    }

    // Serialization
    NSData *messageData = [message serialize];

    // Encryption
    NSString *messageHash = SHA256NSStringFromData(messageData);    // TODO: Implement your own function for SHA256 encyption.

    // Writing the hashed message to a local file
    SaveHashIntoFile(messageHash);      // TODO: Implement your own function which saves a hashed message into a local file.
}
Light Color Skin
Copy
// Saving a serialized message.
func save(message: SBDBaseMessage?) {
    guard let theMessage: SBDBaseMessage = message, theMessage.messageId != 0 else {    // Check the message.
        return      // A value of 0 indicates that the message has not sent to SendBird server yet. The message of which ID is 0 should be excluded from caching   
    }
    
    // Serialization
    let messageData: Data? = theMessage.serialize()

    // Encryption
    let messageHash: String? = SHA256StringFromData(messageData)    // TODO: Implement your own function for SHA256 encyption.
    
    // Writing the hashed message to a local file
    SaveHashIntoFile(messageHash)       // TODO: Implement your own function which saves a hashed message into a local file.
}

Note: In this case, SHA256 hashing is used to generate a hash file for each stored data file. Using this hash file, you can check if the newly generated data differs from the corresponding one already stored in the cache, preventing an unnecessary overwriting.


Load messages with deseralization

When your user enters a chat to view their message history, load saved messages from the cache.

Objective-C
Swift
Light Color Skin
Copy
// Loading messages with deserialization.
+ (NSArray<SBDBaseMessage *> *)loadMessagesInChannelUrl:(NSString *)channelUrl {
    // Load hashed messages from a local file.
    NSArray<NSString *> *hashs = LoadHashsFromFile(channelUrl);

    if (hashs == nil) {
        return nil;
    }

    NSMutableArray<SBDBaseMessage *> *messages = [NSMutableArray array];
    for (NSString *hash in hashs) {
        // Decryption of each hashed message
        NSData *data = DecryptStringBySHA256(hash);     // TODO: Implement your own function for SHA256 decryption.

        // Deserialization
        SBDBaseMessage *message = [SBDBaseMessage buildFromSerializedData:data];
        [messages addObject:message];
    }

    return [messages copy];
}
Light Color Skin
Copy
// Loading messages with deserialization.
func loadMessages(channelUrl: String) -> [SBDBaseMessage] {
    // Load hashed messages from a local file
    let messageHashs: String? = LoadHashFromFile(channelUrl)

    // Decryption of the hashed messages
    let messageData: [Data]? = DecryptStringBySHA256(messageHashs?)     // TODO: Implement your own function for SHA256 decryption.

    // Deserialization
    var messages: [SBDBaseMessage] = []
    for data in messageData {
        let message: SBDBaseMessage? = SBDBaseMessage.build(fromSerializedData: data)
        if let theMessage: SBDBaseMessage = message {
            messages.append(theMessage)
        }
    }

    return messages
}

After receiving an updated message list from SendBird server, clear the current message list and replace it with the updated list. In effect, messages from the cache are overwritten almost instantly if the user's connection is normal.


Save and load channels

We recommend that you only cache group channels in a local file rather than open channels. The property values and participant status of open channels can be frequently changed depending on your use case, and you might go through difficulty in syncing the local file with the changes.

The process of caching channels is similar to caching messages. For the sake of brevity, the following code is provided without additional explanations.

Note: In group channels, information associated with them can be updated or the current user might leave them anytime. To keep your cache synced with data in SendBird server, your client app should check information about changes to the channels regularly and apply the changes to the cache. You can retrieve change logs of the current user's group channels by using getMyGroupChannelChangeLogsByToken:customTypes:completionHandler: or getMyGroupChannelChangeLogsByTimestamp:customTypes:completionHandler: method, with which you can manage cache updates.

Objective-C
Swift
Light Color Skin
Copy
// Saving serialized group channels. 
+ (void)saveChannels:(NSArray<SBDGroupChannel *> *)channels {
    // Check the channels.
    if (channels == nil || channels.count == 0) {
        return;
    }

    NSMutableArray<NSString *> *hashs = [NSMutableArray array];
    for (SBDGroupChannel *channel in channels) {
        // Serialization
        NSData *data = [channel serialize];

        // Encryption
        NSString *hash = EncryptDataBySHA256(data);     // TODO: Implement your own function for SHA256 encyption.

        [hashs addObject:hash];
    }

    // Writing the hashed channels into a local file.
    SaveHashsIntoFile(hashs);       // TODO: Implement your own function which saves hashed channels into a local file.
}

// Loading group channels with deserialization.
+ (NSArray<SBDGroupChannel *> *)loadGroupChannels {
    // Load the hashed channels from a local file.
    NSArray<NSString *> *hashs = LoadChannelHashFromFile();

    if (hashs == nil) {
        return nil;
    }

    NSMutableArray<SBDGroupChannel *> *channels = [NSMutableArray array];
    for (NSString *hash in hashs) {
        // Decryption
        NSData *data = DecryptStringBySHA256(hash);

        // Deserialization
        SBDGroupChannel *channel = [SBDGroupChannel buildFromSerializedData:data];
        [channels addObject:channel];
    }
        
    return [channels copy];
}
Light Color Skin
Copy
// Saving serialized group channels.
func save(channels: [SBDGroupChannel]?) {
    guard let theChannels: [SBDGroupChannel] = channels else {
        return
    }

    var hashs: [String] = []
    for channel in theChannels {
        // Serialization 
        let data: Data? = channel.serialize()

        // Encryption
        let hash: String? = SHA256StringFromData(data)      // TODO: Implement your own function for SHA256 encyption.

        if let theHash: String = hash {
            hashs.append(theHash)
        }
    }

    // Writing the hashed channels into a local file.
    SaveHashIntoFile(hashs)         // TODO: Implement your own function which saves hashed channels into a local file.
}

// Loading group channels with deserialization.
func loadChannels() -> [SBDGroupChannel] {
    // Load the hashed channels from from a local file.
    let hashs: [String] = LoadChannelHashsFromFile()
    
    var channels: [SBDGroupChannel] = []
    for hash in hashs {
        // Decryption
        let data: Data? = DecryptStringBySHA256(hash)

        // Deserialization
        let channel: SBDGroupChannel? = SBDGroupChannel.build(fromSerializedData: data)
        if let theChannel: SBDGroupChannel = channel {
            channels.append(theChannel)
        }
    }

    return channels
}