I have the following function
func refreshFeedItems(completion: #escaping ActivityFeedCompletion) {
let currentTab = feedTab
//Result<([FeedItem], Bool)>) -> Void
// Load the cache in and start the spinner for the network request we're about to make
completion(.success(cache[currentTab], true))
ActivityFeedService.sharedInstance.refreshCommunityFeed(tab: currentTab) { result in
// A quick user might switch tabs before this
// call completes since we call completion twice
guard currentTab == self.feedTab else {
return
}
switch result {
case .failure(let error):
Log.warn(error)
completion(.failure(error))
case .success(let items):
self.cache[self.feedTab] = items
let tuple = Result.success(items,true) as ActivityFeedCompletion
completion((tuple,false))
}
}
}
But this line
completion(.success(cache[currentTab], true))
and this one
let tuple = Result.success(items,true) as ActivityFeedCompletion
Both throw me an "Extra argument in call" error.
This is my acticvity completion typealias
typealias ActivityFeedCompletion = (Result<([FeedItem], Bool)>) -> Void
I am not sure why I am getting that error, I think it is misleading but I ran out of ideas of what to do to fix it.
The second error is pretty clear (the bridge cast is most likely redundant)
let tuple = Result.success(items,true) // as ActivityFeedCompletion
represents already the result so you have to write
completion(tuple)
The first error is probably something similar, it's unclear what cache is
You are hiding many relevant parts of your code, so I needed to fill many parts by guess. If my answer is far from what you expect, you should better update your question and show relevant parts of your code. For example, whole definition of your ActivityFeedCache.
With this definition:
typealias ActivityFeedCompletion = (Result<([FeedItem], Bool)>) -> Void
The success case of the Result of your ActivityFeedCompletion takes a single argument of tuple type ([FeedItem], Bool).
In this line:
completion(.success(cache[currentTab], true))
You are passing two arguments, to success, so the message is clear enough. You need to pass a single argument.
completion(.success((cache[currentTab], true)))
And the latter part:
let tuple = Result.success(items,true) as ActivityFeedCompletion
completion((tuple,false))
You are completely mistaking the types. Result cannot be converted to ActivityFeedCompletion, and you cannot pass a raw tuple (tuple,false) to completion which takes Result<([FeedItem], Bool)>.
Please try something like this:
completion(.success((items, true/* or false, which you want to pass? */)))
Related
I'm fairly new to RxSwift and have been banging my head against the following problem for two days now.
I wrapped a closure that reads a partial JSON formatted string from an API:
func readResult() -> Observable<String> {
return Observable<String>.create { observable -> Disposable in
API.readValue() { (result: Result<String>) in
switch result {
case .success(let value): observable.onNext(value)
case .failure(let error): observable.onError(error)
}
observable.onCompleted()
}
return Disposables.create()
}
}
As the result from readValue only contains a chunk of the JSON formatted string, and I need to recursively call this method to get the full string. Therefore, it is important to start a new reading only when the previous one has finished.
I tried using an Observable.timer and scan to accumulate the results until I can successfully decode the json, but using a timer does not guarantee that the previous reading finished.
I also thought about using concat but as I don't know the length of the full JSON string in advance, I cannot write something like this:
Observable.concat(readResult(), readResult())
How could I ensure that the readResult function gets called until I can successfully decode the resulting JSON string?
In principle, .reduce() should be the right tool for the job.
Not sure why are you building Observable from scratch the hard way instead of using .from() factory method.
I would probably do it as follows (pseudocode):
let subject = PublishSubject<Observable<String>>.create()
let result = subject.switchLatest().reduce { /* update result */ }
subject.onNext(
Observable.from( /* service call */ ).subscribeOn( /* some serial scheduler */ )
) // repeat as needed
UPDATE
See the more specific solution in comments.
I have been following a video tutorial, and have written the following code:
func downloadWeatherDetails(completed: ()->() ) {
let currentWeatherURL = URL(string: CURRENT_WEATHER_URL)!
Alamofire
.request(currentWeatherURL)
.responseJSON(completionHandler: { response in
let result = response.result
print(result)
})
completed()
}
So basically, my understanding is as follows. The .responseJSON handler lets you call code after the request has been fired. It allows you to specify a completionHandler, which in my case, is the closure:
{ response in
let result = response.result
print(result)
}
However, what I don't understand is what the "response" keyword actually signifies. I researched the usage of closures and saw that the syntax is:
{(param) -> returnType in { code here }
Thus, is the "response" keyword a parameter? If so, how is it being declared and where is the data coming from? How is the data passed into the "response" object? Also, why is only one parameter allowed? The code did not work if I made it as follows, for example:
{ (response, test) in
let result = response.result
print(result)
}
I would really appreciate a thorough explanation on this as I've found no help elsewhere online. I've gone through Apple's "The Swift Programming Language", a multitude of different explanations, and similar questions, but still do not understand completely.
Just to clarify, I do not believe my question is a duplicate since my question revolves primarily on the captured value stored in response rather than the syntax of closures as a whole. I went through the linked question while trying to figure out my own problem, but it did not help me sufficiently.
Minor clarification needed:
Is it always the case that when a method takes a closure as one of its parameters, for example, .testMethod(testParam: (String) -> ()) and would thus in practice be used: .testMethod(testParam: { (capturedVar) in statements} (correct me if im wrong), is it always the case that the parameter of the closure ((String) in this case) will be captured and stored in capturedVar? Will there always be data passed into the variable you define? Or is this cycle specific to alamofire?
Swift closures are defined as:
{ (parameters) -> return_type in
statements
}
That is, the names in parenthesis are the variables the closure has captured, and the -> type is the optional return type (optional because the compiler can usually infer it). Alamofire's responseJSON method captures a DataResponse<Any> parameter, which you can name whatever you want, but which is usually just named response. You can then access it inside that closure.
Also, your completed() call should be inside the responseJSON call, not outside, otherwise it just gets called immediately.
When calling a function in Swift 3 that throws, you have to be exhaustive in catching all possible errors, which often means you have an unnecessary extra catch {} at the end to handle errors that won't happen.
Is it possible to say throws MyErrorType so that the compiler can know you have indeed been exhaustive when you handle all cases from that enumeration?
There's no simple way to be type-safe with thrown errors. Consider this, if the compiler allowed you to specify throws MyErrorType, then it would also have to ensure within that function body that you're not trying a function that could potentially throw a different type outside of a do/catch block. (Well there is but it would add layers of unnecessary complexity). The Swift compiler can already be slow and get stuck in loops when inferring types, inferring Thrown types all the way up a chain of throwing functions could be a nightmare.
The running idea is that for most errors you're going to handle them in a small subset of ways anyhow.
That being said, there's no need for you to add extra catch let error as MyErrorType clauses, you can simply use a switch in a catch block like so:
do {
try something()
} catch let e {
switch e {
case let m as MyErrorType: handleMyError(m)
case let o as OtherErrorType: handleOther(o)
case is ThirdErrorType: print("error \(e)")
default: handleElse(e)
}
}
My suggestion for this problem is instead of throwing an error return a Result type in your function. It would be something like this.
enum MyCustomError: Error {
case genericError
}
func operationThatFails() -> Result<Response, MyCustomError> {
guard requiredConsition() else {
return .failure(.genericError)
}
return Response()
}
Then you can handle the error like this:
let result = operationThatFails()
switch result {
case .success(let value):
// handle success
case .failure(let error):
// handle error
}
This way your error is always type safe
I'm writing some debug code to which I need to pass a parameter of type Any. For printing purposes I'd like to unwrap the parameter value iff it's an optional, but I can't figure out how to test that - every syntactic form I can think of is rejected by the compiler. E.g.,
switch val {
case as Optional<Any>:
.
.
and a variety of let forms (including trying .dynamicType) aren't legitimate. Does anyone know how to actually do this? Overall, what I'm trying to accomplish is such that whether or not the value is an optional, I get the actual value into a string and not Optional.
Martin is absolutely correct. From the linked post, modified slightly because I wanted a different return for nil:
func unwrap(any:Any, ifNil: Any = "nil") -> Any {
let mi = Mirror(reflecting: any)
if mi.displayStyle != .Optional {
return any
}
if mi.children.count == 0 { return ifNil }
let (_, some) = mi.children.first!
return some
}
Let's say I have function which returns optional. nil if error and value if success:
func foo() -> Bar? { ... }
I can use following code to work with this function:
let fooResultOpt = foo()
if let fooResult = fooResultOpt {
// continue correct operations here
} else {
// handle error
}
However there are few problems with this approach for any non-trivial code:
Error handling performed in the end and it's easy to miss something. It's much better, when error handling code follows function call.
Correct operations code is indented by one level. If we have another function to call, we have to indent one more time.
With C one usually could write something like this:
Bar *fooResult = foo();
if (fooResult == null) {
// handle error and return
}
// continue correct operations here
I found two ways to achieve similar code style with Swift, but I don't like either.
let fooResultOpt = foo()
if fooResult == nil {
// handle error and return
}
// use fooResultOpt! from here
let fooResult = fooResultOpt! // or define another variable
If I'll write "!" everywhere, it just looks bad for my taste. I could introduce another variable, but that doesn't look good either. Ideally I would like to see the following:
if !let fooResult = foo() {
// handle error and return
}
// fooResult has Bar type and can be used in the top level
Did I miss something in the specification or is there some another way to write good looking Swift code?
Your assumptions are correct—there isn't a "negated if-let" syntax in Swift.
I suspect one reason for that might be grammar integrity. Throughout Swift (and commonly in other C-inspired languages), if you have a statement that can bind local symbols (i.e. name new variables and give them values) and that can have a block body (e.g. if, while, for), those bindings are scoped to said block. Letting a block statement bind symbols to its enclosing scope instead would be inconsistent.
It's still a reasonable thing to think about, though — I'd recommend filing a bug and seeing what Apple does about it.
This is what pattern matching is all about, and is the tool meant for this job:
let x: String? = "Yes"
switch x {
case .Some(let value):
println("I have a value: \(value)")
case .None:
println("I'm empty")
}
The if-let form is just a convenience for when you don't need both legs.
If what you are writing is a set of functions performing the same sequence of transformation, such as when processing a result returned by a REST call (check for response not nil, check status, check for app/server error, parse response, etc.), what I would do is create a pipeline that at each steps transforms the input data, and at the end returns either nil or a transformed result of a certain type.
I chose the >>> custom operator, that visually indicates the data flow, but of course feel free to choose your own:
infix operator >>> { associativity left }
func >>> <T, V> (params: T?, next: T -> V?) -> V? {
if let params = params {
return next(params)
}
return nil
}
The operator is a function that receives as input a value of a certain type, and a closure that transforms the value into a value of another type. If the value is not nil, the function invokes the closure, passing the value, and returns its return value. If the value is nil, then the operator returns nil.
An example is probably needed, so let's suppose I have an array of integers, and I want to perform the following operations in sequence:
sum all elements of the array
calculate the power of 2
divide by 5 and return the integer part and the remainder
sum the above 2 numbers together
These are the 4 functions:
func sumArray(array: [Int]?) -> Int? {
if let array = array {
return array.reduce(0, combine: +)
}
return nil
}
func powerOf2(num: Int?) -> Int? {
if let num = num {
return num * num
}
return nil
}
func module5(num: Int?) -> (Int, Int)? {
if let num = num {
return (num / 5, num % 5)
}
return nil
}
func sum(params: (num1: Int, num2: Int)?) -> Int? {
if let params = params {
return params.num1 + params.num2
}
return nil
}
and this is how I would use:
let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum
The result of this expression is either nil or a value of the type as defined in the last function of the pipeline, which in the above example is an Int.
If you need to do better error handling, you can define an enum like this:
enum Result<T> {
case Value(T)
case Error(MyErrorType)
}
and replace all optionals in the above functions with Result<T>, returning Result.Error() instead of nil.
I've found a way that looks better than alternatives, but it uses language features in unrecommended way.
Example using code from the question:
let fooResult: Bar! = foo();
if fooResult == nil {
// handle error and return
}
// continue correct operations here
fooResult might be used as normal variable and it's not needed to use "?" or "!" suffixes.
Apple documentation says:
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
How about the following:
func foo(i:Int) ->Int? {
switch i {
case 0: return 0
case 1: return 1
default: return nil
}
}
var error:Int {
println("Error")
return 99
}
for i in 0...2 {
var bob:Int = foo(i) ?? error
println("\(i) produces \(bob)")
}
Results in the following output:
0 produces 0
1 produces 1
Error
2 produces 99