//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
import GRDB
import SignalCoreKit

// NOTE: This file is generated by /Scripts/sds_codegen/sds_generate.py.
// Do not manually edit it, instead run `sds_codegen.sh`.

// MARK: - Record

public struct InteractionRecord: SDSRecord {
    public weak var delegate: SDSRecordDelegate?

    public var tableMetadata: SDSTableMetadata {
        TSInteractionSerializer.table
    }

    public static var databaseTableName: String {
        TSInteractionSerializer.table.tableName
    }

    public var id: Int64?

    // This defines all of the columns used in the table
    // where this model (and any subclasses) are persisted.
    public let recordType: SDSRecordType
    public let uniqueId: String

    // Properties
    public let receivedAtTimestamp: UInt64
    public let timestamp: UInt64
    public let threadUniqueId: String
    public let attachmentIds: Data?
    public let authorId: String?
    public let authorPhoneNumber: String?
    public let authorUUID: String?
    public let body: String?
    public let callType: RPRecentCallType?
    public let configurationDurationSeconds: UInt32?
    public let configurationIsEnabled: Bool?
    public let contactShare: Data?
    public let createdByRemoteName: String?
    public let createdInExistingGroup: Bool?
    public let customMessage: String?
    public let envelopeData: Data?
    public let errorType: TSErrorMessageType?
    public let expireStartedAt: UInt64?
    public let expiresAt: UInt64?
    public let expiresInSeconds: UInt32?
    public let groupMetaMessage: TSGroupMetaMessage?
    public let hasLegacyMessageState: Bool?
    public let hasSyncedTranscript: Bool?
    public let wasNotCreatedLocally: Bool?
    public let isLocalChange: Bool?
    public let isViewOnceComplete: Bool?
    public let isViewOnceMessage: Bool?
    public let isVoiceMessage: Bool?
    public let legacyMessageState: TSOutgoingMessageState?
    public let legacyWasDelivered: Bool?
    public let linkPreview: Data?
    public let messageId: String?
    public let messageSticker: Data?
    public let messageType: TSInfoMessageType?
    public let mostRecentFailureText: String?
    public let preKeyBundle: Data?
    public let protocolVersion: UInt?
    public let quotedMessage: Data?
    public let read: Bool?
    public let recipientAddress: Data?
    public let recipientAddressStates: Data?
    public let sender: Data?
    public let serverTimestamp: UInt64?
    public let deprecated_sourceDeviceId: UInt32?
    public let storedMessageState: TSOutgoingMessageState?
    public let storedShouldStartExpireTimer: Bool?
    public let unregisteredAddress: Data?
    public let verificationState: OWSVerificationState?
    public let wasReceivedByUD: Bool?
    public let infoMessageUserInfo: Data?
    public let wasRemotelyDeleted: Bool?
    public let bodyRanges: Data?
    public let offerType: TSRecentCallOfferType?
    public let serverDeliveryTimestamp: UInt64?
    public let eraId: String?
    public let hasEnded: Bool?
    public let creatorUuid: String?
    public let joinedMemberUuids: Data?
    public let wasIdentityVerified: Bool?
    public let paymentCancellation: Data?
    public let paymentNotification: Data?
    public let paymentRequest: Data?
    public let viewed: Bool?
    public let serverGuid: String?
    public let storyAuthorUuidString: String?
    public let storyTimestamp: UInt64?
    public let isGroupStoryReply: Bool?
    public let storyReactionEmoji: String?
    public let giftBadge: Data?
    public let editState: TSEditState?

    public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable {
        case id
        case recordType
        case uniqueId
        case receivedAtTimestamp
        case timestamp
        case threadUniqueId = "uniqueThreadId"
        case attachmentIds
        case authorId
        case authorPhoneNumber
        case authorUUID
        case body
        case callType
        case configurationDurationSeconds
        case configurationIsEnabled
        case contactShare
        case createdByRemoteName
        case createdInExistingGroup
        case customMessage
        case envelopeData
        case errorType
        case expireStartedAt
        case expiresAt
        case expiresInSeconds
        case groupMetaMessage
        case hasLegacyMessageState
        case hasSyncedTranscript
        case wasNotCreatedLocally
        case isLocalChange
        case isViewOnceComplete
        case isViewOnceMessage
        case isVoiceMessage
        case legacyMessageState
        case legacyWasDelivered
        case linkPreview
        case messageId
        case messageSticker
        case messageType
        case mostRecentFailureText
        case preKeyBundle
        case protocolVersion
        case quotedMessage
        case read
        case recipientAddress
        case recipientAddressStates
        case sender
        case serverTimestamp
        case deprecated_sourceDeviceId
        case storedMessageState
        case storedShouldStartExpireTimer
        case unregisteredAddress
        case verificationState
        case wasReceivedByUD
        case infoMessageUserInfo
        case wasRemotelyDeleted
        case bodyRanges
        case offerType
        case serverDeliveryTimestamp
        case eraId
        case hasEnded
        case creatorUuid
        case joinedMemberUuids
        case wasIdentityVerified
        case paymentCancellation
        case paymentNotification
        case paymentRequest
        case viewed
        case serverGuid
        case storyAuthorUuidString
        case storyTimestamp
        case isGroupStoryReply
        case storyReactionEmoji
        case giftBadge
        case editState
    }

    public static func columnName(_ column: InteractionRecord.CodingKeys, fullyQualified: Bool = false) -> String {
        fullyQualified ? "\(databaseTableName).\(column.rawValue)" : column.rawValue
    }

    public func didInsert(with rowID: Int64, for column: String?) {
        guard let delegate = delegate else {
            owsFailDebug("Missing delegate.")
            return
        }
        delegate.updateRowId(rowID)
    }
}

// MARK: - Row Initializer

public extension InteractionRecord {
    static var databaseSelection: [SQLSelectable] {
        CodingKeys.allCases
    }

    init(row: Row) {
        id = row[0]
        recordType = row[1]
        uniqueId = row[2]
        receivedAtTimestamp = row[3]
        timestamp = row[4]
        threadUniqueId = row[5]
        attachmentIds = row[6]
        authorId = row[7]
        authorPhoneNumber = row[8]
        authorUUID = row[9]
        body = row[10]
        callType = row[11]
        configurationDurationSeconds = row[12]
        configurationIsEnabled = row[13]
        contactShare = row[14]
        createdByRemoteName = row[15]
        createdInExistingGroup = row[16]
        customMessage = row[17]
        envelopeData = row[18]
        errorType = row[19]
        expireStartedAt = row[20]
        expiresAt = row[21]
        expiresInSeconds = row[22]
        groupMetaMessage = row[23]
        hasLegacyMessageState = row[24]
        hasSyncedTranscript = row[25]
        wasNotCreatedLocally = row[26]
        isLocalChange = row[27]
        isViewOnceComplete = row[28]
        isViewOnceMessage = row[29]
        isVoiceMessage = row[30]
        legacyMessageState = row[31]
        legacyWasDelivered = row[32]
        linkPreview = row[33]
        messageId = row[34]
        messageSticker = row[35]
        messageType = row[36]
        mostRecentFailureText = row[37]
        preKeyBundle = row[38]
        protocolVersion = row[39]
        quotedMessage = row[40]
        read = row[41]
        recipientAddress = row[42]
        recipientAddressStates = row[43]
        sender = row[44]
        serverTimestamp = row[45]
        deprecated_sourceDeviceId = row[46]
        storedMessageState = row[47]
        storedShouldStartExpireTimer = row[48]
        unregisteredAddress = row[49]
        verificationState = row[50]
        wasReceivedByUD = row[51]
        infoMessageUserInfo = row[52]
        wasRemotelyDeleted = row[53]
        bodyRanges = row[54]
        offerType = row[55]
        serverDeliveryTimestamp = row[56]
        eraId = row[57]
        hasEnded = row[58]
        creatorUuid = row[59]
        joinedMemberUuids = row[60]
        wasIdentityVerified = row[61]
        paymentCancellation = row[62]
        paymentNotification = row[63]
        paymentRequest = row[64]
        viewed = row[65]
        serverGuid = row[66]
        storyAuthorUuidString = row[67]
        storyTimestamp = row[68]
        isGroupStoryReply = row[69]
        storyReactionEmoji = row[70]
        giftBadge = row[71]
        editState = row[72]
    }
}

// MARK: - StringInterpolation

public extension String.StringInterpolation {
    mutating func appendInterpolation(interactionColumn column: InteractionRecord.CodingKeys) {
        appendLiteral(InteractionRecord.columnName(column))
    }
    mutating func appendInterpolation(interactionColumnFullyQualified column: InteractionRecord.CodingKeys) {
        appendLiteral(InteractionRecord.columnName(column, fullyQualified: true))
    }
}

// MARK: - Deserialization

extension TSInteraction {
    // This method defines how to deserialize a model, given a
    // database row.  The recordType column is used to determine
    // the corresponding model class.
    class func fromRecord(_ record: InteractionRecord) throws -> TSInteraction {

        guard let recordId = record.id else {
            throw SDSError.invalidValue()
        }

        switch record.recordType {
        case .addToContactsOfferMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo
            let infoMessageUserInfo: [InfoMessageUserInfoKey: AnyObject]? = try SDSDeserialization.optionalUnarchive(infoMessageUserInfoSerialized, name: "infoMessageUserInfo")
            guard let messageType: TSInfoMessageType = record.messageType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let serverGuid: String? = record.serverGuid
            let unregisteredAddressSerialized: Data? = record.unregisteredAddress
            let unregisteredAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(unregisteredAddressSerialized, name: "unregisteredAddress")

            return OWSAddToContactsOfferMessage(grdbId: recordId,
                                                uniqueId: uniqueId,
                                                receivedAtTimestamp: receivedAtTimestamp,
                                                sortId: sortId,
                                                timestamp: timestamp,
                                                uniqueThreadId: uniqueThreadId,
                                                attachmentIds: attachmentIds,
                                                body: body,
                                                bodyRanges: bodyRanges,
                                                contactShare: contactShare,
                                                editState: editState,
                                                expireStartedAt: expireStartedAt,
                                                expiresAt: expiresAt,
                                                expiresInSeconds: expiresInSeconds,
                                                giftBadge: giftBadge,
                                                isGroupStoryReply: isGroupStoryReply,
                                                isViewOnceComplete: isViewOnceComplete,
                                                isViewOnceMessage: isViewOnceMessage,
                                                linkPreview: linkPreview,
                                                messageSticker: messageSticker,
                                                quotedMessage: quotedMessage,
                                                storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                storyAuthorUuidString: storyAuthorUuidString,
                                                storyReactionEmoji: storyReactionEmoji,
                                                storyTimestamp: storyTimestamp,
                                                wasRemotelyDeleted: wasRemotelyDeleted,
                                                customMessage: customMessage,
                                                infoMessageUserInfo: infoMessageUserInfo,
                                                messageType: messageType,
                                                read: read,
                                                serverGuid: serverGuid,
                                                unregisteredAddress: unregisteredAddress)

        case .addToProfileWhitelistOfferMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo
            let infoMessageUserInfo: [InfoMessageUserInfoKey: AnyObject]? = try SDSDeserialization.optionalUnarchive(infoMessageUserInfoSerialized, name: "infoMessageUserInfo")
            guard let messageType: TSInfoMessageType = record.messageType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let serverGuid: String? = record.serverGuid
            let unregisteredAddressSerialized: Data? = record.unregisteredAddress
            let unregisteredAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(unregisteredAddressSerialized, name: "unregisteredAddress")

            return OWSAddToProfileWhitelistOfferMessage(grdbId: recordId,
                                                        uniqueId: uniqueId,
                                                        receivedAtTimestamp: receivedAtTimestamp,
                                                        sortId: sortId,
                                                        timestamp: timestamp,
                                                        uniqueThreadId: uniqueThreadId,
                                                        attachmentIds: attachmentIds,
                                                        body: body,
                                                        bodyRanges: bodyRanges,
                                                        contactShare: contactShare,
                                                        editState: editState,
                                                        expireStartedAt: expireStartedAt,
                                                        expiresAt: expiresAt,
                                                        expiresInSeconds: expiresInSeconds,
                                                        giftBadge: giftBadge,
                                                        isGroupStoryReply: isGroupStoryReply,
                                                        isViewOnceComplete: isViewOnceComplete,
                                                        isViewOnceMessage: isViewOnceMessage,
                                                        linkPreview: linkPreview,
                                                        messageSticker: messageSticker,
                                                        quotedMessage: quotedMessage,
                                                        storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                        storyAuthorUuidString: storyAuthorUuidString,
                                                        storyReactionEmoji: storyReactionEmoji,
                                                        storyTimestamp: storyTimestamp,
                                                        wasRemotelyDeleted: wasRemotelyDeleted,
                                                        customMessage: customMessage,
                                                        infoMessageUserInfo: infoMessageUserInfo,
                                                        messageType: messageType,
                                                        read: read,
                                                        serverGuid: serverGuid,
                                                        unregisteredAddress: unregisteredAddress)

        case .disappearingConfigurationUpdateInfoMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo
            let infoMessageUserInfo: [InfoMessageUserInfoKey: AnyObject]? = try SDSDeserialization.optionalUnarchive(infoMessageUserInfoSerialized, name: "infoMessageUserInfo")
            guard let messageType: TSInfoMessageType = record.messageType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let serverGuid: String? = record.serverGuid
            let unregisteredAddressSerialized: Data? = record.unregisteredAddress
            let unregisteredAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(unregisteredAddressSerialized, name: "unregisteredAddress")
            let configurationDurationSeconds: UInt32 = try SDSDeserialization.required(record.configurationDurationSeconds, name: "configurationDurationSeconds")
            let configurationIsEnabled: Bool = try SDSDeserialization.required(record.configurationIsEnabled, name: "configurationIsEnabled")
            let createdByRemoteName: String? = record.createdByRemoteName
            let createdInExistingGroup: Bool = try SDSDeserialization.required(record.createdInExistingGroup, name: "createdInExistingGroup")

            return OWSDisappearingConfigurationUpdateInfoMessage(grdbId: recordId,
                                                                 uniqueId: uniqueId,
                                                                 receivedAtTimestamp: receivedAtTimestamp,
                                                                 sortId: sortId,
                                                                 timestamp: timestamp,
                                                                 uniqueThreadId: uniqueThreadId,
                                                                 attachmentIds: attachmentIds,
                                                                 body: body,
                                                                 bodyRanges: bodyRanges,
                                                                 contactShare: contactShare,
                                                                 editState: editState,
                                                                 expireStartedAt: expireStartedAt,
                                                                 expiresAt: expiresAt,
                                                                 expiresInSeconds: expiresInSeconds,
                                                                 giftBadge: giftBadge,
                                                                 isGroupStoryReply: isGroupStoryReply,
                                                                 isViewOnceComplete: isViewOnceComplete,
                                                                 isViewOnceMessage: isViewOnceMessage,
                                                                 linkPreview: linkPreview,
                                                                 messageSticker: messageSticker,
                                                                 quotedMessage: quotedMessage,
                                                                 storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                                 storyAuthorUuidString: storyAuthorUuidString,
                                                                 storyReactionEmoji: storyReactionEmoji,
                                                                 storyTimestamp: storyTimestamp,
                                                                 wasRemotelyDeleted: wasRemotelyDeleted,
                                                                 customMessage: customMessage,
                                                                 infoMessageUserInfo: infoMessageUserInfo,
                                                                 messageType: messageType,
                                                                 read: read,
                                                                 serverGuid: serverGuid,
                                                                 unregisteredAddress: unregisteredAddress,
                                                                 configurationDurationSeconds: configurationDurationSeconds,
                                                                 configurationIsEnabled: configurationIsEnabled,
                                                                 createdByRemoteName: createdByRemoteName,
                                                                 createdInExistingGroup: createdInExistingGroup)

        case .groupCallMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let creatorUuid: String? = record.creatorUuid
            let eraId: String? = record.eraId
            let hasEnded: Bool = try SDSDeserialization.required(record.hasEnded, name: "hasEnded")
            let joinedMemberUuidsSerialized: Data? = record.joinedMemberUuids
            let joinedMemberUuids: [String]? = try SDSDeserialization.optionalUnarchive(joinedMemberUuidsSerialized, name: "joinedMemberUuids")
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")

            return OWSGroupCallMessage(grdbId: recordId,
                                       uniqueId: uniqueId,
                                       receivedAtTimestamp: receivedAtTimestamp,
                                       sortId: sortId,
                                       timestamp: timestamp,
                                       uniqueThreadId: uniqueThreadId,
                                       creatorUuid: creatorUuid,
                                       eraId: eraId,
                                       hasEnded: hasEnded,
                                       joinedMemberUuids: joinedMemberUuids,
                                       read: read)

        case .incomingPaymentMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let authorPhoneNumber: String? = record.authorPhoneNumber
            let authorUUID: String? = record.authorUUID
            let deprecated_sourceDeviceId: UInt32 = try SDSDeserialization.required(record.deprecated_sourceDeviceId, name: "deprecated_sourceDeviceId")
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let serverDeliveryTimestamp: UInt64 = try SDSDeserialization.required(record.serverDeliveryTimestamp, name: "serverDeliveryTimestamp")
            let serverGuid: String? = record.serverGuid
            let serverTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.serverTimestamp, name: "serverTimestamp", conversion: { NSNumber(value: $0) })
            let viewed: Bool = try SDSDeserialization.required(record.viewed, name: "viewed")
            let wasReceivedByUD: Bool = try SDSDeserialization.required(record.wasReceivedByUD, name: "wasReceivedByUD")
            let paymentCancellation: Data? = SDSDeserialization.optionalData(record.paymentCancellation, name: "paymentCancellation")
            let paymentNotificationSerialized: Data? = record.paymentNotification
            let paymentNotification: TSPaymentNotification? = try SDSDeserialization.optionalUnarchive(paymentNotificationSerialized, name: "paymentNotification")
            let paymentRequest: Data? = SDSDeserialization.optionalData(record.paymentRequest, name: "paymentRequest")

            return OWSIncomingPaymentMessage(grdbId: recordId,
                                             uniqueId: uniqueId,
                                             receivedAtTimestamp: receivedAtTimestamp,
                                             sortId: sortId,
                                             timestamp: timestamp,
                                             uniqueThreadId: uniqueThreadId,
                                             attachmentIds: attachmentIds,
                                             body: body,
                                             bodyRanges: bodyRanges,
                                             contactShare: contactShare,
                                             editState: editState,
                                             expireStartedAt: expireStartedAt,
                                             expiresAt: expiresAt,
                                             expiresInSeconds: expiresInSeconds,
                                             giftBadge: giftBadge,
                                             isGroupStoryReply: isGroupStoryReply,
                                             isViewOnceComplete: isViewOnceComplete,
                                             isViewOnceMessage: isViewOnceMessage,
                                             linkPreview: linkPreview,
                                             messageSticker: messageSticker,
                                             quotedMessage: quotedMessage,
                                             storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                             storyAuthorUuidString: storyAuthorUuidString,
                                             storyReactionEmoji: storyReactionEmoji,
                                             storyTimestamp: storyTimestamp,
                                             wasRemotelyDeleted: wasRemotelyDeleted,
                                             authorPhoneNumber: authorPhoneNumber,
                                             authorUUID: authorUUID,
                                             deprecated_sourceDeviceId: deprecated_sourceDeviceId,
                                             read: read,
                                             serverDeliveryTimestamp: serverDeliveryTimestamp,
                                             serverGuid: serverGuid,
                                             serverTimestamp: serverTimestamp,
                                             viewed: viewed,
                                             wasReceivedByUD: wasReceivedByUD,
                                             paymentCancellation: paymentCancellation,
                                             paymentNotification: paymentNotification,
                                             paymentRequest: paymentRequest)

        case .outgoingPaymentMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            guard let groupMetaMessage: TSGroupMetaMessage = record.groupMetaMessage else {
               throw SDSError.missingRequiredField()
            }
            let hasLegacyMessageState: Bool = try SDSDeserialization.required(record.hasLegacyMessageState, name: "hasLegacyMessageState")
            let hasSyncedTranscript: Bool = try SDSDeserialization.required(record.hasSyncedTranscript, name: "hasSyncedTranscript")
            let isVoiceMessage: Bool = try SDSDeserialization.required(record.isVoiceMessage, name: "isVoiceMessage")
            guard let legacyMessageState: TSOutgoingMessageState = record.legacyMessageState else {
               throw SDSError.missingRequiredField()
            }
            let legacyWasDelivered: Bool = try SDSDeserialization.required(record.legacyWasDelivered, name: "legacyWasDelivered")
            let mostRecentFailureText: String? = record.mostRecentFailureText
            let recipientAddressStatesSerialized: Data? = record.recipientAddressStates
            let recipientAddressStates: [SignalServiceAddress: TSOutgoingMessageRecipientState]? = try SDSDeserialization.optionalUnarchive(recipientAddressStatesSerialized, name: "recipientAddressStates")
            guard let storedMessageState: TSOutgoingMessageState = record.storedMessageState else {
               throw SDSError.missingRequiredField()
            }
            let wasNotCreatedLocally: Bool = try SDSDeserialization.required(record.wasNotCreatedLocally, name: "wasNotCreatedLocally")
            let paymentCancellation: Data? = SDSDeserialization.optionalData(record.paymentCancellation, name: "paymentCancellation")
            let paymentNotificationSerialized: Data? = record.paymentNotification
            let paymentNotification: TSPaymentNotification? = try SDSDeserialization.optionalUnarchive(paymentNotificationSerialized, name: "paymentNotification")
            let paymentRequest: Data? = SDSDeserialization.optionalData(record.paymentRequest, name: "paymentRequest")

            return OWSOutgoingPaymentMessage(grdbId: recordId,
                                             uniqueId: uniqueId,
                                             receivedAtTimestamp: receivedAtTimestamp,
                                             sortId: sortId,
                                             timestamp: timestamp,
                                             uniqueThreadId: uniqueThreadId,
                                             attachmentIds: attachmentIds,
                                             body: body,
                                             bodyRanges: bodyRanges,
                                             contactShare: contactShare,
                                             editState: editState,
                                             expireStartedAt: expireStartedAt,
                                             expiresAt: expiresAt,
                                             expiresInSeconds: expiresInSeconds,
                                             giftBadge: giftBadge,
                                             isGroupStoryReply: isGroupStoryReply,
                                             isViewOnceComplete: isViewOnceComplete,
                                             isViewOnceMessage: isViewOnceMessage,
                                             linkPreview: linkPreview,
                                             messageSticker: messageSticker,
                                             quotedMessage: quotedMessage,
                                             storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                             storyAuthorUuidString: storyAuthorUuidString,
                                             storyReactionEmoji: storyReactionEmoji,
                                             storyTimestamp: storyTimestamp,
                                             wasRemotelyDeleted: wasRemotelyDeleted,
                                             customMessage: customMessage,
                                             groupMetaMessage: groupMetaMessage,
                                             hasLegacyMessageState: hasLegacyMessageState,
                                             hasSyncedTranscript: hasSyncedTranscript,
                                             isVoiceMessage: isVoiceMessage,
                                             legacyMessageState: legacyMessageState,
                                             legacyWasDelivered: legacyWasDelivered,
                                             mostRecentFailureText: mostRecentFailureText,
                                             recipientAddressStates: recipientAddressStates,
                                             storedMessageState: storedMessageState,
                                             wasNotCreatedLocally: wasNotCreatedLocally,
                                             paymentCancellation: paymentCancellation,
                                             paymentNotification: paymentNotification,
                                             paymentRequest: paymentRequest)

        case .paymentActivationRequestFinishedMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            guard let groupMetaMessage: TSGroupMetaMessage = record.groupMetaMessage else {
               throw SDSError.missingRequiredField()
            }
            let hasLegacyMessageState: Bool = try SDSDeserialization.required(record.hasLegacyMessageState, name: "hasLegacyMessageState")
            let hasSyncedTranscript: Bool = try SDSDeserialization.required(record.hasSyncedTranscript, name: "hasSyncedTranscript")
            let isVoiceMessage: Bool = try SDSDeserialization.required(record.isVoiceMessage, name: "isVoiceMessage")
            guard let legacyMessageState: TSOutgoingMessageState = record.legacyMessageState else {
               throw SDSError.missingRequiredField()
            }
            let legacyWasDelivered: Bool = try SDSDeserialization.required(record.legacyWasDelivered, name: "legacyWasDelivered")
            let mostRecentFailureText: String? = record.mostRecentFailureText
            let recipientAddressStatesSerialized: Data? = record.recipientAddressStates
            let recipientAddressStates: [SignalServiceAddress: TSOutgoingMessageRecipientState]? = try SDSDeserialization.optionalUnarchive(recipientAddressStatesSerialized, name: "recipientAddressStates")
            guard let storedMessageState: TSOutgoingMessageState = record.storedMessageState else {
               throw SDSError.missingRequiredField()
            }
            let wasNotCreatedLocally: Bool = try SDSDeserialization.required(record.wasNotCreatedLocally, name: "wasNotCreatedLocally")

            return OWSPaymentActivationRequestFinishedMessage(grdbId: recordId,
                                                              uniqueId: uniqueId,
                                                              receivedAtTimestamp: receivedAtTimestamp,
                                                              sortId: sortId,
                                                              timestamp: timestamp,
                                                              uniqueThreadId: uniqueThreadId,
                                                              attachmentIds: attachmentIds,
                                                              body: body,
                                                              bodyRanges: bodyRanges,
                                                              contactShare: contactShare,
                                                              editState: editState,
                                                              expireStartedAt: expireStartedAt,
                                                              expiresAt: expiresAt,
                                                              expiresInSeconds: expiresInSeconds,
                                                              giftBadge: giftBadge,
                                                              isGroupStoryReply: isGroupStoryReply,
                                                              isViewOnceComplete: isViewOnceComplete,
                                                              isViewOnceMessage: isViewOnceMessage,
                                                              linkPreview: linkPreview,
                                                              messageSticker: messageSticker,
                                                              quotedMessage: quotedMessage,
                                                              storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                              storyAuthorUuidString: storyAuthorUuidString,
                                                              storyReactionEmoji: storyReactionEmoji,
                                                              storyTimestamp: storyTimestamp,
                                                              wasRemotelyDeleted: wasRemotelyDeleted,
                                                              customMessage: customMessage,
                                                              groupMetaMessage: groupMetaMessage,
                                                              hasLegacyMessageState: hasLegacyMessageState,
                                                              hasSyncedTranscript: hasSyncedTranscript,
                                                              isVoiceMessage: isVoiceMessage,
                                                              legacyMessageState: legacyMessageState,
                                                              legacyWasDelivered: legacyWasDelivered,
                                                              mostRecentFailureText: mostRecentFailureText,
                                                              recipientAddressStates: recipientAddressStates,
                                                              storedMessageState: storedMessageState,
                                                              wasNotCreatedLocally: wasNotCreatedLocally)

        case .paymentActivationRequestMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            guard let groupMetaMessage: TSGroupMetaMessage = record.groupMetaMessage else {
               throw SDSError.missingRequiredField()
            }
            let hasLegacyMessageState: Bool = try SDSDeserialization.required(record.hasLegacyMessageState, name: "hasLegacyMessageState")
            let hasSyncedTranscript: Bool = try SDSDeserialization.required(record.hasSyncedTranscript, name: "hasSyncedTranscript")
            let isVoiceMessage: Bool = try SDSDeserialization.required(record.isVoiceMessage, name: "isVoiceMessage")
            guard let legacyMessageState: TSOutgoingMessageState = record.legacyMessageState else {
               throw SDSError.missingRequiredField()
            }
            let legacyWasDelivered: Bool = try SDSDeserialization.required(record.legacyWasDelivered, name: "legacyWasDelivered")
            let mostRecentFailureText: String? = record.mostRecentFailureText
            let recipientAddressStatesSerialized: Data? = record.recipientAddressStates
            let recipientAddressStates: [SignalServiceAddress: TSOutgoingMessageRecipientState]? = try SDSDeserialization.optionalUnarchive(recipientAddressStatesSerialized, name: "recipientAddressStates")
            guard let storedMessageState: TSOutgoingMessageState = record.storedMessageState else {
               throw SDSError.missingRequiredField()
            }
            let wasNotCreatedLocally: Bool = try SDSDeserialization.required(record.wasNotCreatedLocally, name: "wasNotCreatedLocally")

            return OWSPaymentActivationRequestMessage(grdbId: recordId,
                                                      uniqueId: uniqueId,
                                                      receivedAtTimestamp: receivedAtTimestamp,
                                                      sortId: sortId,
                                                      timestamp: timestamp,
                                                      uniqueThreadId: uniqueThreadId,
                                                      attachmentIds: attachmentIds,
                                                      body: body,
                                                      bodyRanges: bodyRanges,
                                                      contactShare: contactShare,
                                                      editState: editState,
                                                      expireStartedAt: expireStartedAt,
                                                      expiresAt: expiresAt,
                                                      expiresInSeconds: expiresInSeconds,
                                                      giftBadge: giftBadge,
                                                      isGroupStoryReply: isGroupStoryReply,
                                                      isViewOnceComplete: isViewOnceComplete,
                                                      isViewOnceMessage: isViewOnceMessage,
                                                      linkPreview: linkPreview,
                                                      messageSticker: messageSticker,
                                                      quotedMessage: quotedMessage,
                                                      storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                      storyAuthorUuidString: storyAuthorUuidString,
                                                      storyReactionEmoji: storyReactionEmoji,
                                                      storyTimestamp: storyTimestamp,
                                                      wasRemotelyDeleted: wasRemotelyDeleted,
                                                      customMessage: customMessage,
                                                      groupMetaMessage: groupMetaMessage,
                                                      hasLegacyMessageState: hasLegacyMessageState,
                                                      hasSyncedTranscript: hasSyncedTranscript,
                                                      isVoiceMessage: isVoiceMessage,
                                                      legacyMessageState: legacyMessageState,
                                                      legacyWasDelivered: legacyWasDelivered,
                                                      mostRecentFailureText: mostRecentFailureText,
                                                      recipientAddressStates: recipientAddressStates,
                                                      storedMessageState: storedMessageState,
                                                      wasNotCreatedLocally: wasNotCreatedLocally)

        case .recoverableDecryptionPlaceholder:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            guard let errorType: TSErrorMessageType = record.errorType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let recipientAddressSerialized: Data? = record.recipientAddress
            let recipientAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(recipientAddressSerialized, name: "recipientAddress")
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(senderSerialized, name: "sender")
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")

            return OWSRecoverableDecryptionPlaceholder(grdbId: recordId,
                                                       uniqueId: uniqueId,
                                                       receivedAtTimestamp: receivedAtTimestamp,
                                                       sortId: sortId,
                                                       timestamp: timestamp,
                                                       uniqueThreadId: uniqueThreadId,
                                                       attachmentIds: attachmentIds,
                                                       body: body,
                                                       bodyRanges: bodyRanges,
                                                       contactShare: contactShare,
                                                       editState: editState,
                                                       expireStartedAt: expireStartedAt,
                                                       expiresAt: expiresAt,
                                                       expiresInSeconds: expiresInSeconds,
                                                       giftBadge: giftBadge,
                                                       isGroupStoryReply: isGroupStoryReply,
                                                       isViewOnceComplete: isViewOnceComplete,
                                                       isViewOnceMessage: isViewOnceMessage,
                                                       linkPreview: linkPreview,
                                                       messageSticker: messageSticker,
                                                       quotedMessage: quotedMessage,
                                                       storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                       storyAuthorUuidString: storyAuthorUuidString,
                                                       storyReactionEmoji: storyReactionEmoji,
                                                       storyTimestamp: storyTimestamp,
                                                       wasRemotelyDeleted: wasRemotelyDeleted,
                                                       errorType: errorType,
                                                       read: read,
                                                       recipientAddress: recipientAddress,
                                                       sender: sender,
                                                       wasIdentityVerified: wasIdentityVerified)

        case .unknownContactBlockOfferMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            guard let errorType: TSErrorMessageType = record.errorType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let recipientAddressSerialized: Data? = record.recipientAddress
            let recipientAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(recipientAddressSerialized, name: "recipientAddress")
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(senderSerialized, name: "sender")
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")

            return OWSUnknownContactBlockOfferMessage(grdbId: recordId,
                                                      uniqueId: uniqueId,
                                                      receivedAtTimestamp: receivedAtTimestamp,
                                                      sortId: sortId,
                                                      timestamp: timestamp,
                                                      uniqueThreadId: uniqueThreadId,
                                                      attachmentIds: attachmentIds,
                                                      body: body,
                                                      bodyRanges: bodyRanges,
                                                      contactShare: contactShare,
                                                      editState: editState,
                                                      expireStartedAt: expireStartedAt,
                                                      expiresAt: expiresAt,
                                                      expiresInSeconds: expiresInSeconds,
                                                      giftBadge: giftBadge,
                                                      isGroupStoryReply: isGroupStoryReply,
                                                      isViewOnceComplete: isViewOnceComplete,
                                                      isViewOnceMessage: isViewOnceMessage,
                                                      linkPreview: linkPreview,
                                                      messageSticker: messageSticker,
                                                      quotedMessage: quotedMessage,
                                                      storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                      storyAuthorUuidString: storyAuthorUuidString,
                                                      storyReactionEmoji: storyReactionEmoji,
                                                      storyTimestamp: storyTimestamp,
                                                      wasRemotelyDeleted: wasRemotelyDeleted,
                                                      errorType: errorType,
                                                      read: read,
                                                      recipientAddress: recipientAddress,
                                                      sender: sender,
                                                      wasIdentityVerified: wasIdentityVerified)

        case .unknownProtocolVersionMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo
            let infoMessageUserInfo: [InfoMessageUserInfoKey: AnyObject]? = try SDSDeserialization.optionalUnarchive(infoMessageUserInfoSerialized, name: "infoMessageUserInfo")
            guard let messageType: TSInfoMessageType = record.messageType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let serverGuid: String? = record.serverGuid
            let unregisteredAddressSerialized: Data? = record.unregisteredAddress
            let unregisteredAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(unregisteredAddressSerialized, name: "unregisteredAddress")
            let protocolVersion: UInt = try SDSDeserialization.required(record.protocolVersion, name: "protocolVersion")
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(senderSerialized, name: "sender")

            return OWSUnknownProtocolVersionMessage(grdbId: recordId,
                                                    uniqueId: uniqueId,
                                                    receivedAtTimestamp: receivedAtTimestamp,
                                                    sortId: sortId,
                                                    timestamp: timestamp,
                                                    uniqueThreadId: uniqueThreadId,
                                                    attachmentIds: attachmentIds,
                                                    body: body,
                                                    bodyRanges: bodyRanges,
                                                    contactShare: contactShare,
                                                    editState: editState,
                                                    expireStartedAt: expireStartedAt,
                                                    expiresAt: expiresAt,
                                                    expiresInSeconds: expiresInSeconds,
                                                    giftBadge: giftBadge,
                                                    isGroupStoryReply: isGroupStoryReply,
                                                    isViewOnceComplete: isViewOnceComplete,
                                                    isViewOnceMessage: isViewOnceMessage,
                                                    linkPreview: linkPreview,
                                                    messageSticker: messageSticker,
                                                    quotedMessage: quotedMessage,
                                                    storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                    storyAuthorUuidString: storyAuthorUuidString,
                                                    storyReactionEmoji: storyReactionEmoji,
                                                    storyTimestamp: storyTimestamp,
                                                    wasRemotelyDeleted: wasRemotelyDeleted,
                                                    customMessage: customMessage,
                                                    infoMessageUserInfo: infoMessageUserInfo,
                                                    messageType: messageType,
                                                    read: read,
                                                    serverGuid: serverGuid,
                                                    unregisteredAddress: unregisteredAddress,
                                                    protocolVersion: protocolVersion,
                                                    sender: sender)

        case .verificationStateChangeMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo
            let infoMessageUserInfo: [InfoMessageUserInfoKey: AnyObject]? = try SDSDeserialization.optionalUnarchive(infoMessageUserInfoSerialized, name: "infoMessageUserInfo")
            guard let messageType: TSInfoMessageType = record.messageType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let serverGuid: String? = record.serverGuid
            let unregisteredAddressSerialized: Data? = record.unregisteredAddress
            let unregisteredAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(unregisteredAddressSerialized, name: "unregisteredAddress")
            let isLocalChange: Bool = try SDSDeserialization.required(record.isLocalChange, name: "isLocalChange")
            let recipientAddressSerialized: Data? = record.recipientAddress
            let recipientAddress: SignalServiceAddress = try SDSDeserialization.unarchive(recipientAddressSerialized, name: "recipientAddress")
            guard let verificationState: OWSVerificationState = record.verificationState else {
               throw SDSError.missingRequiredField()
            }

            return OWSVerificationStateChangeMessage(grdbId: recordId,
                                                     uniqueId: uniqueId,
                                                     receivedAtTimestamp: receivedAtTimestamp,
                                                     sortId: sortId,
                                                     timestamp: timestamp,
                                                     uniqueThreadId: uniqueThreadId,
                                                     attachmentIds: attachmentIds,
                                                     body: body,
                                                     bodyRanges: bodyRanges,
                                                     contactShare: contactShare,
                                                     editState: editState,
                                                     expireStartedAt: expireStartedAt,
                                                     expiresAt: expiresAt,
                                                     expiresInSeconds: expiresInSeconds,
                                                     giftBadge: giftBadge,
                                                     isGroupStoryReply: isGroupStoryReply,
                                                     isViewOnceComplete: isViewOnceComplete,
                                                     isViewOnceMessage: isViewOnceMessage,
                                                     linkPreview: linkPreview,
                                                     messageSticker: messageSticker,
                                                     quotedMessage: quotedMessage,
                                                     storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                     storyAuthorUuidString: storyAuthorUuidString,
                                                     storyReactionEmoji: storyReactionEmoji,
                                                     storyTimestamp: storyTimestamp,
                                                     wasRemotelyDeleted: wasRemotelyDeleted,
                                                     customMessage: customMessage,
                                                     infoMessageUserInfo: infoMessageUserInfo,
                                                     messageType: messageType,
                                                     read: read,
                                                     serverGuid: serverGuid,
                                                     unregisteredAddress: unregisteredAddress,
                                                     isLocalChange: isLocalChange,
                                                     recipientAddress: recipientAddress,
                                                     verificationState: verificationState)

        case .call:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            guard let callType: RPRecentCallType = record.callType else {
               throw SDSError.missingRequiredField()
            }
            guard let offerType: TSRecentCallOfferType = record.offerType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")

            return TSCall(grdbId: recordId,
                          uniqueId: uniqueId,
                          receivedAtTimestamp: receivedAtTimestamp,
                          sortId: sortId,
                          timestamp: timestamp,
                          uniqueThreadId: uniqueThreadId,
                          callType: callType,
                          offerType: offerType,
                          read: read)

        case .errorMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            guard let errorType: TSErrorMessageType = record.errorType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let recipientAddressSerialized: Data? = record.recipientAddress
            let recipientAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(recipientAddressSerialized, name: "recipientAddress")
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(senderSerialized, name: "sender")
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")

            return TSErrorMessage(grdbId: recordId,
                                  uniqueId: uniqueId,
                                  receivedAtTimestamp: receivedAtTimestamp,
                                  sortId: sortId,
                                  timestamp: timestamp,
                                  uniqueThreadId: uniqueThreadId,
                                  attachmentIds: attachmentIds,
                                  body: body,
                                  bodyRanges: bodyRanges,
                                  contactShare: contactShare,
                                  editState: editState,
                                  expireStartedAt: expireStartedAt,
                                  expiresAt: expiresAt,
                                  expiresInSeconds: expiresInSeconds,
                                  giftBadge: giftBadge,
                                  isGroupStoryReply: isGroupStoryReply,
                                  isViewOnceComplete: isViewOnceComplete,
                                  isViewOnceMessage: isViewOnceMessage,
                                  linkPreview: linkPreview,
                                  messageSticker: messageSticker,
                                  quotedMessage: quotedMessage,
                                  storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                  storyAuthorUuidString: storyAuthorUuidString,
                                  storyReactionEmoji: storyReactionEmoji,
                                  storyTimestamp: storyTimestamp,
                                  wasRemotelyDeleted: wasRemotelyDeleted,
                                  errorType: errorType,
                                  read: read,
                                  recipientAddress: recipientAddress,
                                  sender: sender,
                                  wasIdentityVerified: wasIdentityVerified)

        case .incomingMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let authorPhoneNumber: String? = record.authorPhoneNumber
            let authorUUID: String? = record.authorUUID
            let deprecated_sourceDeviceId: UInt32 = try SDSDeserialization.required(record.deprecated_sourceDeviceId, name: "deprecated_sourceDeviceId")
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let serverDeliveryTimestamp: UInt64 = try SDSDeserialization.required(record.serverDeliveryTimestamp, name: "serverDeliveryTimestamp")
            let serverGuid: String? = record.serverGuid
            let serverTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.serverTimestamp, name: "serverTimestamp", conversion: { NSNumber(value: $0) })
            let viewed: Bool = try SDSDeserialization.required(record.viewed, name: "viewed")
            let wasReceivedByUD: Bool = try SDSDeserialization.required(record.wasReceivedByUD, name: "wasReceivedByUD")

            return TSIncomingMessage(grdbId: recordId,
                                     uniqueId: uniqueId,
                                     receivedAtTimestamp: receivedAtTimestamp,
                                     sortId: sortId,
                                     timestamp: timestamp,
                                     uniqueThreadId: uniqueThreadId,
                                     attachmentIds: attachmentIds,
                                     body: body,
                                     bodyRanges: bodyRanges,
                                     contactShare: contactShare,
                                     editState: editState,
                                     expireStartedAt: expireStartedAt,
                                     expiresAt: expiresAt,
                                     expiresInSeconds: expiresInSeconds,
                                     giftBadge: giftBadge,
                                     isGroupStoryReply: isGroupStoryReply,
                                     isViewOnceComplete: isViewOnceComplete,
                                     isViewOnceMessage: isViewOnceMessage,
                                     linkPreview: linkPreview,
                                     messageSticker: messageSticker,
                                     quotedMessage: quotedMessage,
                                     storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                     storyAuthorUuidString: storyAuthorUuidString,
                                     storyReactionEmoji: storyReactionEmoji,
                                     storyTimestamp: storyTimestamp,
                                     wasRemotelyDeleted: wasRemotelyDeleted,
                                     authorPhoneNumber: authorPhoneNumber,
                                     authorUUID: authorUUID,
                                     deprecated_sourceDeviceId: deprecated_sourceDeviceId,
                                     read: read,
                                     serverDeliveryTimestamp: serverDeliveryTimestamp,
                                     serverGuid: serverGuid,
                                     serverTimestamp: serverTimestamp,
                                     viewed: viewed,
                                     wasReceivedByUD: wasReceivedByUD)

        case .infoMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            let infoMessageUserInfoSerialized: Data? = record.infoMessageUserInfo
            let infoMessageUserInfo: [InfoMessageUserInfoKey: AnyObject]? = try SDSDeserialization.optionalUnarchive(infoMessageUserInfoSerialized, name: "infoMessageUserInfo")
            guard let messageType: TSInfoMessageType = record.messageType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let serverGuid: String? = record.serverGuid
            let unregisteredAddressSerialized: Data? = record.unregisteredAddress
            let unregisteredAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(unregisteredAddressSerialized, name: "unregisteredAddress")

            return TSInfoMessage(grdbId: recordId,
                                 uniqueId: uniqueId,
                                 receivedAtTimestamp: receivedAtTimestamp,
                                 sortId: sortId,
                                 timestamp: timestamp,
                                 uniqueThreadId: uniqueThreadId,
                                 attachmentIds: attachmentIds,
                                 body: body,
                                 bodyRanges: bodyRanges,
                                 contactShare: contactShare,
                                 editState: editState,
                                 expireStartedAt: expireStartedAt,
                                 expiresAt: expiresAt,
                                 expiresInSeconds: expiresInSeconds,
                                 giftBadge: giftBadge,
                                 isGroupStoryReply: isGroupStoryReply,
                                 isViewOnceComplete: isViewOnceComplete,
                                 isViewOnceMessage: isViewOnceMessage,
                                 linkPreview: linkPreview,
                                 messageSticker: messageSticker,
                                 quotedMessage: quotedMessage,
                                 storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                 storyAuthorUuidString: storyAuthorUuidString,
                                 storyReactionEmoji: storyReactionEmoji,
                                 storyTimestamp: storyTimestamp,
                                 wasRemotelyDeleted: wasRemotelyDeleted,
                                 customMessage: customMessage,
                                 infoMessageUserInfo: infoMessageUserInfo,
                                 messageType: messageType,
                                 read: read,
                                 serverGuid: serverGuid,
                                 unregisteredAddress: unregisteredAddress)

        case .interaction:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId

            return TSInteraction(grdbId: recordId,
                                 uniqueId: uniqueId,
                                 receivedAtTimestamp: receivedAtTimestamp,
                                 sortId: sortId,
                                 timestamp: timestamp,
                                 uniqueThreadId: uniqueThreadId)

        case .invalidIdentityKeyErrorMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            guard let errorType: TSErrorMessageType = record.errorType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let recipientAddressSerialized: Data? = record.recipientAddress
            let recipientAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(recipientAddressSerialized, name: "recipientAddress")
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(senderSerialized, name: "sender")
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")

            return TSInvalidIdentityKeyErrorMessage(grdbId: recordId,
                                                    uniqueId: uniqueId,
                                                    receivedAtTimestamp: receivedAtTimestamp,
                                                    sortId: sortId,
                                                    timestamp: timestamp,
                                                    uniqueThreadId: uniqueThreadId,
                                                    attachmentIds: attachmentIds,
                                                    body: body,
                                                    bodyRanges: bodyRanges,
                                                    contactShare: contactShare,
                                                    editState: editState,
                                                    expireStartedAt: expireStartedAt,
                                                    expiresAt: expiresAt,
                                                    expiresInSeconds: expiresInSeconds,
                                                    giftBadge: giftBadge,
                                                    isGroupStoryReply: isGroupStoryReply,
                                                    isViewOnceComplete: isViewOnceComplete,
                                                    isViewOnceMessage: isViewOnceMessage,
                                                    linkPreview: linkPreview,
                                                    messageSticker: messageSticker,
                                                    quotedMessage: quotedMessage,
                                                    storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                    storyAuthorUuidString: storyAuthorUuidString,
                                                    storyReactionEmoji: storyReactionEmoji,
                                                    storyTimestamp: storyTimestamp,
                                                    wasRemotelyDeleted: wasRemotelyDeleted,
                                                    errorType: errorType,
                                                    read: read,
                                                    recipientAddress: recipientAddress,
                                                    sender: sender,
                                                    wasIdentityVerified: wasIdentityVerified)

        case .invalidIdentityKeyReceivingErrorMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            guard let errorType: TSErrorMessageType = record.errorType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let recipientAddressSerialized: Data? = record.recipientAddress
            let recipientAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(recipientAddressSerialized, name: "recipientAddress")
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(senderSerialized, name: "sender")
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")
            let authorId: String = try SDSDeserialization.required(record.authorId, name: "authorId")
            let envelopeData: Data? = SDSDeserialization.optionalData(record.envelopeData, name: "envelopeData")

            return TSInvalidIdentityKeyReceivingErrorMessage(grdbId: recordId,
                                                             uniqueId: uniqueId,
                                                             receivedAtTimestamp: receivedAtTimestamp,
                                                             sortId: sortId,
                                                             timestamp: timestamp,
                                                             uniqueThreadId: uniqueThreadId,
                                                             attachmentIds: attachmentIds,
                                                             body: body,
                                                             bodyRanges: bodyRanges,
                                                             contactShare: contactShare,
                                                             editState: editState,
                                                             expireStartedAt: expireStartedAt,
                                                             expiresAt: expiresAt,
                                                             expiresInSeconds: expiresInSeconds,
                                                             giftBadge: giftBadge,
                                                             isGroupStoryReply: isGroupStoryReply,
                                                             isViewOnceComplete: isViewOnceComplete,
                                                             isViewOnceMessage: isViewOnceMessage,
                                                             linkPreview: linkPreview,
                                                             messageSticker: messageSticker,
                                                             quotedMessage: quotedMessage,
                                                             storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                             storyAuthorUuidString: storyAuthorUuidString,
                                                             storyReactionEmoji: storyReactionEmoji,
                                                             storyTimestamp: storyTimestamp,
                                                             wasRemotelyDeleted: wasRemotelyDeleted,
                                                             errorType: errorType,
                                                             read: read,
                                                             recipientAddress: recipientAddress,
                                                             sender: sender,
                                                             wasIdentityVerified: wasIdentityVerified,
                                                             authorId: authorId,
                                                             envelopeData: envelopeData)

        case .invalidIdentityKeySendingErrorMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            guard let errorType: TSErrorMessageType = record.errorType else {
               throw SDSError.missingRequiredField()
            }
            let read: Bool = try SDSDeserialization.required(record.read, name: "read")
            let recipientAddressSerialized: Data? = record.recipientAddress
            let recipientAddress: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(recipientAddressSerialized, name: "recipientAddress")
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(senderSerialized, name: "sender")
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")
            let messageId: String = try SDSDeserialization.required(record.messageId, name: "messageId")
            let preKeyBundleSerialized: Data? = record.preKeyBundle
            let preKeyBundle: PreKeyBundle = try SDSDeserialization.unarchive(preKeyBundleSerialized, name: "preKeyBundle")

            return TSInvalidIdentityKeySendingErrorMessage(grdbId: recordId,
                                                           uniqueId: uniqueId,
                                                           receivedAtTimestamp: receivedAtTimestamp,
                                                           sortId: sortId,
                                                           timestamp: timestamp,
                                                           uniqueThreadId: uniqueThreadId,
                                                           attachmentIds: attachmentIds,
                                                           body: body,
                                                           bodyRanges: bodyRanges,
                                                           contactShare: contactShare,
                                                           editState: editState,
                                                           expireStartedAt: expireStartedAt,
                                                           expiresAt: expiresAt,
                                                           expiresInSeconds: expiresInSeconds,
                                                           giftBadge: giftBadge,
                                                           isGroupStoryReply: isGroupStoryReply,
                                                           isViewOnceComplete: isViewOnceComplete,
                                                           isViewOnceMessage: isViewOnceMessage,
                                                           linkPreview: linkPreview,
                                                           messageSticker: messageSticker,
                                                           quotedMessage: quotedMessage,
                                                           storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                           storyAuthorUuidString: storyAuthorUuidString,
                                                           storyReactionEmoji: storyReactionEmoji,
                                                           storyTimestamp: storyTimestamp,
                                                           wasRemotelyDeleted: wasRemotelyDeleted,
                                                           errorType: errorType,
                                                           read: read,
                                                           recipientAddress: recipientAddress,
                                                           sender: sender,
                                                           wasIdentityVerified: wasIdentityVerified,
                                                           messageId: messageId,
                                                           preKeyBundle: preKeyBundle)

        case .message:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")

            return TSMessage(grdbId: recordId,
                             uniqueId: uniqueId,
                             receivedAtTimestamp: receivedAtTimestamp,
                             sortId: sortId,
                             timestamp: timestamp,
                             uniqueThreadId: uniqueThreadId,
                             attachmentIds: attachmentIds,
                             body: body,
                             bodyRanges: bodyRanges,
                             contactShare: contactShare,
                             editState: editState,
                             expireStartedAt: expireStartedAt,
                             expiresAt: expiresAt,
                             expiresInSeconds: expiresInSeconds,
                             giftBadge: giftBadge,
                             isGroupStoryReply: isGroupStoryReply,
                             isViewOnceComplete: isViewOnceComplete,
                             isViewOnceMessage: isViewOnceMessage,
                             linkPreview: linkPreview,
                             messageSticker: messageSticker,
                             quotedMessage: quotedMessage,
                             storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                             storyAuthorUuidString: storyAuthorUuidString,
                             storyReactionEmoji: storyReactionEmoji,
                             storyTimestamp: storyTimestamp,
                             wasRemotelyDeleted: wasRemotelyDeleted)

        case .outgoingMessage:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId
            let attachmentIdsSerialized: Data? = record.attachmentIds
            let attachmentIds: [String] = try SDSDeserialization.unarchive(attachmentIdsSerialized, name: "attachmentIds")
            let body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try SDSDeserialization.optionalUnarchive(bodyRangesSerialized, name: "bodyRanges")
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try SDSDeserialization.optionalUnarchive(contactShareSerialized, name: "contactShare")
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expiresAt: UInt64 = try SDSDeserialization.required(record.expiresAt, name: "expiresAt")
            let expiresInSeconds: UInt32 = try SDSDeserialization.required(record.expiresInSeconds, name: "expiresInSeconds")
            let giftBadgeSerialized: Data? = record.giftBadge
            let giftBadge: OWSGiftBadge? = try SDSDeserialization.optionalUnarchive(giftBadgeSerialized, name: "giftBadge")
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isViewOnceComplete: Bool = try SDSDeserialization.required(record.isViewOnceComplete, name: "isViewOnceComplete")
            let isViewOnceMessage: Bool = try SDSDeserialization.required(record.isViewOnceMessage, name: "isViewOnceMessage")
            let linkPreviewSerialized: Data? = record.linkPreview
            let linkPreview: OWSLinkPreview? = try SDSDeserialization.optionalUnarchive(linkPreviewSerialized, name: "linkPreview")
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try SDSDeserialization.optionalUnarchive(messageStickerSerialized, name: "messageSticker")
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try SDSDeserialization.optionalUnarchive(quotedMessageSerialized, name: "quotedMessage")
            let storedShouldStartExpireTimer: Bool = try SDSDeserialization.required(record.storedShouldStartExpireTimer, name: "storedShouldStartExpireTimer")
            let storyAuthorUuidString: String? = record.storyAuthorUuidString
            let storyReactionEmoji: String? = record.storyReactionEmoji
            let storyTimestamp: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.storyTimestamp, name: "storyTimestamp", conversion: { NSNumber(value: $0) })
            let wasRemotelyDeleted: Bool = try SDSDeserialization.required(record.wasRemotelyDeleted, name: "wasRemotelyDeleted")
            let customMessage: String? = record.customMessage
            guard let groupMetaMessage: TSGroupMetaMessage = record.groupMetaMessage else {
               throw SDSError.missingRequiredField()
            }
            let hasLegacyMessageState: Bool = try SDSDeserialization.required(record.hasLegacyMessageState, name: "hasLegacyMessageState")
            let hasSyncedTranscript: Bool = try SDSDeserialization.required(record.hasSyncedTranscript, name: "hasSyncedTranscript")
            let isVoiceMessage: Bool = try SDSDeserialization.required(record.isVoiceMessage, name: "isVoiceMessage")
            guard let legacyMessageState: TSOutgoingMessageState = record.legacyMessageState else {
               throw SDSError.missingRequiredField()
            }
            let legacyWasDelivered: Bool = try SDSDeserialization.required(record.legacyWasDelivered, name: "legacyWasDelivered")
            let mostRecentFailureText: String? = record.mostRecentFailureText
            let recipientAddressStatesSerialized: Data? = record.recipientAddressStates
            let recipientAddressStates: [SignalServiceAddress: TSOutgoingMessageRecipientState]? = try SDSDeserialization.optionalUnarchive(recipientAddressStatesSerialized, name: "recipientAddressStates")
            guard let storedMessageState: TSOutgoingMessageState = record.storedMessageState else {
               throw SDSError.missingRequiredField()
            }
            let wasNotCreatedLocally: Bool = try SDSDeserialization.required(record.wasNotCreatedLocally, name: "wasNotCreatedLocally")

            return TSOutgoingMessage(grdbId: recordId,
                                     uniqueId: uniqueId,
                                     receivedAtTimestamp: receivedAtTimestamp,
                                     sortId: sortId,
                                     timestamp: timestamp,
                                     uniqueThreadId: uniqueThreadId,
                                     attachmentIds: attachmentIds,
                                     body: body,
                                     bodyRanges: bodyRanges,
                                     contactShare: contactShare,
                                     editState: editState,
                                     expireStartedAt: expireStartedAt,
                                     expiresAt: expiresAt,
                                     expiresInSeconds: expiresInSeconds,
                                     giftBadge: giftBadge,
                                     isGroupStoryReply: isGroupStoryReply,
                                     isViewOnceComplete: isViewOnceComplete,
                                     isViewOnceMessage: isViewOnceMessage,
                                     linkPreview: linkPreview,
                                     messageSticker: messageSticker,
                                     quotedMessage: quotedMessage,
                                     storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                     storyAuthorUuidString: storyAuthorUuidString,
                                     storyReactionEmoji: storyReactionEmoji,
                                     storyTimestamp: storyTimestamp,
                                     wasRemotelyDeleted: wasRemotelyDeleted,
                                     customMessage: customMessage,
                                     groupMetaMessage: groupMetaMessage,
                                     hasLegacyMessageState: hasLegacyMessageState,
                                     hasSyncedTranscript: hasSyncedTranscript,
                                     isVoiceMessage: isVoiceMessage,
                                     legacyMessageState: legacyMessageState,
                                     legacyWasDelivered: legacyWasDelivered,
                                     mostRecentFailureText: mostRecentFailureText,
                                     recipientAddressStates: recipientAddressStates,
                                     storedMessageState: storedMessageState,
                                     wasNotCreatedLocally: wasNotCreatedLocally)

        case .unreadIndicatorInteraction:

            let uniqueId: String = record.uniqueId
            let receivedAtTimestamp: UInt64 = record.receivedAtTimestamp
            let sortId: UInt64 = UInt64(recordId)
            let timestamp: UInt64 = record.timestamp
            let uniqueThreadId: String = record.threadUniqueId

            return TSUnreadIndicatorInteraction(grdbId: recordId,
                                                uniqueId: uniqueId,
                                                receivedAtTimestamp: receivedAtTimestamp,
                                                sortId: sortId,
                                                timestamp: timestamp,
                                                uniqueThreadId: uniqueThreadId)

        default:
            owsFailDebug("Unexpected record type: \(record.recordType)")
            throw SDSError.invalidValue()
        }
    }
}

// MARK: - SDSModel

extension TSInteraction: SDSModel {
    public var serializer: SDSSerializer {
        // Any subclass can be cast to it's superclass,
        // so the order of this switch statement matters.
        // We need to do a "depth first" search by type.
        switch self {
        case let model as TSUnreadIndicatorInteraction:
            assert(type(of: model) == TSUnreadIndicatorInteraction.self)
            return TSUnreadIndicatorInteractionSerializer(model: model)
        case let model as OWSPaymentActivationRequestMessage:
            assert(type(of: model) == OWSPaymentActivationRequestMessage.self)
            return OWSPaymentActivationRequestMessageSerializer(model: model)
        case let model as OWSPaymentActivationRequestFinishedMessage:
            assert(type(of: model) == OWSPaymentActivationRequestFinishedMessage.self)
            return OWSPaymentActivationRequestFinishedMessageSerializer(model: model)
        case let model as OWSOutgoingPaymentMessage:
            assert(type(of: model) == OWSOutgoingPaymentMessage.self)
            return OWSOutgoingPaymentMessageSerializer(model: model)
        case let model as TSOutgoingMessage:
            assert(type(of: model) == TSOutgoingMessage.self)
            return TSOutgoingMessageSerializer(model: model)
        case let model as OWSVerificationStateChangeMessage:
            assert(type(of: model) == OWSVerificationStateChangeMessage.self)
            return OWSVerificationStateChangeMessageSerializer(model: model)
        case let model as OWSUnknownProtocolVersionMessage:
            assert(type(of: model) == OWSUnknownProtocolVersionMessage.self)
            return OWSUnknownProtocolVersionMessageSerializer(model: model)
        case let model as OWSDisappearingConfigurationUpdateInfoMessage:
            assert(type(of: model) == OWSDisappearingConfigurationUpdateInfoMessage.self)
            return OWSDisappearingConfigurationUpdateInfoMessageSerializer(model: model)
        case let model as OWSAddToProfileWhitelistOfferMessage:
            assert(type(of: model) == OWSAddToProfileWhitelistOfferMessage.self)
            return OWSAddToProfileWhitelistOfferMessageSerializer(model: model)
        case let model as OWSAddToContactsOfferMessage:
            assert(type(of: model) == OWSAddToContactsOfferMessage.self)
            return OWSAddToContactsOfferMessageSerializer(model: model)
        case let model as TSInfoMessage:
            assert(type(of: model) == TSInfoMessage.self)
            return TSInfoMessageSerializer(model: model)
        case let model as OWSIncomingPaymentMessage:
            assert(type(of: model) == OWSIncomingPaymentMessage.self)
            return OWSIncomingPaymentMessageSerializer(model: model)
        case let model as TSIncomingMessage:
            assert(type(of: model) == TSIncomingMessage.self)
            return TSIncomingMessageSerializer(model: model)
        case let model as TSInvalidIdentityKeySendingErrorMessage:
            assert(type(of: model) == TSInvalidIdentityKeySendingErrorMessage.self)
            return TSInvalidIdentityKeySendingErrorMessageSerializer(model: model)
        case let model as TSInvalidIdentityKeyReceivingErrorMessage:
            assert(type(of: model) == TSInvalidIdentityKeyReceivingErrorMessage.self)
            return TSInvalidIdentityKeyReceivingErrorMessageSerializer(model: model)
        case let model as TSInvalidIdentityKeyErrorMessage:
            assert(type(of: model) == TSInvalidIdentityKeyErrorMessage.self)
            return TSInvalidIdentityKeyErrorMessageSerializer(model: model)
        case let model as OWSUnknownContactBlockOfferMessage:
            assert(type(of: model) == OWSUnknownContactBlockOfferMessage.self)
            return OWSUnknownContactBlockOfferMessageSerializer(model: model)
        case let model as OWSRecoverableDecryptionPlaceholder:
            assert(type(of: model) == OWSRecoverableDecryptionPlaceholder.self)
            return OWSRecoverableDecryptionPlaceholderSerializer(model: model)
        case let model as TSErrorMessage:
            assert(type(of: model) == TSErrorMessage.self)
            return TSErrorMessageSerializer(model: model)
        case let model as TSMessage:
            assert(type(of: model) == TSMessage.self)
            return TSMessageSerializer(model: model)
        case let model as TSCall:
            assert(type(of: model) == TSCall.self)
            return TSCallSerializer(model: model)
        case let model as OWSGroupCallMessage:
            assert(type(of: model) == OWSGroupCallMessage.self)
            return OWSGroupCallMessageSerializer(model: model)
        default:
            return TSInteractionSerializer(model: self)
        }
    }

    public func asRecord() throws -> SDSRecord {
        try serializer.asRecord()
    }

    public var sdsTableName: String {
        InteractionRecord.databaseTableName
    }

    public static var table: SDSTableMetadata {
        TSInteractionSerializer.table
    }
}

// MARK: - DeepCopyable

extension TSInteraction: DeepCopyable {

    public func deepCopy() throws -> AnyObject {
        // Any subclass can be cast to it's superclass,
        // so the order of this switch statement matters.
        // We need to do a "depth first" search by type.
        guard let id = self.grdbId?.int64Value else {
            throw OWSAssertionError("Model missing grdbId.")
        }

        if let modelToCopy = self as? TSUnreadIndicatorInteraction {
            assert(type(of: modelToCopy) == TSUnreadIndicatorInteraction.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId

            return TSUnreadIndicatorInteraction(grdbId: id,
                                                uniqueId: uniqueId,
                                                receivedAtTimestamp: receivedAtTimestamp,
                                                sortId: sortId,
                                                timestamp: timestamp,
                                                uniqueThreadId: uniqueThreadId)
        }

        if let modelToCopy = self as? OWSPaymentActivationRequestMessage {
            assert(type(of: modelToCopy) == OWSPaymentActivationRequestMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            let groupMetaMessage: TSGroupMetaMessage = modelToCopy.groupMetaMessage
            let hasLegacyMessageState: Bool = modelToCopy.hasLegacyMessageState
            let hasSyncedTranscript: Bool = modelToCopy.hasSyncedTranscript
            let isVoiceMessage: Bool = modelToCopy.isVoiceMessage
            let legacyMessageState: TSOutgoingMessageState = modelToCopy.legacyMessageState
            let legacyWasDelivered: Bool = modelToCopy.legacyWasDelivered
            let mostRecentFailureText: String? = modelToCopy.mostRecentFailureText
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddressStates: [SignalServiceAddress: TSOutgoingMessageRecipientState]?
            if let recipientAddressStatesForCopy = modelToCopy.recipientAddressStates {
               recipientAddressStates = try DeepCopies.deepCopy(recipientAddressStatesForCopy)
            } else {
               recipientAddressStates = nil
            }
            let storedMessageState: TSOutgoingMessageState = modelToCopy.storedMessageState
            let wasNotCreatedLocally: Bool = modelToCopy.wasNotCreatedLocally

            return OWSPaymentActivationRequestMessage(grdbId: id,
                                                      uniqueId: uniqueId,
                                                      receivedAtTimestamp: receivedAtTimestamp,
                                                      sortId: sortId,
                                                      timestamp: timestamp,
                                                      uniqueThreadId: uniqueThreadId,
                                                      attachmentIds: attachmentIds,
                                                      body: body,
                                                      bodyRanges: bodyRanges,
                                                      contactShare: contactShare,
                                                      editState: editState,
                                                      expireStartedAt: expireStartedAt,
                                                      expiresAt: expiresAt,
                                                      expiresInSeconds: expiresInSeconds,
                                                      giftBadge: giftBadge,
                                                      isGroupStoryReply: isGroupStoryReply,
                                                      isViewOnceComplete: isViewOnceComplete,
                                                      isViewOnceMessage: isViewOnceMessage,
                                                      linkPreview: linkPreview,
                                                      messageSticker: messageSticker,
                                                      quotedMessage: quotedMessage,
                                                      storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                      storyAuthorUuidString: storyAuthorUuidString,
                                                      storyReactionEmoji: storyReactionEmoji,
                                                      storyTimestamp: storyTimestamp,
                                                      wasRemotelyDeleted: wasRemotelyDeleted,
                                                      customMessage: customMessage,
                                                      groupMetaMessage: groupMetaMessage,
                                                      hasLegacyMessageState: hasLegacyMessageState,
                                                      hasSyncedTranscript: hasSyncedTranscript,
                                                      isVoiceMessage: isVoiceMessage,
                                                      legacyMessageState: legacyMessageState,
                                                      legacyWasDelivered: legacyWasDelivered,
                                                      mostRecentFailureText: mostRecentFailureText,
                                                      recipientAddressStates: recipientAddressStates,
                                                      storedMessageState: storedMessageState,
                                                      wasNotCreatedLocally: wasNotCreatedLocally)
        }

        if let modelToCopy = self as? OWSPaymentActivationRequestFinishedMessage {
            assert(type(of: modelToCopy) == OWSPaymentActivationRequestFinishedMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            let groupMetaMessage: TSGroupMetaMessage = modelToCopy.groupMetaMessage
            let hasLegacyMessageState: Bool = modelToCopy.hasLegacyMessageState
            let hasSyncedTranscript: Bool = modelToCopy.hasSyncedTranscript
            let isVoiceMessage: Bool = modelToCopy.isVoiceMessage
            let legacyMessageState: TSOutgoingMessageState = modelToCopy.legacyMessageState
            let legacyWasDelivered: Bool = modelToCopy.legacyWasDelivered
            let mostRecentFailureText: String? = modelToCopy.mostRecentFailureText
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddressStates: [SignalServiceAddress: TSOutgoingMessageRecipientState]?
            if let recipientAddressStatesForCopy = modelToCopy.recipientAddressStates {
               recipientAddressStates = try DeepCopies.deepCopy(recipientAddressStatesForCopy)
            } else {
               recipientAddressStates = nil
            }
            let storedMessageState: TSOutgoingMessageState = modelToCopy.storedMessageState
            let wasNotCreatedLocally: Bool = modelToCopy.wasNotCreatedLocally

            return OWSPaymentActivationRequestFinishedMessage(grdbId: id,
                                                              uniqueId: uniqueId,
                                                              receivedAtTimestamp: receivedAtTimestamp,
                                                              sortId: sortId,
                                                              timestamp: timestamp,
                                                              uniqueThreadId: uniqueThreadId,
                                                              attachmentIds: attachmentIds,
                                                              body: body,
                                                              bodyRanges: bodyRanges,
                                                              contactShare: contactShare,
                                                              editState: editState,
                                                              expireStartedAt: expireStartedAt,
                                                              expiresAt: expiresAt,
                                                              expiresInSeconds: expiresInSeconds,
                                                              giftBadge: giftBadge,
                                                              isGroupStoryReply: isGroupStoryReply,
                                                              isViewOnceComplete: isViewOnceComplete,
                                                              isViewOnceMessage: isViewOnceMessage,
                                                              linkPreview: linkPreview,
                                                              messageSticker: messageSticker,
                                                              quotedMessage: quotedMessage,
                                                              storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                              storyAuthorUuidString: storyAuthorUuidString,
                                                              storyReactionEmoji: storyReactionEmoji,
                                                              storyTimestamp: storyTimestamp,
                                                              wasRemotelyDeleted: wasRemotelyDeleted,
                                                              customMessage: customMessage,
                                                              groupMetaMessage: groupMetaMessage,
                                                              hasLegacyMessageState: hasLegacyMessageState,
                                                              hasSyncedTranscript: hasSyncedTranscript,
                                                              isVoiceMessage: isVoiceMessage,
                                                              legacyMessageState: legacyMessageState,
                                                              legacyWasDelivered: legacyWasDelivered,
                                                              mostRecentFailureText: mostRecentFailureText,
                                                              recipientAddressStates: recipientAddressStates,
                                                              storedMessageState: storedMessageState,
                                                              wasNotCreatedLocally: wasNotCreatedLocally)
        }

        if let modelToCopy = self as? OWSOutgoingPaymentMessage {
            assert(type(of: modelToCopy) == OWSOutgoingPaymentMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            let groupMetaMessage: TSGroupMetaMessage = modelToCopy.groupMetaMessage
            let hasLegacyMessageState: Bool = modelToCopy.hasLegacyMessageState
            let hasSyncedTranscript: Bool = modelToCopy.hasSyncedTranscript
            let isVoiceMessage: Bool = modelToCopy.isVoiceMessage
            let legacyMessageState: TSOutgoingMessageState = modelToCopy.legacyMessageState
            let legacyWasDelivered: Bool = modelToCopy.legacyWasDelivered
            let mostRecentFailureText: String? = modelToCopy.mostRecentFailureText
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddressStates: [SignalServiceAddress: TSOutgoingMessageRecipientState]?
            if let recipientAddressStatesForCopy = modelToCopy.recipientAddressStates {
               recipientAddressStates = try DeepCopies.deepCopy(recipientAddressStatesForCopy)
            } else {
               recipientAddressStates = nil
            }
            let storedMessageState: TSOutgoingMessageState = modelToCopy.storedMessageState
            let wasNotCreatedLocally: Bool = modelToCopy.wasNotCreatedLocally
            let paymentCancellation: Data? = modelToCopy.paymentCancellation
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let paymentNotification: TSPaymentNotification?
            if let paymentNotificationForCopy = modelToCopy.paymentNotification {
               paymentNotification = try DeepCopies.deepCopy(paymentNotificationForCopy)
            } else {
               paymentNotification = nil
            }
            let paymentRequest: Data? = modelToCopy.paymentRequest

            return OWSOutgoingPaymentMessage(grdbId: id,
                                             uniqueId: uniqueId,
                                             receivedAtTimestamp: receivedAtTimestamp,
                                             sortId: sortId,
                                             timestamp: timestamp,
                                             uniqueThreadId: uniqueThreadId,
                                             attachmentIds: attachmentIds,
                                             body: body,
                                             bodyRanges: bodyRanges,
                                             contactShare: contactShare,
                                             editState: editState,
                                             expireStartedAt: expireStartedAt,
                                             expiresAt: expiresAt,
                                             expiresInSeconds: expiresInSeconds,
                                             giftBadge: giftBadge,
                                             isGroupStoryReply: isGroupStoryReply,
                                             isViewOnceComplete: isViewOnceComplete,
                                             isViewOnceMessage: isViewOnceMessage,
                                             linkPreview: linkPreview,
                                             messageSticker: messageSticker,
                                             quotedMessage: quotedMessage,
                                             storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                             storyAuthorUuidString: storyAuthorUuidString,
                                             storyReactionEmoji: storyReactionEmoji,
                                             storyTimestamp: storyTimestamp,
                                             wasRemotelyDeleted: wasRemotelyDeleted,
                                             customMessage: customMessage,
                                             groupMetaMessage: groupMetaMessage,
                                             hasLegacyMessageState: hasLegacyMessageState,
                                             hasSyncedTranscript: hasSyncedTranscript,
                                             isVoiceMessage: isVoiceMessage,
                                             legacyMessageState: legacyMessageState,
                                             legacyWasDelivered: legacyWasDelivered,
                                             mostRecentFailureText: mostRecentFailureText,
                                             recipientAddressStates: recipientAddressStates,
                                             storedMessageState: storedMessageState,
                                             wasNotCreatedLocally: wasNotCreatedLocally,
                                             paymentCancellation: paymentCancellation,
                                             paymentNotification: paymentNotification,
                                             paymentRequest: paymentRequest)
        }

        if let modelToCopy = self as? TSOutgoingMessage {
            assert(type(of: modelToCopy) == TSOutgoingMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            let groupMetaMessage: TSGroupMetaMessage = modelToCopy.groupMetaMessage
            let hasLegacyMessageState: Bool = modelToCopy.hasLegacyMessageState
            let hasSyncedTranscript: Bool = modelToCopy.hasSyncedTranscript
            let isVoiceMessage: Bool = modelToCopy.isVoiceMessage
            let legacyMessageState: TSOutgoingMessageState = modelToCopy.legacyMessageState
            let legacyWasDelivered: Bool = modelToCopy.legacyWasDelivered
            let mostRecentFailureText: String? = modelToCopy.mostRecentFailureText
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddressStates: [SignalServiceAddress: TSOutgoingMessageRecipientState]?
            if let recipientAddressStatesForCopy = modelToCopy.recipientAddressStates {
               recipientAddressStates = try DeepCopies.deepCopy(recipientAddressStatesForCopy)
            } else {
               recipientAddressStates = nil
            }
            let storedMessageState: TSOutgoingMessageState = modelToCopy.storedMessageState
            let wasNotCreatedLocally: Bool = modelToCopy.wasNotCreatedLocally

            return TSOutgoingMessage(grdbId: id,
                                     uniqueId: uniqueId,
                                     receivedAtTimestamp: receivedAtTimestamp,
                                     sortId: sortId,
                                     timestamp: timestamp,
                                     uniqueThreadId: uniqueThreadId,
                                     attachmentIds: attachmentIds,
                                     body: body,
                                     bodyRanges: bodyRanges,
                                     contactShare: contactShare,
                                     editState: editState,
                                     expireStartedAt: expireStartedAt,
                                     expiresAt: expiresAt,
                                     expiresInSeconds: expiresInSeconds,
                                     giftBadge: giftBadge,
                                     isGroupStoryReply: isGroupStoryReply,
                                     isViewOnceComplete: isViewOnceComplete,
                                     isViewOnceMessage: isViewOnceMessage,
                                     linkPreview: linkPreview,
                                     messageSticker: messageSticker,
                                     quotedMessage: quotedMessage,
                                     storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                     storyAuthorUuidString: storyAuthorUuidString,
                                     storyReactionEmoji: storyReactionEmoji,
                                     storyTimestamp: storyTimestamp,
                                     wasRemotelyDeleted: wasRemotelyDeleted,
                                     customMessage: customMessage,
                                     groupMetaMessage: groupMetaMessage,
                                     hasLegacyMessageState: hasLegacyMessageState,
                                     hasSyncedTranscript: hasSyncedTranscript,
                                     isVoiceMessage: isVoiceMessage,
                                     legacyMessageState: legacyMessageState,
                                     legacyWasDelivered: legacyWasDelivered,
                                     mostRecentFailureText: mostRecentFailureText,
                                     recipientAddressStates: recipientAddressStates,
                                     storedMessageState: storedMessageState,
                                     wasNotCreatedLocally: wasNotCreatedLocally)
        }

        if let modelToCopy = self as? OWSVerificationStateChangeMessage {
            assert(type(of: modelToCopy) == OWSVerificationStateChangeMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let infoMessageUserInfo: [InfoMessageUserInfoKey: Any]?
            if let infoMessageUserInfoForCopy = modelToCopy.infoMessageUserInfo {
               infoMessageUserInfo = try DeepCopies.deepCopy(infoMessageUserInfoForCopy)
            } else {
               infoMessageUserInfo = nil
            }
            let messageType: TSInfoMessageType = modelToCopy.messageType
            let read: Bool = modelToCopy.wasRead
            let serverGuid: String? = modelToCopy.serverGuid
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let unregisteredAddress: SignalServiceAddress?
            if let unregisteredAddressForCopy = modelToCopy.unregisteredAddress {
               unregisteredAddress = try DeepCopies.deepCopy(unregisteredAddressForCopy)
            } else {
               unregisteredAddress = nil
            }
            let isLocalChange: Bool = modelToCopy.isLocalChange
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let recipientAddress: SignalServiceAddress = try DeepCopies.deepCopy(modelToCopy.recipientAddress)
            let verificationState: OWSVerificationState = modelToCopy.verificationState

            return OWSVerificationStateChangeMessage(grdbId: id,
                                                     uniqueId: uniqueId,
                                                     receivedAtTimestamp: receivedAtTimestamp,
                                                     sortId: sortId,
                                                     timestamp: timestamp,
                                                     uniqueThreadId: uniqueThreadId,
                                                     attachmentIds: attachmentIds,
                                                     body: body,
                                                     bodyRanges: bodyRanges,
                                                     contactShare: contactShare,
                                                     editState: editState,
                                                     expireStartedAt: expireStartedAt,
                                                     expiresAt: expiresAt,
                                                     expiresInSeconds: expiresInSeconds,
                                                     giftBadge: giftBadge,
                                                     isGroupStoryReply: isGroupStoryReply,
                                                     isViewOnceComplete: isViewOnceComplete,
                                                     isViewOnceMessage: isViewOnceMessage,
                                                     linkPreview: linkPreview,
                                                     messageSticker: messageSticker,
                                                     quotedMessage: quotedMessage,
                                                     storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                     storyAuthorUuidString: storyAuthorUuidString,
                                                     storyReactionEmoji: storyReactionEmoji,
                                                     storyTimestamp: storyTimestamp,
                                                     wasRemotelyDeleted: wasRemotelyDeleted,
                                                     customMessage: customMessage,
                                                     infoMessageUserInfo: infoMessageUserInfo,
                                                     messageType: messageType,
                                                     read: read,
                                                     serverGuid: serverGuid,
                                                     unregisteredAddress: unregisteredAddress,
                                                     isLocalChange: isLocalChange,
                                                     recipientAddress: recipientAddress,
                                                     verificationState: verificationState)
        }

        if let modelToCopy = self as? OWSUnknownProtocolVersionMessage {
            assert(type(of: modelToCopy) == OWSUnknownProtocolVersionMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let infoMessageUserInfo: [InfoMessageUserInfoKey: Any]?
            if let infoMessageUserInfoForCopy = modelToCopy.infoMessageUserInfo {
               infoMessageUserInfo = try DeepCopies.deepCopy(infoMessageUserInfoForCopy)
            } else {
               infoMessageUserInfo = nil
            }
            let messageType: TSInfoMessageType = modelToCopy.messageType
            let read: Bool = modelToCopy.wasRead
            let serverGuid: String? = modelToCopy.serverGuid
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let unregisteredAddress: SignalServiceAddress?
            if let unregisteredAddressForCopy = modelToCopy.unregisteredAddress {
               unregisteredAddress = try DeepCopies.deepCopy(unregisteredAddressForCopy)
            } else {
               unregisteredAddress = nil
            }
            let protocolVersion: UInt = modelToCopy.protocolVersion
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let sender: SignalServiceAddress?
            if let senderForCopy = modelToCopy.sender {
               sender = try DeepCopies.deepCopy(senderForCopy)
            } else {
               sender = nil
            }

            return OWSUnknownProtocolVersionMessage(grdbId: id,
                                                    uniqueId: uniqueId,
                                                    receivedAtTimestamp: receivedAtTimestamp,
                                                    sortId: sortId,
                                                    timestamp: timestamp,
                                                    uniqueThreadId: uniqueThreadId,
                                                    attachmentIds: attachmentIds,
                                                    body: body,
                                                    bodyRanges: bodyRanges,
                                                    contactShare: contactShare,
                                                    editState: editState,
                                                    expireStartedAt: expireStartedAt,
                                                    expiresAt: expiresAt,
                                                    expiresInSeconds: expiresInSeconds,
                                                    giftBadge: giftBadge,
                                                    isGroupStoryReply: isGroupStoryReply,
                                                    isViewOnceComplete: isViewOnceComplete,
                                                    isViewOnceMessage: isViewOnceMessage,
                                                    linkPreview: linkPreview,
                                                    messageSticker: messageSticker,
                                                    quotedMessage: quotedMessage,
                                                    storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                    storyAuthorUuidString: storyAuthorUuidString,
                                                    storyReactionEmoji: storyReactionEmoji,
                                                    storyTimestamp: storyTimestamp,
                                                    wasRemotelyDeleted: wasRemotelyDeleted,
                                                    customMessage: customMessage,
                                                    infoMessageUserInfo: infoMessageUserInfo,
                                                    messageType: messageType,
                                                    read: read,
                                                    serverGuid: serverGuid,
                                                    unregisteredAddress: unregisteredAddress,
                                                    protocolVersion: protocolVersion,
                                                    sender: sender)
        }

        if let modelToCopy = self as? OWSDisappearingConfigurationUpdateInfoMessage {
            assert(type(of: modelToCopy) == OWSDisappearingConfigurationUpdateInfoMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let infoMessageUserInfo: [InfoMessageUserInfoKey: Any]?
            if let infoMessageUserInfoForCopy = modelToCopy.infoMessageUserInfo {
               infoMessageUserInfo = try DeepCopies.deepCopy(infoMessageUserInfoForCopy)
            } else {
               infoMessageUserInfo = nil
            }
            let messageType: TSInfoMessageType = modelToCopy.messageType
            let read: Bool = modelToCopy.wasRead
            let serverGuid: String? = modelToCopy.serverGuid
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let unregisteredAddress: SignalServiceAddress?
            if let unregisteredAddressForCopy = modelToCopy.unregisteredAddress {
               unregisteredAddress = try DeepCopies.deepCopy(unregisteredAddressForCopy)
            } else {
               unregisteredAddress = nil
            }
            let configurationDurationSeconds: UInt32 = modelToCopy.configurationDurationSeconds
            let configurationIsEnabled: Bool = modelToCopy.configurationIsEnabled
            let createdByRemoteName: String? = modelToCopy.createdByRemoteName
            let createdInExistingGroup: Bool = modelToCopy.createdInExistingGroup

            return OWSDisappearingConfigurationUpdateInfoMessage(grdbId: id,
                                                                 uniqueId: uniqueId,
                                                                 receivedAtTimestamp: receivedAtTimestamp,
                                                                 sortId: sortId,
                                                                 timestamp: timestamp,
                                                                 uniqueThreadId: uniqueThreadId,
                                                                 attachmentIds: attachmentIds,
                                                                 body: body,
                                                                 bodyRanges: bodyRanges,
                                                                 contactShare: contactShare,
                                                                 editState: editState,
                                                                 expireStartedAt: expireStartedAt,
                                                                 expiresAt: expiresAt,
                                                                 expiresInSeconds: expiresInSeconds,
                                                                 giftBadge: giftBadge,
                                                                 isGroupStoryReply: isGroupStoryReply,
                                                                 isViewOnceComplete: isViewOnceComplete,
                                                                 isViewOnceMessage: isViewOnceMessage,
                                                                 linkPreview: linkPreview,
                                                                 messageSticker: messageSticker,
                                                                 quotedMessage: quotedMessage,
                                                                 storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                                 storyAuthorUuidString: storyAuthorUuidString,
                                                                 storyReactionEmoji: storyReactionEmoji,
                                                                 storyTimestamp: storyTimestamp,
                                                                 wasRemotelyDeleted: wasRemotelyDeleted,
                                                                 customMessage: customMessage,
                                                                 infoMessageUserInfo: infoMessageUserInfo,
                                                                 messageType: messageType,
                                                                 read: read,
                                                                 serverGuid: serverGuid,
                                                                 unregisteredAddress: unregisteredAddress,
                                                                 configurationDurationSeconds: configurationDurationSeconds,
                                                                 configurationIsEnabled: configurationIsEnabled,
                                                                 createdByRemoteName: createdByRemoteName,
                                                                 createdInExistingGroup: createdInExistingGroup)
        }

        if let modelToCopy = self as? OWSAddToProfileWhitelistOfferMessage {
            assert(type(of: modelToCopy) == OWSAddToProfileWhitelistOfferMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let infoMessageUserInfo: [InfoMessageUserInfoKey: Any]?
            if let infoMessageUserInfoForCopy = modelToCopy.infoMessageUserInfo {
               infoMessageUserInfo = try DeepCopies.deepCopy(infoMessageUserInfoForCopy)
            } else {
               infoMessageUserInfo = nil
            }
            let messageType: TSInfoMessageType = modelToCopy.messageType
            let read: Bool = modelToCopy.wasRead
            let serverGuid: String? = modelToCopy.serverGuid
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let unregisteredAddress: SignalServiceAddress?
            if let unregisteredAddressForCopy = modelToCopy.unregisteredAddress {
               unregisteredAddress = try DeepCopies.deepCopy(unregisteredAddressForCopy)
            } else {
               unregisteredAddress = nil
            }

            return OWSAddToProfileWhitelistOfferMessage(grdbId: id,
                                                        uniqueId: uniqueId,
                                                        receivedAtTimestamp: receivedAtTimestamp,
                                                        sortId: sortId,
                                                        timestamp: timestamp,
                                                        uniqueThreadId: uniqueThreadId,
                                                        attachmentIds: attachmentIds,
                                                        body: body,
                                                        bodyRanges: bodyRanges,
                                                        contactShare: contactShare,
                                                        editState: editState,
                                                        expireStartedAt: expireStartedAt,
                                                        expiresAt: expiresAt,
                                                        expiresInSeconds: expiresInSeconds,
                                                        giftBadge: giftBadge,
                                                        isGroupStoryReply: isGroupStoryReply,
                                                        isViewOnceComplete: isViewOnceComplete,
                                                        isViewOnceMessage: isViewOnceMessage,
                                                        linkPreview: linkPreview,
                                                        messageSticker: messageSticker,
                                                        quotedMessage: quotedMessage,
                                                        storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                        storyAuthorUuidString: storyAuthorUuidString,
                                                        storyReactionEmoji: storyReactionEmoji,
                                                        storyTimestamp: storyTimestamp,
                                                        wasRemotelyDeleted: wasRemotelyDeleted,
                                                        customMessage: customMessage,
                                                        infoMessageUserInfo: infoMessageUserInfo,
                                                        messageType: messageType,
                                                        read: read,
                                                        serverGuid: serverGuid,
                                                        unregisteredAddress: unregisteredAddress)
        }

        if let modelToCopy = self as? OWSAddToContactsOfferMessage {
            assert(type(of: modelToCopy) == OWSAddToContactsOfferMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let infoMessageUserInfo: [InfoMessageUserInfoKey: Any]?
            if let infoMessageUserInfoForCopy = modelToCopy.infoMessageUserInfo {
               infoMessageUserInfo = try DeepCopies.deepCopy(infoMessageUserInfoForCopy)
            } else {
               infoMessageUserInfo = nil
            }
            let messageType: TSInfoMessageType = modelToCopy.messageType
            let read: Bool = modelToCopy.wasRead
            let serverGuid: String? = modelToCopy.serverGuid
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let unregisteredAddress: SignalServiceAddress?
            if let unregisteredAddressForCopy = modelToCopy.unregisteredAddress {
               unregisteredAddress = try DeepCopies.deepCopy(unregisteredAddressForCopy)
            } else {
               unregisteredAddress = nil
            }

            return OWSAddToContactsOfferMessage(grdbId: id,
                                                uniqueId: uniqueId,
                                                receivedAtTimestamp: receivedAtTimestamp,
                                                sortId: sortId,
                                                timestamp: timestamp,
                                                uniqueThreadId: uniqueThreadId,
                                                attachmentIds: attachmentIds,
                                                body: body,
                                                bodyRanges: bodyRanges,
                                                contactShare: contactShare,
                                                editState: editState,
                                                expireStartedAt: expireStartedAt,
                                                expiresAt: expiresAt,
                                                expiresInSeconds: expiresInSeconds,
                                                giftBadge: giftBadge,
                                                isGroupStoryReply: isGroupStoryReply,
                                                isViewOnceComplete: isViewOnceComplete,
                                                isViewOnceMessage: isViewOnceMessage,
                                                linkPreview: linkPreview,
                                                messageSticker: messageSticker,
                                                quotedMessage: quotedMessage,
                                                storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                storyAuthorUuidString: storyAuthorUuidString,
                                                storyReactionEmoji: storyReactionEmoji,
                                                storyTimestamp: storyTimestamp,
                                                wasRemotelyDeleted: wasRemotelyDeleted,
                                                customMessage: customMessage,
                                                infoMessageUserInfo: infoMessageUserInfo,
                                                messageType: messageType,
                                                read: read,
                                                serverGuid: serverGuid,
                                                unregisteredAddress: unregisteredAddress)
        }

        if let modelToCopy = self as? TSInfoMessage {
            assert(type(of: modelToCopy) == TSInfoMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let customMessage: String? = modelToCopy.customMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let infoMessageUserInfo: [InfoMessageUserInfoKey: Any]?
            if let infoMessageUserInfoForCopy = modelToCopy.infoMessageUserInfo {
               infoMessageUserInfo = try DeepCopies.deepCopy(infoMessageUserInfoForCopy)
            } else {
               infoMessageUserInfo = nil
            }
            let messageType: TSInfoMessageType = modelToCopy.messageType
            let read: Bool = modelToCopy.wasRead
            let serverGuid: String? = modelToCopy.serverGuid
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let unregisteredAddress: SignalServiceAddress?
            if let unregisteredAddressForCopy = modelToCopy.unregisteredAddress {
               unregisteredAddress = try DeepCopies.deepCopy(unregisteredAddressForCopy)
            } else {
               unregisteredAddress = nil
            }

            return TSInfoMessage(grdbId: id,
                                 uniqueId: uniqueId,
                                 receivedAtTimestamp: receivedAtTimestamp,
                                 sortId: sortId,
                                 timestamp: timestamp,
                                 uniqueThreadId: uniqueThreadId,
                                 attachmentIds: attachmentIds,
                                 body: body,
                                 bodyRanges: bodyRanges,
                                 contactShare: contactShare,
                                 editState: editState,
                                 expireStartedAt: expireStartedAt,
                                 expiresAt: expiresAt,
                                 expiresInSeconds: expiresInSeconds,
                                 giftBadge: giftBadge,
                                 isGroupStoryReply: isGroupStoryReply,
                                 isViewOnceComplete: isViewOnceComplete,
                                 isViewOnceMessage: isViewOnceMessage,
                                 linkPreview: linkPreview,
                                 messageSticker: messageSticker,
                                 quotedMessage: quotedMessage,
                                 storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                 storyAuthorUuidString: storyAuthorUuidString,
                                 storyReactionEmoji: storyReactionEmoji,
                                 storyTimestamp: storyTimestamp,
                                 wasRemotelyDeleted: wasRemotelyDeleted,
                                 customMessage: customMessage,
                                 infoMessageUserInfo: infoMessageUserInfo,
                                 messageType: messageType,
                                 read: read,
                                 serverGuid: serverGuid,
                                 unregisteredAddress: unregisteredAddress)
        }

        if let modelToCopy = self as? OWSIncomingPaymentMessage {
            assert(type(of: modelToCopy) == OWSIncomingPaymentMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let authorPhoneNumber: String? = modelToCopy.authorPhoneNumber
            let authorUUID: String? = modelToCopy.authorUUID
            let deprecated_sourceDeviceId: UInt32 = modelToCopy.deprecated_sourceDeviceId
            let read: Bool = modelToCopy.wasRead
            let serverDeliveryTimestamp: UInt64 = modelToCopy.serverDeliveryTimestamp
            let serverGuid: String? = modelToCopy.serverGuid
            let serverTimestamp: NSNumber? = modelToCopy.serverTimestamp
            let viewed: Bool = modelToCopy.wasViewed
            let wasReceivedByUD: Bool = modelToCopy.wasReceivedByUD
            let paymentCancellation: Data? = modelToCopy.paymentCancellation
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let paymentNotification: TSPaymentNotification?
            if let paymentNotificationForCopy = modelToCopy.paymentNotification {
               paymentNotification = try DeepCopies.deepCopy(paymentNotificationForCopy)
            } else {
               paymentNotification = nil
            }
            let paymentRequest: Data? = modelToCopy.paymentRequest

            return OWSIncomingPaymentMessage(grdbId: id,
                                             uniqueId: uniqueId,
                                             receivedAtTimestamp: receivedAtTimestamp,
                                             sortId: sortId,
                                             timestamp: timestamp,
                                             uniqueThreadId: uniqueThreadId,
                                             attachmentIds: attachmentIds,
                                             body: body,
                                             bodyRanges: bodyRanges,
                                             contactShare: contactShare,
                                             editState: editState,
                                             expireStartedAt: expireStartedAt,
                                             expiresAt: expiresAt,
                                             expiresInSeconds: expiresInSeconds,
                                             giftBadge: giftBadge,
                                             isGroupStoryReply: isGroupStoryReply,
                                             isViewOnceComplete: isViewOnceComplete,
                                             isViewOnceMessage: isViewOnceMessage,
                                             linkPreview: linkPreview,
                                             messageSticker: messageSticker,
                                             quotedMessage: quotedMessage,
                                             storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                             storyAuthorUuidString: storyAuthorUuidString,
                                             storyReactionEmoji: storyReactionEmoji,
                                             storyTimestamp: storyTimestamp,
                                             wasRemotelyDeleted: wasRemotelyDeleted,
                                             authorPhoneNumber: authorPhoneNumber,
                                             authorUUID: authorUUID,
                                             deprecated_sourceDeviceId: deprecated_sourceDeviceId,
                                             read: read,
                                             serverDeliveryTimestamp: serverDeliveryTimestamp,
                                             serverGuid: serverGuid,
                                             serverTimestamp: serverTimestamp,
                                             viewed: viewed,
                                             wasReceivedByUD: wasReceivedByUD,
                                             paymentCancellation: paymentCancellation,
                                             paymentNotification: paymentNotification,
                                             paymentRequest: paymentRequest)
        }

        if let modelToCopy = self as? TSIncomingMessage {
            assert(type(of: modelToCopy) == TSIncomingMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let authorPhoneNumber: String? = modelToCopy.authorPhoneNumber
            let authorUUID: String? = modelToCopy.authorUUID
            let deprecated_sourceDeviceId: UInt32 = modelToCopy.deprecated_sourceDeviceId
            let read: Bool = modelToCopy.wasRead
            let serverDeliveryTimestamp: UInt64 = modelToCopy.serverDeliveryTimestamp
            let serverGuid: String? = modelToCopy.serverGuid
            let serverTimestamp: NSNumber? = modelToCopy.serverTimestamp
            let viewed: Bool = modelToCopy.wasViewed
            let wasReceivedByUD: Bool = modelToCopy.wasReceivedByUD

            return TSIncomingMessage(grdbId: id,
                                     uniqueId: uniqueId,
                                     receivedAtTimestamp: receivedAtTimestamp,
                                     sortId: sortId,
                                     timestamp: timestamp,
                                     uniqueThreadId: uniqueThreadId,
                                     attachmentIds: attachmentIds,
                                     body: body,
                                     bodyRanges: bodyRanges,
                                     contactShare: contactShare,
                                     editState: editState,
                                     expireStartedAt: expireStartedAt,
                                     expiresAt: expiresAt,
                                     expiresInSeconds: expiresInSeconds,
                                     giftBadge: giftBadge,
                                     isGroupStoryReply: isGroupStoryReply,
                                     isViewOnceComplete: isViewOnceComplete,
                                     isViewOnceMessage: isViewOnceMessage,
                                     linkPreview: linkPreview,
                                     messageSticker: messageSticker,
                                     quotedMessage: quotedMessage,
                                     storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                     storyAuthorUuidString: storyAuthorUuidString,
                                     storyReactionEmoji: storyReactionEmoji,
                                     storyTimestamp: storyTimestamp,
                                     wasRemotelyDeleted: wasRemotelyDeleted,
                                     authorPhoneNumber: authorPhoneNumber,
                                     authorUUID: authorUUID,
                                     deprecated_sourceDeviceId: deprecated_sourceDeviceId,
                                     read: read,
                                     serverDeliveryTimestamp: serverDeliveryTimestamp,
                                     serverGuid: serverGuid,
                                     serverTimestamp: serverTimestamp,
                                     viewed: viewed,
                                     wasReceivedByUD: wasReceivedByUD)
        }

        if let modelToCopy = self as? TSInvalidIdentityKeySendingErrorMessage {
            assert(type(of: modelToCopy) == TSInvalidIdentityKeySendingErrorMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let errorType: TSErrorMessageType = modelToCopy.errorType
            let read: Bool = modelToCopy.wasRead
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let sender: SignalServiceAddress?
            if let senderForCopy = modelToCopy.sender {
               sender = try DeepCopies.deepCopy(senderForCopy)
            } else {
               sender = nil
            }
            let wasIdentityVerified: Bool = modelToCopy.wasIdentityVerified
            let messageId: String = modelToCopy.messageId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let preKeyBundle: PreKeyBundle = try DeepCopies.deepCopy(modelToCopy.preKeyBundle)

            return TSInvalidIdentityKeySendingErrorMessage(grdbId: id,
                                                           uniqueId: uniqueId,
                                                           receivedAtTimestamp: receivedAtTimestamp,
                                                           sortId: sortId,
                                                           timestamp: timestamp,
                                                           uniqueThreadId: uniqueThreadId,
                                                           attachmentIds: attachmentIds,
                                                           body: body,
                                                           bodyRanges: bodyRanges,
                                                           contactShare: contactShare,
                                                           editState: editState,
                                                           expireStartedAt: expireStartedAt,
                                                           expiresAt: expiresAt,
                                                           expiresInSeconds: expiresInSeconds,
                                                           giftBadge: giftBadge,
                                                           isGroupStoryReply: isGroupStoryReply,
                                                           isViewOnceComplete: isViewOnceComplete,
                                                           isViewOnceMessage: isViewOnceMessage,
                                                           linkPreview: linkPreview,
                                                           messageSticker: messageSticker,
                                                           quotedMessage: quotedMessage,
                                                           storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                           storyAuthorUuidString: storyAuthorUuidString,
                                                           storyReactionEmoji: storyReactionEmoji,
                                                           storyTimestamp: storyTimestamp,
                                                           wasRemotelyDeleted: wasRemotelyDeleted,
                                                           errorType: errorType,
                                                           read: read,
                                                           recipientAddress: recipientAddress,
                                                           sender: sender,
                                                           wasIdentityVerified: wasIdentityVerified,
                                                           messageId: messageId,
                                                           preKeyBundle: preKeyBundle)
        }

        if let modelToCopy = self as? TSInvalidIdentityKeyReceivingErrorMessage {
            assert(type(of: modelToCopy) == TSInvalidIdentityKeyReceivingErrorMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let errorType: TSErrorMessageType = modelToCopy.errorType
            let read: Bool = modelToCopy.wasRead
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let sender: SignalServiceAddress?
            if let senderForCopy = modelToCopy.sender {
               sender = try DeepCopies.deepCopy(senderForCopy)
            } else {
               sender = nil
            }
            let wasIdentityVerified: Bool = modelToCopy.wasIdentityVerified
            let authorId: String = modelToCopy.authorId
            let envelopeData: Data? = modelToCopy.envelopeData

            return TSInvalidIdentityKeyReceivingErrorMessage(grdbId: id,
                                                             uniqueId: uniqueId,
                                                             receivedAtTimestamp: receivedAtTimestamp,
                                                             sortId: sortId,
                                                             timestamp: timestamp,
                                                             uniqueThreadId: uniqueThreadId,
                                                             attachmentIds: attachmentIds,
                                                             body: body,
                                                             bodyRanges: bodyRanges,
                                                             contactShare: contactShare,
                                                             editState: editState,
                                                             expireStartedAt: expireStartedAt,
                                                             expiresAt: expiresAt,
                                                             expiresInSeconds: expiresInSeconds,
                                                             giftBadge: giftBadge,
                                                             isGroupStoryReply: isGroupStoryReply,
                                                             isViewOnceComplete: isViewOnceComplete,
                                                             isViewOnceMessage: isViewOnceMessage,
                                                             linkPreview: linkPreview,
                                                             messageSticker: messageSticker,
                                                             quotedMessage: quotedMessage,
                                                             storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                             storyAuthorUuidString: storyAuthorUuidString,
                                                             storyReactionEmoji: storyReactionEmoji,
                                                             storyTimestamp: storyTimestamp,
                                                             wasRemotelyDeleted: wasRemotelyDeleted,
                                                             errorType: errorType,
                                                             read: read,
                                                             recipientAddress: recipientAddress,
                                                             sender: sender,
                                                             wasIdentityVerified: wasIdentityVerified,
                                                             authorId: authorId,
                                                             envelopeData: envelopeData)
        }

        if let modelToCopy = self as? TSInvalidIdentityKeyErrorMessage {
            assert(type(of: modelToCopy) == TSInvalidIdentityKeyErrorMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let errorType: TSErrorMessageType = modelToCopy.errorType
            let read: Bool = modelToCopy.wasRead
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let sender: SignalServiceAddress?
            if let senderForCopy = modelToCopy.sender {
               sender = try DeepCopies.deepCopy(senderForCopy)
            } else {
               sender = nil
            }
            let wasIdentityVerified: Bool = modelToCopy.wasIdentityVerified

            return TSInvalidIdentityKeyErrorMessage(grdbId: id,
                                                    uniqueId: uniqueId,
                                                    receivedAtTimestamp: receivedAtTimestamp,
                                                    sortId: sortId,
                                                    timestamp: timestamp,
                                                    uniqueThreadId: uniqueThreadId,
                                                    attachmentIds: attachmentIds,
                                                    body: body,
                                                    bodyRanges: bodyRanges,
                                                    contactShare: contactShare,
                                                    editState: editState,
                                                    expireStartedAt: expireStartedAt,
                                                    expiresAt: expiresAt,
                                                    expiresInSeconds: expiresInSeconds,
                                                    giftBadge: giftBadge,
                                                    isGroupStoryReply: isGroupStoryReply,
                                                    isViewOnceComplete: isViewOnceComplete,
                                                    isViewOnceMessage: isViewOnceMessage,
                                                    linkPreview: linkPreview,
                                                    messageSticker: messageSticker,
                                                    quotedMessage: quotedMessage,
                                                    storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                    storyAuthorUuidString: storyAuthorUuidString,
                                                    storyReactionEmoji: storyReactionEmoji,
                                                    storyTimestamp: storyTimestamp,
                                                    wasRemotelyDeleted: wasRemotelyDeleted,
                                                    errorType: errorType,
                                                    read: read,
                                                    recipientAddress: recipientAddress,
                                                    sender: sender,
                                                    wasIdentityVerified: wasIdentityVerified)
        }

        if let modelToCopy = self as? OWSUnknownContactBlockOfferMessage {
            assert(type(of: modelToCopy) == OWSUnknownContactBlockOfferMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let errorType: TSErrorMessageType = modelToCopy.errorType
            let read: Bool = modelToCopy.wasRead
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let sender: SignalServiceAddress?
            if let senderForCopy = modelToCopy.sender {
               sender = try DeepCopies.deepCopy(senderForCopy)
            } else {
               sender = nil
            }
            let wasIdentityVerified: Bool = modelToCopy.wasIdentityVerified

            return OWSUnknownContactBlockOfferMessage(grdbId: id,
                                                      uniqueId: uniqueId,
                                                      receivedAtTimestamp: receivedAtTimestamp,
                                                      sortId: sortId,
                                                      timestamp: timestamp,
                                                      uniqueThreadId: uniqueThreadId,
                                                      attachmentIds: attachmentIds,
                                                      body: body,
                                                      bodyRanges: bodyRanges,
                                                      contactShare: contactShare,
                                                      editState: editState,
                                                      expireStartedAt: expireStartedAt,
                                                      expiresAt: expiresAt,
                                                      expiresInSeconds: expiresInSeconds,
                                                      giftBadge: giftBadge,
                                                      isGroupStoryReply: isGroupStoryReply,
                                                      isViewOnceComplete: isViewOnceComplete,
                                                      isViewOnceMessage: isViewOnceMessage,
                                                      linkPreview: linkPreview,
                                                      messageSticker: messageSticker,
                                                      quotedMessage: quotedMessage,
                                                      storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                      storyAuthorUuidString: storyAuthorUuidString,
                                                      storyReactionEmoji: storyReactionEmoji,
                                                      storyTimestamp: storyTimestamp,
                                                      wasRemotelyDeleted: wasRemotelyDeleted,
                                                      errorType: errorType,
                                                      read: read,
                                                      recipientAddress: recipientAddress,
                                                      sender: sender,
                                                      wasIdentityVerified: wasIdentityVerified)
        }

        if let modelToCopy = self as? OWSRecoverableDecryptionPlaceholder {
            assert(type(of: modelToCopy) == OWSRecoverableDecryptionPlaceholder.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let errorType: TSErrorMessageType = modelToCopy.errorType
            let read: Bool = modelToCopy.wasRead
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let sender: SignalServiceAddress?
            if let senderForCopy = modelToCopy.sender {
               sender = try DeepCopies.deepCopy(senderForCopy)
            } else {
               sender = nil
            }
            let wasIdentityVerified: Bool = modelToCopy.wasIdentityVerified

            return OWSRecoverableDecryptionPlaceholder(grdbId: id,
                                                       uniqueId: uniqueId,
                                                       receivedAtTimestamp: receivedAtTimestamp,
                                                       sortId: sortId,
                                                       timestamp: timestamp,
                                                       uniqueThreadId: uniqueThreadId,
                                                       attachmentIds: attachmentIds,
                                                       body: body,
                                                       bodyRanges: bodyRanges,
                                                       contactShare: contactShare,
                                                       editState: editState,
                                                       expireStartedAt: expireStartedAt,
                                                       expiresAt: expiresAt,
                                                       expiresInSeconds: expiresInSeconds,
                                                       giftBadge: giftBadge,
                                                       isGroupStoryReply: isGroupStoryReply,
                                                       isViewOnceComplete: isViewOnceComplete,
                                                       isViewOnceMessage: isViewOnceMessage,
                                                       linkPreview: linkPreview,
                                                       messageSticker: messageSticker,
                                                       quotedMessage: quotedMessage,
                                                       storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                                       storyAuthorUuidString: storyAuthorUuidString,
                                                       storyReactionEmoji: storyReactionEmoji,
                                                       storyTimestamp: storyTimestamp,
                                                       wasRemotelyDeleted: wasRemotelyDeleted,
                                                       errorType: errorType,
                                                       read: read,
                                                       recipientAddress: recipientAddress,
                                                       sender: sender,
                                                       wasIdentityVerified: wasIdentityVerified)
        }

        if let modelToCopy = self as? TSErrorMessage {
            assert(type(of: modelToCopy) == TSErrorMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted
            let errorType: TSErrorMessageType = modelToCopy.errorType
            let read: Bool = modelToCopy.wasRead
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let sender: SignalServiceAddress?
            if let senderForCopy = modelToCopy.sender {
               sender = try DeepCopies.deepCopy(senderForCopy)
            } else {
               sender = nil
            }
            let wasIdentityVerified: Bool = modelToCopy.wasIdentityVerified

            return TSErrorMessage(grdbId: id,
                                  uniqueId: uniqueId,
                                  receivedAtTimestamp: receivedAtTimestamp,
                                  sortId: sortId,
                                  timestamp: timestamp,
                                  uniqueThreadId: uniqueThreadId,
                                  attachmentIds: attachmentIds,
                                  body: body,
                                  bodyRanges: bodyRanges,
                                  contactShare: contactShare,
                                  editState: editState,
                                  expireStartedAt: expireStartedAt,
                                  expiresAt: expiresAt,
                                  expiresInSeconds: expiresInSeconds,
                                  giftBadge: giftBadge,
                                  isGroupStoryReply: isGroupStoryReply,
                                  isViewOnceComplete: isViewOnceComplete,
                                  isViewOnceMessage: isViewOnceMessage,
                                  linkPreview: linkPreview,
                                  messageSticker: messageSticker,
                                  quotedMessage: quotedMessage,
                                  storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                                  storyAuthorUuidString: storyAuthorUuidString,
                                  storyReactionEmoji: storyReactionEmoji,
                                  storyTimestamp: storyTimestamp,
                                  wasRemotelyDeleted: wasRemotelyDeleted,
                                  errorType: errorType,
                                  read: read,
                                  recipientAddress: recipientAddress,
                                  sender: sender,
                                  wasIdentityVerified: wasIdentityVerified)
        }

        if let modelToCopy = self as? TSMessage {
            assert(type(of: modelToCopy) == TSMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            // NOTE: If this generates build errors, you made need to
            // implement DeepCopyable for this type in DeepCopy.swift.
            let attachmentIds: [String] = try DeepCopies.deepCopy(modelToCopy.attachmentIds)
            let body: String? = modelToCopy.body
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let quotedMessage: TSQuotedMessage?
            if let quotedMessageForCopy = modelToCopy.quotedMessage {
               quotedMessage = try DeepCopies.deepCopy(quotedMessageForCopy)
            } else {
               quotedMessage = nil
            }
            let storedShouldStartExpireTimer: Bool = modelToCopy.storedShouldStartExpireTimer
            let storyAuthorUuidString: String? = modelToCopy.storyAuthorUuidString
            let storyReactionEmoji: String? = modelToCopy.storyReactionEmoji
            let storyTimestamp: NSNumber? = modelToCopy.storyTimestamp
            let wasRemotelyDeleted: Bool = modelToCopy.wasRemotelyDeleted

            return TSMessage(grdbId: id,
                             uniqueId: uniqueId,
                             receivedAtTimestamp: receivedAtTimestamp,
                             sortId: sortId,
                             timestamp: timestamp,
                             uniqueThreadId: uniqueThreadId,
                             attachmentIds: attachmentIds,
                             body: body,
                             bodyRanges: bodyRanges,
                             contactShare: contactShare,
                             editState: editState,
                             expireStartedAt: expireStartedAt,
                             expiresAt: expiresAt,
                             expiresInSeconds: expiresInSeconds,
                             giftBadge: giftBadge,
                             isGroupStoryReply: isGroupStoryReply,
                             isViewOnceComplete: isViewOnceComplete,
                             isViewOnceMessage: isViewOnceMessage,
                             linkPreview: linkPreview,
                             messageSticker: messageSticker,
                             quotedMessage: quotedMessage,
                             storedShouldStartExpireTimer: storedShouldStartExpireTimer,
                             storyAuthorUuidString: storyAuthorUuidString,
                             storyReactionEmoji: storyReactionEmoji,
                             storyTimestamp: storyTimestamp,
                             wasRemotelyDeleted: wasRemotelyDeleted)
        }

        if let modelToCopy = self as? TSCall {
            assert(type(of: modelToCopy) == TSCall.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            let callType: RPRecentCallType = modelToCopy.callType
            let offerType: TSRecentCallOfferType = modelToCopy.offerType
            let read: Bool = modelToCopy.wasRead

            return TSCall(grdbId: id,
                          uniqueId: uniqueId,
                          receivedAtTimestamp: receivedAtTimestamp,
                          sortId: sortId,
                          timestamp: timestamp,
                          uniqueThreadId: uniqueThreadId,
                          callType: callType,
                          offerType: offerType,
                          read: read)
        }

        if let modelToCopy = self as? OWSGroupCallMessage {
            assert(type(of: modelToCopy) == OWSGroupCallMessage.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId
            let creatorUuid: String? = modelToCopy.creatorUuid
            let eraId: String? = modelToCopy.eraId
            let hasEnded: Bool = modelToCopy.hasEnded
            // NOTE: If this generates build errors, you made need to
            // modify DeepCopy.swift to support this type.
            //
            // That might mean:
            //
            // * Implement DeepCopyable for this type (e.g. a model).
            // * Modify DeepCopies.deepCopy() to support this type (e.g. a collection).
            let joinedMemberUuids: [String]?
            if let joinedMemberUuidsForCopy = modelToCopy.joinedMemberUuids {
               joinedMemberUuids = try DeepCopies.deepCopy(joinedMemberUuidsForCopy)
            } else {
               joinedMemberUuids = nil
            }
            let read: Bool = modelToCopy.wasRead

            return OWSGroupCallMessage(grdbId: id,
                                       uniqueId: uniqueId,
                                       receivedAtTimestamp: receivedAtTimestamp,
                                       sortId: sortId,
                                       timestamp: timestamp,
                                       uniqueThreadId: uniqueThreadId,
                                       creatorUuid: creatorUuid,
                                       eraId: eraId,
                                       hasEnded: hasEnded,
                                       joinedMemberUuids: joinedMemberUuids,
                                       read: read)
        }

        do {
            let modelToCopy = self
            assert(type(of: modelToCopy) == TSInteraction.self)
            let uniqueId: String = modelToCopy.uniqueId
            let receivedAtTimestamp: UInt64 = modelToCopy.receivedAtTimestamp
            let sortId: UInt64 = modelToCopy.sortId
            let timestamp: UInt64 = modelToCopy.timestamp
            let uniqueThreadId: String = modelToCopy.uniqueThreadId

            return TSInteraction(grdbId: id,
                                 uniqueId: uniqueId,
                                 receivedAtTimestamp: receivedAtTimestamp,
                                 sortId: sortId,
                                 timestamp: timestamp,
                                 uniqueThreadId: uniqueThreadId)
        }

    }
}

// MARK: - Table Metadata

extension TSInteractionSerializer {

    // This defines all of the columns used in the table
    // where this model (and any subclasses) are persisted.
    static var idColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "id", columnType: .primaryKey) }
    static var recordTypeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "recordType", columnType: .int64) }
    static var uniqueIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "uniqueId", columnType: .unicodeString, isUnique: true) }
    // Properties
    static var receivedAtTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "receivedAtTimestamp", columnType: .int64) }
    static var timestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "timestamp", columnType: .int64) }
    static var uniqueThreadIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "uniqueThreadId", columnType: .unicodeString) }
    static var attachmentIdsColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "attachmentIds", columnType: .blob, isOptional: true) }
    static var authorIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "authorId", columnType: .unicodeString, isOptional: true) }
    static var authorPhoneNumberColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "authorPhoneNumber", columnType: .unicodeString, isOptional: true) }
    static var authorUUIDColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "authorUUID", columnType: .unicodeString, isOptional: true) }
    static var bodyColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "body", columnType: .unicodeString, isOptional: true) }
    static var callTypeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "callType", columnType: .int, isOptional: true) }
    static var configurationDurationSecondsColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "configurationDurationSeconds", columnType: .int64, isOptional: true) }
    static var configurationIsEnabledColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "configurationIsEnabled", columnType: .int, isOptional: true) }
    static var contactShareColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "contactShare", columnType: .blob, isOptional: true) }
    static var createdByRemoteNameColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "createdByRemoteName", columnType: .unicodeString, isOptional: true) }
    static var createdInExistingGroupColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "createdInExistingGroup", columnType: .int, isOptional: true) }
    static var customMessageColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "customMessage", columnType: .unicodeString, isOptional: true) }
    static var envelopeDataColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "envelopeData", columnType: .blob, isOptional: true) }
    static var errorTypeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "errorType", columnType: .int, isOptional: true) }
    static var expireStartedAtColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "expireStartedAt", columnType: .int64, isOptional: true) }
    static var expiresAtColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "expiresAt", columnType: .int64, isOptional: true) }
    static var expiresInSecondsColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "expiresInSeconds", columnType: .int64, isOptional: true) }
    static var groupMetaMessageColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "groupMetaMessage", columnType: .int, isOptional: true) }
    static var hasLegacyMessageStateColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "hasLegacyMessageState", columnType: .int, isOptional: true) }
    static var hasSyncedTranscriptColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "hasSyncedTranscript", columnType: .int, isOptional: true) }
    static var wasNotCreatedLocallyColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "wasNotCreatedLocally", columnType: .int, isOptional: true) }
    static var isLocalChangeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isLocalChange", columnType: .int, isOptional: true) }
    static var isViewOnceCompleteColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isViewOnceComplete", columnType: .int, isOptional: true) }
    static var isViewOnceMessageColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isViewOnceMessage", columnType: .int, isOptional: true) }
    static var isVoiceMessageColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isVoiceMessage", columnType: .int, isOptional: true) }
    static var legacyMessageStateColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "legacyMessageState", columnType: .int, isOptional: true) }
    static var legacyWasDeliveredColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "legacyWasDelivered", columnType: .int, isOptional: true) }
    static var linkPreviewColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "linkPreview", columnType: .blob, isOptional: true) }
    static var messageIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "messageId", columnType: .unicodeString, isOptional: true) }
    static var messageStickerColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "messageSticker", columnType: .blob, isOptional: true) }
    static var messageTypeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "messageType", columnType: .int, isOptional: true) }
    static var mostRecentFailureTextColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "mostRecentFailureText", columnType: .unicodeString, isOptional: true) }
    static var preKeyBundleColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "preKeyBundle", columnType: .blob, isOptional: true) }
    static var protocolVersionColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "protocolVersion", columnType: .int64, isOptional: true) }
    static var quotedMessageColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "quotedMessage", columnType: .blob, isOptional: true) }
    static var readColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "read", columnType: .int, isOptional: true) }
    static var recipientAddressColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "recipientAddress", columnType: .blob, isOptional: true) }
    static var recipientAddressStatesColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "recipientAddressStates", columnType: .blob, isOptional: true) }
    static var senderColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "sender", columnType: .blob, isOptional: true) }
    static var serverTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "serverTimestamp", columnType: .int64, isOptional: true) }
    static var deprecated_sourceDeviceIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "deprecated_sourceDeviceId", columnType: .int64, isOptional: true) }
    static var storedMessageStateColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storedMessageState", columnType: .int, isOptional: true) }
    static var storedShouldStartExpireTimerColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storedShouldStartExpireTimer", columnType: .int, isOptional: true) }
    static var unregisteredAddressColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "unregisteredAddress", columnType: .blob, isOptional: true) }
    static var verificationStateColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "verificationState", columnType: .int, isOptional: true) }
    static var wasReceivedByUDColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "wasReceivedByUD", columnType: .int, isOptional: true) }
    static var infoMessageUserInfoColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "infoMessageUserInfo", columnType: .blob, isOptional: true) }
    static var wasRemotelyDeletedColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "wasRemotelyDeleted", columnType: .int, isOptional: true) }
    static var bodyRangesColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "bodyRanges", columnType: .blob, isOptional: true) }
    static var offerTypeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "offerType", columnType: .int, isOptional: true) }
    static var serverDeliveryTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "serverDeliveryTimestamp", columnType: .int64, isOptional: true) }
    static var eraIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "eraId", columnType: .unicodeString, isOptional: true) }
    static var hasEndedColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "hasEnded", columnType: .int, isOptional: true) }
    static var creatorUuidColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "creatorUuid", columnType: .unicodeString, isOptional: true) }
    static var joinedMemberUuidsColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "joinedMemberUuids", columnType: .blob, isOptional: true) }
    static var wasIdentityVerifiedColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "wasIdentityVerified", columnType: .int, isOptional: true) }
    static var paymentCancellationColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "paymentCancellation", columnType: .blob, isOptional: true) }
    static var paymentNotificationColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "paymentNotification", columnType: .blob, isOptional: true) }
    static var paymentRequestColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "paymentRequest", columnType: .blob, isOptional: true) }
    static var viewedColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "viewed", columnType: .int, isOptional: true) }
    static var serverGuidColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "serverGuid", columnType: .unicodeString, isOptional: true) }
    static var storyAuthorUuidStringColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyAuthorUuidString", columnType: .unicodeString, isOptional: true) }
    static var storyTimestampColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyTimestamp", columnType: .int64, isOptional: true) }
    static var isGroupStoryReplyColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isGroupStoryReply", columnType: .int, isOptional: true) }
    static var storyReactionEmojiColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "storyReactionEmoji", columnType: .unicodeString, isOptional: true) }
    static var giftBadgeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "giftBadge", columnType: .blob, isOptional: true) }
    static var editStateColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "editState", columnType: .int, isOptional: true) }

    public static var table: SDSTableMetadata {
        SDSTableMetadata(collection: TSInteraction.collection(),
                         tableName: "model_TSInteraction",
                         columns: [
        idColumn,
        recordTypeColumn,
        uniqueIdColumn,
        receivedAtTimestampColumn,
        timestampColumn,
        uniqueThreadIdColumn,
        attachmentIdsColumn,
        authorIdColumn,
        authorPhoneNumberColumn,
        authorUUIDColumn,
        bodyColumn,
        callTypeColumn,
        configurationDurationSecondsColumn,
        configurationIsEnabledColumn,
        contactShareColumn,
        createdByRemoteNameColumn,
        createdInExistingGroupColumn,
        customMessageColumn,
        envelopeDataColumn,
        errorTypeColumn,
        expireStartedAtColumn,
        expiresAtColumn,
        expiresInSecondsColumn,
        groupMetaMessageColumn,
        hasLegacyMessageStateColumn,
        hasSyncedTranscriptColumn,
        wasNotCreatedLocallyColumn,
        isLocalChangeColumn,
        isViewOnceCompleteColumn,
        isViewOnceMessageColumn,
        isVoiceMessageColumn,
        legacyMessageStateColumn,
        legacyWasDeliveredColumn,
        linkPreviewColumn,
        messageIdColumn,
        messageStickerColumn,
        messageTypeColumn,
        mostRecentFailureTextColumn,
        preKeyBundleColumn,
        protocolVersionColumn,
        quotedMessageColumn,
        readColumn,
        recipientAddressColumn,
        recipientAddressStatesColumn,
        senderColumn,
        serverTimestampColumn,
        deprecated_sourceDeviceIdColumn,
        storedMessageStateColumn,
        storedShouldStartExpireTimerColumn,
        unregisteredAddressColumn,
        verificationStateColumn,
        wasReceivedByUDColumn,
        infoMessageUserInfoColumn,
        wasRemotelyDeletedColumn,
        bodyRangesColumn,
        offerTypeColumn,
        serverDeliveryTimestampColumn,
        eraIdColumn,
        hasEndedColumn,
        creatorUuidColumn,
        joinedMemberUuidsColumn,
        wasIdentityVerifiedColumn,
        paymentCancellationColumn,
        paymentNotificationColumn,
        paymentRequestColumn,
        viewedColumn,
        serverGuidColumn,
        storyAuthorUuidStringColumn,
        storyTimestampColumn,
        isGroupStoryReplyColumn,
        storyReactionEmojiColumn,
        giftBadgeColumn,
        editStateColumn
        ])
    }
}

// MARK: - Save/Remove/Update

@objc
public extension TSInteraction {
    func anyInsert(transaction: SDSAnyWriteTransaction) {
        sdsSave(saveMode: .insert, transaction: transaction)
    }

    // Avoid this method whenever feasible.
    //
    // If the record has previously been saved, this method does an overwriting
    // update of the corresponding row, otherwise if it's a new record, this
    // method inserts a new row.
    //
    // For performance, when possible, you should explicitly specify whether
    // you are inserting or updating rather than calling this method.
    func anyUpsert(transaction: SDSAnyWriteTransaction) {
        let isInserting: Bool
        if TSInteraction.anyFetch(uniqueId: uniqueId, transaction: transaction) != nil {
            isInserting = false
        } else {
            isInserting = true
        }
        sdsSave(saveMode: isInserting ? .insert : .update, transaction: transaction)
    }

    // This method is used by "updateWith..." methods.
    //
    // This model may be updated from many threads. We don't want to save
    // our local copy (this instance) since it may be out of date.  We also
    // want to avoid re-saving a model that has been deleted.  Therefore, we
    // use "updateWith..." methods to:
    //
    // a) Update a property of this instance.
    // b) If a copy of this model exists in the database, load an up-to-date copy,
    //    and update and save that copy.
    // b) If a copy of this model _DOES NOT_ exist in the database, do _NOT_ save
    //    this local instance.
    //
    // After "updateWith...":
    //
    // a) Any copy of this model in the database will have been updated.
    // b) The local property on this instance will always have been updated.
    // c) Other properties on this instance may be out of date.
    //
    // All mutable properties of this class have been made read-only to
    // prevent accidentally modifying them directly.
    //
    // This isn't a perfect arrangement, but in practice this will prevent
    // data loss and will resolve all known issues.
    func anyUpdate(transaction: SDSAnyWriteTransaction, block: (TSInteraction) -> Void) {

        block(self)

        guard let dbCopy = type(of: self).anyFetch(uniqueId: uniqueId,
                                                   transaction: transaction) else {
            return
        }

        // Don't apply the block twice to the same instance.
        // It's at least unnecessary and actually wrong for some blocks.
        // e.g. `block: { $0 in $0.someField++ }`
        if dbCopy !== self {
            block(dbCopy)
        }

        dbCopy.sdsSave(saveMode: .update, transaction: transaction)
    }

    // This method is an alternative to `anyUpdate(transaction:block:)` methods.
    //
    // We should generally use `anyUpdate` to ensure we're not unintentionally
    // clobbering other columns in the database when another concurrent update
    // has occurred.
    //
    // There are cases when this doesn't make sense, e.g. when  we know we've
    // just loaded the model in the same transaction. In those cases it is
    // safe and faster to do a "overwriting" update
    func anyOverwritingUpdate(transaction: SDSAnyWriteTransaction) {
        sdsSave(saveMode: .update, transaction: transaction)
    }

    func anyReload(transaction: SDSAnyReadTransaction) {
        anyReload(transaction: transaction, ignoreMissing: false)
    }

    func anyReload(transaction: SDSAnyReadTransaction, ignoreMissing: Bool) {
        guard let latestVersion = type(of: self).anyFetch(uniqueId: uniqueId, transaction: transaction) else {
            if !ignoreMissing {
                owsFailDebug("`latest` was unexpectedly nil")
            }
            return
        }

        setValuesForKeys(latestVersion.dictionaryValue)
    }
}

// MARK: - TSInteractionCursor

@objc
public class TSInteractionCursor: NSObject, SDSCursor {
    private let transaction: GRDBReadTransaction
    private let cursor: RecordCursor<InteractionRecord>?

    init(transaction: GRDBReadTransaction, cursor: RecordCursor<InteractionRecord>?) {
        self.transaction = transaction
        self.cursor = cursor
    }

    public func next() throws -> TSInteraction? {
        guard let cursor = cursor else {
            return nil
        }
        guard let record = try cursor.next() else {
            return nil
        }
        let value = try TSInteraction.fromRecord(record)
        Self.modelReadCaches.interactionReadCache.didReadInteraction(value, transaction: transaction.asAnyRead)
        return value
    }

    public func all() throws -> [TSInteraction] {
        var result = [TSInteraction]()
        while true {
            guard let model = try next() else {
                break
            }
            result.append(model)
        }
        return result
    }
}

// MARK: - Obj-C Fetch

@objc
public extension TSInteraction {
    class func grdbFetchCursor(transaction: GRDBReadTransaction) -> TSInteractionCursor {
        let database = transaction.database
        do {
            let cursor = try InteractionRecord.fetchCursor(database)
            return TSInteractionCursor(transaction: transaction, cursor: cursor)
        } catch {
            DatabaseCorruptionState.flagDatabaseReadCorruptionIfNecessary(
                userDefaults: CurrentAppContext().appUserDefaults(),
                error: error
            )
            owsFailDebug("Read failed: \(error)")
            return TSInteractionCursor(transaction: transaction, cursor: nil)
        }
    }

    // Fetches a single model by "unique id".
    class func anyFetch(uniqueId: String,
                        transaction: SDSAnyReadTransaction) -> TSInteraction? {
        assert(!uniqueId.isEmpty)

        return anyFetch(uniqueId: uniqueId, transaction: transaction, ignoreCache: false)
    }

    // Fetches a single model by "unique id".
    class func anyFetch(uniqueId: String,
                        transaction: SDSAnyReadTransaction,
                        ignoreCache: Bool) -> TSInteraction? {
        assert(!uniqueId.isEmpty)

        if !ignoreCache,
            let cachedCopy = Self.modelReadCaches.interactionReadCache.getInteraction(uniqueId: uniqueId, transaction: transaction) {
            return cachedCopy
        }

        switch transaction.readTransaction {
        case .grdbRead(let grdbTransaction):
            let sql = "SELECT * FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .uniqueId) = ?"
            return grdbFetchOne(sql: sql, arguments: [uniqueId], transaction: grdbTransaction)
        }
    }

    // Traverses all records.
    // Records are not visited in any particular order.
    class func anyEnumerate(
        transaction: SDSAnyReadTransaction,
        block: (TSInteraction, UnsafeMutablePointer<ObjCBool>) -> Void
    ) {
        anyEnumerate(transaction: transaction, batched: false, block: block)
    }

    // Traverses all records.
    // Records are not visited in any particular order.
    class func anyEnumerate(
        transaction: SDSAnyReadTransaction,
        batched: Bool = false,
        block: (TSInteraction, UnsafeMutablePointer<ObjCBool>) -> Void
    ) {
        let batchSize = batched ? Batching.kDefaultBatchSize : 0
        anyEnumerate(transaction: transaction, batchSize: batchSize, block: block)
    }

    // Traverses all records.
    // Records are not visited in any particular order.
    //
    // If batchSize > 0, the enumeration is performed in autoreleased batches.
    class func anyEnumerate(
        transaction: SDSAnyReadTransaction,
        batchSize: UInt,
        block: (TSInteraction, UnsafeMutablePointer<ObjCBool>) -> Void
    ) {
        switch transaction.readTransaction {
        case .grdbRead(let grdbTransaction):
            let cursor = TSInteraction.grdbFetchCursor(transaction: grdbTransaction)
            Batching.loop(batchSize: batchSize,
                          loopBlock: { stop in
                                do {
                                    guard let value = try cursor.next() else {
                                        stop.pointee = true
                                        return
                                    }
                                    block(value, stop)
                                } catch let error {
                                    owsFailDebug("Couldn't fetch model: \(error)")
                                }
                              })
        }
    }

    // Traverses all records' unique ids.
    // Records are not visited in any particular order.
    class func anyEnumerateUniqueIds(
        transaction: SDSAnyReadTransaction,
        block: (String, UnsafeMutablePointer<ObjCBool>) -> Void
    ) {
        anyEnumerateUniqueIds(transaction: transaction, batched: false, block: block)
    }

    // Traverses all records' unique ids.
    // Records are not visited in any particular order.
    class func anyEnumerateUniqueIds(
        transaction: SDSAnyReadTransaction,
        batched: Bool = false,
        block: (String, UnsafeMutablePointer<ObjCBool>) -> Void
    ) {
        let batchSize = batched ? Batching.kDefaultBatchSize : 0
        anyEnumerateUniqueIds(transaction: transaction, batchSize: batchSize, block: block)
    }

    // Traverses all records' unique ids.
    // Records are not visited in any particular order.
    //
    // If batchSize > 0, the enumeration is performed in autoreleased batches.
    class func anyEnumerateUniqueIds(
        transaction: SDSAnyReadTransaction,
        batchSize: UInt,
        block: (String, UnsafeMutablePointer<ObjCBool>) -> Void
    ) {
        switch transaction.readTransaction {
        case .grdbRead(let grdbTransaction):
            grdbEnumerateUniqueIds(transaction: grdbTransaction,
                                   sql: """
                    SELECT \(interactionColumn: .uniqueId)
                    FROM \(InteractionRecord.databaseTableName)
                """,
                batchSize: batchSize,
                block: block)
        }
    }

    // Does not order the results.
    class func anyFetchAll(transaction: SDSAnyReadTransaction) -> [TSInteraction] {
        var result = [TSInteraction]()
        anyEnumerate(transaction: transaction) { (model, _) in
            result.append(model)
        }
        return result
    }

    // Does not order the results.
    class func anyAllUniqueIds(transaction: SDSAnyReadTransaction) -> [String] {
        var result = [String]()
        anyEnumerateUniqueIds(transaction: transaction) { (uniqueId, _) in
            result.append(uniqueId)
        }
        return result
    }

    class func anyCount(transaction: SDSAnyReadTransaction) -> UInt {
        switch transaction.readTransaction {
        case .grdbRead(let grdbTransaction):
            return InteractionRecord.ows_fetchCount(grdbTransaction.database)
        }
    }

    class func anyExists(
        uniqueId: String,
        transaction: SDSAnyReadTransaction
    ) -> Bool {
        assert(!uniqueId.isEmpty)

        switch transaction.readTransaction {
        case .grdbRead(let grdbTransaction):
            let sql = "SELECT EXISTS ( SELECT 1 FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .uniqueId) = ? )"
            let arguments: StatementArguments = [uniqueId]
            do {
                return try Bool.fetchOne(grdbTransaction.database, sql: sql, arguments: arguments) ?? false
            } catch {
                DatabaseCorruptionState.flagDatabaseReadCorruptionIfNecessary(
                    userDefaults: CurrentAppContext().appUserDefaults(),
                    error: error
                )
                owsFail("Missing instance.")
            }
        }
    }
}

// MARK: - Swift Fetch

public extension TSInteraction {
    class func grdbFetchCursor(sql: String,
                               arguments: StatementArguments = StatementArguments(),
                               transaction: GRDBReadTransaction) -> TSInteractionCursor {
        do {
            let sqlRequest = SQLRequest<Void>(sql: sql, arguments: arguments, cached: true)
            let cursor = try InteractionRecord.fetchCursor(transaction.database, sqlRequest)
            return TSInteractionCursor(transaction: transaction, cursor: cursor)
        } catch {
            DatabaseCorruptionState.flagDatabaseReadCorruptionIfNecessary(
                userDefaults: CurrentAppContext().appUserDefaults(),
                error: error
            )
            owsFailDebug("Read failed: \(error)")
            return TSInteractionCursor(transaction: transaction, cursor: nil)
        }
    }

    class func grdbFetchOne(sql: String,
                            arguments: StatementArguments = StatementArguments(),
                            transaction: GRDBReadTransaction) -> TSInteraction? {
        assert(!sql.isEmpty)

        do {
            let sqlRequest = SQLRequest<Void>(sql: sql, arguments: arguments, cached: true)
            guard let record = try InteractionRecord.fetchOne(transaction.database, sqlRequest) else {
                return nil
            }

            let value = try TSInteraction.fromRecord(record)
            Self.modelReadCaches.interactionReadCache.didReadInteraction(value, transaction: transaction.asAnyRead)
            return value
        } catch {
            owsFailDebug("error: \(error)")
            return nil
        }
    }
}

// MARK: - SDSSerializer

// The SDSSerializer protocol specifies how to insert and update the
// row that corresponds to this model.
class TSInteractionSerializer: SDSSerializer {

    private let model: TSInteraction
    public init(model: TSInteraction) {
        self.model = model
    }

    // MARK: - Record

    func asRecord() throws -> SDSRecord {
        let id: Int64? = model.sortId > 0 ? Int64(model.sortId) : model.grdbId?.int64Value

        let recordType: SDSRecordType = .interaction
        let uniqueId: String = model.uniqueId

        // Properties
        let receivedAtTimestamp: UInt64 = model.receivedAtTimestamp
        let timestamp: UInt64 = model.timestamp
        let threadUniqueId: String = model.uniqueThreadId
        let attachmentIds: Data? = nil
        let authorId: String? = nil
        let authorPhoneNumber: String? = nil
        let authorUUID: String? = nil
        let body: String? = nil
        let callType: RPRecentCallType? = nil
        let configurationDurationSeconds: UInt32? = nil
        let configurationIsEnabled: Bool? = nil
        let contactShare: Data? = nil
        let createdByRemoteName: String? = nil
        let createdInExistingGroup: Bool? = nil
        let customMessage: String? = nil
        let envelopeData: Data? = nil
        let errorType: TSErrorMessageType? = nil
        let expireStartedAt: UInt64? = nil
        let expiresAt: UInt64? = nil
        let expiresInSeconds: UInt32? = nil
        let groupMetaMessage: TSGroupMetaMessage? = nil
        let hasLegacyMessageState: Bool? = nil
        let hasSyncedTranscript: Bool? = nil
        let wasNotCreatedLocally: Bool? = nil
        let isLocalChange: Bool? = nil
        let isViewOnceComplete: Bool? = nil
        let isViewOnceMessage: Bool? = nil
        let isVoiceMessage: Bool? = nil
        let legacyMessageState: TSOutgoingMessageState? = nil
        let legacyWasDelivered: Bool? = nil
        let linkPreview: Data? = nil
        let messageId: String? = nil
        let messageSticker: Data? = nil
        let messageType: TSInfoMessageType? = nil
        let mostRecentFailureText: String? = nil
        let preKeyBundle: Data? = nil
        let protocolVersion: UInt? = nil
        let quotedMessage: Data? = nil
        let read: Bool? = nil
        let recipientAddress: Data? = nil
        let recipientAddressStates: Data? = nil
        let sender: Data? = nil
        let serverTimestamp: UInt64? = nil
        let deprecated_sourceDeviceId: UInt32? = nil
        let storedMessageState: TSOutgoingMessageState? = nil
        let storedShouldStartExpireTimer: Bool? = nil
        let unregisteredAddress: Data? = nil
        let verificationState: OWSVerificationState? = nil
        let wasReceivedByUD: Bool? = nil
        let infoMessageUserInfo: Data? = nil
        let wasRemotelyDeleted: Bool? = nil
        let bodyRanges: Data? = nil
        let offerType: TSRecentCallOfferType? = nil
        let serverDeliveryTimestamp: UInt64? = nil
        let eraId: String? = nil
        let hasEnded: Bool? = nil
        let creatorUuid: String? = nil
        let joinedMemberUuids: Data? = nil
        let wasIdentityVerified: Bool? = nil
        let paymentCancellation: Data? = nil
        let paymentNotification: Data? = nil
        let paymentRequest: Data? = nil
        let viewed: Bool? = nil
        let serverGuid: String? = nil
        let storyAuthorUuidString: String? = nil
        let storyTimestamp: UInt64? = nil
        let isGroupStoryReply: Bool? = nil
        let storyReactionEmoji: String? = nil
        let giftBadge: Data? = nil
        let editState: TSEditState? = nil

        return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, wasNotCreatedLocally: wasNotCreatedLocally, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, deprecated_sourceDeviceId: deprecated_sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, storyAuthorUuidString: storyAuthorUuidString, storyTimestamp: storyTimestamp, isGroupStoryReply: isGroupStoryReply, storyReactionEmoji: storyReactionEmoji, giftBadge: giftBadge, editState: editState)
    }
}

// MARK: - Deep Copy

#if TESTABLE_BUILD
@objc
public extension TSInteraction {
    // We're not using this method at the moment,
    // but we might use it for validation of
    // other deep copy methods.
    func deepCopyUsingRecord() throws -> TSInteraction {
        guard let record = try asRecord() as? InteractionRecord else {
            throw OWSAssertionError("Could not convert to record.")
        }
        return try TSInteraction.fromRecord(record)
    }
}
#endif
