Skip to content

Commit

Permalink
renaming and code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
ypopovych committed Nov 22, 2023
1 parent b12d689 commit 00529aa
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 144 deletions.
211 changes: 107 additions & 104 deletions Sources/TesseractTransportsClient/iOS/IPCTransportIOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import TesseractTransportsShared
#endif

public protocol ViewControllerPresenter {
func present(vc: UIViewController) async -> Result<(), IPCTransportIOSConnection.Error>
func present(vc: UIViewController) async -> Result<(), IPCTransportIOS.Error>
}

public struct RootViewControllerPresenter: ViewControllerPresenter {
Expand All @@ -29,7 +29,7 @@ public struct RootViewControllerPresenter: ViewControllerPresenter {
public init() {}

@MainActor
public func present(vc: UIViewController) async -> Result<(), IPCTransportIOSConnection.Error> {
public func present(vc: UIViewController) async -> Result<(), IPCTransportIOS.Error> {
return await withUnsafeContinuation { cont in
guard let rootView = self.rootViewController else {
cont.resume(
Expand All @@ -56,7 +56,7 @@ public class IPCTransportIOS: Transport {
public func status(proto: String) async -> Status {
guard let url = Self.url(proto: proto) else {
return .error(
IPCTransportIOSConnection.Error.wrongProtocolId(proto).tesseract
Error.wrongProtocolId(proto).tesseract
)
}
if await UIApplication.shared.canOpenURL(url) {
Expand All @@ -67,134 +67,137 @@ public class IPCTransportIOS: Transport {
}

public func connect(proto: String) -> Connection {
IPCTransportIOSConnection(proto: proto, presenter: presenter)
TransportConnection(proto: proto, presenter: presenter)
}

public static func url(proto: String) -> URL? {
URL(string: "tesseract+\(proto)://")
}
}

public class IPCTransportIOSConnection: Connection {
private var requests: Array<(UIActivityViewController,
UnsafeContinuation<Result<(), Error>, Never>)>
private var continuations: Array<UnsafeContinuation<Result<Data, Error>, Never>>

public let proto: String
public let presenter: ViewControllerPresenter

public init(proto: String, presenter: ViewControllerPresenter) {
self.requests = []
self.continuations = []
self.proto = proto
self.presenter = presenter
}

public var uti: String { "one.tesseract.\(proto)" }

@MainActor
public func send(request: Data) async -> Result<(), TesseractError> {
let vc = UIActivityViewController(
activityItems: [NSItemProvider(item: request as NSData,
typeIdentifier: uti)],
applicationActivities: nil
)
extension IPCTransportIOS {
class TransportConnection: Connection {
private var requests: Array<(UIActivityViewController,
UnsafeContinuation<Result<(), Error>, Never>)>
private var continuations: Array<UnsafeContinuation<Result<Data, Error>, Never>>

vc.excludedActivityTypes = UIActivity.ActivityType.all
public let proto: String
public let presenter: ViewControllerPresenter

vc.completionWithItemsHandler = { activityType, completed, returnedItems, error in
Task {
if let error = error {
await self.response(cancelled: !completed,
result: .failure(.nested(error as NSError)))
} else {
await self.response(cancelled: !completed,
result: .success(returnedItems ?? []))
}
}
public init(proto: String, presenter: ViewControllerPresenter) {
self.requests = []
self.continuations = []
self.proto = proto
self.presenter = presenter
}

return await show(vc: vc).castError()
}

@MainActor
public func receive() async -> Result<Data, TesseractError> {
return await withUnsafeContinuation { cont in
self.continuations.append(cont)
}.castError()
}

@MainActor
private func show(vc: UIActivityViewController) async -> Result<(), Error> {
return await withUnsafeContinuation { cont in
self.requests.append((vc, cont))
if self.requests.count == 1 {
Task { try! await self.present().get() }
public var uti: String { "one.tesseract.\(proto)" }

@MainActor
public func send(request: Data) async -> Result<(), TesseractError> {
let vc = UIActivityViewController(
activityItems: [NSItemProvider(item: request as NSData,
typeIdentifier: uti)],
applicationActivities: nil
)

vc.excludedActivityTypes = UIActivity.ActivityType.all

vc.completionWithItemsHandler = { activityType, completed, returnedItems, error in
Task {
if let error = error {
await self.response(cancelled: !completed,
result: .failure(.nested(error as NSError)))
} else {
await self.response(cancelled: !completed,
result: .success(returnedItems ?? []))
}
}
}

return await show(vc: vc).castError()
}
}

@MainActor
private func present() async -> Result<(), Error> {
guard let (vc, cont) = requests.first else {
return .failure(.wrongInternalState("nothing to present"))
@MainActor
public func receive() async -> Result<Data, TesseractError> {
return await withUnsafeContinuation { cont in
self.continuations.append(cont)
}.castError()
}
cont.resume(returning: await self.presenter.present(vc: vc))
return .success(())
}

@MainActor
private func response(cancelled: Bool, result: Result<[Any], Error>) async {
guard let receiver = continuations.first else {
print("Error: empty receivers")
return

@MainActor
private func show(vc: UIActivityViewController) async -> Result<(), Error> {
return await withUnsafeContinuation { cont in
self.requests.append((vc, cont))
if self.requests.count == 1 {
Task { try! await self.present().get() }
}
}
}
continuations.removeFirst()
guard requests.first != nil else {
receiver.resume(returning: .failure(.wrongInternalState("empty requests")))
return

@MainActor
private func present() async -> Result<(), Error> {
guard let (vc, cont) = requests.first else {
return .failure(.wrongInternalState("nothing to present"))
}
cont.resume(returning: await self.presenter.present(vc: vc))
return .success(())
}
requests.removeFirst()

switch result {
case .failure(let error):
receiver.resume(returning: .failure(error))
case .success(let items):
if cancelled {
receiver.resume(returning: .failure(.cancelled))
} else {
let attachments = items.compactMap {$0 as? NSExtensionItem}.compactMap{$0.attachments}.flatMap{$0}
guard let item = attachments.first else {
receiver.resume(
returning: .failure(.emptyResponse)
)
return
}
do {
let result = try await item.loadItem(forTypeIdentifier: uti)
if let data = result as? Data {
receiver.resume(returning: .success(data))
} else {
@MainActor
private func response(cancelled: Bool, result: Result<[Any], Error>) async {
guard let receiver = continuations.first else {
print("Error: empty receivers")
return
}
continuations.removeFirst()
guard requests.first != nil else {
receiver.resume(returning: .failure(.wrongInternalState("empty requests")))
return
}
requests.removeFirst()

switch result {
case .failure(let error):
receiver.resume(returning: .failure(error))
case .success(let items):
if cancelled {
receiver.resume(returning: .failure(.cancelled))
} else {
let attachments = items.compactMap {$0 as? NSExtensionItem}.compactMap{$0.attachments}.flatMap{$0}
guard let item = attachments.first else {
receiver.resume(
returning: .failure(
.unsupportedDataType("\(type(of: result))")
)
returning: .failure(.emptyResponse)
)
return
}
do {
let result = try await item.loadItem(forTypeIdentifier: uti)
if let data = result as? Data {
receiver.resume(returning: .success(data))
} else {
receiver.resume(
returning: .failure(
.unsupportedDataType("\(type(of: result))")
)
)
}
} catch {
receiver.resume(returning: .failure(.nested(error as NSError)))
}
} catch {
receiver.resume(returning: .failure(.nested(error as NSError)))
}
}
}

if !requests.isEmpty {
try! await self.present().get()
if !requests.isEmpty {
try! await self.present().get()
}
}
}
}


public extension IPCTransportIOSConnection {

public extension IPCTransportIOS {
enum Error: Swift.Error, TesseractErrorConvertible, CustomNSError {
case cancelled
case wrongInternalState(String)
Expand Down
2 changes: 1 addition & 1 deletion Sources/TesseractTransportsService/CoreTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public protocol CoreTransportConvertible {
func toCore() -> ServiceTransport
}

open class CoreTransport: CoreTransportConvertible {
open class CoreTransportBase: CoreTransportConvertible {
public private(set) var core: ServiceTransport!

public init(
Expand Down
79 changes: 40 additions & 39 deletions Sources/TesseractTransportsService/iOS/IPCTransportIOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public class IPCTransportIOS {
self.context = context
}

public convenience init(_ vc: UIViewController) {
self.init(context: vc.extensionContext!)
}

func rawRequest() async -> Result<(data: Data, uti: String), Error> {
let item = context.inputItems
.compactMap{$0 as? NSExtensionItem}
Expand Down Expand Up @@ -70,45 +74,6 @@ public class IPCTransportIOS {
}
}

public class BoundIPCTransportIOS: BoundTransport {
public let transport: IPCTransportIOS
public let processor: TransportProcessor

public init(transport: IPCTransportIOS, processor: TransportProcessor) {
self.transport = transport
self.processor = processor
self.process()
}

private func process() {
Task {
let result = await self.transport.rawRequest()
.castError()
.asyncFlatMap { (data, uti) in
await self.processor.process(data: data).asyncFlatMap {
await self.transport.sendResponse(data: $0, uti: uti).castError()
}
}
switch result {
case .failure(let err): self.transport.sendError(error: err as NSError)
default: break
}
}
}
}

extension IPCTransportIOS: Transport {
public func bind(processor: TransportProcessor) -> BoundTransport {
BoundIPCTransportIOS(transport: self, processor: processor)
}
}

extension IPCTransportIOS {
public convenience init(_ vc: UIViewController) {
self.init(context: vc.extensionContext!)
}
}

public extension IPCTransportIOS {
enum Error: Swift.Error, TesseractErrorConvertible, CustomNSError {
case emptyRequest
Expand Down Expand Up @@ -161,3 +126,39 @@ public extension IPCTransportIOS {
}
}
}

extension IPCTransportIOS {
class Bound: BoundTransport {
public let transport: IPCTransportIOS
public let processor: TransportProcessor

public init(transport: IPCTransportIOS, processor: TransportProcessor) {
self.transport = transport
self.processor = processor
self.process()
}

private func process() {
Task {
let result = await self.transport.rawRequest()
.castError()
.asyncFlatMap { (data, uti) in
await self.processor.process(data: data).map { ($0, uti) }
}
.asyncFlatMap { (data, uti) in
await self.transport.sendResponse(data: data, uti: uti).castError()
}
switch result {
case .failure(let err): self.transport.sendError(error: err as NSError)
default: break
}
}
}
}
}

extension IPCTransportIOS: Transport {
public func bind(processor: TransportProcessor) -> BoundTransport {
Bound(transport: self, processor: processor)
}
}

0 comments on commit 00529aa

Please sign in to comment.