Generic messages in message-based architecture - swift

I am experimenting with message-based architecture in Swift. I am trying to do something similar to the Elm Architecture, for example. This is how my code looks:
enum SideEffect<Message> {
case sendRequest((String) -> Message)
}
protocol Component {
associatedtype Message
mutating func send(msg: Message) -> [SideEffect<Message>]
}
struct State: Component {
var something: String?
enum Message {
case downloadSomething
case receiveResponse(String)
}
mutating func send(msg: Message) -> [SideEffect<Message>] {
switch msg {
case .downloadSomething:
return [.sendRequest(Message.receiveResponse)]
case .receiveResponse(let response):
something = response
return []
}
}
}
So the state is modelled by State and you can change it by sending Messages. If there are any side effects to compute, they are returned as a SideEffect message and will be taken care of by someone else. Each SideEffect message takes a “callback” argument, a Message to send when the side effect is finished. This works great.
Now, what if I want to have a generic side effect message? I would like to have something like this:
struct Request<ReturnType> { … }
And have a related side effect to load the request and return a value of type ReturnType:
enum SideEffect<Message> {
case sendRequest(Request<T>, (T) -> Message)
}
But this (obviously) doesn’t compile, as the case would have to be generic over T. I can’t make the whole SideEffect generic over T, since there’s other side effects that have nothing to do with T.
Can I somehow create a SideEffect message with a Request<T> that would later dispatch a Message with T? (I think I want something like this feature discussed on swift-evolution.)

You'll want to type erase T – usually this can be done with closures, as they can reference context from the site at which they're created, without exposing that context to the outside world.
For example, with a mock Request<T> (assuming it's an async operation):
struct Request<T> {
var mock: T
func doRequest(_ completion: #escaping (T) -> Void) {
// ...
completion(mock)
}
}
We can build a RequestSideEffect<Message> that holds a closure that takes a given (Message) -> Void callback, and then performs a request on a captured Request<T> instance, forwarding the result through a (T) -> Message, the result of which can then be passed back to the callback (thus keeping the type variable T 'contained' in the closure):
struct RequestSideEffect<Message> {
private let _doRequest: (#escaping (Message) -> Void) -> Void
init<T>(request: Request<T>, nextMessage: #escaping (T) -> Message) {
self._doRequest = { callback in
request.doRequest {
callback(nextMessage($0))
}
}
}
func doRequest(_ completion: #escaping (Message) -> Void) {
_doRequest(completion)
}
}
Now your SideEffect<Message> can look like this:
enum SideEffect<Message> {
case sendRequest(RequestSideEffect<Message>)
}
And you can implement State like this:
protocol Component {
associatedtype Message
mutating func send(msg: Message) -> [SideEffect<Message>]
}
struct State: Component {
var something: String
enum Message {
case downloadSomething
case receiveResponse(String)
}
mutating func send(msg: Message) -> [SideEffect<Message>] {
switch msg {
case .downloadSomething:
let sideEffect = RequestSideEffect(
request: Request(mock: "foo"), nextMessage: Message.receiveResponse
)
return [.sendRequest(sideEffect)]
case .receiveResponse(let response):
something = response
return []
}
}
}
var s = State(something: "hello")
let sideEffects = s.send(msg: .downloadSomething)
for case .sendRequest(let sideEffect) in sideEffects {
sideEffect.doRequest {
_ = s.send(msg: $0) // no side effects expected
print(s) // State(something: "foo")
}
}

Related

Making custom Deffered Future Publisher in Swift Combine?

Like the title says I would like to make custom publisher that will basically function like deffered future. Normally when I want to encapsulate code in some Future, but want it to execute on subscription, I would need to write something like this:
Deffered {
Future { promise in
}
}
Now I was thinking of making custom publisher, something along the lines DefferedFuture that will have exact same functionality as Future, but will execute promise only on subscription?
The most obvious answer is this:
func single<Output, Failure>(_ promise: #escaping (#escaping (Result<Output, Failure>) -> Void) -> Void) -> Deferred<Future<Output, Failure>> where Failure: Error {
Deferred {
Future<Output, Failure>(promise)
}
}
If it absolutely must be a type rather than a function then:
extension Publishers {
struct Single<Output, Failure>: Publisher where Failure: Error {
let promise: (#escaping (Result<Output, Failure>) -> Void) -> Void
func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input {
Deferred { Future(promise) }
.subscribe(subscriber)
}
}
}

Polymorphism with a final class that implements an associatedtype protocol in swift

I'm using Apollo v0.49.0. It's a library for calling graphQL endpoints, and the way it does this is by generating code before you compile your code.
Before I talk about the generated code, I'd like to talk about what the generated code implements. For this question, it's the GraphQLMutation that's relevant. Here's what it looks like:
public enum GraphQLOperationType {
case query
case mutation
case subscription
}
public protocol GraphQLOperation: AnyObject {
var operationType: GraphQLOperationType { get }
var operationDefinition: String { get }
var operationIdentifier: String? { get }
var operationName: String { get }
var queryDocument: String { get }
var variables: GraphQLMap? { get }
associatedtype Data: GraphQLSelectionSet
}
public extension GraphQLOperation {
var queryDocument: String {
return operationDefinition
}
var operationIdentifier: String? {
return nil
}
var variables: GraphQLMap? {
return nil
}
}
public protocol GraphQLQuery: GraphQLOperation {}
public extension GraphQLQuery {
var operationType: GraphQLOperationType { return .query }
}
public protocol GraphQLMutation: GraphQLOperation {}
public extension GraphQLMutation {
var operationType: GraphQLOperationType { return .mutation }
}
This is 80% of the file; the last 20% is irrelevant IMHO. Note how GraphQLMutation implements GraphQLOperation and the latter has an associatedtype.
The library generates classes based on your graphql server endpoints. Here's what they look like:
public final class ConcreteMutation: GraphQLMutation {
...
public struct Data: GraphQLSelectionSet {
...
}
...
}
As far as I know (I'm new to Swift), I have no control over any of the code I've mentioned so far (other than forking the repo and modifying it). I could change them locally, but they would just be overridden every time they were regenerated.
To use any of these generated classes, I have to pass them into this ApolloClient function (also a library class):
#discardableResult
public func perform<Mutation: GraphQLMutation>(mutation: Mutation,
publishResultToStore: Bool = true,
queue: DispatchQueue = .main,
resultHandler: GraphQLResultHandler<Mutation.Data>? = nil) -> Cancellable {
return self.networkTransport.send(
operation: mutation,
cachePolicy: publishResultToStore ? .default : .fetchIgnoringCacheCompletely,
contextIdentifier: nil,
callbackQueue: queue,
completionHandler: { result in
resultHandler?(result)
}
)
}
I can't figure out how to deal with ConcreteMutation in a generic way. I want to be able to write a factory function like so:
extension SomeEnum {
func getMutation<T: GraphQLMutation>() -> T {
switch self {
case .a:
return ConcreteMutation1(first_name: value) as T
case .b:
return ConcreteMutation2(last_name: value) as T
case .c:
return ConcreteMutation3(bio: value) as T
...
}
}
}
The fact that this func is in an enum is irrelevant to me: that same code could be in a struct/class/whatever. What matters is the function signature. I want a factory method that returns a GraphQLMutation that can be passed into ApolloClient.perform()
Because I can't figure out a way to do either of those things, I end up writing a bunch of functions like this instead:
func useConcreteMutation1(value) -> Void {
let mutation = ConcreteMutation1(first_name: value)
apolloClient.perform(mutation: mutation)
}
func useConcreteMutation2(value) -> Void {
let mutation = ConcreteMutation2(last_name: value)
apolloClient.perform(mutation: mutation)
}
...
That's a lot of duplicated code.
Depending on how I fiddle with my getMutation signature -- e.g., <T: GraphQLMutation>() -> T? etc. -- I can get the func to compile, but I get a different compile error when I try to pass it into ApolloClient.perform(). Something saying "protocol can only be used as a generic constraint because it has Self or associated type requirements."
I've researched this a lot, and my research found this article, but I don't think it's an option if the concrete classes implementing the associated type are final?
It's really difficult to figure out if it's possible to use polymorphism in this situation. I can find plenty of articles of what you can do, but no articles on what you can't do. My question is:
How do I write getMutation so it returns a value that can be passed into ApolloClient.perform()?
The fundamental problem you are running into is that this function signature:
func getMutation<T: GraphQLMutation>() -> T
is ambiguous. The reason it's ambiguous is because GraphQLMutation has an associated type (Data) and that information doesn't appear anywhere in your function declaration.
When you do this:
extension SomeEnum {
func getMutation<T: GraphQLMutation>() -> T {
switch self {
case .a:
return ConcreteMutation1(first_name: value) as T
case .b:
return ConcreteMutation2(last_name: value) as T
case .c:
return ConcreteMutation3(bio: value) as T
...
}
}
}
Each of those branches could have a different type. ConcreteMutation1 could have a Data that is Dormouse while ConcreteMutation3 might have a data value that's an IceCreamTruck. You may be able to tell the compiler to ignore that but then you run into problems later because Dormouse and IceCreamTruck are two structs with VERY different sizes and the compiler might need to use different strategies to pass them as parameters.
Apollo.perform is also a template. The compiler is going to write a different function based on that template for each type of mutation you call it with. In order to do that must know the full type signature of the mutation including what its Data associated type is. Should the responseHandler callback be able to handle something the size of a Dormouse, or does it need to be able to handle something the size of an IceCreamTruck?
If the compiler doesn't know, it can't set up the proper calling sequence for the responseHandler. Bad things would happen if you tried to squeeze something the size of an IceCreamTruck through a callback calling sequence that was designed for a parameter the size of a Dormouse!
If the compiler doesn't know what type of Data the mutation has to offer, it can't write a correct version of perform from the template.
If you've only handed it the result of func getMutation<T: GraphQLMutation>() -> T where you've eliminated evidence of what the Data type is, it doesn't know what version of perform it should write.
You are trying to hide the type of Data, but you also want the compiler to create a perform function where the type of Data is known. You can't do both.
Maybe you need to implement AnyGraphQLMutation type erased over the associatedtype.
There are a bunch of resources online for that matter (type erasure), I've found this one pretty exhaustive.
I hope this helps in someway:
class GraphQLQueryHelper
{
static let shared = GraphQLQueryHelper()
class func performGraphQLQuery<T:GraphQLQuery>(query: T, completion:#escaping(GraphQLSelectionSet) -> ())
{
Network.shared.apollo().fetch(query: query, cachePolicy: .default) { (result) in
switch result
{
case .success(let res):
if let data = res.data
{
completion(data)
}
else if let error = res.errors?.first
{
if let dict = error["extensions"] as? NSDictionary
{
switch dict.value(forKey: "code") as? String ?? "" {
case "invalid-jwt": /*Handle Refresh Token Expired*/
default: /*Handle error*/
break
}
}
else
{
/*Handle error*/
}
}
else
{
/*Handle Network error*/
}
break
case .failure(let error):
/*Handle Network error*/
break
}
}
}
class func peroformGraphQLMutation<T:GraphQLMutation>(mutation: T, completion:#escaping(GraphQLSelectionSet) -> ())
{
Network.shared.apollo().perform(mutation: mutation) { (result) in
switch result
{
case .success(let res):
if let data = res.data
{
completion(data)
}
else if let error = res.errors?.first
{
if let dict = error["extensions"] as? NSDictionary
{
switch dict.value(forKey: "code") as? String ?? "" {
case "invalid-jwt": /*Handle Refresh Token Expired*/
default: /*Handle error*/
break
}
}
else
{
/*Handle error*/
}
}
else
{
/*Handle Network error*/
}
break
case .failure(let error):
/*Handle error*/
break
}
}
}
}

Is there a way of using a completion handler passed through as an argument to detect when a long request is completed

I'm having trouble understanding how to use a closure to handle completed events when passing in a function as a parameter.
Here's a very contrived example:
class MessageService {
func sendMessage(s: String) {
print(s)
}
var messenger: Messenger {
createMessenger(completion: sendMessage(s:))
}
}
func createMessenger(completion: #escaping (String) -> Void) -> Messenger {
return Messenger { completion("This is a hardcoded message.") }
}
struct Messenger {
let sendMessage: () -> Void
init(sendMessage: #escaping () -> Void) {
self.sendMessage = sendMessage
}
}
let service = MessageService()
let messenger = service.messenger
messenger.sendMessage()
I want to find out when sendMessage is finished (if for example it was performing something like a network request), so is there a way of having a completion handler for sendMessage so that I could write something along the lines of:
messenger.sendMessage {
print("I finished sending a message!")
}
I've tried adding a completion handler like this in the service class:
func sendMessage(s: String, completion: #escaping () -> Void) {
MessageRequest(with: s) {
completion()
}
}
But things started getting very confusing when I'm trying to use the createMessenger method, because the above function has some crazy type of (String, () -> ()) -> () which I don't know how to handle. Any help would be greatly appreciated, thanks.
So, it sounds like what you want is an arbitrary Messenger type, whose creator tell it what action to do, and once the action is done, it invokes its caller's completion handler.
It helps if you use typealias with descriptive names to keep track of all the closures. And if you don't mind, I'll name it more generically as Agent:
struct Agent {
typealias Completion = () -> Void
typealias Action = (Completion) -> Void
private let action: Action
static func create(action: #escaping Action) -> Agent {
Agent(action: action)
}
func execute(_ completion: #escaping Completion) {
action(completion)
}
}
So, Agent can be created with an arbitrary action that accepts a completion handler to signal when it's done:
let agent = Agent.create { completion in
print("started executing action")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { completion() }
}
agent.execute { print("done") }
Now, you can adapt it to your MessengerService class:
class MessageService {
func sendMessage(s: String) {
print(s)
}
var messenger: Agent {
Agent.create { completion in
sendMessage("This is a hardcoded message.")
completion()
}
}
}

Swift extension type constraints for generic class where generic type is yet another generic type

I'm trying to wrap my head around generic type constraints in Swift. Here is my starting point:
class Promise<T> {
func resolve(_ block:#escaping (T) ->Void) {}
func fulfill(_ result:T) {}
}
A Promise is just that, something that can be fulfilled in the future. This becomes very useful when it is used with Swift's Result type when handing results back from a background queue to the main queue:
let promise = Promise<Result<String, Error>>()
promise.fulfill(.success("Hello"))
promise.fulfill(.failure(NSError()))
Now I would like to add an extension to all Promise instances that use Result to add these helper methods:
extension Promise where T == Result<X, Error> {
⬆︎ Here's the problem ⚡️
func failure(_ error:Error) {
fulfill(.failure(error))
}
func success(_ result:X) {
fulfill(.success(result))
}
}
// Shorter:
let promise = Promise<Result<String, Error>>()
promise.success("Hello")
promise.failure(NSError())
Only problem is that the above code does not compile, because X is not defined. What I want to express is this:
Extend Promise when its generic type T is of type Result<X,Z> where X can be anything and Z must be of type Error → Result<*, Error>. Is this possible?
What you want is possible, it's just that the syntax is slightly verbose. You can't put the where constraint on the extension. You have to put it on each method.
extension Promise {
func failure<U>(_ error: Error) where T == Result<U, Error> {
fulfill(.failure(error))
}
func success<U>(_ result: U) where T == Result<U, Error> {
fulfill(.success(result))
}
}
You can also do it with a protocol, but for enums I find this very klunky, because enum cases can't be treated as conforming methods.
protocol ResultType {
associatedtype Success
associatedtype Failure: Error
static func makeFailure(_: Failure) -> Self
static func makeSuccess(_: Success) -> Self
}
extension Result: ResultType {
static func makeFailure(_ failure: Failure) -> Result<Success, Failure> { .failure(failure) }
static func makeSuccess(_ success: Success) -> Result<Success, Failure> { .success(success) }
}
extension Promise where T: ResultType {
func failure(_ error: T.Failure) {
fulfill(T.makeFailure(error))
}
func success(_ result: T.Success) {
fulfill(T.makeSuccess(result))
}
}

Swift polymorphic closure dispatch with struct/protocols

I have a case where I want to register either one argument or no argument closures with a service. There's always an argument available, but for brevity, I want to be able to register no arg closures as well, and then just dispatch the closure without the available argument in that case. Coming from a strong OO and dynamic types background where we love polymorphic dispatch and class inheritance trees and let the types figure themselves out, I can throw the following together:
class AbstractAction<T> {
func publish(value:T) {
fatalError("you should override this")
}
}
class NullaryAction<T>: AbstractAction<T> {
var closure:() -> ()
override func publish(_:T) {
closure()
}
init(closure:()->()) {
self.closure = closure
}
}
class UnaryAction<T>: AbstractAction<T> {
var closure:(T) -> ()
override func publish(value:T) {
closure(value)
}
init(closure:(T)->()) {
self.closure = closure
}
}
var action:AbstractAction = UnaryAction<Int>(closure: { print("\($0)") })
action.publish(42)
action = NullaryAction<Int>(closure: { print("something happened") } )
action.publish(42)
So I see 42 followed by something happened in my console. Great.
But I'd like to explore doing this with struct and/or enum. Value semantics are all the rage. The enum approach was relatively straightforward, I think:
enum Action<T> {
case Nullary( ()->() )
case Unary( (T)->() )
func publish(value:T) {
switch self {
case .Nullary(let closure):
closure()
case .Unary(let closure):
closure(value)
}
}
}
var action = Action.Unary({ (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = Action<Int>.Unary( { print("shorthand too \($0)") } )
action.publish(42)
action = Action<Int>.Nullary({ print("something happened") })
action.publish(42)
To do a struct approach, I it is my understanding that I should use a protocol to capture common interface of publish(value:T). But that's where things get confusing, because protocols apparently can't be mixed with generics? I tried:
struct NullaryAction<T> {
typealias ValueType = T
var closure:() -> ()
}
struct UnaryAction<T> {
typealias ValueType = T
var closure:(T) -> ()
}
protocol Action {
typealias ValueType
func publish(value:ValueType)
}
extension NullaryAction: Action {
func publish(_:ValueType) {
self.closure()
}
}
extension UnaryAction: Action {
func publish(value:ValueType) {
self.closure(value)
}
}
var action:Action = UnaryAction(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
action = NullaryAction<Int>(closure:{ print("something happened") })
action.publish(42)
This just produces a lot of errors at the bottom. I had tried to do the extensions as generics (e.g. extension NullaryAction<T>:Action), but it told me that T was unused, even though I had placed the typealias expressions in the extensions.
Is it possible to do this with struct/protocol? I'm happy with the enum solution, but was disappointed I couldn't realize it with the struct/protocol approach.
Judging by the fact that you want to cast your structs to their protocols (by using var action: Action = UnaryAction {...}), I'm assuming you don't need the publish method to have the right signature upon evoking it.
In other words, by declaring your Action protocol with a typealias, the compiler is expecting to specialise the publish method for each instance of your structs.
This means that you have two options:
Remove the type casting:
Example:
var action /*removed the :Action type casting */ = UnaryAction<Int>(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
var anotherAction = NullaryAction<Int>(closure:{ print("something happened") }) //need to use another variable for this last one
anotherAction.publish(42)
This solution makes your publish methods also have the same signature that your structs have. If your struct is specialised to work with Ints, then you'll have .publish(value: Int).
Make the protocol non-generic
Example:
protocol Action {
func publish(value:Any)
}
struct NullaryAction<T>: Action {
let closure: () -> ()
init(closure: () -> ()) {
self.closure = closure
}
func publish(value:Any) {
self.closure()
}
}
struct UnaryAction<T>: Action {
let closure: (T) -> ()
init(closure: (T) -> ()) {
self.closure = closure
}
func publish(value:Any) {
self.closure(value as! T) //Need to type cast here
}
}
var action: Action = UnaryAction<Int>(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
action = NullaryAction<Int>(closure:{ print("something happened") })
action.publish(42)
This solution allows you to keep on type casting, but your publish methods will all have the same signature (.publish(value: Any)). You also need to account for that upon executing the closure.