From 24643f338684e065d68c5af412e31d76f0d58552 Mon Sep 17 00:00:00 2001 From: Max Radermacher Date: Wed, 4 Feb 2026 15:10:26 -0600 Subject: [PATCH] Uncodegen StickerPack --- Signal.xcodeproj/project.pbxproj | 20 +- .../ManageStickersViewController.swift | 6 +- .../BackupArchiveStickerPackArchiver.swift | 7 +- .../DownloadStickerPackOperation.swift | 9 +- .../Messages/Stickers/StickerInfo.h | 2 +- .../Messages/Stickers/StickerInfo.m | 2 +- .../Messages/Stickers/StickerManager.swift | 66 +-- .../Messages/Stickers/StickerPack+SDS.swift | 509 ------------------ .../Messages/Stickers/StickerPack.h | 90 ---- .../Messages/Stickers/StickerPack.m | 276 ---------- .../Messages/Stickers/StickerPackItem.swift | 65 +++ .../Messages/Stickers/StickerPackRecord.swift | 163 ++++++ SignalServiceKit/SignalServiceKit.h | 1 - .../Storage/Database/DatabaseRecovery.swift | 2 +- .../SDSCodableModel+SDSSerialization.swift | 14 + .../Stickers/StickerPackCollectionView.swift | 8 +- SignalUI/Stickers/StickerPackDataSource.swift | 20 +- SignalUI/Stickers/StickerPickerView.swift | 12 +- 18 files changed, 316 insertions(+), 956 deletions(-) delete mode 100644 SignalServiceKit/Messages/Stickers/StickerPack+SDS.swift delete mode 100644 SignalServiceKit/Messages/Stickers/StickerPack.h delete mode 100644 SignalServiceKit/Messages/Stickers/StickerPack.m create mode 100644 SignalServiceKit/Messages/Stickers/StickerPackItem.swift create mode 100644 SignalServiceKit/Messages/Stickers/StickerPackRecord.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index f946d40df7..7c4d1f81e4 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -672,6 +672,7 @@ 503C2F432977752B00217527 /* OWSURLSessionEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503C2F422977752B00217527 /* OWSURLSessionEndpoint.swift */; }; 50423CA42BBF427900DCB8F5 /* StaleProfileFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50423CA32BBF427900DCB8F5 /* StaleProfileFetcher.swift */; }; 504271B62BB4C54500E33C01 /* SystemContact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504271B52BB4C54500E33C01 /* SystemContact.swift */; }; + 50428EA52F2BD81700B0C969 /* StickerPackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50428EA42F2BD81700B0C969 /* StickerPackItem.swift */; }; 5042EAA3287F96FB00C9B19F /* VisibleBadgeResolverTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5042EAA2287F96FB00C9B19F /* VisibleBadgeResolverTest.swift */; }; 50438A8E2ECBBDF600FCB28F /* BackupRefreshManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B975452E43A4AA00E20364 /* BackupRefreshManager.swift */; }; 50438A8F2ECBBE1D00FCB28F /* BackupRefreshManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B975472E43BFE000E20364 /* BackupRefreshManagerTest.swift */; }; @@ -745,6 +746,7 @@ 507C07402F116E9200ECFEFA /* NormalizedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507C073F2F116E9200ECFEFA /* NormalizedImage.swift */; }; 507C6AF52ED10BD900A7C74C /* RegisteredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507C6AF42ED10BD900A7C74C /* RegisteredState.swift */; }; 507CD5E529660D5100E47DAC /* ServiceId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CD5E429660D5100E47DAC /* ServiceId.swift */; }; + 507F4F0E2F2A86F8000E6D7D /* StickerPackRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507F4F0D2F2A86F8000E6D7D /* StickerPackRecord.swift */; }; 508622AD2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508622AC2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift */; }; 508C72242C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508C72232C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift */; }; 508F0346296F72F4001D88D0 /* CustomCellBackgroundColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508F0345296F72F4001D88D0 /* CustomCellBackgroundColor.swift */; }; @@ -3595,14 +3597,11 @@ F9C5CC0B289453B300548EEE /* StickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C919289453B100548EEE /* StickerManager.swift */; }; F9C5CC0C289453B300548EEE /* CDNDownloadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C91A289453B100548EEE /* CDNDownloadOperation.swift */; }; F9C5CC0D289453B300548EEE /* StickerMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C91B289453B100548EEE /* StickerMetadata.swift */; }; - F9C5CC0E289453B300548EEE /* StickerPack+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C91C289453B100548EEE /* StickerPack+SDS.swift */; }; - F9C5CC0F289453B300548EEE /* StickerPack.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5C91D289453B100548EEE /* StickerPack.h */; settings = {ATTRIBUTES = (Public, ); }; }; F9C5CC11289453B300548EEE /* DefaultStickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C91F289453B100548EEE /* DefaultStickers.swift */; }; F9C5CC12289453B300548EEE /* InstalledSticker+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C920289453B100548EEE /* InstalledSticker+SDS.swift */; }; F9C5CC13289453B300548EEE /* StickerInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C921289453B100548EEE /* StickerInfo.m */; }; F9C5CC14289453B300548EEE /* DownloadStickerOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C922289453B100548EEE /* DownloadStickerOperation.swift */; }; F9C5CC15289453B300548EEE /* InstalledSticker.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C923289453B100548EEE /* InstalledSticker.m */; }; - F9C5CC17289453B300548EEE /* StickerPack.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C925289453B100548EEE /* StickerPack.m */; }; F9C5CC18289453B300548EEE /* DownloadStickerPackOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C926289453B100548EEE /* DownloadStickerPackOperation.swift */; }; F9C5CC19289453B300548EEE /* MessageSticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C927289453B100548EEE /* MessageSticker.swift */; }; F9C5CC1B289453B300548EEE /* StickerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C929289453B100548EEE /* StickerError.swift */; }; @@ -4835,6 +4834,7 @@ 503C2F422977752B00217527 /* OWSURLSessionEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSURLSessionEndpoint.swift; sourceTree = ""; }; 50423CA32BBF427900DCB8F5 /* StaleProfileFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaleProfileFetcher.swift; sourceTree = ""; }; 504271B52BB4C54500E33C01 /* SystemContact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemContact.swift; sourceTree = ""; }; + 50428EA42F2BD81700B0C969 /* StickerPackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerPackItem.swift; sourceTree = ""; }; 5042EAA2287F96FB00C9B19F /* VisibleBadgeResolverTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleBadgeResolverTest.swift; sourceTree = ""; }; 50468F2829EE130A00948E02 /* InteractionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionStore.swift; sourceTree = ""; }; 50468F2A29EE19C300948E02 /* PhoneNumberChangedMessageInserterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneNumberChangedMessageInserterTest.swift; sourceTree = ""; }; @@ -4900,6 +4900,7 @@ 507D614B2BE433EE00DA7BA3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = translations/be.lproj/InfoPlist.strings; sourceTree = ""; }; 507D614C2BE433EE00DA7BA3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = translations/be.lproj/Localizable.strings; sourceTree = ""; }; 507D614D2BE433EE00DA7BA3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = be; path = translations/be.lproj/PluralAware.stringsdict; sourceTree = ""; }; + 507F4F0D2F2A86F8000E6D7D /* StickerPackRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerPackRecord.swift; sourceTree = ""; }; 508347052AABBF9900DD2EC0 /* ProfileManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileManagerTest.swift; sourceTree = ""; }; 508622AC2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanonicalPhoneNumberTest.swift; sourceTree = ""; }; 508C72232C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSOutgoingResendResponseTest.swift; sourceTree = ""; }; @@ -7794,14 +7795,11 @@ F9C5C919289453B100548EEE /* StickerManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerManager.swift; sourceTree = ""; }; F9C5C91A289453B100548EEE /* CDNDownloadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CDNDownloadOperation.swift; sourceTree = ""; }; F9C5C91B289453B100548EEE /* StickerMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerMetadata.swift; sourceTree = ""; }; - F9C5C91C289453B100548EEE /* StickerPack+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StickerPack+SDS.swift"; sourceTree = ""; }; - F9C5C91D289453B100548EEE /* StickerPack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StickerPack.h; sourceTree = ""; }; F9C5C91F289453B100548EEE /* DefaultStickers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultStickers.swift; sourceTree = ""; }; F9C5C920289453B100548EEE /* InstalledSticker+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "InstalledSticker+SDS.swift"; sourceTree = ""; }; F9C5C921289453B100548EEE /* StickerInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StickerInfo.m; sourceTree = ""; }; F9C5C922289453B100548EEE /* DownloadStickerOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadStickerOperation.swift; sourceTree = ""; }; F9C5C923289453B100548EEE /* InstalledSticker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstalledSticker.m; sourceTree = ""; }; - F9C5C925289453B100548EEE /* StickerPack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StickerPack.m; sourceTree = ""; }; F9C5C926289453B100548EEE /* DownloadStickerPackOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadStickerPackOperation.swift; sourceTree = ""; }; F9C5C927289453B100548EEE /* MessageSticker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSticker.swift; sourceTree = ""; }; F9C5C929289453B100548EEE /* StickerError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerError.swift; sourceTree = ""; }; @@ -14545,10 +14543,9 @@ F9C5C921289453B100548EEE /* StickerInfo.m */, F9C5C919289453B100548EEE /* StickerManager.swift */, F9C5C91B289453B100548EEE /* StickerMetadata.swift */, - F9C5C91C289453B100548EEE /* StickerPack+SDS.swift */, - F9C5C91D289453B100548EEE /* StickerPack.h */, - F9C5C925289453B100548EEE /* StickerPack.m */, F9479EF7293D212C003B3503 /* StickerPackInfo.swift */, + 50428EA42F2BD81700B0C969 /* StickerPackItem.swift */, + 507F4F0D2F2A86F8000E6D7D /* StickerPackRecord.swift */, ); path = Stickers; sourceTree = ""; @@ -15303,7 +15300,6 @@ F9C5C8A7289451B900548EEE /* SignalServiceKit.h in Headers */, F9C5CD2C289453B300548EEE /* SSKAccessors+SDS.h in Headers */, F9C5CC1C289453B300548EEE /* StickerInfo.h in Headers */, - F9C5CC0F289453B300548EEE /* StickerPack.h in Headers */, 668A01142C2B6077007B8808 /* Threading.h in Headers */, F9C5CBC8289453B300548EEE /* TSCall.h in Headers */, F9C5CCD1289453B300548EEE /* TSContactThread.h in Headers */, @@ -19391,9 +19387,9 @@ F9C5CC13289453B300548EEE /* StickerInfo.m in Sources */, F9C5CC0B289453B300548EEE /* StickerManager.swift in Sources */, F9C5CC0D289453B300548EEE /* StickerMetadata.swift in Sources */, - F9C5CC0E289453B300548EEE /* StickerPack+SDS.swift in Sources */, - F9C5CC17289453B300548EEE /* StickerPack.m in Sources */, F9479EF8293D212C003B3503 /* StickerPackInfo.swift in Sources */, + 50428EA52F2BD81700B0C969 /* StickerPackItem.swift in Sources */, + 507F4F0E2F2A86F8000E6D7D /* StickerPackRecord.swift in Sources */, 500CC1EB2F15D1E5001A86F7 /* StickerPackSyncMessage.swift in Sources */, 725465532BA0282D00EABFD2 /* StorageService+GroupsV2.swift in Sources */, F9C5CCA3289453B300548EEE /* StorageService.pb.swift in Sources */, diff --git a/Signal/src/ViewControllers/Stickers/ManageStickersViewController.swift b/Signal/src/ViewControllers/Stickers/ManageStickersViewController.swift index 2d28eda430..1eb1de58a3 100644 --- a/Signal/src/ViewControllers/Stickers/ManageStickersViewController.swift +++ b/Signal/src/ViewControllers/Stickers/ManageStickersViewController.swift @@ -140,8 +140,8 @@ public class ManageStickersViewController: OWSTableViewController2 { updateMapWithOldSources(&oldTransientSources, availableBuiltInStickerPackSources) updateMapWithOldSources(&oldTransientSources, knownStickerPackSources) - var installedStickerPacks = [StickerPack]() - var availableBuiltInStickerPacks = [StickerPack]() + var installedStickerPacks = [StickerPackRecord]() + var availableBuiltInStickerPacks = [StickerPackRecord]() var availableKnownStickerPacksFromMessages = [DatedStickerPackInfo]() SSKEnvironment.shared.databaseStorageRef.read { transaction in let allPacks = StickerManager.allStickerPacks(transaction: transaction) @@ -521,7 +521,7 @@ public class ManageStickersViewController: OWSTableViewController2 { self.sendMessageFlow = sendMessageFlow } - private func install(stickerPack: StickerPack) { + private func install(stickerPack: StickerPackRecord) { AssertIsOnMainThread() let modalVC = ModalActivityIndicatorViewController(canCancel: false, presentationDelay: 0) diff --git a/SignalServiceKit/Backups/Archiving/Archivers/StickerPack/BackupArchiveStickerPackArchiver.swift b/SignalServiceKit/Backups/Archiving/Archivers/StickerPack/BackupArchiveStickerPackArchiver.swift index 512e7deff6..cc5d8cf03f 100644 --- a/SignalServiceKit/Backups/Archiving/Archivers/StickerPack/BackupArchiveStickerPackArchiver.swift +++ b/SignalServiceKit/Backups/Archiving/Archivers/StickerPack/BackupArchiveStickerPackArchiver.swift @@ -60,7 +60,7 @@ public class BackupArchiveStickerPackArchiver: BackupArchiveProtoStreamWriter { var handledPacks = Set() func archiveInstalledStickerPack( - _ installedStickerPack: StickerPack, + _ installedStickerPack: StickerPackRecord, _ frameBencher: BackupArchive.Bencher.FrameBencher, ) { autoreleasepool { @@ -88,12 +88,11 @@ public class BackupArchiveStickerPackArchiver: BackupArchiveProtoStreamWriter { } } - func enumerateStickerPackRecord(tx: DBReadTransaction, block: (StickerPack) throws -> Void) throws { + func enumerateStickerPackRecord(tx: DBReadTransaction, block: (StickerPackRecord) throws -> Void) throws { let cursor = try StickerPackRecord .filter(Column(StickerPackRecord.CodingKeys.isInstalled) == true) .fetchCursor(tx.database) - while let next = try cursor.next() { - let stickerPack = try StickerPack.fromRecord(next) + while let stickerPack = try cursor.next() { try block(stickerPack) } } diff --git a/SignalServiceKit/Messages/Stickers/DownloadStickerPackOperation.swift b/SignalServiceKit/Messages/Stickers/DownloadStickerPackOperation.swift index 49baa37a9e..9c8499ac86 100644 --- a/SignalServiceKit/Messages/Stickers/DownloadStickerPackOperation.swift +++ b/SignalServiceKit/Messages/Stickers/DownloadStickerPackOperation.swift @@ -6,13 +6,13 @@ import Foundation enum DownloadStickerPackOperation { - static func run(stickerPackInfo: StickerPackInfo) async throws -> StickerPack { + static func run(stickerPackInfo: StickerPackInfo) async throws -> StickerPackRecord { return try await Retry.performWithBackoff(maxAttempts: 4) { return try await self._run(stickerPackInfo: stickerPackInfo) } } - private static func _run(stickerPackInfo: StickerPackInfo) async throws -> StickerPack { + private static func _run(stickerPackInfo: StickerPackInfo) async throws -> StickerPackRecord { owsAssertDebug(stickerPackInfo.packId.count > 0) owsAssertDebug(stickerPackInfo.packKey.count > 0) @@ -54,7 +54,7 @@ enum DownloadStickerPackOperation { } } - private static func parseStickerPackManifest(stickerPackInfo: StickerPackInfo, manifestData: Data) throws -> StickerPack { + private static func parseStickerPackManifest(stickerPackInfo: StickerPackInfo, manifestData: Data) throws -> StickerPackRecord { owsAssertDebug(manifestData.count > 0) let manifestProto: SSKProtoPack @@ -79,8 +79,7 @@ enum DownloadStickerPackOperation { } let cover = manifestCover ?? firstItem - let stickerPack = StickerPack(info: stickerPackInfo, title: title, author: author, cover: cover, stickers: items) - return stickerPack + return StickerPackRecord(info: stickerPackInfo, title: title, author: author, cover: cover, items: items) } private static func parseOptionalString(_ value: String?) -> String? { diff --git a/SignalServiceKit/Messages/Stickers/StickerInfo.h b/SignalServiceKit/Messages/Stickers/StickerInfo.h index 3481cb7d1c..d4eb603693 100644 --- a/SignalServiceKit/Messages/Stickers/StickerInfo.h +++ b/SignalServiceKit/Messages/Stickers/StickerInfo.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSData *packKey; @property (nonatomic, readonly) UInt32 stickerId; -- (instancetype)initWithPackId:(NSData *)packId packKey:(NSData *)packKey stickerId:(UInt32)stickerId; +- (instancetype)initWithPackId:(nullable NSData *)packId packKey:(nullable NSData *)packKey stickerId:(UInt32)stickerId; - (NSString *)asKey; + (NSString *)keyWithPackId:(NSData *)packId stickerId:(UInt32)stickerId; diff --git a/SignalServiceKit/Messages/Stickers/StickerInfo.m b/SignalServiceKit/Messages/Stickers/StickerInfo.m index 56a802ca4b..6b3b181f12 100644 --- a/SignalServiceKit/Messages/Stickers/StickerInfo.m +++ b/SignalServiceKit/Messages/Stickers/StickerInfo.m @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN @implementation StickerInfo -- (instancetype)initWithPackId:(NSData *)packId packKey:(NSData *)packKey stickerId:(UInt32)stickerId +- (instancetype)initWithPackId:(nullable NSData *)packId packKey:(nullable NSData *)packKey stickerId:(UInt32)stickerId { self = [super init]; diff --git a/SignalServiceKit/Messages/Stickers/StickerManager.swift b/SignalServiceKit/Messages/Stickers/StickerManager.swift index fa2d7834e5..c055094ac6 100644 --- a/SignalServiceKit/Messages/Stickers/StickerManager.swift +++ b/SignalServiceKit/Messages/Stickers/StickerManager.swift @@ -160,19 +160,19 @@ public class StickerManager: NSObject { // MARK: - Sticker Packs - public class func allStickerPacks() -> [StickerPack] { - var result = [StickerPack]() + public class func allStickerPacks() -> [StickerPackRecord] { + var result = [StickerPackRecord]() SSKEnvironment.shared.databaseStorageRef.read { transaction in result += allStickerPacks(transaction: transaction) } return result } - public class func allStickerPacks(transaction: DBReadTransaction) -> [StickerPack] { - return StickerPack.anyFetchAll(transaction: transaction) + public class func allStickerPacks(transaction: DBReadTransaction) -> [StickerPackRecord] { + return StickerPackRecord.anyFetchAll(transaction: transaction) } - public class func installedStickerPacks(transaction: DBReadTransaction) -> [StickerPack] { + public class func installedStickerPacks(transaction: DBReadTransaction) -> [StickerPackRecord] { return allStickerPacks(transaction: transaction).filter { $0.isInstalled } @@ -204,7 +204,7 @@ public class StickerManager: NSObject { if shouldRemove { uninstallSticker(stickerInfo: stickerPack.coverInfo, transaction: transaction) - for stickerInfo in stickerPack.stickerInfos { + for stickerInfo in stickerPack.stickerInfos() { if stickerInfo == stickerPack.coverInfo { // Don't uninstall the cover for saved packs. continue @@ -214,7 +214,7 @@ public class StickerManager: NSObject { stickerPack.anyRemove(transaction: transaction) } else { - stickerPack.update(withIsInstalled: false, transaction: transaction) + stickerPack.updateWith(isInstalled: false, tx: transaction) } if wasLocallyInitiated { @@ -231,7 +231,7 @@ public class StickerManager: NSObject { } public class func installStickerPack( - stickerPack: StickerPack, + stickerPack: StickerPackRecord, wasLocallyInitiated: Bool, transaction: DBWriteTransaction, ) { @@ -243,15 +243,15 @@ public class StickerManager: NSObject { ) } - public class func fetchStickerPack(stickerPackInfo: StickerPackInfo) -> StickerPack? { + public class func fetchStickerPack(stickerPackInfo: StickerPackInfo) -> StickerPackRecord? { return SSKEnvironment.shared.databaseStorageRef.read { transaction in return fetchStickerPack(stickerPackInfo: stickerPackInfo, transaction: transaction) } } - public class func fetchStickerPack(stickerPackInfo: StickerPackInfo, transaction: DBReadTransaction) -> StickerPack? { - let uniqueId = StickerPack.uniqueId(for: stickerPackInfo) - return StickerPack.anyFetch(uniqueId: uniqueId, transaction: transaction) + public class func fetchStickerPack(stickerPackInfo: StickerPackInfo, transaction: DBReadTransaction) -> StickerPackRecord? { + let uniqueId = StickerPackRecord.uniqueId(forStickerPackInfo: stickerPackInfo) + return StickerPackRecord.anyFetch(uniqueId: uniqueId, transaction: transaction) } private class func tryToDownloadAndSaveStickerPack( @@ -270,7 +270,7 @@ public class StickerManager: NSObject { private let packOperationQueue = ConcurrentTaskQueue(concurrentLimit: 3) - private func tryToDownloadStickerPack(stickerPackInfo: StickerPackInfo) -> Promise { + private func tryToDownloadStickerPack(stickerPackInfo: StickerPackInfo) -> Promise { return Promise.wrapAsync { [packOperationQueue] in return try await packOperationQueue.run { return try await DownloadStickerPackOperation.run(stickerPackInfo: stickerPackInfo) @@ -279,12 +279,12 @@ public class StickerManager: NSObject { } // This method is public so that we can download "transient" (uninstalled) sticker packs. - public class func tryToDownloadStickerPack(stickerPackInfo: StickerPackInfo) -> Promise { + public class func tryToDownloadStickerPack(stickerPackInfo: StickerPackInfo) -> Promise { return SSKEnvironment.shared.stickerManagerRef.tryToDownloadStickerPack(stickerPackInfo: stickerPackInfo) } private class func upsertStickerPack( - stickerPack: StickerPack, + stickerPack: StickerPackRecord, installMode: InstallMode, wasLocallyInitiated: Bool, ) { @@ -299,7 +299,7 @@ public class StickerManager: NSObject { } private class func upsertStickerPack( - stickerPack stickerPackParam: StickerPack, + stickerPack stickerPackParam: StickerPackRecord, installMode: InstallMode, wasLocallyInitiated: Bool, transaction: DBWriteTransaction, @@ -307,15 +307,15 @@ public class StickerManager: NSObject { // If we re-insert a sticker pack, make sure that it // has a new row id. _ = stickerPackParam as NSCopying - let stickerPack = stickerPackParam.copy() as! StickerPack - stickerPack.clearRowId() + let stickerPack = stickerPackParam.copy() as! StickerPackRecord + stickerPack.id = nil let oldCopy = fetchStickerPack(stickerPackInfo: stickerPack.info, transaction: transaction) let wasSaved = oldCopy != nil // Preserve old mutable state. if let oldCopy { - stickerPack.update(withIsInstalled: oldCopy.isInstalled, transaction: transaction) + stickerPack.updateWith(isInstalled: oldCopy.isInstalled, tx: transaction) } else { stickerPack.anyInsert(transaction: transaction) } @@ -363,7 +363,7 @@ public class StickerManager: NSObject { } private class func markSavedStickerPackAsInstalled( - stickerPack: StickerPack, + stickerPack: StickerPackRecord, wasLocallyInitiated: Bool, transaction: DBWriteTransaction, ) -> Promise { @@ -371,7 +371,7 @@ public class StickerManager: NSObject { return .value(()) } - stickerPack.update(withIsInstalled: true, transaction: transaction) + stickerPack.updateWith(isInstalled: true, tx: transaction) let promise = installStickerPackContents(stickerPack: stickerPack, transaction: transaction) @@ -386,7 +386,7 @@ public class StickerManager: NSObject { } private class func installStickerPackContents( - stickerPack: StickerPack, + stickerPack: StickerPackRecord, transaction: DBReadTransaction, onlyInstallCover: Bool = false, ) -> Promise { @@ -456,7 +456,7 @@ public class StickerManager: NSObject { } public class func installedStickers( - forStickerPack stickerPack: StickerPack, + forStickerPack stickerPack: StickerPackRecord, verifyExists: Bool, ) -> [StickerInfo] { return SSKEnvironment.shared.databaseStorageRef.read { transaction in @@ -469,12 +469,12 @@ public class StickerManager: NSObject { } public class func installedStickers( - forStickerPack stickerPack: StickerPack, + forStickerPack stickerPack: StickerPackRecord, verifyExists: Bool, transaction: DBReadTransaction, ) -> [StickerInfo] { var result = [StickerInfo]() - for stickerInfo in stickerPack.stickerInfos { + for stickerInfo in stickerPack.stickerInfos() { let uniqueId = InstalledSticker.uniqueId(for: stickerInfo) guard let installedSticker = InstalledSticker.anyFetch(uniqueId: uniqueId, transaction: transaction) else { continue @@ -723,11 +723,11 @@ public class StickerManager: NSObject { } private class func tryToDownloadAndInstallSticker( - stickerPack: StickerPack, + stickerPack: StickerPackRecord, item: StickerPackItem, transaction: DBReadTransaction, ) -> Promise { - let stickerInfo: StickerInfo = item.stickerInfo(with: stickerPack) + let stickerInfo: StickerInfo = item.stickerInfoWith(stickerPack: stickerPack) let emojiString = item.emojiString guard !self.isStickerInstalled(stickerInfo: stickerInfo, transaction: transaction) else { @@ -1001,7 +1001,7 @@ public class StickerManager: NSObject { } } - public class func ensureDownloadsAsync(forStickerPack stickerPack: StickerPack) -> Promise { + public class func ensureDownloadsAsync(forStickerPack stickerPack: StickerPackRecord) -> Promise { let (promise, future) = Promise.pending() DispatchQueue.global().async { SSKEnvironment.shared.databaseStorageRef.read { transaction in @@ -1018,7 +1018,7 @@ public class StickerManager: NSObject { } @discardableResult - private class func ensureDownloads(forStickerPack stickerPack: StickerPack, transaction: DBReadTransaction) -> Promise { + private class func ensureDownloads(forStickerPack stickerPack: StickerPackRecord, transaction: DBReadTransaction) -> Promise { // TODO: As an optimization, we could flag packs as "complete" if we know all // of their stickers are installed. @@ -1054,11 +1054,11 @@ public class StickerManager: NSObject { } } - private static func fetchOrphanedPacksAndStickers(tx: DBReadTransaction) -> ([StickerPack], [InstalledSticker]) { - var stickerPacks = [String: StickerPack]() - var packsToRemove = [StickerPack]() + private static func fetchOrphanedPacksAndStickers(tx: DBReadTransaction) -> ([StickerPackRecord], [InstalledSticker]) { + var stickerPacks = [String: StickerPackRecord]() + var packsToRemove = [StickerPackRecord]() - for stickerPack in StickerPack.anyFetchAll(transaction: tx) { + for stickerPack in StickerPackRecord.anyFetchAll(transaction: tx) { if stickerPack.isInstalled || self.isDefaultStickerPack(packId: stickerPack.info.packId) { stickerPacks[stickerPack.info.asKey] = stickerPack } else { diff --git a/SignalServiceKit/Messages/Stickers/StickerPack+SDS.swift b/SignalServiceKit/Messages/Stickers/StickerPack+SDS.swift deleted file mode 100644 index 3985e0e2dc..0000000000 --- a/SignalServiceKit/Messages/Stickers/StickerPack+SDS.swift +++ /dev/null @@ -1,509 +0,0 @@ -// -// 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 StickerPackRecord: SDSRecord { - public weak var delegate: SDSRecordDelegate? - - public var tableMetadata: SDSTableMetadata { - StickerPackSerializer.table - } - - public static var databaseTableName: String { - StickerPackSerializer.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 author: String? - public let cover: Data - public let dateCreated: Double - public let info: Data - public let isInstalled: Bool - public let items: Data - public let title: String? - - public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable { - case id - case recordType - case uniqueId - case author - case cover - case dateCreated - case info - case isInstalled - case items - case title - } - - public static func columnName(_ column: StickerPackRecord.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 StickerPackRecord { - static var databaseSelection: [SQLSelectable] { - CodingKeys.allCases - } - - init(row: Row) { - id = row[0] - recordType = row[1].flatMap { SDSRecordType(rawValue: $0) } - uniqueId = row[2] - author = row[3] - cover = row[4] - dateCreated = row[5] - info = row[6] - isInstalled = row[7] - items = row[8] - title = row[9] - } -} - -// MARK: - StringInterpolation - -public extension String.StringInterpolation { - mutating func appendInterpolation(stickerPackColumn column: StickerPackRecord.CodingKeys) { - appendLiteral(StickerPackRecord.columnName(column)) - } - mutating func appendInterpolation(stickerPackColumnFullyQualified column: StickerPackRecord.CodingKeys) { - appendLiteral(StickerPackRecord.columnName(column, fullyQualified: true)) - } -} - -// MARK: - Deserialization - -extension StickerPack { - // 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: StickerPackRecord) throws -> StickerPack { - - 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 .stickerPack: - - let uniqueId: String = record.uniqueId - let author: String? = record.author - let coverSerialized: Data = record.cover - let cover: StickerPackItem = try SDSDeserialization.unarchivedObject(ofClass: StickerPackItem.self, from: coverSerialized) - let dateCreatedInterval: Double = record.dateCreated - let dateCreated: Date = SDSDeserialization.requiredDoubleAsDate(dateCreatedInterval, name: "dateCreated") - let infoSerialized: Data = record.info - let info: StickerPackInfo = try SDSDeserialization.unarchivedObject(ofClass: StickerPackInfo.self, from: infoSerialized) - let isInstalled: Bool = record.isInstalled - let itemsSerialized: Data = record.items - let items: [StickerPackItem] = try SDSDeserialization.unarchivedArrayOfObjects(ofClass: StickerPackItem.self, from: itemsSerialized) - let title: String? = record.title - - return StickerPack(grdbId: recordId, - uniqueId: uniqueId, - author: author, - cover: cover, - dateCreated: dateCreated, - info: info, - isInstalled: isInstalled, - items: items, - title: title) - - default: - owsFailDebug("Unexpected record type: \(recordType)") - throw SDSError.invalidValue() - } - } -} - -// MARK: - SDSModel - -extension StickerPack: 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 { - default: - return StickerPackSerializer(model: self) - } - } - - public func asRecord() -> SDSRecord { - serializer.asRecord() - } - - public var sdsTableName: String { - StickerPackRecord.databaseTableName - } - - public static var table: SDSTableMetadata { - StickerPackSerializer.table - } -} - -// MARK: - DeepCopyable - -extension StickerPack: 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. - - do { - let modelToCopy = self - assert(type(of: modelToCopy) == StickerPack.self) - let uniqueId: String = modelToCopy.uniqueId - let author: String? = modelToCopy.author - let cover: StickerPackItem = try DeepCopies.deepCopy(modelToCopy.cover) - let dateCreated: Date = modelToCopy.dateCreated - let info: StickerPackInfo = try DeepCopies.deepCopy(modelToCopy.info) - let isInstalled: Bool = modelToCopy.isInstalled - let items: [StickerPackItem] = try DeepCopies.deepCopy(modelToCopy.items) - let title: String? = modelToCopy.title - - return StickerPack(grdbId: id, - uniqueId: uniqueId, - author: author, - cover: cover, - dateCreated: dateCreated, - info: info, - isInstalled: isInstalled, - items: items, - title: title) - } - - } -} - -// MARK: - Table Metadata - -extension StickerPackSerializer { - - // 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 authorColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "author", columnType: .unicodeString, isOptional: true) } - static var coverColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "cover", columnType: .blob) } - static var dateCreatedColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "dateCreated", columnType: .double) } - static var infoColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "info", columnType: .blob) } - static var isInstalledColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isInstalled", columnType: .int) } - static var itemsColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "items", columnType: .blob) } - static var titleColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "title", columnType: .unicodeString, isOptional: true) } - - public static var table: SDSTableMetadata { - SDSTableMetadata( - tableName: "model_StickerPack", - columns: [ - idColumn, - recordTypeColumn, - uniqueIdColumn, - authorColumn, - coverColumn, - dateCreatedColumn, - infoColumn, - isInstalledColumn, - itemsColumn, - titleColumn, - ] - ) - } -} - -// MARK: - Save/Remove/Update - -@objc -public extension StickerPack { - 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 StickerPack.anyFetch(uniqueId: uniqueId, transaction: transaction) != nil { - isInserting = false - } else { - isInserting = true - } - sdsSave(saveMode: isInserting ? .insert : .update, transaction: transaction) - } - - // This method is used by "updateWith..." methods. - // - // This model may be updated from many threads. We don't want to save - // our local copy (this instance) since it may be out of date. We also - // want to avoid re-saving a model that has been deleted. Therefore, we - // use "updateWith..." methods to: - // - // a) Update a property of this instance. - // b) If a copy of this model exists in the database, load an up-to-date copy, - // and update and save that copy. - // b) If a copy of this model _DOES NOT_ exist in the database, do _NOT_ save - // this local instance. - // - // After "updateWith...": - // - // a) Any copy of this model in the database will have been updated. - // b) The local property on this instance will always have been updated. - // c) Other properties on this instance may be out of date. - // - // All mutable properties of this class have been made read-only to - // prevent accidentally modifying them directly. - // - // This isn't a perfect arrangement, but in practice this will prevent - // data loss and will resolve all known issues. - func anyUpdate(transaction: DBWriteTransaction, block: (StickerPack) -> 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) - } - - func anyRemove(transaction: DBWriteTransaction) { - sdsRemove(transaction: transaction) - } -} - -// MARK: - StickerPackCursor - -@objc -public class StickerPackCursor: NSObject, SDSCursor { - private let transaction: DBReadTransaction - private let cursor: RecordCursor - - init(transaction: DBReadTransaction, cursor: RecordCursor) { - self.transaction = transaction - self.cursor = cursor - } - - public func next() throws -> StickerPack? { - guard let record = try cursor.next() else { - return nil - } - return try StickerPack.fromRecord(record) - } - - public func all() throws -> [StickerPack] { - var result = [StickerPack]() - while true { - guard let model = try next() else { - break - } - result.append(model) - } - return result - } -} - -// MARK: - Obj-C Fetch - -@objc -public extension StickerPack { - @nonobjc - class func grdbFetchCursor(transaction: DBReadTransaction) -> StickerPackCursor { - let database = transaction.database - return failIfThrows { - let cursor = try StickerPackRecord.fetchCursor(database) - return StickerPackCursor(transaction: transaction, cursor: cursor) - } - } - - // Fetches a single model by "unique id". - class func anyFetch(uniqueId: String, - transaction: DBReadTransaction) -> StickerPack? { - assert(!uniqueId.isEmpty) - - let sql = "SELECT * FROM \(StickerPackRecord.databaseTableName) WHERE \(stickerPackColumn: .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: (StickerPack, UnsafeMutablePointer) -> Void - ) { - anyEnumerate(transaction: transaction, batched: false, block: block) - } - - // Traverses all records. - // Records are not visited in any particular order. - class func anyEnumerate( - transaction: DBReadTransaction, - batched: Bool = false, - block: (StickerPack, UnsafeMutablePointer) -> Void - ) { - let batchSize = batched ? Batching.kDefaultBatchSize : 0 - anyEnumerate(transaction: transaction, batchSize: batchSize, block: block) - } - - // Traverses all records. - // Records are not visited in any particular order. - // - // If batchSize > 0, the enumeration is performed in autoreleased batches. - class func anyEnumerate( - transaction: DBReadTransaction, - batchSize: UInt, - block: (StickerPack, UnsafeMutablePointer) -> Void - ) { - let cursor = StickerPack.grdbFetchCursor(transaction: transaction) - Batching.loop(batchSize: batchSize, - loopBlock: { stop in - do { - guard let value = try cursor.next() else { - stop.pointee = true - return - } - block(value, stop) - } catch let error { - owsFailDebug("Couldn't fetch model: \(error)") - } - }) - } - - // Does not order the results. - class func anyFetchAll(transaction: DBReadTransaction) -> [StickerPack] { - var result = [StickerPack]() - anyEnumerate(transaction: transaction) { (model, _) in - result.append(model) - } - return result - } - - class func anyCount(transaction: DBReadTransaction) -> UInt { - return StickerPackRecord.ows_fetchCount(transaction.database) - } -} - -// MARK: - Swift Fetch - -public extension StickerPack { - class func grdbFetchCursor(sql: String, - arguments: StatementArguments = StatementArguments(), - transaction: DBReadTransaction) -> StickerPackCursor { - return failIfThrows { - let sqlRequest = SQLRequest(sql: sql, arguments: arguments, cached: true) - let cursor = try StickerPackRecord.fetchCursor(transaction.database, sqlRequest) - return StickerPackCursor(transaction: transaction, cursor: cursor) - } - } - - class func grdbFetchOne(sql: String, - arguments: StatementArguments = StatementArguments(), - transaction: DBReadTransaction) -> StickerPack? { - assert(!sql.isEmpty) - - do { - let sqlRequest = SQLRequest(sql: sql, arguments: arguments, cached: true) - guard let record = try StickerPackRecord.fetchOne(transaction.database, sqlRequest) else { - return nil - } - - return try StickerPack.fromRecord(record) - } 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 StickerPackSerializer: SDSSerializer { - - private let model: StickerPack - public init(model: StickerPack) { - self.model = model - } - - // MARK: - Record - - func asRecord() -> SDSRecord { - let id: Int64? = model.grdbId?.int64Value - - let recordType: SDSRecordType = .stickerPack - let uniqueId: String = model.uniqueId - - // Properties - let author: String? = model.author - let cover: Data = requiredArchive(model.cover) - let dateCreated: Double = archiveDate(model.dateCreated) - let info: Data = requiredArchive(model.info) - let isInstalled: Bool = model.isInstalled - let items: Data = requiredArchive(model.items) - let title: String? = model.title - - return StickerPackRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, author: author, cover: cover, dateCreated: dateCreated, info: info, isInstalled: isInstalled, items: items, title: title) - } -} diff --git a/SignalServiceKit/Messages/Stickers/StickerPack.h b/SignalServiceKit/Messages/Stickers/StickerPack.h deleted file mode 100644 index 6682e2ee42..0000000000 --- a/SignalServiceKit/Messages/Stickers/StickerPack.h +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright 2019 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only -// - -@import Foundation; - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class DBWriteTransaction; -@class StickerPack; - -@interface StickerPackItem : NSObject - -@property (nonatomic, readonly) UInt32 stickerId; -@property (nonatomic, readonly) NSString *emojiString; -@property (nonatomic, readonly, nullable) NSString *contentType; - -- (instancetype)initWithStickerId:(UInt32)stickerId - emojiString:(NSString *)emojiString - contentType:(nullable NSString *)contentType; - -- (StickerInfo *)stickerInfoWithStickerPack:(StickerPack *)stickerPack; - -@end - -#pragma mark - - -@interface StickerPack : BaseModel - -@property (nonatomic, readonly) StickerPackInfo *info; - -@property (nonatomic, readonly, nullable) NSString *title; -@property (nonatomic, readonly, nullable) NSString *author; -@property (nonatomic, readonly) StickerPackItem *cover; -@property (nonatomic, readonly) NSArray *items; - -// Convenience accessors. -@property (nonatomic, readonly) NSData *packId; -@property (nonatomic, readonly) NSData *packKey; -@property (nonatomic, readonly) StickerInfo *coverInfo; -@property (nonatomic, readonly) NSArray *stickerInfos; - -@property (nonatomic, readonly) NSDate *dateCreated; -@property (nonatomic, readonly) BOOL isInstalled; - -+ (instancetype)new NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; -- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; -- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE; -- (instancetype)initWithGrdbId:(int64_t)grdbId uniqueId:(NSString *)uniqueId NS_UNAVAILABLE; - -- (instancetype)initWithInfo:(StickerPackInfo *)info - title:(nullable NSString *)title - author:(nullable NSString *)author - cover:(StickerPackItem *)cover - stickers:(NSArray *)items NS_DESIGNATED_INITIALIZER; - -// --- CODE GENERATION MARKER - -// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run -// `sds_codegen.sh`. - -// clang-format off - -- (instancetype)initWithGrdbId:(int64_t)grdbId - uniqueId:(NSString *)uniqueId - author:(nullable NSString *)author - cover:(StickerPackItem *)cover - dateCreated:(NSDate *)dateCreated - info:(StickerPackInfo *)info - isInstalled:(BOOL)isInstalled - items:(NSArray *)items - title:(nullable NSString *)title -NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:author:cover:dateCreated:info:isInstalled:items:title:)); - -// clang-format on - -// --- CODE GENERATION MARKER - -+ (NSString *)uniqueIdForStickerPackInfo:(StickerPackInfo *)info; - -- (void)updateWithIsInstalled:(BOOL)isInstalled transaction:(DBWriteTransaction *)transaction; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/Messages/Stickers/StickerPack.m b/SignalServiceKit/Messages/Stickers/StickerPack.m deleted file mode 100644 index d3690c78b7..0000000000 --- a/SignalServiceKit/Messages/Stickers/StickerPack.m +++ /dev/null @@ -1,276 +0,0 @@ -// -// Copyright 2019 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only -// - -#import "StickerPack.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@implementation StickerPackItem - -- (instancetype)initWithStickerId:(UInt32)stickerId - emojiString:(NSString *)emojiString - contentType:(nullable NSString *)contentType -{ - self = [super init]; - - if (!self) { - return self; - } - - _stickerId = stickerId; - _emojiString = emojiString; - if (contentType.length > 0) { - _contentType = contentType; - } - - return self; -} - -+ (BOOL)supportsSecureCoding -{ - return YES; -} - -- (void)encodeWithCoder:(NSCoder *)coder -{ - NSString *contentType = self.contentType; - if (contentType != nil) { - [coder encodeObject:contentType forKey:@"contentType"]; - } - NSString *emojiString = self.emojiString; - if (emojiString != nil) { - [coder encodeObject:emojiString forKey:@"emojiString"]; - } - [coder encodeObject:[self valueForKey:@"stickerId"] forKey:@"stickerId"]; -} - -- (nullable instancetype)initWithCoder:(NSCoder *)coder -{ - self = [super init]; - if (!self) { - return self; - } - self->_contentType = [coder decodeObjectOfClass:[NSString class] forKey:@"contentType"]; - self->_emojiString = [coder decodeObjectOfClass:[NSString class] forKey:@"emojiString"]; - self->_stickerId = [(NSNumber *)[coder decodeObjectOfClass:[NSNumber class] forKey:@"stickerId"] unsignedIntValue]; - return self; -} - -- (NSUInteger)hash -{ - NSUInteger result = 0; - result ^= self.contentType.hash; - result ^= self.emojiString.hash; - result ^= self.stickerId; - return result; -} - -- (BOOL)isEqual:(id)other -{ - if (![other isMemberOfClass:self.class]) { - return NO; - } - StickerPackItem *typedOther = (StickerPackItem *)other; - if (![NSObject isObject:self.contentType equalToObject:typedOther.contentType]) { - return NO; - } - if (![NSObject isObject:self.emojiString equalToObject:typedOther.emojiString]) { - return NO; - } - if (self.stickerId != typedOther.stickerId) { - return NO; - } - return YES; -} - -- (id)copyWithZone:(nullable NSZone *)zone -{ - StickerPackItem *result = [[[self class] allocWithZone:zone] init]; - result->_contentType = self.contentType; - result->_emojiString = self.emojiString; - result->_stickerId = self.stickerId; - return result; -} - -- (StickerInfo *)stickerInfoWithStickerPack:(StickerPack *)stickerPack -{ - return [[StickerInfo alloc] initWithPackId:stickerPack.packId packKey:stickerPack.packKey stickerId:self.stickerId]; -} - -@end - -#pragma mark - - -@interface StickerPack () - -@property (nonatomic) BOOL isInstalled; - -@end - -#pragma mark - - -@implementation StickerPack - -- (NSUInteger)hash -{ - NSUInteger result = [super hash]; - result ^= self.author.hash; - result ^= self.cover.hash; - result ^= self.dateCreated.hash; - result ^= self.info.hash; - result ^= self.isInstalled; - result ^= self.items.hash; - result ^= self.title.hash; - return result; -} - -- (BOOL)isEqual:(id)other -{ - if (![super isEqual:other]) { - return NO; - } - StickerPack *typedOther = (StickerPack *)other; - if (![NSObject isObject:self.author equalToObject:typedOther.author]) { - return NO; - } - if (![NSObject isObject:self.cover equalToObject:typedOther.cover]) { - return NO; - } - if (![NSObject isObject:self.dateCreated equalToObject:typedOther.dateCreated]) { - return NO; - } - if (![NSObject isObject:self.info equalToObject:typedOther.info]) { - return NO; - } - if (self.isInstalled != typedOther.isInstalled) { - return NO; - } - if (![NSObject isObject:self.items equalToObject:typedOther.items]) { - return NO; - } - if (![NSObject isObject:self.title equalToObject:typedOther.title]) { - return NO; - } - return YES; -} - -- (id)copyWithZone:(nullable NSZone *)zone -{ - StickerPack *result = [self copyAndAssignIdsWithZone:zone]; - result->_author = self.author; - result->_cover = self.cover; - result->_dateCreated = self.dateCreated; - result->_info = self.info; - result->_isInstalled = self.isInstalled; - result->_items = self.items; - result->_title = self.title; - return result; -} - -- (instancetype)initWithInfo:(StickerPackInfo *)info - title:(nullable NSString *)title - author:(nullable NSString *)author - cover:(StickerPackItem *)cover - stickers:(NSArray *)items -{ - OWSAssertDebug(info.packId.length > 0); - OWSAssertDebug(info.packKey.length > 0); - // Title and empty might be nil or empty. - OWSAssertDebug(cover); - OWSAssertDebug(items.count > 0); - - self = [super initWithUniqueId:[StickerPack uniqueIdForStickerPackInfo:info]]; - - if (!self) { - return self; - } - - _info = info; - _title = title; - _author = author; - _cover = cover; - _items = items; - _dateCreated = [NSDate new]; - - return self; -} - -- (NSData *)packId -{ - return self.info.packId; -} - -- (NSData *)packKey -{ - return self.info.packKey; -} - -- (StickerInfo *)coverInfo -{ - return [[StickerInfo alloc] initWithPackId:self.packId packKey:self.packKey stickerId:self.cover.stickerId]; -} - -- (NSArray *)stickerInfos -{ - NSMutableArray *stickerInfos = [NSMutableArray new]; - for (StickerPackItem *item in self.items) { - [stickerInfos addObject:[item stickerInfoWithStickerPack:self]]; - } - return stickerInfos; -} - -// --- CODE GENERATION MARKER - -// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run -// `sds_codegen.sh`. - -// clang-format off - -- (instancetype)initWithGrdbId:(int64_t)grdbId - uniqueId:(NSString *)uniqueId - author:(nullable NSString *)author - cover:(StickerPackItem *)cover - dateCreated:(NSDate *)dateCreated - info:(StickerPackInfo *)info - isInstalled:(BOOL)isInstalled - items:(NSArray *)items - title:(nullable NSString *)title -{ - self = [super initWithGrdbId:grdbId - uniqueId:uniqueId]; - - if (!self) { - return self; - } - - _author = author; - _cover = cover; - _dateCreated = dateCreated; - _info = info; - _isInstalled = isInstalled; - _items = items; - _title = title; - - return self; -} - -// clang-format on - -// --- CODE GENERATION MARKER - -+ (NSString *)uniqueIdForStickerPackInfo:(StickerPackInfo *)info -{ - return info.asKey; -} - -- (void)updateWithIsInstalled:(BOOL)isInstalled transaction:(DBWriteTransaction *)transaction -{ - [self anyUpdateWithTransaction:transaction block:^(StickerPack *instance) { instance.isInstalled = isInstalled; }]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/Messages/Stickers/StickerPackItem.swift b/SignalServiceKit/Messages/Stickers/StickerPackItem.swift new file mode 100644 index 0000000000..5765f29732 --- /dev/null +++ b/SignalServiceKit/Messages/Stickers/StickerPackItem.swift @@ -0,0 +1,65 @@ +// +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +import Foundation + +@objc(StickerPackItem) +public final class StickerPackItem: NSObject, NSSecureCoding, NSCopying { + public let stickerId: UInt32 + public let emojiString: String + public let contentType: String? + + init( + stickerId: UInt32, + emojiString: String, + contentType: String?, + ) { + self.stickerId = stickerId + self.emojiString = emojiString + self.contentType = contentType?.nilIfEmpty + } + + public static var supportsSecureCoding: Bool { true } + + public func encode(with coder: NSCoder) { + if let contentType { + coder.encode(contentType, forKey: "contentType") + } + coder.encode(self.emojiString, forKey: "emojiString") + coder.encode(NSNumber(value: self.stickerId), forKey: "stickerId") + } + + public init?(coder: NSCoder) { + self.contentType = (coder.decodeObject(of: NSString.self, forKey: "contentType") as String?)?.nilIfEmpty + self.emojiString = coder.decodeObject(of: NSString.self, forKey: "emojiString") as String? ?? "" + self.stickerId = coder.decodeObject(of: NSNumber.self, forKey: "stickerId")?.uint32Value ?? 0 + } + + override public var hash: Int { + var hasher = Hasher() + hasher.combine(self.contentType) + hasher.combine(self.emojiString) + hasher.combine(self.stickerId) + return hasher.finalize() + } + + override public func isEqual(_ object: Any?) -> Bool { + guard let object = object as? Self else { return false } + guard self.contentType == object.contentType else { return false } + guard self.emojiString == object.emojiString else { return false } + guard self.stickerId == object.stickerId else { return false } + return true + } + + public func copy(with zone: NSZone? = nil) -> Any { + return self + } + + func stickerInfoWith(stickerPack: StickerPackRecord) -> StickerInfo { + let packId = stickerPack.info.packId + let packKey = stickerPack.info.packKey + return StickerInfo(packId: packId, packKey: packKey, stickerId: self.stickerId) + } +} diff --git a/SignalServiceKit/Messages/Stickers/StickerPackRecord.swift b/SignalServiceKit/Messages/Stickers/StickerPackRecord.swift new file mode 100644 index 0000000000..66a4141606 --- /dev/null +++ b/SignalServiceKit/Messages/Stickers/StickerPackRecord.swift @@ -0,0 +1,163 @@ +// +// Copyright 2026 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +import Foundation +public import GRDB + +public final class StickerPackRecord: SDSCodableModel, Decodable, Equatable, NSCopying { + public static let databaseTableName: String = "model_StickerPack" + public static let recordType: UInt = SDSRecordType.stickerPack.rawValue + + public var id: Int64? + public let uniqueId: String + public let info: StickerPackInfo + public let title: String? + public let author: String? + public let cover: StickerPackItem + public let items: [StickerPackItem] + public let dateCreated: Date + public private(set) var isInstalled: Bool + + public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable { + case id + case recordType + case uniqueId + case author + case cover + case dateCreated + case info + case isInstalled + case items + case title + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(Int64.self, forKey: .id) + self.uniqueId = try container.decode(String.self, forKey: .uniqueId) + let infoData = try container.decode(Data.self, forKey: .info) + self.info = try LegacySDSSerializer().deserializeLegacySDSData(infoData, ofClass: StickerPackInfo.self) + self.title = try container.decodeIfPresent(String.self, forKey: .title) + self.author = try container.decodeIfPresent(String.self, forKey: .author) + let coverData = try container.decode(Data.self, forKey: .cover) + self.cover = try LegacySDSSerializer().deserializeLegacySDSData(coverData, ofClass: StickerPackItem.self) + let itemsData = try container.decode(Data.self, forKey: .items) + self.items = try LegacySDSSerializer().deserializeLegacyArchivedArray(itemsData, ofClass: StickerPackItem.self) + self.dateCreated = Date(timeIntervalSince1970: try container.decode(TimeInterval.self, forKey: .dateCreated)) + self.isInstalled = try container.decode(Bool.self, forKey: .isInstalled) + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(self.id, forKey: .id) + try container.encode(self.uniqueId, forKey: .uniqueId) + try container.encode(Self.recordType, forKey: .recordType) + try container.encode(LegacySDSSerializer().serializeAsLegacySDSData(self.info), forKey: .info) + try container.encodeIfPresent(self.title, forKey: .title) + try container.encodeIfPresent(self.author, forKey: .author) + try container.encode(LegacySDSSerializer().serializeAsLegacySDSData(self.cover), forKey: .cover) + try container.encode(LegacySDSSerializer().serializeAsLegacySDSData(self.items as [NSSecureCoding] as NSArray), forKey: .items) + try container.encode(self.dateCreated.timeIntervalSince1970, forKey: .dateCreated) + try container.encode(self.isInstalled, forKey: .isInstalled) + } + + public static func ==(lhs: StickerPackRecord, rhs: StickerPackRecord) -> Bool { + guard lhs.author == rhs.author else { return false } + guard lhs.cover == rhs.cover else { return false } + guard lhs.dateCreated == rhs.dateCreated else { return false } + guard lhs.info == rhs.info else { return false } + guard lhs.isInstalled == rhs.isInstalled else { return false } + guard lhs.items == rhs.items else { return false } + guard lhs.title == rhs.title else { return false } + return true + } + + convenience init( + info: StickerPackInfo, + title: String?, + author: String?, + cover: StickerPackItem, + items: [StickerPackItem], + ) { + owsAssertDebug(!info.packId.isEmpty) + owsAssertDebug(!info.packKey.isEmpty) + owsAssertDebug(!items.isEmpty) + self.init( + id: nil, + uniqueId: Self.uniqueId(forStickerPackInfo: info), + info: info, + title: title, + author: author, + cover: cover, + items: items, + dateCreated: Date(), + isInstalled: false, + ) + } + + private init( + id: Int64?, + uniqueId: String, + info: StickerPackInfo, + title: String?, + author: String?, + cover: StickerPackItem, + items: [StickerPackItem], + dateCreated: Date, + isInstalled: Bool, + ) { + self.id = id + self.uniqueId = uniqueId + self.info = info + self.title = title + self.author = author + self.cover = cover + self.items = items + self.dateCreated = dateCreated + self.isInstalled = isInstalled + } + + public func copy(with zone: NSZone? = nil) -> Any { + return Self( + id: self.id, + uniqueId: self.uniqueId, + info: self.info, + title: self.title, + author: self.author, + cover: self.cover, + items: self.items, + dateCreated: self.dateCreated, + isInstalled: self.isInstalled, + ) + } + + public var packId: Data { + // This was the effective behavior in Swift via Obj-C. + return self.info.packId ?? Data() + } + + public var packKey: Data { + // This was the effective behavior in Swift via Obj-C. + return self.info.packKey ?? Data() + } + + public var coverInfo: StickerInfo { + let packId = self.info.packId + let packKey = self.info.packKey + return StickerInfo(packId: packId, packKey: packKey, stickerId: self.cover.stickerId) + } + + public func stickerInfos() -> [StickerInfo] { + return self.items.map({ $0.stickerInfoWith(stickerPack: self) }) + } + + static func uniqueId(forStickerPackInfo stickerPackInfo: StickerPackInfo) -> String { + return stickerPackInfo.asKey + } + + func updateWith(isInstalled: Bool, tx: DBWriteTransaction) { + anyUpdate(transaction: tx, block: { $0.isInstalled = isInstalled }) + } +} diff --git a/SignalServiceKit/SignalServiceKit.h b/SignalServiceKit/SignalServiceKit.h index dfac52b5e8..9e3916d9e1 100644 --- a/SignalServiceKit/SignalServiceKit.h +++ b/SignalServiceKit/SignalServiceKit.h @@ -34,7 +34,6 @@ FOUNDATION_EXPORT const unsigned char SignalServiceKitVersionString[]; #import #import #import -#import #import #import #import diff --git a/SignalServiceKit/Storage/Database/DatabaseRecovery.swift b/SignalServiceKit/Storage/Database/DatabaseRecovery.swift index aa34d18a23..355bae896e 100644 --- a/SignalServiceKit/Storage/Database/DatabaseRecovery.swift +++ b/SignalServiceKit/Storage/Database/DatabaseRecovery.swift @@ -527,7 +527,7 @@ public enum DatabaseRecovery { PendingViewedReceiptRecord.databaseTableName, // Can be recovered in other ways, after recovery is done. ProfileBadge.databaseTableName, - StickerPack.table.tableName, + StickerPackRecord.databaseTableName, HiddenRecipient.databaseTableName, // Not essential. StoryContextAssociatedData.databaseTableName, diff --git a/SignalServiceKit/Storage/Database/SDSCodableModel/SDSCodableModel+SDSSerialization.swift b/SignalServiceKit/Storage/Database/SDSCodableModel/SDSCodableModel+SDSSerialization.swift index 7d48791405..492269c630 100644 --- a/SignalServiceKit/Storage/Database/SDSCodableModel/SDSCodableModel+SDSSerialization.swift +++ b/SignalServiceKit/Storage/Database/SDSCodableModel/SDSCodableModel+SDSSerialization.swift @@ -40,4 +40,18 @@ struct SDSCodableModelLegacySerializer: SDSSerializer { } return result } + + func deserializeLegacyArchivedArray(_ encodedValue: Data, ofClass cls: T.Type) throws -> [T] { + let result: [T]? + do { + result = try NSKeyedUnarchiver.unarchivedArrayOfObjects(ofClass: cls, from: encodedValue) + } catch { + Logger.warn("couldn't decode legacy data: \(error)") + throw SDSError.invalidValue() + } + guard let result else { + throw SDSError.invalidValue() + } + return result + } } diff --git a/SignalUI/Stickers/StickerPackCollectionView.swift b/SignalUI/Stickers/StickerPackCollectionView.swift index 4eae184265..25de26d09d 100644 --- a/SignalUI/Stickers/StickerPackCollectionView.swift +++ b/SignalUI/Stickers/StickerPackCollectionView.swift @@ -102,11 +102,11 @@ public class StickerPackCollectionView: UICollectionView { // MARK: Modes - public func showInstalledPack(stickerPack: StickerPack) { + public func showInstalledPack(stickerPack: StickerPackRecord) { stickerPackDataSource = InstalledStickerPackDataSource(stickerPackInfo: stickerPack.info) } - public func showUninstalledPack(stickerPack: StickerPack) { + public func showUninstalledPack(stickerPack: StickerPackRecord) { stickerPackDataSource = TransientStickerPackDataSource( stickerPackInfo: stickerPack.info, shouldDownloadAllStickers: true, @@ -117,7 +117,7 @@ public class StickerPackCollectionView: UICollectionView { stickerPackDataSource = RecentStickerPackDataSource() } - public func showInstalledPackOrRecents(stickerPack: StickerPack?) { + public func showInstalledPackOrRecents(stickerPack: StickerPackRecord?) { if let stickerPack { showInstalledPack(stickerPack: stickerPack) } else { @@ -225,7 +225,7 @@ public class StickerPackCollectionView: UICollectionView { let installedStickerInfos = stickerPackDataSource.installedStickerInfos if stickerPackDataSource is TransientStickerPackDataSource { - guard let allStickerInfos = stickerPackDataSource.getStickerPack()?.stickerInfos else { + guard let allStickerInfos = stickerPackDataSource.getStickerPack()?.stickerInfos() else { stickerInfos = [] owsAssertDebug(installedStickerInfos.isEmpty) return diff --git a/SignalUI/Stickers/StickerPackDataSource.swift b/SignalUI/Stickers/StickerPackDataSource.swift index 2bda2dbd8f..675e48028e 100644 --- a/SignalUI/Stickers/StickerPackDataSource.swift +++ b/SignalUI/Stickers/StickerPackDataSource.swift @@ -22,7 +22,7 @@ public protocol StickerPackDataSource: AnyObject { var title: String? { get } var author: String? { get } - func getStickerPack() -> StickerPack? + func getStickerPack() -> StickerPackRecord? var installedCoverInfo: StickerInfo? { get } var installedStickerInfos: [StickerInfo] { get } @@ -118,7 +118,7 @@ public class InstalledStickerPackDataSource: BaseStickerPackDataSource { private let stickerPackInfo: StickerPackInfo - fileprivate var stickerPack: StickerPack? { + fileprivate var stickerPack: StickerPackRecord? { didSet { AssertIsOnMainThread() @@ -195,7 +195,7 @@ public class InstalledStickerPackDataSource: BaseStickerPackDataSource { } private static func fetchInstalledState(for stickerPackInfo: StickerPackInfo, readTx: DBReadTransaction) -> ( - stickerPack: StickerPack?, + stickerPack: StickerPackRecord?, installedCoverInfo: StickerInfo?, installedStickers: [StickerInfo], ) { @@ -273,7 +273,7 @@ extension InstalledStickerPackDataSource: StickerPackDataSource { return stickerPack?.author } - public func getStickerPack() -> StickerPack? { + public func getStickerPack() -> StickerPackRecord? { return stickerPack } @@ -313,7 +313,7 @@ public class TransientStickerPackDataSource: BaseStickerPackDataSource { // If false, only download manifest and cover. private let shouldDownloadAllStickers: Bool - fileprivate var stickerPack: StickerPack? { + fileprivate var stickerPack: StickerPackRecord? { didSet { AssertIsOnMainThread() @@ -394,7 +394,7 @@ public class TransientStickerPackDataSource: BaseStickerPackDataSource { if shouldDownloadAllStickers { var downloadedStickerInfos = [StickerInfo]() - for stickerInfo in stickerPack.stickerInfos { + for stickerInfo in stickerPack.stickerInfos() { if ensureStickerDownload(stickerPack: stickerPack, stickerInfo: stickerInfo) { downloadedStickerInfos.append(stickerInfo) } @@ -448,7 +448,7 @@ public class TransientStickerPackDataSource: BaseStickerPackDataSource { // Returns true if sticker is already downloaded. // If not, kicks off the download. - private func ensureStickerDownload(stickerPack: StickerPack, stickerInfo: StickerInfo) -> Bool { + private func ensureStickerDownload(stickerPack: StickerPackRecord, stickerInfo: StickerInfo) -> Bool { AssertIsOnMainThread() guard let stickerPackItem = stickerPack.stickerPackItem(forStickerInfo: stickerInfo) else { @@ -552,7 +552,7 @@ extension TransientStickerPackDataSource: StickerPackDataSource { return stickerPack?.author } - public func getStickerPack() -> StickerPack? { + public func getStickerPack() -> StickerPackRecord? { AssertIsOnMainThread() if let stickerPack = installedDataSource.getStickerPack() { @@ -657,7 +657,7 @@ extension RecentStickerPackDataSource: StickerPackDataSource { return nil } - public func getStickerPack() -> StickerPack? { + public func getStickerPack() -> StickerPackRecord? { owsFailDebug("This method should never be called.") return nil } @@ -684,7 +684,7 @@ extension RecentStickerPackDataSource: StickerPackDataSource { // MARK: - -extension StickerPack { +extension StickerPackRecord { func stickerPackItem(forStickerInfo stickerInfo: StickerInfo) -> StickerPackItem? { if cover.stickerId == stickerInfo.stickerId { return cover diff --git a/SignalUI/Stickers/StickerPickerView.swift b/SignalUI/Stickers/StickerPickerView.swift index e109f9fb75..11eb832a26 100644 --- a/SignalUI/Stickers/StickerPickerView.swift +++ b/SignalUI/Stickers/StickerPickerView.swift @@ -377,9 +377,9 @@ private class StickerPickerPageView: UIView { private let storyStickerConfiguration: StoryStickerConfiguration - private var stickerPacks = [StickerPack]() + private var stickerPacks = [StickerPackRecord]() - private var selectedStickerPack: StickerPack? { + private var selectedStickerPack: StickerPackRecord? { didSet { selectedPackChanged(oldSelectedPack: oldValue) } @@ -534,7 +534,7 @@ private class StickerPickerPageView: UIView { updateSelectedStickerPack(nil) } - private func updateSelectedStickerPack(_ stickerPack: StickerPack?, scrollToSelected: Bool = false) { + private func updateSelectedStickerPack(_ stickerPack: StickerPackRecord?, scrollToSelected: Bool = false) { selectedStickerPack = stickerPack delegate?.updateSelections(scrollToSelectedItem: scrollToSelected) } @@ -575,7 +575,7 @@ private class StickerPickerPageView: UIView { return scrollView }() - private var nextPageStickerPack: StickerPack? { + private var nextPageStickerPack: StickerPackRecord? { // If we don't have a pack defined, the first pack is always up next guard let stickerPack = selectedStickerPack else { return stickerPacks.first } @@ -586,7 +586,7 @@ private class StickerPickerPageView: UIView { return stickerPacks[index + 1] } - private var previousPageStickerPack: StickerPack? { + private var previousPageStickerPack: StickerPackRecord? { // If we don't have a pack defined, the last pack is always previous guard let stickerPack = selectedStickerPack else { return stickerPacks.last } @@ -692,7 +692,7 @@ private class StickerPickerPageView: UIView { pendingPageChangeUpdates = nil } - private func selectedPackChanged(oldSelectedPack: StickerPack?) { + private func selectedPackChanged(oldSelectedPack: StickerPackRecord?) { AssertIsOnMainThread() // We're paging backwards!