mirror of
https://github.com/leonboe1/QuickDrop.git
synced 2026-11-11 02:51:11 +00:00
More robust outgoing connection
This commit is contained in:
1 parent
db70d40e43
commit
6b0e14de3e
3 files changed
+255
-21
No files matched your search
@@ -10,6 +10,7 @@ import UIKit
|
||||
import DeviceKit
|
||||
#endif
|
||||
|
||||
import Darwin
|
||||
import Foundation
|
||||
import Network
|
||||
import System
|
||||
@@ -34,6 +35,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
private var inboundAppDelegates: [InboundAppDelegate] = []
|
||||
private var outgoingTransfers: [String: OutgoingTransferInfo] = [:]
|
||||
private var pendingOutgoingTransferPreparations: [String: UUID] = [:]
|
||||
private var outgoingServiceResolvers: [String: NearbyOutgoingServiceResolver] = [:]
|
||||
private let pendingNotificationSyncTrustQueue = DispatchQueue(label: "NearbyConnectionManager.pendingNotificationSyncTrust")
|
||||
private var pendingNotificationSyncTrust: [String: PendingReceiverTrust] = [:]
|
||||
private var startedDeviceDiscovery = false
|
||||
@@ -445,6 +447,8 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
self.addFoundDevice(service: res)
|
||||
case let .removed(res):
|
||||
self.removeFoundDevice(service: res)
|
||||
case let .changed(_, new, _):
|
||||
self.addFoundDevice(service: new)
|
||||
default:
|
||||
log("[NearbyConnectionManager] Ignoring change \(change)")
|
||||
}
|
||||
@@ -536,11 +540,11 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
|
||||
|
||||
public func cancelTransfer(id: String) {
|
||||
|
||||
if let transfer = incomingConnections[id] {
|
||||
transfer.cancel()
|
||||
}
|
||||
pendingOutgoingTransferPreparations.removeValue(forKey: id)
|
||||
outgoingServiceResolvers.removeValue(forKey: id)?.cancel()
|
||||
if let info = outgoingTransfers[id] {
|
||||
info.connection.cancel()
|
||||
}
|
||||
@@ -716,7 +720,13 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
private func removeFoundDevice(service: NWBrowser.Result) {
|
||||
guard let endpointID = endpointID(for: service) else { return }
|
||||
deviceNameResolver.cancelLookup(for: endpointID)
|
||||
guard let _ = foundServices.removeValue(forKey: endpointID) else { return }
|
||||
guard let currentService = foundServices[endpointID] else { return }
|
||||
// Ignore delayed removals for an older Bonjour result after a changed result
|
||||
// has already replaced the service for this endpoint ID.
|
||||
guard serviceIdentity(currentService.service) == serviceIdentity(service) else {
|
||||
return
|
||||
}
|
||||
foundServices.removeValue(forKey: endpointID)
|
||||
for delegate in outboundAppDelegates {
|
||||
delegate.removeDevice(id: endpointID)
|
||||
}
|
||||
@@ -795,30 +805,41 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
return
|
||||
}
|
||||
|
||||
self.pendingOutgoingTransferPreparations.removeValue(forKey: deviceID)
|
||||
guard let delegate else { return }
|
||||
guard let delegate else {
|
||||
self.pendingOutgoingTransferPreparations.removeValue(forKey: deviceID)
|
||||
return
|
||||
}
|
||||
|
||||
preparationFinished?()
|
||||
|
||||
let tcp = NWProtocolTCP.Options()
|
||||
tcp.noDelay = true
|
||||
let nwconn = NWConnection(to: info.service.endpoint, using: NWParameters(tls: .none, tcp: tcp))
|
||||
self.resolveOutgoingEndpoint(for: info.service, deviceID: deviceID) { endpoint in
|
||||
guard let info = self.foundServices[deviceID],
|
||||
self.pendingOutgoingTransferPreparations[deviceID] == preparationID else {
|
||||
return
|
||||
}
|
||||
|
||||
let conn = OutboundNearbyConnection(
|
||||
connection: nwconn,
|
||||
id: deviceID,
|
||||
urlsToSend: localUrls,
|
||||
textToSend: textToSend,
|
||||
isClipboardSyncTransfer: isClipboardSyncTransfer,
|
||||
receiverAuthenticationPolicy: receiverAuthenticationPolicy
|
||||
)
|
||||
conn.remoteDeviceInfo = info.device
|
||||
conn.delegate = self
|
||||
conn.qrCodePrivateKey = self.qrCodePrivateKey
|
||||
self.pendingOutgoingTransferPreparations.removeValue(forKey: deviceID)
|
||||
|
||||
let transfer = OutgoingTransferInfo(service: info.service, device: info.device!, connection: conn, delegate: delegate)
|
||||
self.outgoingTransfers[deviceID] = transfer
|
||||
conn.start()
|
||||
let tcp = NWProtocolTCP.Options()
|
||||
tcp.noDelay = true
|
||||
let nwconn = NWConnection(to: endpoint, using: NWParameters(tls: .none, tcp: tcp))
|
||||
|
||||
let conn = OutboundNearbyConnection(
|
||||
connection: nwconn,
|
||||
id: deviceID,
|
||||
urlsToSend: localUrls,
|
||||
textToSend: textToSend,
|
||||
isClipboardSyncTransfer: isClipboardSyncTransfer,
|
||||
receiverAuthenticationPolicy: receiverAuthenticationPolicy
|
||||
)
|
||||
conn.remoteDeviceInfo = info.device
|
||||
conn.delegate = self
|
||||
conn.qrCodePrivateKey = self.qrCodePrivateKey
|
||||
|
||||
let transfer = OutgoingTransferInfo(service: info.service, device: info.device!, connection: conn, delegate: delegate)
|
||||
self.outgoingTransfers[deviceID] = transfer
|
||||
conn.start()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
log("[NearbyConnectionManager] Error storing URLs: \(error)")
|
||||
@@ -837,6 +858,33 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
}
|
||||
|
||||
|
||||
private func resolveOutgoingEndpoint(
|
||||
for service: NWBrowser.Result,
|
||||
deviceID: String,
|
||||
completion: @escaping (NWEndpoint) -> Void
|
||||
) {
|
||||
guard let resolver = NearbyOutgoingServiceResolver(result: service, resolveTimeout: 2.5, completion: { [weak self] endpoints in
|
||||
guard let self else { return }
|
||||
self.outgoingServiceResolvers.removeValue(forKey: deviceID)
|
||||
|
||||
if let endpoint = endpoints.first {
|
||||
completion(endpoint.endpoint)
|
||||
} else {
|
||||
log("[NearbyConnectionManager] Could not resolve outgoing service for \(deviceID); falling back to Bonjour endpoint \(service.endpoint)")
|
||||
completion(service.endpoint)
|
||||
}
|
||||
}) else {
|
||||
log("[NearbyConnectionManager] Cannot create resolver for outgoing service; falling back to Bonjour endpoint")
|
||||
completion(service.endpoint)
|
||||
return
|
||||
}
|
||||
|
||||
outgoingServiceResolvers.removeValue(forKey: deviceID)?.cancel()
|
||||
outgoingServiceResolvers[deviceID] = resolver
|
||||
resolver.start()
|
||||
}
|
||||
|
||||
|
||||
private func normalizedKeyFingerprint(_ keyFingerprint: String?) -> String? {
|
||||
let normalized = keyFingerprint?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
@@ -845,6 +893,11 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
|
||||
}
|
||||
|
||||
|
||||
private func serviceIdentity(_ service: NWBrowser.Result) -> String {
|
||||
String(describing: service.endpoint)
|
||||
}
|
||||
|
||||
|
||||
func connectionWasEstablished(connection: OutboundNearbyConnection) {
|
||||
guard let transfer = outgoingTransfers[connection.id] else { return }
|
||||
DispatchQueue.main.async {
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// NearbyOutgoingServiceResolver.swift
|
||||
// QuickDrop
|
||||
//
|
||||
// Created by Leon Böttger on 19.05.26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
final class NearbyOutgoingServiceResolver: NSObject, NetServiceDelegate {
|
||||
private let service: NetService
|
||||
private let resolveTimeout: TimeInterval
|
||||
private var timeoutWorkItem: DispatchWorkItem?
|
||||
private var completion: (([NearbyResolvedServiceEndpoint]) -> Void)?
|
||||
private var finished = false
|
||||
|
||||
init?(
|
||||
result: NWBrowser.Result,
|
||||
resolveTimeout: TimeInterval,
|
||||
completion: @escaping ([NearbyResolvedServiceEndpoint]) -> Void
|
||||
) {
|
||||
guard case let NWEndpoint.service(name: name, type: type, domain: domain, interface: _) = result.endpoint else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.service = NetService(
|
||||
domain: Self.normalizedServiceDomain(domain),
|
||||
type: Self.normalizedServiceType(type),
|
||||
name: name
|
||||
)
|
||||
self.resolveTimeout = resolveTimeout
|
||||
self.completion = completion
|
||||
super.init()
|
||||
}
|
||||
|
||||
|
||||
func start() {
|
||||
service.delegate = self
|
||||
service.resolve(withTimeout: resolveTimeout)
|
||||
|
||||
let timeoutWorkItem = DispatchWorkItem { [weak self] in
|
||||
self?.finish(endpoints: [])
|
||||
}
|
||||
self.timeoutWorkItem = timeoutWorkItem
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + resolveTimeout + 0.5, execute: timeoutWorkItem)
|
||||
}
|
||||
|
||||
|
||||
func cancel() {
|
||||
finish(endpoints: [], notify: false)
|
||||
}
|
||||
|
||||
|
||||
func netServiceDidResolveAddress(_ sender: NetService) {
|
||||
finish(endpoints: Self.resolvedEndpoints(from: sender))
|
||||
}
|
||||
|
||||
|
||||
func netService(_ sender: NetService, didNotResolve errorDict: [String: NSNumber]) {
|
||||
finish(endpoints: [])
|
||||
}
|
||||
|
||||
|
||||
private func finish(endpoints: [NearbyResolvedServiceEndpoint], notify: Bool = true) {
|
||||
guard !finished else { return }
|
||||
finished = true
|
||||
timeoutWorkItem?.cancel()
|
||||
service.stop()
|
||||
service.delegate = nil
|
||||
|
||||
let completion = completion
|
||||
self.completion = nil
|
||||
|
||||
if notify {
|
||||
DispatchQueue.main.async {
|
||||
completion?(endpoints)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static func resolvedEndpoints(from service: NetService) -> [NearbyResolvedServiceEndpoint] {
|
||||
let endpoints = (service.addresses ?? [])
|
||||
.compactMap(resolvedEndpoint)
|
||||
|
||||
var seenDescriptions = Set<String>()
|
||||
let uniqueEndpoints = endpoints.filter { endpoint in
|
||||
seenDescriptions.insert(endpoint.description).inserted
|
||||
}
|
||||
|
||||
return uniqueEndpoints.sorted { lhs, rhs in
|
||||
if lhs.isIPv4 != rhs.isIPv4 {
|
||||
return lhs.isIPv4
|
||||
}
|
||||
return lhs.description < rhs.description
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static func resolvedEndpoint(from addressData: Data) -> NearbyResolvedServiceEndpoint? {
|
||||
addressData.withUnsafeBytes { rawBuffer in
|
||||
guard let baseAddress = rawBuffer.baseAddress else { return nil }
|
||||
|
||||
let sockaddrPointer = baseAddress.assumingMemoryBound(to: sockaddr.self)
|
||||
let addressFamily = Int32(sockaddrPointer.pointee.sa_family)
|
||||
|
||||
guard addressFamily == AF_INET || addressFamily == AF_INET6 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
||||
var portBuffer = [CChar](repeating: 0, count: Int(NI_MAXSERV))
|
||||
let result = getnameinfo(
|
||||
sockaddrPointer,
|
||||
socklen_t(addressData.count),
|
||||
&hostBuffer,
|
||||
socklen_t(hostBuffer.count),
|
||||
&portBuffer,
|
||||
socklen_t(portBuffer.count),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV
|
||||
)
|
||||
|
||||
guard result == 0 else { return nil }
|
||||
|
||||
let host = String(cString: hostBuffer)
|
||||
guard let portRawValue = UInt16(String(cString: portBuffer)),
|
||||
let port = NWEndpoint.Port(rawValue: portRawValue) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(host), port: port)
|
||||
let description: String
|
||||
if addressFamily == AF_INET6 {
|
||||
description = "[\(host)]:\(portRawValue)"
|
||||
} else {
|
||||
description = "\(host):\(portRawValue)"
|
||||
}
|
||||
|
||||
return NearbyResolvedServiceEndpoint(
|
||||
endpoint: endpoint,
|
||||
description: description,
|
||||
isIPv4: addressFamily == AF_INET
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static func normalizedServiceDomain(_ domain: String) -> String {
|
||||
let trimmedDomain = domain.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmedDomain.isEmpty else { return "local." }
|
||||
return trimmedDomain.hasSuffix(".") ? trimmedDomain : "\(trimmedDomain)."
|
||||
}
|
||||
|
||||
|
||||
private static func normalizedServiceType(_ type: String) -> String {
|
||||
let trimmedType = type.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return trimmedType.hasSuffix(".") ? trimmedType : "\(trimmedType)."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct NearbyResolvedServiceEndpoint {
|
||||
let endpoint: NWEndpoint
|
||||
let description: String
|
||||
let isIPv4: Bool
|
||||
}
|
||||
@@ -31,6 +31,12 @@
|
||||
EB05DFBE2E59FC8900EF567A /* TrustedDevicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB05DFBD2E59FC8000EF567A /* TrustedDevicesView.swift */; };
|
||||
EB05DFBF2E59FD0A00EF567A /* TrustedDevicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB05DFBD2E59FC8000EF567A /* TrustedDevicesView.swift */; };
|
||||
EB05DFC02E5A2EAA00EF567A /* TrustedDevicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB05DFBD2E59FC8000EF567A /* TrustedDevicesView.swift */; };
|
||||
EB0A22E32FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB0A22E22FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift */; };
|
||||
EB0A22E42FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB0A22E22FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift */; };
|
||||
EB0A22E52FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB0A22E22FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift */; };
|
||||
EB0A22E62FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB0A22E22FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift */; };
|
||||
EB0A22E72FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB0A22E22FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift */; };
|
||||
EB0A22E82FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB0A22E22FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift */; };
|
||||
EB1325D32E701D8E00FD749C /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1325D22E701D8900FD749C /* Settings.swift */; };
|
||||
EB1325D42E701D8E00FD749C /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1325D22E701D8900FD749C /* Settings.swift */; };
|
||||
EB1325D52E701D8E00FD749C /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1325D22E701D8900FD749C /* Settings.swift */; };
|
||||
@@ -518,6 +524,7 @@
|
||||
A1F5B3D82F3A1A0100A1B2C3 /* PairingToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairingToken.swift; sourceTree = "<group>"; };
|
||||
A1F5B3D92F3A1A0100A1B2C4 /* PairingQrSheetManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairingQrSheetManager.swift; sourceTree = "<group>"; };
|
||||
EB05DFBD2E59FC8000EF567A /* TrustedDevicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrustedDevicesView.swift; sourceTree = "<group>"; };
|
||||
EB0A22E22FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyOutgoingServiceResolver.swift; sourceTree = "<group>"; };
|
||||
EB1325D22E701D8900FD749C /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
|
||||
EB1325E72E7025D900FD749C /* ActiveNetworkInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveNetworkInterface.swift; sourceTree = "<group>"; };
|
||||
EB1325E82E7025D900FD749C /* ApIsolation6Tester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApIsolation6Tester.swift; sourceTree = "<group>"; };
|
||||
@@ -801,6 +808,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
69DA9A1E29E0C0B300A442DA /* NearbyConnectionManager.swift */,
|
||||
EB0A22E22FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift */,
|
||||
EB3B9F192EF7150400D67050 /* NearbyDeviceNameResolver.swift */,
|
||||
69DA9A2229E17F0400A442DA /* InboundNearbyConnection.swift */,
|
||||
A1B2C3DA2F33AA0100ABC001 /* MirroredNotificationSupport.swift */,
|
||||
@@ -1312,6 +1320,7 @@
|
||||
EB5C37C52E377A780052EFD7 /* securemessage.pb.swift in Sources */,
|
||||
EB5C37C62E377A780052EFD7 /* securegcm.pb.swift in Sources */,
|
||||
EB5C37C72E377A780052EFD7 /* wire_format.pb.swift in Sources */,
|
||||
EB0A22E42FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */,
|
||||
EB5C37C82E377A780052EFD7 /* ukey.pb.swift in Sources */,
|
||||
EB5C37C92E377A780052EFD7 /* device_to_device_messages.pb.swift in Sources */,
|
||||
EB950B742E520957002D68FA /* Attachments.swift in Sources */,
|
||||
@@ -1390,6 +1399,7 @@
|
||||
EBC05C822E33B58300EECC18 /* ApIsolationIssueView.swift in Sources */,
|
||||
EBC05C832E33B58300EECC18 /* FirewallIssueView.swift in Sources */,
|
||||
EB1325FA2E70288300FD749C /* SendToExtension.swift in Sources */,
|
||||
EB0A22E72FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */,
|
||||
EB1325F42E7025D900FD749C /* ApIsolationTester.swift in Sources */,
|
||||
EB1325F52E7025D900FD749C /* ActiveNetworkInterface.swift in Sources */,
|
||||
EB1325F62E7025D900FD749C /* ApIsolation6Tester.swift in Sources */,
|
||||
@@ -1450,6 +1460,7 @@
|
||||
EB3B9F272EF7150400D6704E /* ApIsolationIssueView.swift in Sources */,
|
||||
EB3B9F282EF7150400D6704E /* FirewallIssueView.swift in Sources */,
|
||||
EB3B9F292EF7150400D6704E /* SendToExtension.swift in Sources */,
|
||||
EB0A22E82FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */,
|
||||
EB3B9F2A2EF7150400D6704E /* ApIsolationTester.swift in Sources */,
|
||||
EB3B9F2B2EF7150400D6704E /* ActiveNetworkInterface.swift in Sources */,
|
||||
EB3B9F2C2EF7150400D6704E /* ApIsolation6Tester.swift in Sources */,
|
||||
@@ -1480,6 +1491,7 @@
|
||||
EB3B9F592EF7151900D6704E /* securemessage.pb.swift in Sources */,
|
||||
EB3B9F5A2EF7151900D6704E /* securegcm.pb.swift in Sources */,
|
||||
EB3B9F5B2EF7151900D6704E /* wire_format.pb.swift in Sources */,
|
||||
EB0A22E52FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */,
|
||||
EB3B9F5C2EF7151900D6704E /* ukey.pb.swift in Sources */,
|
||||
EB3B9F5D2EF7151900D6704E /* device_to_device_messages.pb.swift in Sources */,
|
||||
EB3B9F5F2EF7151900D6704E /* Attachments.swift in Sources */,
|
||||
@@ -1528,6 +1540,7 @@
|
||||
EB5C37F92E377CE70052EFD7 /* securegcm.pb.swift in Sources */,
|
||||
EB5C37FA2E377CE70052EFD7 /* Helpers.swift in Sources */,
|
||||
EB5C37FB2E377CE70052EFD7 /* InboundNearbyConnection.swift in Sources */,
|
||||
EB0A22E62FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */,
|
||||
A1B2C3D82F33AA0100ABC001 /* MirroredNotificationSupport.swift in Sources */,
|
||||
EB9925802E45D53E00357055 /* sharing_enums.pb.swift in Sources */,
|
||||
EB5C37FC2E377CE70052EFD7 /* OutboundNearbyConnection.swift in Sources */,
|
||||
@@ -1572,6 +1585,7 @@
|
||||
EB16B6132E58B4B9007D9792 /* IdentityManager.swift in Sources */,
|
||||
EB950B4D2E520436002D68FA /* NearbyConnection.swift in Sources */,
|
||||
EB950B502E520436002D68FA /* wire_format.pb.swift in Sources */,
|
||||
EB0A22E32FBCF59300B78FF5 /* NearbyOutgoingServiceResolver.swift in Sources */,
|
||||
EB950B582E5205B4002D68FA /* Zip.swift in Sources */,
|
||||
EB950B512E520436002D68FA /* securemessage.pb.swift in Sources */,
|
||||
EBA1BB1F2EF1F6030068E46E /* ChangeDeviceNameModifier.swift in Sources */,
|
||||
|
||||
Reference in new issue
Block a user