ReactiveSwift SignalProducer : Argument 'failed' must precede argument 'value' - swift

I am trying to update an upgrade an old piece of code using ReactSwift because I want ti run it with XCode 9. I have defined a class Network
final class Network: NSObject {
static func request(_ URLRequest: URLRequestConvertible) -> SignalProducer<Any, NetworkError> {
return SignalProducer { sink, disposable in
Alamofire.request(URLRequest)
.validate()
.responseJSON { response in
switch response.result {
case .success(let value):
sink.send(value: value)
sink.sendCompleted()
case .failure(let error):
// modification en as NSError
sink.send(error: NetworkError(error: error as NSError))
}
}
}
}
}
that is called in function using generic type:
private func fetch<T: Himotoki.Decodable>(URLRequest: URLRequestConvertible) -> SignalProducer<T, NetworkError> {
return Network.request(URLRequest)
.attemptMap { JSON in
do {
return .success(try decodeValue(JSON) as T)
} catch let error {
return .failure(.incorrectDataReturned)
}
}
}
The generic function is used to make network requests using Alamofire, like the following one.
// Get static UserData
static func fetchUserData() -> SignalProducer<UserData, NetworkError> {
return fetch(URLRequest: MoneyCupUsersBackEndRouter.getUserData)
}
So, I call :
API.fetchUserData()
.on(started: {
SVProgressHUD.show()
},
value: { cfg in
globalConfig = cfg
},
failed: { [weak self] error in
self?.view.window?.dodo.error("Impossible de récupérer la configuration globale")
},
terminated: {
SVProgressHUD.dismiss()
})
.start()
}
This scheme has worked well before and I have a large number of it across my code. But since I have upgraded to ReactiveCocoa, my code no longer compile. And I have a message stating that :
Argument 'failed' must precede argument 'value'

It used to be that for Swift functions where the arguments have default values you could reorder the arguments as desired at the call site. This feature was removed in Swift 3, which means you now must specify arguments in the same order as they are defined. So for the call to the on function, you must move the value: parameter to the end.

Related

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
}
}
}
}

Swift: How to cast error to generic struct

I am being passed an Error at runtime. This error is actually a generic struct. I need to cast the error to this struct so I can get its details. How can I do this?
Code Example:
protocol MinorErrorType: Error {}
struct MajorError<T: MinorErrorType>: Error {
let minorError: T
}
enum SomeMinorError: MinorErrorType {
case error
}
func getName(_ error: Error) -> String {
"Some Error"
}
func getName<T: MinorErrorType>(_ error: MajorError<T>) -> String {
"MajorError"
}
func printName(_ error: Error) {
print(getName(error))
}
let error = MajorError<SomeMinorError>(minorError: .error)
printName(error)
// output:
// Some Error
You can see in the above code the generic getName is not called. If there is a solution where I only need to modify func printName that would be awesome.
Update: In production, I want to use this pattern for logging. I want a logger to be passed an error and be able to log MajorError's. I do not want to have to cast to MajorError<SomeMinorError> in getName as that would mean I would need to cast for all new implementations of MinorErrorType. This would make my logger need to know about too much information.
In the meantime, I used type erasure (here is a great article on the subject)
protocol MajorErrorType: Error {
func eraseToAnyMajorError() -> AnyMajorError
}
enum AnyMajorError {
case majorError(MinorErrorType)
}
protocol MinorErrorType: Error {}
struct MajorError<T: MinorErrorType>: Error {
let minorError: T
func eraseToAnyMajorError() -> AnyMajorError {
.majorError(minorError)
}
}
...
func printName(_ error: Error) {
if let majorError = (error as? MajorErrorType)?.eraseToAnyMajorError() {
print(getName(majorError))
else {
print(getName(error))
}
}
This lets the rest of my code use the generic structs (fun) and my logger in the dark about other error types.
Not sure if it helps, but I would use proper encapsulation to help with this issue. I.e. define getName as a function of your struct, enum, etc., so you can call it on appropriate type:
protocol MinorErrorType: Error {}
extension Error {
func getName() -> String {
"Some Error in extension"
}
}
struct MajorError<T: MinorErrorType>: Error {
let minorError: T
func getName() -> String {
"MajorError in struct"
}
}
enum SomeMinorError: MinorErrorType {
case error
}
Now inside printError you can control what to call:
func printName(_ error: Error) {
if let majorError = error as? MajorError<SomeMinorError> {
print(majorError.getName())
} else {
print(error.getName())
}
}
So if you call
let error = MajorError<SomeMinorError>(minorError: .error)
printName(error) // prints MajorError in struct
But you don't need printName function with it's casting at all: you can call getName(), and it will call getName from Error extension only if nothing else is defined:
let error = MajorError<SomeMinorError>(minorError: .error)
let smthElse = NSError(domain: "s", code: 123, userInfo: nil)
print(error.getName()) // prints MajorError in struct as before
print(smthElse.getName()) // prints Some Error in extension
You just need to switch your error. No need to create a another method:
func getName(_ error: Error) -> String {
switch error {
case is MajorError<SomeMinorError>: return "MajorError"
default: return "Some Error"
}
}
To access your error properties you can cast the error instead of checking its type:
func getName(_ error: Error) -> String {
switch error {
case let error as MajorError<SomeMinorError>:
return "\(error.minorError)"
default: return "Some Error"
}
}
Playground testing:
protocol MinorErrorType: Error {}
struct MajorError<T: MinorErrorType>: Error {
let minorError: T
}
enum SomeMinorError: MinorErrorType {
case minor
}
func getName(_ error: Error) -> String {
switch error {
case let error as MajorError<SomeMinorError>:
return "\(error.minorError)"
default: return "Some Error"
}
}
let error = MajorError<SomeMinorError>(minorError: .minor)
print(getName(error)) // "minor\n"
You can also give your error a RawValue:
enum SomeMinorError: String, MinorErrorType {
case minor
}
func getName(_ error: Error) -> String {
switch error {
case let error as MajorError<SomeMinorError>:
return error.minorError.rawValue
default: return "Some Error"
}
}

PromiseKit wrapping external closure in Promises

I am using an external library in Swift so I cannot control the return statements. My understanding is that I should wrap these returns in promises in order to use PromiseKit. Is this correct?
Assuming so, I have working code as follows:
private func getChannelImage(for channel: TCHChannel, completion: #escaping (UIImage?, CAProfileError?) -> Void) {
if let members = channel.members {
members.members(completion: { (result, paginator) in
if result.isSuccessful() {
// ... do something
}
else {
completion(nil, CAProfileError.UnknownError)
}
})
}
}
This can be difficult to read. I am trying to simplify this using PromiseKit. First, I want to simplify members.members(completion: { (result, paginator) in to a promise that I can call with the firstly { ... } syntax. I thus try and do as follows:
private func asPromise(members: TCHMembers) -> Promise<TCHMemberPaginator> {
return Promise<TCHMemberPaginator> { fulfill, reject in
members.members(completion: { (result, paginator) in
if result.isSuccesful() {
fulfill(paginator)
} else {
reject()
}
})
}
}
But this approach does not work and I get "Unable to infer closure type in the current context". I'm trying to find a good example of this use case done online but am having trouble. Any thoughts on how to properly return promises?
Assuming the TCHMemberPaginator and TCHMembers as below,
class TCHMemberPaginator {}
class TCHMembers {
func members(completion: (Bool, TCHMemberPaginator?) -> Void) {}
}
Here is the method to return a Promise,
private func asPromise(members: TCHMembers) -> Promise<TCHMemberPaginator> {
return Promise { seal in
members.members(completion: { (result, paginator) in
if result == true, let p = paginator {
seal.fulfill(p)
} else {
seal.reject(NSError())
}
})
}
}

Proper way to propagate exceptions in defer block

In the following example, the function usingTemporaryDirectory() creates and deletes a temporary directory, calling a passed function body() in between. If an exception is thrown by createTemporaryDirectory() or the passed function body(), it is propagated to the caller. But the exception thrown by removeDirectory() cannot be passed to the caller because no exception may escape a defer block.
import Foundation
func createTemporaryDirectory() throws -> URL { ... }
func removeDirectory(_ url: URL) throws { ... }
func usingTemporaryDirectory(body: (URL) throws -> ()) throws {
let tempDir = try createTemporaryDirectory()
defer {
// Errors thrown from here are not handled.
try removeDirectory(tempDir)
}
try body(tempDir)
}
What is the proper way to handle such an exception? I see two options:
Just catch the exception and log a message.
Catch the exception, log a message and abort the process.
I don't want to use option 1 because that could lead in this example to an arbitrary number of temporary directories piling up. And I also don't want to use option 2 because that would prevent outer stack frames from completing their cleanup work (e.g. if multiple temporary directories have been created, removal for all of them should at least be attempted).
Java has a feature called suppressed exceptions. In this case the exception thrown in the defer block could be added as a suppressed exception to the exception thrown by body(), if any. Does Swift have a comparable feature?
In this case the exception thrown in the defer block could be added as a suppressed exception to the exception thrown by body(), if any. Does Swift have a comparable feature?
Not that I know of – you could however build something similar yourself. First, let's define an Error type that can store multiple underlying errors:
struct ErrorCollection : Error {
private var errors: [Error] = []
init() {}
init<S : Sequence>(_ sequence: S) where S.Element == Error {
for error in sequence {
append(error)
}
}
mutating func append(_ error: Error) {
switch error {
case let x as ErrorCollection: // ensure we flatten out any nested error collections.
errors.append(contentsOf: x.errors)
case let x:
errors.append(x)
}
}
}
extension ErrorCollection : RandomAccessCollection {
typealias Index = Int
typealias Element = Error
var startIndex: Index { return errors.startIndex }
var endIndex: Index { return errors.endIndex }
func index(_ i: Index, offsetBy n: Index) -> Index {
return errors.index(i, offsetBy: n)
}
subscript(index: Index) -> Element { return errors[index] }
}
Then we can define a Result<T> type which we can use in order to evaluate a closure, storing the return value if successful, otherwise the error it threw. We can then add a then(_:) method to allow us to catch any additional errors, potentially causing the success value to be invalidated:
enum Result<T> {
case success(T)
case failure(Error)
init(_ body: () throws -> T) {
do {
self = .success(try body())
} catch {
self = .failure(error)
}
}
func then(_ body: () throws -> Void) -> Result {
do {
try body()
return self
} catch let nextError {
switch self {
case .success: // invalidate the success value and store the error.
return .failure(nextError)
case .failure(let error): // concatenate the errors.
return .failure(ErrorCollection([error, nextError]))
}
}
}
func materialize() throws -> T {
switch self {
case .success(let value):
return value
case .failure(let error):
throw error
}
}
}
You can then use this like so:
func usingTemporaryDirectory<R>(body: (URL) throws -> R) throws -> R {
let tempDir = try createTemporaryDirectory()
return try Result { try body(tempDir) }
.then { try removeDirectory(tempDir) }
.materialize()
}

Trailing Closures on generics?

Hi I am trying to understand the following code from Alamofire. How can you initialise a struct with "{}" I know that you can call a closure with Trailing Closures. I know I am totally missing something, but what?
extension Request {
public func responseObject<T: ResponseObjectSerializable>(completionHandler: Response<T, NSError> -> Void) -> Self {
let responseSerializer = ResponseSerializer<T, NSError> { // What is this?
request, response, data, error in
guard error == nil else { return .Failure(error!) }
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, data, error)
switch result {
case .Success(let value):
if let
response = response,
responseObject = T(response: response, representation: value)
{
return .Success(responseObject)
} else {
let failureReason = "JSON could not be serialized into response object: \(value)"
let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
return .Failure(error)
}
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
The struct ResponseSerializer from Alamofire
public struct ResponseSerializer<Value, Error: ErrorType>: ResponseSerializerType {
/// The type of serialized object to be created by this `ResponseSerializer`.
public typealias SerializedObject = Value
/// The type of error to be created by this `ResponseSerializer` if serialization fails.
public typealias ErrorObject = Error
/**
A closure used by response handlers that takes a request, response, data and error and returns a result.
*/
public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>
/**
Initializes the `ResponseSerializer` instance with the given serialize response closure.
- parameter serializeResponse: The closure used to serialize the response.
- returns: The new generic response serializer instance.
*/
public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>) {
self.serializeResponse = serializeResponse
}
}
Your question can be greatly pared down (and should have been). Here is the relevant declaration of ResponseSerializer:
public struct ResponseSerializer<Value, Error: ErrorType>: ResponseSerializerType {
public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>) {
self.serializeResponse = serializeResponse
}
}
So this initializer, init(serializeResponse:), takes one parameter — a function taking four parameters and returning one parameter (of the specified types).
Thus, we can initialize like this:
func f (request:NSURLRequest?, response:NSHTTPURLResponse?, data:NSData?, error:NSError?) -> Result<Value, Error>) {
guard error == nil else { return .Failure(error!)
}
let responseSerializer = ResponseSerializer<T, NSError>(serializeResponse:f)
However, this can be condensed. We don't really need the function f for anything else, so we can supply it as an anonymous function; it doesn't need a name or a full declaration. Moreover, there is a "shortcut" rule for anonymous functions, that if an anonymous function is the last parameter to a function, it can be provided literally after the function's closing parentheses, with the parameter name omitted. And if the function takes no other parameters, its parentheses can be omitted altogether.
Well, this init is exactly such a function — it takes a function as its last (and only) parameter — so that is exactly what the code in question does:
let responseSerializer = ResponseSerializer<T, NSError> {
request, response, data, error in
guard error == nil else { return .Failure(error!)
}
If I read it all correctly the code above has a pattern similar to this:
// just a something
struct Blah {
var stuffs : (message:String) -> Void
init(closure:(message:String) -> Void) {
self.stuffs = closure
}
}
// an extension because the code above is also in an extension, but not needed at all
extension Blah {
// a function with a closure that also returns an instance of Self
func spawnChild(closure:(message:String) -> Void) -> Blah {
return Blah(closure: closure) // closure is passed to spawn
}
}
Tests :
let alpha = Blah { (message) -> Void in
print("alpha",message)
}
let beta = alpha.spawnChild { (message) -> Void in
print("beta", message)
}
alpha.stuffs(message: "parrent") // alpha parent
beta.stuffs(message: "spawn") // beta spawn
Remember that the trailing closure is just syntactic sugar for an input parameter that is a closure. So anything that takes input parameters can have a trailing closure.