Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/tag_and_deploy_to_cocoapods.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ jobs:
- name: Extract LibXMTPSwiftFFI URL from Package.swift
id: extract_url
run: |
ZIP_URL=$(grep -A 3 'name: "LibXMTPSwiftFFI"' Package.swift | grep -o 'https://[^"]*')
ZIP_URL=$(grep -A 3 'name: "LibXMTPSwiftFFIDynamic"' Package.swift | grep -o 'https://[^"]*')
echo "ZIP_URL=$ZIP_URL" >> $GITHUB_ENV
echo "Extracted URL: $ZIP_URL"

- name: Build release archive (Sources + LibXMTPSwiftFFI.xcframework)
run: |
set -euo pipefail
curl -L "$ZIP_URL" -o LibXMTPSwiftFFI.zip
rm -rf LibXMTPSwiftFFI.xcframework
unzip -qo LibXMTPSwiftFFI.zip 'LibXMTPSwiftFFI.xcframework/*' -d .
rm -f LibXMTPSwiftFFI.zip
curl -L "$ZIP_URL" -o LibXMTPSwiftFFIDynamic.zip
rm -rf LibXMTPSwiftFFIDynamic.xcframework
unzip -qo LibXMTPSwiftFFIDynamic.zip 'LibXMTPSwiftFFIDynamic.xcframework/*' -d .
rm -f LibXMTPSwiftFFIDynamic.zip
rm -rf bundle && mkdir -p bundle
rsync -a --exclude 'LibXMTPSwiftFFI.xcframework' Sources/ bundle/Sources/
mv LibXMTPSwiftFFI.xcframework bundle/
rsync -a --exclude 'LibXMTPSwiftFFIDynamic.xcframework' Sources/ bundle/Sources/
mv LibXMTPSwiftFFIDynamic.xcframework bundle/
(cd bundle && zip -qry ../XMTP-${UPDATED_VERSION}.zip .)

- name: Create GitHub Release and upload asset
Expand All @@ -73,6 +73,7 @@ jobs:
name: ${{ env.UPDATED_VERSION }}
files: XMTP-${{ env.UPDATED_VERSION }}.zip
make_latest: false
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand Down
9 changes: 5 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ let package = Package(
products: [
.library(
name: "XMTPiOS",
type: .dynamic,
targets: ["XMTPiOS"]
),
.library(
Expand All @@ -24,15 +25,15 @@ let package = Package(
],
targets: [
.binaryTarget(
name: "LibXMTPSwiftFFI",
url: "https://github.com/xmtp/libxmtp/releases/download/swift-bindings-1.6.1.4d46a4a/LibXMTPSwiftFFI.zip",
checksum: "a3bc53c3bfec02fb52064c0576fa0633bf3bd60fc61d8fd5f6b80355763f91d4"
name: "LibXMTPSwiftFFIDynamic",
url: "https://github.com/xmtp/libxmtp/releases/download/swift-bindings-1.6.3.ab0df09/LibXMTPSwiftFFIDynamic.zip",
checksum: "66ea16f78499a60b99e33e6f09f75411bca7d3c3a41e3a6621144f78047fc1d3"
),
.target(
name: "XMTPiOS",
dependencies: [
.product(name: "Connect", package: "connect-swift"),
"LibXMTPSwiftFFI",
"LibXMTPSwiftFFIDynamic",
.product(name: "CryptoSwift", package: "CryptoSwift"),
]
),
Expand Down
22 changes: 16 additions & 6 deletions Sources/XMTPiOS/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,10 @@ public final class Client {
v3Host: api.env.url,
gatewayHost: api.gatewayHost,
isSecure: api.isSecure,
appVersion: api.appVersion
clientMode: FfiClientMode.default,
appVersion: api.appVersion,
authCallback: nil,
authHandle: nil
)
await apiCache.setClient(newClient, forKey: cacheKey)
return newClient
Expand All @@ -484,7 +487,10 @@ public final class Client {
v3Host: api.env.url,
gatewayHost: api.gatewayHost,
isSecure: api.isSecure,
appVersion: api.appVersion
clientMode: FfiClientMode.default,
appVersion: api.appVersion,
authCallback: nil,
authHandle: nil
)
await apiCache.setSyncClient(newClient, forKey: cacheKey)
return newClient
Expand Down Expand Up @@ -749,7 +755,10 @@ public final class Client {
}

public func revokeAllOtherInstallations(signingKey: SigningKey) async throws {
let signatureRequest = try await ffiRevokeAllOtherInstallations()
guard let signatureRequest = try await ffiRevokeAllOtherInstallations() else {
// No other installations to revoke – nothing to do.
return
}
do {
try await Client.handleSignature(
for: signatureRequest.ffiSignatureRequest,
Expand Down Expand Up @@ -945,10 +954,11 @@ public final class Client {
"""
)
public func ffiRevokeAllOtherInstallations() async throws
-> SignatureRequest
-> SignatureRequest?
{
let ffiSigReq = try await ffiClient.revokeAllOtherInstallations()
return SignatureRequest(ffiSignatureRequest: ffiSigReq)
try await ffiClient
.revokeAllOtherInstallationsSignatureRequest()
.map(SignatureRequest.init(ffiSignatureRequest:))
}

@available(
Expand Down
13 changes: 11 additions & 2 deletions Sources/XMTPiOS/Codecs/ReactionCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ public let ContentTypeReaction = ContentTypeID(

public struct Reaction: Codable {
public var reference: String
public var referenceInboxId: String?
public var action: ReactionAction
public var content: String
public var schema: ReactionSchema

public init(reference: String, action: ReactionAction, content: String, schema: ReactionSchema) {
public init(
reference: String,
action: ReactionAction,
content: String,
schema: ReactionSchema,
referenceInboxId: String = ""
) {
self.reference = reference
self.referenceInboxId = referenceInboxId
self.action = action
self.content = content
self.schema = schema
Expand Down Expand Up @@ -87,7 +95,8 @@ public struct ReactionCodec: ContentCodec {
reference: content.parameters["reference"] ?? "",
action: ReactionAction(rawValue: content.parameters["action"] ?? ""),
content: String(data: content.content, encoding: .utf8) ?? "",
schema: ReactionSchema(rawValue: content.parameters["schema"] ?? "")
schema: ReactionSchema(rawValue: content.parameters["schema"] ?? ""),
referenceInboxId: ""
)
// swiftlint:enable no_optional_try
}
Expand Down
84 changes: 77 additions & 7 deletions Sources/XMTPiOS/Codecs/ReactionV2Codec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,36 @@ public let ContentTypeReactionV2 = ContentTypeID(
)

public struct ReactionV2Codec: ContentCodec {
public typealias T = FfiReactionPayload
public typealias T = Reaction
public var contentType = ContentTypeReactionV2

public init() {}

public func encode(content: FfiReactionPayload) throws -> EncodedContent {
try EncodedContent(serializedBytes: encodeReaction(reaction: content))
public func encode(content: Reaction) throws -> EncodedContent {
// Convert Reaction to FfiReactionPayload for encoding
let ffiReaction = FfiReactionPayload(
reference: content.reference,
referenceInboxId: content.referenceInboxId ?? "",
action: content.action.toFfiReactionAction(),
content: content.content,
schema: content.schema.toFfiReactionSchema()
)
return try EncodedContent(serializedBytes: encodeReaction(reaction: ffiReaction))
}

public func decode(content: EncodedContent) throws -> FfiReactionPayload {
try decodeReaction(bytes: content.serializedBytes())
public func decode(content: EncodedContent) throws -> Reaction {
let ffiReaction = try decodeReaction(bytes: content.serializedBytes())
// Convert FfiReactionPayload to Reaction
return Reaction(
reference: ffiReaction.reference,
action: ReactionAction.fromFfiReactionAction(ffiReaction.action),
content: ffiReaction.content,
schema: ReactionSchema.fromFfiReactionSchema(ffiReaction.schema),
referenceInboxId: ffiReaction.referenceInboxId
)
}

public func fallback(content: FfiReactionPayload) throws -> String? {
public func fallback(content: Reaction) throws -> String? {
switch content.action {
case .added:
return "Reacted \"\(content.content)\" to an earlier message"
Expand All @@ -39,7 +55,7 @@ public struct ReactionV2Codec: ContentCodec {
}
}

public func shouldPush(content: FfiReactionPayload) throws -> Bool {
public func shouldPush(content: Reaction) throws -> Bool {
switch content.action {
case .added:
return true
Expand All @@ -48,3 +64,57 @@ public struct ReactionV2Codec: ContentCodec {
}
}
}

// MARK: - Conversion Extensions

extension ReactionAction {
func toFfiReactionAction() -> FfiReactionAction {
switch self {
case .added:
return .added
case .removed:
return .removed
case .unknown:
return .unknown
}
}

static func fromFfiReactionAction(_ action: FfiReactionAction) -> ReactionAction {
switch action {
case .added:
return .added
case .removed:
return .removed
case .unknown:
return .unknown
}
}
}

extension ReactionSchema {
func toFfiReactionSchema() -> FfiReactionSchema {
switch self {
case .unicode:
return .unicode
case .shortcode:
return .shortcode
case .custom:
return .custom
case .unknown:
return .unknown
}
}

static func fromFfiReactionSchema(_ schema: FfiReactionSchema) -> ReactionSchema {
switch schema {
case .unicode:
return .unicode
case .shortcode:
return .shortcode
case .custom:
return .custom
case .unknown:
return .unknown
}
}
}
57 changes: 45 additions & 12 deletions Sources/XMTPiOS/Conversation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,22 +280,31 @@ public enum Conversation: Identifiable, Equatable, Hashable {
direction: SortDirection? = .descending,
deliveryStatus: MessageDeliveryStatus = .all,
excludeContentTypes: [StandardContentType]? = nil,
excludeSenderInboxIds: [String]? = nil
excludeSenderInboxIds: [String]? = nil,
sortBy: MessageSortBy? = nil,
insertedAfterNs: Int64? = nil,
insertedBeforeNs: Int64? = nil
) async throws -> [DecodedMessage] {
switch self {
case let .group(group):
return try await group.messages(
beforeNs: beforeNs, afterNs: afterNs, limit: limit,
direction: direction, deliveryStatus: deliveryStatus,
excludeContentTypes: excludeContentTypes,
excludeSenderInboxIds: excludeSenderInboxIds
excludeSenderInboxIds: excludeSenderInboxIds,
sortBy: sortBy,
insertedAfterNs: insertedAfterNs,
insertedBeforeNs: insertedBeforeNs
)
case let .dm(dm):
return try await dm.messages(
beforeNs: beforeNs, afterNs: afterNs, limit: limit,
direction: direction, deliveryStatus: deliveryStatus,
excludeContentTypes: excludeContentTypes,
excludeSenderInboxIds: excludeSenderInboxIds
excludeSenderInboxIds: excludeSenderInboxIds,
sortBy: sortBy,
insertedAfterNs: insertedAfterNs,
insertedBeforeNs: insertedBeforeNs
)
}
}
Expand Down Expand Up @@ -326,22 +335,31 @@ public enum Conversation: Identifiable, Equatable, Hashable {
direction: SortDirection? = .descending,
deliveryStatus: MessageDeliveryStatus = .all,
excludeContentTypes: [StandardContentType]? = nil,
excludeSenderInboxIds: [String]? = nil
excludeSenderInboxIds: [String]? = nil,
sortBy: MessageSortBy? = nil,
insertedAfterNs: Int64? = nil,
insertedBeforeNs: Int64? = nil
) async throws -> [DecodedMessage] {
switch self {
case let .group(group):
return try await group.messagesWithReactions(
beforeNs: beforeNs, afterNs: afterNs, limit: limit,
direction: direction, deliveryStatus: deliveryStatus,
excludeContentTypes: excludeContentTypes,
excludeSenderInboxIds: excludeSenderInboxIds
excludeSenderInboxIds: excludeSenderInboxIds,
sortBy: sortBy,
insertedAfterNs: insertedAfterNs,
insertedBeforeNs: insertedBeforeNs
)
case let .dm(dm):
return try await dm.messagesWithReactions(
beforeNs: beforeNs, afterNs: afterNs, limit: limit,
direction: direction, deliveryStatus: deliveryStatus,
excludeContentTypes: excludeContentTypes,
excludeSenderInboxIds: excludeSenderInboxIds
excludeSenderInboxIds: excludeSenderInboxIds,
sortBy: sortBy,
insertedAfterNs: insertedAfterNs,
insertedBeforeNs: insertedBeforeNs
)
}
}
Expand All @@ -353,22 +371,31 @@ public enum Conversation: Identifiable, Equatable, Hashable {
direction: SortDirection? = .descending,
deliveryStatus: MessageDeliveryStatus = .all,
excludeContentTypes: [StandardContentType]? = nil,
excludeSenderInboxIds: [String]? = nil
excludeSenderInboxIds: [String]? = nil,
sortBy: MessageSortBy? = nil,
insertedAfterNs: Int64? = nil,
insertedBeforeNs: Int64? = nil
) async throws -> [DecodedMessageV2] {
switch self {
case let .group(group):
return try await group.enrichedMessages(
beforeNs: beforeNs, afterNs: afterNs, limit: limit,
direction: direction, deliveryStatus: deliveryStatus,
excludeContentTypes: excludeContentTypes,
excludeSenderInboxIds: excludeSenderInboxIds
excludeSenderInboxIds: excludeSenderInboxIds,
sortBy: sortBy,
insertedAfterNs: insertedAfterNs,
insertedBeforeNs: insertedBeforeNs
)
case let .dm(dm):
return try await dm.enrichedMessages(
beforeNs: beforeNs, afterNs: afterNs, limit: limit,
direction: direction, deliveryStatus: deliveryStatus,
excludeContentTypes: excludeContentTypes,
excludeSenderInboxIds: excludeSenderInboxIds
excludeSenderInboxIds: excludeSenderInboxIds,
sortBy: sortBy,
insertedAfterNs: insertedAfterNs,
insertedBeforeNs: insertedBeforeNs
)
}
}
Expand All @@ -378,20 +405,26 @@ public enum Conversation: Identifiable, Equatable, Hashable {
afterNs: Int64? = nil,
deliveryStatus: MessageDeliveryStatus = .all,
excludeContentTypes: [StandardContentType]? = nil,
excludeSenderInboxIds: [String]? = nil
excludeSenderInboxIds: [String]? = nil,
insertedAfterNs: Int64? = nil,
insertedBeforeNs: Int64? = nil
) throws -> Int64 {
switch self {
case let .group(group):
return try group.countMessages(
beforeNs: beforeNs, afterNs: afterNs, deliveryStatus: deliveryStatus,
excludeContentTypes: excludeContentTypes,
excludeSenderInboxIds: excludeSenderInboxIds
excludeSenderInboxIds: excludeSenderInboxIds,
insertedAfterNs: insertedAfterNs,
insertedBeforeNs: insertedBeforeNs
)
case let .dm(dm):
return try dm.countMessages(
beforeNs: beforeNs, afterNs: afterNs, deliveryStatus: deliveryStatus,
excludeContentTypes: excludeContentTypes,
excludeSenderInboxIds: excludeSenderInboxIds
excludeSenderInboxIds: excludeSenderInboxIds,
insertedAfterNs: insertedAfterNs,
insertedBeforeNs: insertedBeforeNs
)
}
}
Expand Down
Loading
Loading