Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/SignalServiceKit/Messages/Interactions/TSInteraction+SDS.swift
1 views
//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
public import GRDB

// 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 deprecated_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: Int?
    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 let archivedPaymentInfo: Data?
    public let expireTimerVersion: UInt32?
    public let isSmsMessageRestoredFromBackup: Bool?
    public let isPoll: Bool?

    public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable {
        case id
        case recordType
        case uniqueId
        case receivedAtTimestamp
        case timestamp
        case threadUniqueId = "uniqueThreadId"
        case deprecated_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
        case archivedPaymentInfo
        case expireTimerVersion
        case isSmsMessageRestoredFromBackup
        case isPoll
    }

    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].flatMap { SDSRecordType(rawValue: $0) }
        uniqueId = row[2]
        receivedAtTimestamp = row[3]
        timestamp = row[4]
        threadUniqueId = row[5]
        deprecated_attachmentIds = row[6]
        authorId = row[7]
        authorPhoneNumber = row[8]
        authorUUID = row[9]
        body = row[10]
        callType = row[11].flatMap { RPRecentCallType(rawValue: $0) }
        configurationDurationSeconds = row[12]
        configurationIsEnabled = row[13]
        contactShare = row[14]
        createdByRemoteName = row[15]
        createdInExistingGroup = row[16]
        customMessage = row[17]
        envelopeData = row[18]
        errorType = row[19].flatMap { TSErrorMessageType(rawValue: $0) }
        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].flatMap { TSOutgoingMessageState(rawValue: $0) }
        legacyWasDelivered = row[32]
        linkPreview = row[33]
        messageId = row[34]
        messageSticker = row[35]
        messageType = row[36].flatMap { TSInfoMessageType(rawValue: $0) }
        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].flatMap { TSOutgoingMessageState(rawValue: $0) }
        storedShouldStartExpireTimer = row[48]
        unregisteredAddress = row[49]
        verificationState = row[50].flatMap { OWSVerificationState(rawValue: $0) }
        wasReceivedByUD = row[51]
        infoMessageUserInfo = row[52]
        wasRemotelyDeleted = row[53]
        bodyRanges = row[54]
        offerType = row[55].flatMap { TSRecentCallOfferType(rawValue: $0) }
        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].flatMap { TSEditState(rawValue: $0) }
        archivedPaymentInfo = row[73]
        expireTimerVersion = row[74]
        isSmsMessageRestoredFromBackup = row[75]
        isPoll = row[76]
    }
}

// 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.missingRequiredField(fieldName: "id") }
        guard let recordType = record.recordType else { throw SDSError.missingRequiredField(fieldName: "recordType") }

        switch 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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 infoMessageUserInfoSerialized.map({ try SDSDeserialization.unarchivedInfoDictionary(from: $0) })
            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 unregisteredAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })

            return OWSAddToContactsOfferMessage(grdbId: recordId,
                                                uniqueId: uniqueId,
                                                receivedAtTimestamp: receivedAtTimestamp,
                                                sortId: sortId,
                                                timestamp: timestamp,
                                                uniqueThreadId: uniqueThreadId,
                                                body: body,
                                                bodyRanges: bodyRanges,
                                                contactShare: contactShare,
                                                deprecated_attachmentIds: deprecated_attachmentIds,
                                                editState: editState,
                                                expireStartedAt: expireStartedAt,
                                                expireTimerVersion: expireTimerVersion,
                                                expiresAt: expiresAt,
                                                expiresInSeconds: expiresInSeconds,
                                                giftBadge: giftBadge,
                                                isGroupStoryReply: isGroupStoryReply,
                                                isPoll: isPoll,
                                                isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 infoMessageUserInfoSerialized.map({ try SDSDeserialization.unarchivedInfoDictionary(from: $0) })
            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 unregisteredAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })

            return OWSAddToProfileWhitelistOfferMessage(grdbId: recordId,
                                                        uniqueId: uniqueId,
                                                        receivedAtTimestamp: receivedAtTimestamp,
                                                        sortId: sortId,
                                                        timestamp: timestamp,
                                                        uniqueThreadId: uniqueThreadId,
                                                        body: body,
                                                        bodyRanges: bodyRanges,
                                                        contactShare: contactShare,
                                                        deprecated_attachmentIds: deprecated_attachmentIds,
                                                        editState: editState,
                                                        expireStartedAt: expireStartedAt,
                                                        expireTimerVersion: expireTimerVersion,
                                                        expiresAt: expiresAt,
                                                        expiresInSeconds: expiresInSeconds,
                                                        giftBadge: giftBadge,
                                                        isGroupStoryReply: isGroupStoryReply,
                                                        isPoll: isPoll,
                                                        isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                        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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 infoMessageUserInfoSerialized.map({ try SDSDeserialization.unarchivedInfoDictionary(from: $0) })
            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 unregisteredAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            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,
                                                                 body: body,
                                                                 bodyRanges: bodyRanges,
                                                                 contactShare: contactShare,
                                                                 deprecated_attachmentIds: deprecated_attachmentIds,
                                                                 editState: editState,
                                                                 expireStartedAt: expireStartedAt,
                                                                 expireTimerVersion: expireTimerVersion,
                                                                 expiresAt: expiresAt,
                                                                 expiresInSeconds: expiresInSeconds,
                                                                 giftBadge: giftBadge,
                                                                 isGroupStoryReply: isGroupStoryReply,
                                                                 isPoll: isPoll,
                                                                 isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                                 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 joinedMemberUuidsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            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 .incomingArchivedPaymentMessage:

            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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.deprecated_sourceDeviceId, name: "deprecated_sourceDeviceId", conversion: { NSNumber(value: $0) })
            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 archivedPaymentInfoSerialized: Data = try record.archivedPaymentInfo ?? { () -> Data in throw SDSError.missingRequiredField(fieldName: "archivedPaymentInfo") }()
            let archivedPaymentInfo: TSArchivedPaymentInfo = try SDSDeserialization.unarchivedObject(ofClass: TSArchivedPaymentInfo.self, from: archivedPaymentInfoSerialized)

            return OWSIncomingArchivedPaymentMessage(grdbId: recordId,
                                                     uniqueId: uniqueId,
                                                     receivedAtTimestamp: receivedAtTimestamp,
                                                     sortId: sortId,
                                                     timestamp: timestamp,
                                                     uniqueThreadId: uniqueThreadId,
                                                     body: body,
                                                     bodyRanges: bodyRanges,
                                                     contactShare: contactShare,
                                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                                     editState: editState,
                                                     expireStartedAt: expireStartedAt,
                                                     expireTimerVersion: expireTimerVersion,
                                                     expiresAt: expiresAt,
                                                     expiresInSeconds: expiresInSeconds,
                                                     giftBadge: giftBadge,
                                                     isGroupStoryReply: isGroupStoryReply,
                                                     isPoll: isPoll,
                                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                     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,
                                                     archivedPaymentInfo: archivedPaymentInfo)

        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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.deprecated_sourceDeviceId, name: "deprecated_sourceDeviceId", conversion: { NSNumber(value: $0) })
            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 paymentNotificationSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSPaymentNotification.self, from: $0) })
            let paymentRequest: Data? = SDSDeserialization.optionalData(record.paymentRequest, name: "paymentRequest")

            return OWSIncomingPaymentMessage(grdbId: recordId,
                                             uniqueId: uniqueId,
                                             receivedAtTimestamp: receivedAtTimestamp,
                                             sortId: sortId,
                                             timestamp: timestamp,
                                             uniqueThreadId: uniqueThreadId,
                                             body: body,
                                             bodyRanges: bodyRanges,
                                             contactShare: contactShare,
                                             deprecated_attachmentIds: deprecated_attachmentIds,
                                             editState: editState,
                                             expireStartedAt: expireStartedAt,
                                             expireTimerVersion: expireTimerVersion,
                                             expiresAt: expiresAt,
                                             expiresInSeconds: expiresInSeconds,
                                             giftBadge: giftBadge,
                                             isGroupStoryReply: isGroupStoryReply,
                                             isPoll: isPoll,
                                             isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                             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 .outgoingArchivedPaymentMessage:

            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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 groupMetaMessage: Int = try SDSDeserialization.required(record.groupMetaMessage, name: "groupMetaMessage")
            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 recipientAddressStatesSerialized.map({ try SDSDeserialization.unarchivedDictionary(ofKeyClass: SignalServiceAddress.self, objectClass: TSOutgoingMessageRecipientState.self, from: $0) })
            guard let storedMessageState: TSOutgoingMessageState = record.storedMessageState else {
               throw SDSError.missingRequiredField()
            }
            let wasNotCreatedLocally: Bool = try SDSDeserialization.required(record.wasNotCreatedLocally, name: "wasNotCreatedLocally")
            let archivedPaymentInfoSerialized: Data = try record.archivedPaymentInfo ?? { () -> Data in throw SDSError.missingRequiredField(fieldName: "archivedPaymentInfo") }()
            let archivedPaymentInfo: TSArchivedPaymentInfo = try SDSDeserialization.unarchivedObject(ofClass: TSArchivedPaymentInfo.self, from: archivedPaymentInfoSerialized)

            return OWSOutgoingArchivedPaymentMessage(grdbId: recordId,
                                                     uniqueId: uniqueId,
                                                     receivedAtTimestamp: receivedAtTimestamp,
                                                     sortId: sortId,
                                                     timestamp: timestamp,
                                                     uniqueThreadId: uniqueThreadId,
                                                     body: body,
                                                     bodyRanges: bodyRanges,
                                                     contactShare: contactShare,
                                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                                     editState: editState,
                                                     expireStartedAt: expireStartedAt,
                                                     expireTimerVersion: expireTimerVersion,
                                                     expiresAt: expiresAt,
                                                     expiresInSeconds: expiresInSeconds,
                                                     giftBadge: giftBadge,
                                                     isGroupStoryReply: isGroupStoryReply,
                                                     isPoll: isPoll,
                                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                     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,
                                                     archivedPaymentInfo: archivedPaymentInfo)

        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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 groupMetaMessage: Int = try SDSDeserialization.required(record.groupMetaMessage, name: "groupMetaMessage")
            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 recipientAddressStatesSerialized.map({ try SDSDeserialization.unarchivedDictionary(ofKeyClass: SignalServiceAddress.self, objectClass: TSOutgoingMessageRecipientState.self, from: $0) })
            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 paymentNotificationSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSPaymentNotification.self, from: $0) })
            let paymentRequest: Data? = SDSDeserialization.optionalData(record.paymentRequest, name: "paymentRequest")

            return OWSOutgoingPaymentMessage(grdbId: recordId,
                                             uniqueId: uniqueId,
                                             receivedAtTimestamp: receivedAtTimestamp,
                                             sortId: sortId,
                                             timestamp: timestamp,
                                             uniqueThreadId: uniqueThreadId,
                                             body: body,
                                             bodyRanges: bodyRanges,
                                             contactShare: contactShare,
                                             deprecated_attachmentIds: deprecated_attachmentIds,
                                             editState: editState,
                                             expireStartedAt: expireStartedAt,
                                             expireTimerVersion: expireTimerVersion,
                                             expiresAt: expiresAt,
                                             expiresInSeconds: expiresInSeconds,
                                             giftBadge: giftBadge,
                                             isGroupStoryReply: isGroupStoryReply,
                                             isPoll: isPoll,
                                             isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                             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 .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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 recipientAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try senderSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")

            return OWSRecoverableDecryptionPlaceholder(grdbId: recordId,
                                                       uniqueId: uniqueId,
                                                       receivedAtTimestamp: receivedAtTimestamp,
                                                       sortId: sortId,
                                                       timestamp: timestamp,
                                                       uniqueThreadId: uniqueThreadId,
                                                       body: body,
                                                       bodyRanges: bodyRanges,
                                                       contactShare: contactShare,
                                                       deprecated_attachmentIds: deprecated_attachmentIds,
                                                       editState: editState,
                                                       expireStartedAt: expireStartedAt,
                                                       expireTimerVersion: expireTimerVersion,
                                                       expiresAt: expiresAt,
                                                       expiresInSeconds: expiresInSeconds,
                                                       giftBadge: giftBadge,
                                                       isGroupStoryReply: isGroupStoryReply,
                                                       isPoll: isPoll,
                                                       isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                       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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 recipientAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try senderSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")

            return OWSUnknownContactBlockOfferMessage(grdbId: recordId,
                                                      uniqueId: uniqueId,
                                                      receivedAtTimestamp: receivedAtTimestamp,
                                                      sortId: sortId,
                                                      timestamp: timestamp,
                                                      uniqueThreadId: uniqueThreadId,
                                                      body: body,
                                                      bodyRanges: bodyRanges,
                                                      contactShare: contactShare,
                                                      deprecated_attachmentIds: deprecated_attachmentIds,
                                                      editState: editState,
                                                      expireStartedAt: expireStartedAt,
                                                      expireTimerVersion: expireTimerVersion,
                                                      expiresAt: expiresAt,
                                                      expiresInSeconds: expiresInSeconds,
                                                      giftBadge: giftBadge,
                                                      isGroupStoryReply: isGroupStoryReply,
                                                      isPoll: isPoll,
                                                      isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                      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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 infoMessageUserInfoSerialized.map({ try SDSDeserialization.unarchivedInfoDictionary(from: $0) })
            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 unregisteredAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let protocolVersion: UInt = try SDSDeserialization.required(record.protocolVersion, name: "protocolVersion")
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try senderSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })

            return OWSUnknownProtocolVersionMessage(grdbId: recordId,
                                                    uniqueId: uniqueId,
                                                    receivedAtTimestamp: receivedAtTimestamp,
                                                    sortId: sortId,
                                                    timestamp: timestamp,
                                                    uniqueThreadId: uniqueThreadId,
                                                    body: body,
                                                    bodyRanges: bodyRanges,
                                                    contactShare: contactShare,
                                                    deprecated_attachmentIds: deprecated_attachmentIds,
                                                    editState: editState,
                                                    expireStartedAt: expireStartedAt,
                                                    expireTimerVersion: expireTimerVersion,
                                                    expiresAt: expiresAt,
                                                    expiresInSeconds: expiresInSeconds,
                                                    giftBadge: giftBadge,
                                                    isGroupStoryReply: isGroupStoryReply,
                                                    isPoll: isPoll,
                                                    isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                    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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 infoMessageUserInfoSerialized.map({ try SDSDeserialization.unarchivedInfoDictionary(from: $0) })
            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 unregisteredAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let isLocalChange: Bool = try SDSDeserialization.required(record.isLocalChange, name: "isLocalChange")
            let recipientAddressSerialized: Data = try record.recipientAddress ?? { () -> Data in throw SDSError.missingRequiredField(fieldName: "recipientAddress") }()
            let recipientAddress: SignalServiceAddress = try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: recipientAddressSerialized)
            guard let verificationState: OWSVerificationState = record.verificationState else {
               throw SDSError.missingRequiredField()
            }

            return OWSVerificationStateChangeMessage(grdbId: recordId,
                                                     uniqueId: uniqueId,
                                                     receivedAtTimestamp: receivedAtTimestamp,
                                                     sortId: sortId,
                                                     timestamp: timestamp,
                                                     uniqueThreadId: uniqueThreadId,
                                                     body: body,
                                                     bodyRanges: bodyRanges,
                                                     contactShare: contactShare,
                                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                                     editState: editState,
                                                     expireStartedAt: expireStartedAt,
                                                     expireTimerVersion: expireTimerVersion,
                                                     expiresAt: expiresAt,
                                                     expiresInSeconds: expiresInSeconds,
                                                     giftBadge: giftBadge,
                                                     isGroupStoryReply: isGroupStoryReply,
                                                     isPoll: isPoll,
                                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                     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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 recipientAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try senderSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")

            return TSErrorMessage(grdbId: recordId,
                                  uniqueId: uniqueId,
                                  receivedAtTimestamp: receivedAtTimestamp,
                                  sortId: sortId,
                                  timestamp: timestamp,
                                  uniqueThreadId: uniqueThreadId,
                                  body: body,
                                  bodyRanges: bodyRanges,
                                  contactShare: contactShare,
                                  deprecated_attachmentIds: deprecated_attachmentIds,
                                  editState: editState,
                                  expireStartedAt: expireStartedAt,
                                  expireTimerVersion: expireTimerVersion,
                                  expiresAt: expiresAt,
                                  expiresInSeconds: expiresInSeconds,
                                  giftBadge: giftBadge,
                                  isGroupStoryReply: isGroupStoryReply,
                                  isPoll: isPoll,
                                  isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                  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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.deprecated_sourceDeviceId, name: "deprecated_sourceDeviceId", conversion: { NSNumber(value: $0) })
            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,
                                     body: body,
                                     bodyRanges: bodyRanges,
                                     contactShare: contactShare,
                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                     editState: editState,
                                     expireStartedAt: expireStartedAt,
                                     expireTimerVersion: expireTimerVersion,
                                     expiresAt: expiresAt,
                                     expiresInSeconds: expiresInSeconds,
                                     giftBadge: giftBadge,
                                     isGroupStoryReply: isGroupStoryReply,
                                     isPoll: isPoll,
                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                     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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 infoMessageUserInfoSerialized.map({ try SDSDeserialization.unarchivedInfoDictionary(from: $0) })
            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 unregisteredAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })

            return TSInfoMessage(grdbId: recordId,
                                 uniqueId: uniqueId,
                                 receivedAtTimestamp: receivedAtTimestamp,
                                 sortId: sortId,
                                 timestamp: timestamp,
                                 uniqueThreadId: uniqueThreadId,
                                 body: body,
                                 bodyRanges: bodyRanges,
                                 contactShare: contactShare,
                                 deprecated_attachmentIds: deprecated_attachmentIds,
                                 editState: editState,
                                 expireStartedAt: expireStartedAt,
                                 expireTimerVersion: expireTimerVersion,
                                 expiresAt: expiresAt,
                                 expiresInSeconds: expiresInSeconds,
                                 giftBadge: giftBadge,
                                 isGroupStoryReply: isGroupStoryReply,
                                 isPoll: isPoll,
                                 isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                 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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 recipientAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try senderSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")

            return TSInvalidIdentityKeyErrorMessage(grdbId: recordId,
                                                    uniqueId: uniqueId,
                                                    receivedAtTimestamp: receivedAtTimestamp,
                                                    sortId: sortId,
                                                    timestamp: timestamp,
                                                    uniqueThreadId: uniqueThreadId,
                                                    body: body,
                                                    bodyRanges: bodyRanges,
                                                    contactShare: contactShare,
                                                    deprecated_attachmentIds: deprecated_attachmentIds,
                                                    editState: editState,
                                                    expireStartedAt: expireStartedAt,
                                                    expireTimerVersion: expireTimerVersion,
                                                    expiresAt: expiresAt,
                                                    expiresInSeconds: expiresInSeconds,
                                                    giftBadge: giftBadge,
                                                    isGroupStoryReply: isGroupStoryReply,
                                                    isPoll: isPoll,
                                                    isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                    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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 recipientAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try senderSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            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,
                                                             body: body,
                                                             bodyRanges: bodyRanges,
                                                             contactShare: contactShare,
                                                             deprecated_attachmentIds: deprecated_attachmentIds,
                                                             editState: editState,
                                                             expireStartedAt: expireStartedAt,
                                                             expireTimerVersion: expireTimerVersion,
                                                             expiresAt: expiresAt,
                                                             expiresInSeconds: expiresInSeconds,
                                                             giftBadge: giftBadge,
                                                             isGroupStoryReply: isGroupStoryReply,
                                                             isPoll: isPoll,
                                                             isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                             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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 recipientAddressSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let senderSerialized: Data? = record.sender
            let sender: SignalServiceAddress? = try senderSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: SignalServiceAddress.self, from: $0) })
            let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified")
            let messageId: String = try SDSDeserialization.required(record.messageId, name: "messageId")
            let preKeyBundle: Data = try SDSDeserialization.required(record.preKeyBundle, name: "preKeyBundle")

            return TSInvalidIdentityKeySendingErrorMessage(grdbId: recordId,
                                                           uniqueId: uniqueId,
                                                           receivedAtTimestamp: receivedAtTimestamp,
                                                           sortId: sortId,
                                                           timestamp: timestamp,
                                                           uniqueThreadId: uniqueThreadId,
                                                           body: body,
                                                           bodyRanges: bodyRanges,
                                                           contactShare: contactShare,
                                                           deprecated_attachmentIds: deprecated_attachmentIds,
                                                           editState: editState,
                                                           expireStartedAt: expireStartedAt,
                                                           expireTimerVersion: expireTimerVersion,
                                                           expiresAt: expiresAt,
                                                           expiresInSeconds: expiresInSeconds,
                                                           giftBadge: giftBadge,
                                                           isGroupStoryReply: isGroupStoryReply,
                                                           isPoll: isPoll,
                                                           isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                           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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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,
                             body: body,
                             bodyRanges: bodyRanges,
                             contactShare: contactShare,
                             deprecated_attachmentIds: deprecated_attachmentIds,
                             editState: editState,
                             expireStartedAt: expireStartedAt,
                             expireTimerVersion: expireTimerVersion,
                             expiresAt: expiresAt,
                             expiresInSeconds: expiresInSeconds,
                             giftBadge: giftBadge,
                             isGroupStoryReply: isGroupStoryReply,
                             isPoll: isPoll,
                             isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                             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 body: String? = record.body
            let bodyRangesSerialized: Data? = record.bodyRanges
            let bodyRanges: MessageBodyRanges? = try bodyRangesSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageBodyRanges.self, from: $0) })
            let contactShareSerialized: Data? = record.contactShare
            let contactShare: OWSContact? = try contactShareSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSContact.self, from: $0) })
            let deprecated_attachmentIdsSerialized: Data? = record.deprecated_attachmentIds
            let deprecated_attachmentIds: [String]? = try deprecated_attachmentIdsSerialized.map({ try SDSDeserialization.unarchivedArrayOfObjects(ofClass: NSString.self, from: $0) as [String] })
            guard let editState: TSEditState = record.editState else {
               throw SDSError.missingRequiredField()
            }
            let expireStartedAt: UInt64 = try SDSDeserialization.required(record.expireStartedAt, name: "expireStartedAt")
            let expireTimerVersion: NSNumber? = SDSDeserialization.optionalNumericAsNSNumber(record.expireTimerVersion, name: "expireTimerVersion", conversion: { NSNumber(value: $0) })
            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 giftBadgeSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSGiftBadge.self, from: $0) })
            let isGroupStoryReply: Bool = try SDSDeserialization.required(record.isGroupStoryReply, name: "isGroupStoryReply")
            let isPoll: Bool = try SDSDeserialization.required(record.isPoll, name: "isPoll")
            let isSmsMessageRestoredFromBackup: Bool = try SDSDeserialization.required(record.isSmsMessageRestoredFromBackup, name: "isSmsMessageRestoredFromBackup")
            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 linkPreviewSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: OWSLinkPreview.self, from: $0) })
            let messageStickerSerialized: Data? = record.messageSticker
            let messageSticker: MessageSticker? = try messageStickerSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: MessageSticker.self, from: $0) })
            let quotedMessageSerialized: Data? = record.quotedMessage
            let quotedMessage: TSQuotedMessage? = try quotedMessageSerialized.map({ try SDSDeserialization.unarchivedObject(ofClass: TSQuotedMessage.self, from: $0) })
            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 groupMetaMessage: Int = try SDSDeserialization.required(record.groupMetaMessage, name: "groupMetaMessage")
            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 recipientAddressStatesSerialized.map({ try SDSDeserialization.unarchivedDictionary(ofKeyClass: SignalServiceAddress.self, objectClass: TSOutgoingMessageRecipientState.self, from: $0) })
            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,
                                     body: body,
                                     bodyRanges: bodyRanges,
                                     contactShare: contactShare,
                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                     editState: editState,
                                     expireStartedAt: expireStartedAt,
                                     expireTimerVersion: expireTimerVersion,
                                     expiresAt: expiresAt,
                                     expiresInSeconds: expiresInSeconds,
                                     giftBadge: giftBadge,
                                     isGroupStoryReply: isGroupStoryReply,
                                     isPoll: isPoll,
                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                     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: \(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 OWSOutgoingPaymentMessage:
            assert(type(of: model) == OWSOutgoingPaymentMessage.self)
            return OWSOutgoingPaymentMessageSerializer(model: model)
        case let model as OWSOutgoingArchivedPaymentMessage:
            assert(type(of: model) == OWSOutgoingArchivedPaymentMessage.self)
            return OWSOutgoingArchivedPaymentMessageSerializer(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 OWSIncomingArchivedPaymentMessage:
            assert(type(of: model) == OWSIncomingArchivedPaymentMessage.self)
            return OWSIncomingArchivedPaymentMessageSerializer(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() -> SDSRecord {
        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 {
        guard let id = self.grdbId?.int64Value else {
            throw OWSAssertionError("Model missing grdbId.")
        }

        // Any subclass can be cast to its superclass, so the order of these if
        // statements matters. We need to do a "depth first" search by type.

        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? 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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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: Int = 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
            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
            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,
                                             body: body,
                                             bodyRanges: bodyRanges,
                                             contactShare: contactShare,
                                             deprecated_attachmentIds: deprecated_attachmentIds,
                                             editState: editState,
                                             expireStartedAt: expireStartedAt,
                                             expireTimerVersion: expireTimerVersion,
                                             expiresAt: expiresAt,
                                             expiresInSeconds: expiresInSeconds,
                                             giftBadge: giftBadge,
                                             isGroupStoryReply: isGroupStoryReply,
                                             isPoll: isPoll,
                                             isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                             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? OWSOutgoingArchivedPaymentMessage {
            assert(type(of: modelToCopy) == OWSOutgoingArchivedPaymentMessage.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 body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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: Int = 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
            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 archivedPaymentInfo: TSArchivedPaymentInfo = try DeepCopies.deepCopy(modelToCopy.archivedPaymentInfo)

            return OWSOutgoingArchivedPaymentMessage(grdbId: id,
                                                     uniqueId: uniqueId,
                                                     receivedAtTimestamp: receivedAtTimestamp,
                                                     sortId: sortId,
                                                     timestamp: timestamp,
                                                     uniqueThreadId: uniqueThreadId,
                                                     body: body,
                                                     bodyRanges: bodyRanges,
                                                     contactShare: contactShare,
                                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                                     editState: editState,
                                                     expireStartedAt: expireStartedAt,
                                                     expireTimerVersion: expireTimerVersion,
                                                     expiresAt: expiresAt,
                                                     expiresInSeconds: expiresInSeconds,
                                                     giftBadge: giftBadge,
                                                     isGroupStoryReply: isGroupStoryReply,
                                                     isPoll: isPoll,
                                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                     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,
                                                     archivedPaymentInfo: archivedPaymentInfo)
        }

        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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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: Int = 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
            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,
                                     body: body,
                                     bodyRanges: bodyRanges,
                                     contactShare: contactShare,
                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                     editState: editState,
                                     expireStartedAt: expireStartedAt,
                                     expireTimerVersion: expireTimerVersion,
                                     expiresAt: expiresAt,
                                     expiresInSeconds: expiresInSeconds,
                                     giftBadge: giftBadge,
                                     isGroupStoryReply: isGroupStoryReply,
                                     isPoll: isPoll,
                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                     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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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 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
            let unregisteredAddress: SignalServiceAddress?
            if let unregisteredAddressForCopy = modelToCopy.unregisteredAddress {
               unregisteredAddress = try DeepCopies.deepCopy(unregisteredAddressForCopy)
            } else {
               unregisteredAddress = nil
            }
            let isLocalChange: Bool = modelToCopy.isLocalChange
            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,
                                                     body: body,
                                                     bodyRanges: bodyRanges,
                                                     contactShare: contactShare,
                                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                                     editState: editState,
                                                     expireStartedAt: expireStartedAt,
                                                     expireTimerVersion: expireTimerVersion,
                                                     expiresAt: expiresAt,
                                                     expiresInSeconds: expiresInSeconds,
                                                     giftBadge: giftBadge,
                                                     isGroupStoryReply: isGroupStoryReply,
                                                     isPoll: isPoll,
                                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                     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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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 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
            let unregisteredAddress: SignalServiceAddress?
            if let unregisteredAddressForCopy = modelToCopy.unregisteredAddress {
               unregisteredAddress = try DeepCopies.deepCopy(unregisteredAddressForCopy)
            } else {
               unregisteredAddress = nil
            }
            let protocolVersion: UInt = modelToCopy.protocolVersion
            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,
                                                    body: body,
                                                    bodyRanges: bodyRanges,
                                                    contactShare: contactShare,
                                                    deprecated_attachmentIds: deprecated_attachmentIds,
                                                    editState: editState,
                                                    expireStartedAt: expireStartedAt,
                                                    expireTimerVersion: expireTimerVersion,
                                                    expiresAt: expiresAt,
                                                    expiresInSeconds: expiresInSeconds,
                                                    giftBadge: giftBadge,
                                                    isGroupStoryReply: isGroupStoryReply,
                                                    isPoll: isPoll,
                                                    isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                    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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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 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
            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,
                                                                 body: body,
                                                                 bodyRanges: bodyRanges,
                                                                 contactShare: contactShare,
                                                                 deprecated_attachmentIds: deprecated_attachmentIds,
                                                                 editState: editState,
                                                                 expireStartedAt: expireStartedAt,
                                                                 expireTimerVersion: expireTimerVersion,
                                                                 expiresAt: expiresAt,
                                                                 expiresInSeconds: expiresInSeconds,
                                                                 giftBadge: giftBadge,
                                                                 isGroupStoryReply: isGroupStoryReply,
                                                                 isPoll: isPoll,
                                                                 isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                                 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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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 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
            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,
                                                        body: body,
                                                        bodyRanges: bodyRanges,
                                                        contactShare: contactShare,
                                                        deprecated_attachmentIds: deprecated_attachmentIds,
                                                        editState: editState,
                                                        expireStartedAt: expireStartedAt,
                                                        expireTimerVersion: expireTimerVersion,
                                                        expiresAt: expiresAt,
                                                        expiresInSeconds: expiresInSeconds,
                                                        giftBadge: giftBadge,
                                                        isGroupStoryReply: isGroupStoryReply,
                                                        isPoll: isPoll,
                                                        isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                        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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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 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
            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,
                                                body: body,
                                                bodyRanges: bodyRanges,
                                                contactShare: contactShare,
                                                deprecated_attachmentIds: deprecated_attachmentIds,
                                                editState: editState,
                                                expireStartedAt: expireStartedAt,
                                                expireTimerVersion: expireTimerVersion,
                                                expiresAt: expiresAt,
                                                expiresInSeconds: expiresInSeconds,
                                                giftBadge: giftBadge,
                                                isGroupStoryReply: isGroupStoryReply,
                                                isPoll: isPoll,
                                                isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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 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
            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,
                                 body: body,
                                 bodyRanges: bodyRanges,
                                 contactShare: contactShare,
                                 deprecated_attachmentIds: deprecated_attachmentIds,
                                 editState: editState,
                                 expireStartedAt: expireStartedAt,
                                 expireTimerVersion: expireTimerVersion,
                                 expiresAt: expiresAt,
                                 expiresInSeconds: expiresInSeconds,
                                 giftBadge: giftBadge,
                                 isGroupStoryReply: isGroupStoryReply,
                                 isPoll: isPoll,
                                 isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                 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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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: NSNumber? = 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
            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,
                                             body: body,
                                             bodyRanges: bodyRanges,
                                             contactShare: contactShare,
                                             deprecated_attachmentIds: deprecated_attachmentIds,
                                             editState: editState,
                                             expireStartedAt: expireStartedAt,
                                             expireTimerVersion: expireTimerVersion,
                                             expiresAt: expiresAt,
                                             expiresInSeconds: expiresInSeconds,
                                             giftBadge: giftBadge,
                                             isGroupStoryReply: isGroupStoryReply,
                                             isPoll: isPoll,
                                             isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                             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? OWSIncomingArchivedPaymentMessage {
            assert(type(of: modelToCopy) == OWSIncomingArchivedPaymentMessage.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 body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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: NSNumber? = 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 archivedPaymentInfo: TSArchivedPaymentInfo = try DeepCopies.deepCopy(modelToCopy.archivedPaymentInfo)

            return OWSIncomingArchivedPaymentMessage(grdbId: id,
                                                     uniqueId: uniqueId,
                                                     receivedAtTimestamp: receivedAtTimestamp,
                                                     sortId: sortId,
                                                     timestamp: timestamp,
                                                     uniqueThreadId: uniqueThreadId,
                                                     body: body,
                                                     bodyRanges: bodyRanges,
                                                     contactShare: contactShare,
                                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                                     editState: editState,
                                                     expireStartedAt: expireStartedAt,
                                                     expireTimerVersion: expireTimerVersion,
                                                     expiresAt: expiresAt,
                                                     expiresInSeconds: expiresInSeconds,
                                                     giftBadge: giftBadge,
                                                     isGroupStoryReply: isGroupStoryReply,
                                                     isPoll: isPoll,
                                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                     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,
                                                     archivedPaymentInfo: archivedPaymentInfo)
        }

        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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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: NSNumber? = 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,
                                     body: body,
                                     bodyRanges: bodyRanges,
                                     contactShare: contactShare,
                                     deprecated_attachmentIds: deprecated_attachmentIds,
                                     editState: editState,
                                     expireStartedAt: expireStartedAt,
                                     expireTimerVersion: expireTimerVersion,
                                     expiresAt: expiresAt,
                                     expiresInSeconds: expiresInSeconds,
                                     giftBadge: giftBadge,
                                     isGroupStoryReply: isGroupStoryReply,
                                     isPoll: isPoll,
                                     isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                     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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            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
            let preKeyBundle: Data = modelToCopy.preKeyBundle

            return TSInvalidIdentityKeySendingErrorMessage(grdbId: id,
                                                           uniqueId: uniqueId,
                                                           receivedAtTimestamp: receivedAtTimestamp,
                                                           sortId: sortId,
                                                           timestamp: timestamp,
                                                           uniqueThreadId: uniqueThreadId,
                                                           body: body,
                                                           bodyRanges: bodyRanges,
                                                           contactShare: contactShare,
                                                           deprecated_attachmentIds: deprecated_attachmentIds,
                                                           editState: editState,
                                                           expireStartedAt: expireStartedAt,
                                                           expireTimerVersion: expireTimerVersion,
                                                           expiresAt: expiresAt,
                                                           expiresInSeconds: expiresInSeconds,
                                                           giftBadge: giftBadge,
                                                           isGroupStoryReply: isGroupStoryReply,
                                                           isPoll: isPoll,
                                                           isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                           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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            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,
                                                             body: body,
                                                             bodyRanges: bodyRanges,
                                                             contactShare: contactShare,
                                                             deprecated_attachmentIds: deprecated_attachmentIds,
                                                             editState: editState,
                                                             expireStartedAt: expireStartedAt,
                                                             expireTimerVersion: expireTimerVersion,
                                                             expiresAt: expiresAt,
                                                             expiresInSeconds: expiresInSeconds,
                                                             giftBadge: giftBadge,
                                                             isGroupStoryReply: isGroupStoryReply,
                                                             isPoll: isPoll,
                                                             isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                             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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            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,
                                                    body: body,
                                                    bodyRanges: bodyRanges,
                                                    contactShare: contactShare,
                                                    deprecated_attachmentIds: deprecated_attachmentIds,
                                                    editState: editState,
                                                    expireStartedAt: expireStartedAt,
                                                    expireTimerVersion: expireTimerVersion,
                                                    expiresAt: expiresAt,
                                                    expiresInSeconds: expiresInSeconds,
                                                    giftBadge: giftBadge,
                                                    isGroupStoryReply: isGroupStoryReply,
                                                    isPoll: isPoll,
                                                    isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                    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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            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,
                                                      body: body,
                                                      bodyRanges: bodyRanges,
                                                      contactShare: contactShare,
                                                      deprecated_attachmentIds: deprecated_attachmentIds,
                                                      editState: editState,
                                                      expireStartedAt: expireStartedAt,
                                                      expireTimerVersion: expireTimerVersion,
                                                      expiresAt: expiresAt,
                                                      expiresInSeconds: expiresInSeconds,
                                                      giftBadge: giftBadge,
                                                      isGroupStoryReply: isGroupStoryReply,
                                                      isPoll: isPoll,
                                                      isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                      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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            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,
                                                       body: body,
                                                       bodyRanges: bodyRanges,
                                                       contactShare: contactShare,
                                                       deprecated_attachmentIds: deprecated_attachmentIds,
                                                       editState: editState,
                                                       expireStartedAt: expireStartedAt,
                                                       expireTimerVersion: expireTimerVersion,
                                                       expiresAt: expiresAt,
                                                       expiresInSeconds: expiresInSeconds,
                                                       giftBadge: giftBadge,
                                                       isGroupStoryReply: isGroupStoryReply,
                                                       isPoll: isPoll,
                                                       isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                                       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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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
            let recipientAddress: SignalServiceAddress?
            if let recipientAddressForCopy = modelToCopy.recipientAddress {
               recipientAddress = try DeepCopies.deepCopy(recipientAddressForCopy)
            } else {
               recipientAddress = nil
            }
            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,
                                  body: body,
                                  bodyRanges: bodyRanges,
                                  contactShare: contactShare,
                                  deprecated_attachmentIds: deprecated_attachmentIds,
                                  editState: editState,
                                  expireStartedAt: expireStartedAt,
                                  expireTimerVersion: expireTimerVersion,
                                  expiresAt: expiresAt,
                                  expiresInSeconds: expiresInSeconds,
                                  giftBadge: giftBadge,
                                  isGroupStoryReply: isGroupStoryReply,
                                  isPoll: isPoll,
                                  isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                                  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
            let body: String? = modelToCopy.body
            let bodyRanges: MessageBodyRanges?
            if let bodyRangesForCopy = modelToCopy.bodyRanges {
               bodyRanges = try DeepCopies.deepCopy(bodyRangesForCopy)
            } else {
               bodyRanges = nil
            }
            let contactShare: OWSContact?
            if let contactShareForCopy = modelToCopy.contactShare {
               contactShare = try DeepCopies.deepCopy(contactShareForCopy)
            } else {
               contactShare = nil
            }
            let deprecated_attachmentIds: [String]?
            if let deprecated_attachmentIdsForCopy = modelToCopy.deprecated_attachmentIds {
               deprecated_attachmentIds = try DeepCopies.deepCopy(deprecated_attachmentIdsForCopy)
            } else {
               deprecated_attachmentIds = nil
            }
            let editState: TSEditState = modelToCopy.editState
            let expireStartedAt: UInt64 = modelToCopy.expireStartedAt
            let expireTimerVersion: NSNumber? = modelToCopy.expireTimerVersion
            let expiresAt: UInt64 = modelToCopy.expiresAt
            let expiresInSeconds: UInt32 = modelToCopy.expiresInSeconds
            let giftBadge: OWSGiftBadge?
            if let giftBadgeForCopy = modelToCopy.giftBadge {
               giftBadge = try DeepCopies.deepCopy(giftBadgeForCopy)
            } else {
               giftBadge = nil
            }
            let isGroupStoryReply: Bool = modelToCopy.isGroupStoryReply
            let isPoll: Bool = modelToCopy.isPoll
            let isSmsMessageRestoredFromBackup: Bool = modelToCopy.isSmsMessageRestoredFromBackup
            let isViewOnceComplete: Bool = modelToCopy.isViewOnceComplete
            let isViewOnceMessage: Bool = modelToCopy.isViewOnceMessage
            let linkPreview: OWSLinkPreview?
            if let linkPreviewForCopy = modelToCopy.linkPreview {
               linkPreview = try DeepCopies.deepCopy(linkPreviewForCopy)
            } else {
               linkPreview = nil
            }
            let messageSticker: MessageSticker?
            if let messageStickerForCopy = modelToCopy.messageSticker {
               messageSticker = try DeepCopies.deepCopy(messageStickerForCopy)
            } else {
               messageSticker = nil
            }
            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,
                             body: body,
                             bodyRanges: bodyRanges,
                             contactShare: contactShare,
                             deprecated_attachmentIds: deprecated_attachmentIds,
                             editState: editState,
                             expireStartedAt: expireStartedAt,
                             expireTimerVersion: expireTimerVersion,
                             expiresAt: expiresAt,
                             expiresInSeconds: expiresInSeconds,
                             giftBadge: giftBadge,
                             isGroupStoryReply: isGroupStoryReply,
                             isPoll: isPoll,
                             isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup,
                             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
            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 InteractionRecord {

    // This defines all of the columns used in the table
    // where this model (and any subclasses) are persisted.
    internal func asValues() -> [DatabaseValueConvertible?] {
        return [
                recordType?.rawValue,
                            uniqueId,
                            receivedAtTimestamp,
                            timestamp,
                            threadUniqueId,
                            deprecated_attachmentIds,
                            authorId,
                            authorPhoneNumber,
                            authorUUID,
                            body,
                            callType?.rawValue,
                            configurationDurationSeconds,
                            configurationIsEnabled,
                            contactShare,
                            createdByRemoteName,
                            createdInExistingGroup,
                            customMessage,
                            envelopeData,
                            errorType?.rawValue,
                            expireStartedAt,
                            expiresAt,
                            expiresInSeconds,
                            groupMetaMessage,
                            hasLegacyMessageState,
                            hasSyncedTranscript,
                            wasNotCreatedLocally,
                            isLocalChange,
                            isViewOnceComplete,
                            isViewOnceMessage,
                            isVoiceMessage,
                            legacyMessageState?.rawValue,
                            legacyWasDelivered,
                            linkPreview,
                            messageId,
                            messageSticker,
                            messageType?.rawValue,
                            mostRecentFailureText,
                            preKeyBundle,
                            protocolVersion,
                            quotedMessage,
                            read,
                            recipientAddress,
                            recipientAddressStates,
                            sender,
                            serverTimestamp,
                            deprecated_sourceDeviceId,
                            storedMessageState?.rawValue,
                            storedShouldStartExpireTimer,
                            unregisteredAddress,
                            verificationState?.rawValue,
                            wasReceivedByUD,
                            infoMessageUserInfo,
                            wasRemotelyDeleted,
                            bodyRanges,
                            offerType?.rawValue,
                            serverDeliveryTimestamp,
                            eraId,
                            hasEnded,
                            creatorUuid,
                            joinedMemberUuids,
                            wasIdentityVerified,
                            paymentCancellation,
                            paymentNotification,
                            paymentRequest,
                            viewed,
                            serverGuid,
                            storyAuthorUuidString,
                            storyTimestamp,
                            isGroupStoryReply,
                            storyReactionEmoji,
                            giftBadge,
                            editState?.rawValue,
                            archivedPaymentInfo,
                            expireTimerVersion,
                            isSmsMessageRestoredFromBackup,
                            isPoll,

        ]
    }

    internal func asArguments() -> StatementArguments {
        return StatementArguments(asValues())
    }
}

// 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 deprecated_attachmentIdsColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "deprecated_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: .int64, 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) }
    static var archivedPaymentInfoColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "archivedPaymentInfo", columnType: .blob, isOptional: true) }
    static var expireTimerVersionColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "expireTimerVersion", columnType: .int64, isOptional: true) }
    static var isSmsMessageRestoredFromBackupColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isSmsMessageRestoredFromBackup", columnType: .int, isOptional: true) }
    static var isPollColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isPoll", columnType: .int, isOptional: true) }

    public static var table: SDSTableMetadata {
        SDSTableMetadata(
            tableName: "model_TSInteraction",
            columns: [
                idColumn,
                recordTypeColumn,
                uniqueIdColumn,
                receivedAtTimestampColumn,
                timestampColumn,
                uniqueThreadIdColumn,
                deprecated_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,
                archivedPaymentInfoColumn,
                expireTimerVersionColumn,
                isSmsMessageRestoredFromBackupColumn,
                isPollColumn,
            ]
        )
    }
}

// MARK: - Save/Remove/Update

@objc
public extension TSInteraction {
    func anyInsert(transaction: DBWriteTransaction) {
        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: DBWriteTransaction) {
        let isInserting: Bool
        if TSInteraction.fetchViaCache(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: DBWriteTransaction, block: (TSInteraction) -> Void) {

        block(self)

        // If it's not saved, we don't expect to find it in the database, and we
        // won't save any changes we make back into the database.
        guard shouldBeSaved else {
            return
        }

        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: DBWriteTransaction) {
        sdsSave(saveMode: .update, transaction: transaction)
    }
}

// MARK: - TSInteractionCursor

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

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

    public func next() throws -> TSInteraction? {
        guard let record = try cursor.next() else {
            return nil
        }
        let value = try TSInteraction.fromRecord(record)
        SSKEnvironment.shared.modelReadCachesRef.interactionReadCache.didReadInteraction(value, transaction: transaction)
        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 {
    @nonobjc
    class func grdbFetchCursor(transaction: DBReadTransaction) -> TSInteractionCursor {
        let database = transaction.database
        return failIfThrows {
            let cursor = try InteractionRecord.fetchCursor(database)
            return TSInteractionCursor(transaction: transaction, cursor: cursor)
        }
    }

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

        if let cachedCopy = SSKEnvironment.shared.modelReadCachesRef.interactionReadCache.getInteraction(uniqueId: uniqueId, transaction: transaction) {
            return cachedCopy
        }

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

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

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

    // Traverses all records.
    // Records are not visited in any particular order.
    class func anyEnumerate(
        transaction: DBReadTransaction,
        block: (TSInteraction) -> Void,
    ) {
        let cursor = TSInteraction.grdbFetchCursor(transaction: transaction)
        do {
            while let value = try cursor.next() {
                block(value)
            }
        } catch let error {
            owsFailDebug("Couldn't fetch model: \(error)")
        }
    }

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

    class func anyCount(transaction: DBReadTransaction) -> UInt {
        return InteractionRecord.ows_fetchCount(transaction.database)
    }
}

// MARK: - Swift Fetch

public extension TSInteraction {
    class func grdbFetchCursor(sql: String,
                               arguments: StatementArguments = StatementArguments(),
                               transaction: DBReadTransaction) -> TSInteractionCursor {
        return failIfThrows {
            let sqlRequest = SQLRequest<Void>(sql: sql, arguments: arguments, cached: true)
            let cursor = try InteractionRecord.fetchCursor(transaction.database, sqlRequest)
            return TSInteractionCursor(transaction: transaction, cursor: cursor)
        }
    }

    class func grdbFetchOne(sql: String,
                            arguments: StatementArguments = StatementArguments(),
                            transaction: DBReadTransaction) -> 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)
            SSKEnvironment.shared.modelReadCachesRef.interactionReadCache.didReadInteraction(value, transaction: transaction)
            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() -> 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 deprecated_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: Int? = 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
        let archivedPaymentInfo: Data? = nil
        let expireTimerVersion: UInt32? = nil
        let isSmsMessageRestoredFromBackup: Bool? = nil
        let isPoll: Bool? = nil

        return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, deprecated_attachmentIds: deprecated_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, archivedPaymentInfo: archivedPaymentInfo, expireTimerVersion: expireTimerVersion, isSmsMessageRestoredFromBackup: isSmsMessageRestoredFromBackup, isPoll: isPoll)
    }
}