async let to parallelise stuff

fix crash when the fetched db doesnt exist
added loadremoteDB to asyncly fetch remotedb
fixed Tasks not running on background threads
remove expiration from slackresponse
emoji.uiid starts out as emoji.id
added filtering emojis test
hopefully optimised the display of images checking if its local
imessage extension ios 15+
remove the "hii!!" and "hello world" from the emojji picker
This commit is contained in:
neon443
2025-10-30 19:44:59 +00:00
parent b738a06835
commit c974e8136b
8 changed files with 44 additions and 46 deletions

View File

@@ -663,7 +663,7 @@
INFOPLIST_FILE = StickerSlackiMessageExtension/Info.plist; INFOPLIST_FILE = StickerSlackiMessageExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = StickerSlackiMessageExtension; INFOPLIST_KEY_CFBundleDisplayName = StickerSlackiMessageExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 26.0; IPHONEOS_DEPLOYMENT_TARGET = 15;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -695,7 +695,7 @@
INFOPLIST_FILE = StickerSlackiMessageExtension/Info.plist; INFOPLIST_FILE = StickerSlackiMessageExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = StickerSlackiMessageExtension; INFOPLIST_KEY_CFBundleDisplayName = StickerSlackiMessageExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 26.0; IPHONEOS_DEPLOYMENT_TARGET = 15;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",

View File

@@ -13,13 +13,12 @@ import UniformTypeIdentifiers
struct Emoji: Codable, Identifiable, Hashable { struct Emoji: Codable, Identifiable, Hashable {
var id: UUID var id: UUID
var uiID: UUID = UUID() var uiID: UUID
var name: String var name: String
var localImageURL: URL { var localImageURL: URL {
let urlString = remoteImageURL.absoluteString let urlString = remoteImageURL.absoluteString
let split = urlString.split(separator: ".") let split = urlString.split(separator: ".")
let fileExtension = ".\(split.last ?? "png")" let fileExtension = ".\(split.last ?? "png")"
// return EmojiHoarder.container.appendingPathComponent(id.uuidString+fileExtension, conformingTo: .image)
return URL(string: EmojiHoarder.container.absoluteString+id.uuidString+fileExtension)! return URL(string: EmojiHoarder.container.absoluteString+id.uuidString+fileExtension)!
} }
var remoteImageURL: URL var remoteImageURL: URL
@@ -53,6 +52,7 @@ struct Emoji: Codable, Identifiable, Hashable {
init(from decoder: any Decoder) throws { init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(UUID.self, forKey: .id) self.id = try container.decode(UUID.self, forKey: .id)
self.uiID = id
self.name = try container.decode(String.self, forKey: .name) self.name = try container.decode(String.self, forKey: .name)
self.remoteImageURL = try container.decode(URL.self, forKey: .remoteImageURL) self.remoteImageURL = try container.decode(URL.self, forKey: .remoteImageURL)
} }
@@ -62,6 +62,7 @@ struct Emoji: Codable, Identifiable, Hashable {
id: UUID = UUID() id: UUID = UUID()
) { ) {
self.id = id self.id = id
self.uiID = id
self.name = apiEmoji.name self.name = apiEmoji.name
self.remoteImageURL = apiEmoji.url self.remoteImageURL = apiEmoji.url
} }

View File

@@ -13,7 +13,6 @@ struct SlackResponse: Identifiable, Codable {
var name: String var name: String
var imageUrl: String var imageUrl: String
var alias: String? var alias: String?
var expiration: Date
static func toEmojis(from response: [SlackResponse]?) -> [Emoji]? { static func toEmojis(from response: [SlackResponse]?) -> [Emoji]? {
guard let response else { return nil } guard let response else { return nil }

View File

@@ -25,11 +25,9 @@ class EmojiHoarder: ObservableObject {
withAnimation { self.emojis = loadLocalDB() } withAnimation { self.emojis = loadLocalDB() }
withAnimation { self.filteredEmojis = self.emojis } withAnimation { self.filteredEmojis = self.emojis }
Task(priority: .high) { Task.detached {
if let fetched = await self.fetchRemoteDB() { print(Thread.current)
withAnimation { self.emojis = fetched } await self.loadRemoteDB()
withAnimation { self.filteredEmojis = fetched }
}
} }
} }
@@ -55,12 +53,20 @@ class EmojiHoarder: ObservableObject {
return [] return []
} }
func loadRemoteDB() async {
async let fetched = self.fetchRemoteDB()
if let fetched = await fetched {
withAnimation { self.emojis = fetched }
withAnimation { self.filteredEmojis = fetched }
}
}
func fetchRemoteDB() async -> [Emoji]? { func fetchRemoteDB() async -> [Emoji]? {
do { do {
let (data, _) = try await URLSession.shared.data(from: endpoint) async let (data, _) = try URLSession.shared.data(from: endpoint)
decoder.dateDecodingStrategy = .iso8601 decoder.dateDecodingStrategy = .iso8601
let decoded: [SlackResponse] = try! decoder.decode([SlackResponse].self, from: data) let decoded: [SlackResponse] = try decoder.decode([SlackResponse].self, from: await data)
storeDB(data: data) try storeDB(data: await data)
return SlackResponse.toEmojis(from: decoded) return SlackResponse.toEmojis(from: decoded)
} catch { } catch {
print(error.localizedDescription) print(error.localizedDescription)
@@ -69,12 +75,14 @@ class EmojiHoarder: ObservableObject {
} }
func refreshDB(withCallback callback: (() -> Void)? = nil) { func refreshDB(withCallback callback: (() -> Void)? = nil) {
Task { Task.detached {
guard let fetched = try? await fetchRemoteDB() else { return } guard let fetched = await self.fetchRemoteDB() else { return }
withAnimation { self.emojis = fetched } DispatchQueue.main.async {
withAnimation { self.filteredEmojis = fetched } withAnimation { self.emojis = fetched }
if let callback { withAnimation { self.filteredEmojis = fetched }
callback() if let callback {
callback()
}
} }
} }
} }
@@ -84,8 +92,8 @@ class EmojiHoarder: ObservableObject {
withAnimation(.interactiveSpring) { self.filteredEmojis = emojis } withAnimation(.interactiveSpring) { self.filteredEmojis = emojis }
return return
} }
Task { Task.detached {
let filtered = emojis.filter { $0.name.localizedCaseInsensitiveContains(searchTerm) } let filtered = await self.emojis.filter { $0.name.localizedCaseInsensitiveContains(searchTerm) }
DispatchQueue.main.async { DispatchQueue.main.async {
withAnimation(.interactiveSpring) { self.filteredEmojis = filtered } withAnimation(.interactiveSpring) { self.filteredEmojis = filtered }
} }
@@ -97,15 +105,15 @@ class EmojiHoarder: ObservableObject {
filterEmojis(by: searchTerm) filterEmojis(by: searchTerm)
return return
} }
Task { self.filterEmojis(by: searchTerm)
filterEmojis(by: searchTerm) DispatchQueue.main.async {
switch category { switch category {
case .none: case .none:
fallthrough fallthrough
case .downloaded: case .downloaded:
withAnimation(.interactiveSpring) { filteredEmojis = filteredEmojis.filter { $0.isLocal } } withAnimation(.interactiveSpring) { self.filteredEmojis = self.filteredEmojis.filter { $0.isLocal } }
case .notDownloaded: case .notDownloaded:
withAnimation(.interactiveSpring) { filteredEmojis = filteredEmojis.filter { !$0.isLocal } } withAnimation(.interactiveSpring) { self.filteredEmojis = self.filteredEmojis.filter { !$0.isLocal } }
} }
} }
} }

View File

@@ -17,9 +17,8 @@ struct EmojiPreview: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
Text(emoji.name) Text(emoji.name)
Group { Group {
if let localImage = try? Data(contentsOf: emoji.localImageURL), if emoji.isLocal {
let image = UIImage(data: localImage) { Image(uiImage: emoji.image!)
Image(uiImage: image)
.resizable().scaledToFit() .resizable().scaledToFit()
.border(.orange) .border(.orange)
.overlay(alignment: .bottomLeading) { .overlay(alignment: .bottomLeading) {

View File

@@ -23,4 +23,13 @@ struct StickerSlackTests {
} }
} }
@Test func filteringEmojis() async throws {
let searchQueries = ["heavysob", "yay", "afsdjk", "afhjskf", "g4", "aqua-osx", "neotunes", "", "", ""]
for query in searchQueries {
print(query)
hoarder.filteredEmojis = []
hoarder.filterEmojis(by: query)
print(hoarder.filteredEmojis.count)
}
}
} }

View File

@@ -15,19 +15,8 @@
<view key="view" contentMode="scaleToFill" id="zMn-AG-sqS"> <view key="view" contentMode="scaleToFill" id="zMn-AG-sqS">
<rect key="frame" x="0.0" y="0.0" width="320" height="528"/> <rect key="frame" x="0.0" y="0.0" width="320" height="528"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hello World" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="d1e-fi-ked">
<rect key="frame" x="116" y="254.00000000000003" width="88" height="20.333333333333343"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="LDy-ih-0nr"/> <viewLayoutGuide key="safeArea" id="LDy-ih-0nr"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="centerY" secondItem="d1e-fi-ked" secondAttribute="centerY" id="H0s-hz-dDP"/>
<constraint firstAttribute="centerX" secondItem="d1e-fi-ked" secondAttribute="centerX" id="wFy-hW-Bib"/>
</constraints>
</view> </view>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="528"/> <size key="freeformSize" width="320" height="528"/>

View File

@@ -20,13 +20,6 @@ class MessagesViewController: MSMessagesAppViewController {
// MARK: - Conversation Handling // MARK: - Conversation Handling
override func willBecomeActive(with conversation: MSConversation) { override func willBecomeActive(with conversation: MSConversation) {
let l = UILabel()
l.frame = CGRect(x: 20, y: 20, width: 200, height: 40)
l.textColor = .systemOrange
l.text = "hii!"
view.addSubview(l)
view.bringSubviewToFront(l)
let stickerBrowser = MSStickerBrowserView(frame: .zero, stickerSize: .regular) let stickerBrowser = MSStickerBrowserView(frame: .zero, stickerSize: .regular)
stickerBrowser.frame = CGRect(x: 60, y: 20, width: 200, height: 600) stickerBrowser.frame = CGRect(x: 60, y: 20, width: 200, height: 600)
stickerBrowser.dataSource = dataSource stickerBrowser.dataSource = dataSource