func writeImageToFile(path: String, completeBlock: (success: Bool) -> Void){ } - swift

i am trying to upload images using DKimagepickercontroller and this is a way to upload it to a url but i am confuse what i am suppose to put into the completeBlock: (success: Bool) - Void
func writeImageToFile(path: String, completeBlock: (success: Bool) -> Void){
}
this is the code that i wrote in xcode
let apath = "http://localhost/swift/upload.php"
writeImageToFile(apath,completeBlock: (success: false) -> Void)
but i got this error
expected expression in the list of expressions and this error expexted ',' separator

You have to call it like that writeImageToFile(path) { success in print(success) }

This block is supposed to be used as a trailing closure, don't leave it in the signature.
It looks like this in Xcode: stackoverflow.com/a/33020097/2227743
The closure argument is used once the task is done, in your case it will indicate if the file has been written or not.
You can for example test success in the closure:
writeImageToFile(somePath) { (success) in
if success {
// the file has been written
} else {
// the file hasn't been written
}
}

Related

Does a Completion Handler end the function?

Perhaps I do not understand the concept of a completion handler, but I have a function like so;
func someFunction(completion: #escaping (Bool, LoginError?) -> Void) {
self.checkDevice() { allowed, error in
if let e = error {
completion(false, e)
}
completion(true, nil)
}
}
While being light on what checkDevice() does, the basic premise is that it performs an asynchronous network call, and returns either true with no error (nil), or returns false with an error.
When I run this code, I am finding that the completion handler is being called twice. It sends a completion as a tuple (as false, error) and also as (true, nil). I've done some debugging and there seems to be no manner in which someFunction() is called twice.
I was of the belief that once a completion is sent, the function would end. In my test case, I am forcing an error from checkDevice(), which should result in me sending the completion as (false, error), but I am seeing both (false, error) and (true, nil). Does a completion not immediately end the function?
I was of the belief that once a completion is sent, the function would end.
No, why would that be? When you call this function it’s like calling any other function. The name has no magic power.
Rewrite it like this:
func someFunction(completion: #escaping (Bool, LoginError?) -> Void) {
self.checkDevice() { allowed, error in
if let e = error {
completion(false, e)
return // *
}
completion(true, nil)
}
}
Actually, I should mention that this is a poor way to write your completion handler. Instead of taking two parameters, a Bool and an Optional LoginError to be used only if the Bool is false, it should take one parameter — a Result, which carries both whether we succeeded or failed and, if we failed, what the error was:
func someFunction(completion: #escaping (Result<Void, Error>) -> Void) {
self.checkDevice() { allowed, error in
completion( Result {
if let e = error { throw e }
})
}
}
As you can see, using a Result as your parameter allows you to respond much more elegantly to what happened.
Indeed, checkDevice itself could pass a Result into its completion handler, and then things would be even more elegant (and simpler).
A completion handler does not end a function. The typical way to end a function is to insert a return.
See comment re: your specific case
Consider including parameter names (for reference). And the completion should only be called once. You can do that using return or by using a full if-else conditional.
func someFunction(completion: #escaping (_ done: Bool, _ error: LoginError?) -> Void) {
checkDevice() { (allowed, error) in
if let e = error {
completion(false, e)
} else {
completion(true, nil)
}
}
}
func someFunction(completion: #escaping (_ done: Bool, _ error: LoginError?) -> Void) {
checkDevice() { (allowed, error) in
if let e = error {
completion(false, e)
return
}
completion(true, nil)
}
}
By giving parameters names, when calling the function, you can now reference the signature to denote the meanings of its parameters:
someFunction { (done, error) in
if let error = error {
...
} else if done {
...
}
}

Variable outside DispatchQueue.main.async is empty

I tried to resolve this error since days but I dont understand why am I getting this error in the first place.
Please help...
func createData(request:Crudpb_CreateRequest) -> String {
DispatchQueue.main.async {
self.response = try! self.client.create(request)
print("This is response 1: " + self.response.result) // <-------- This is priting the right response
}
print("This is response outside DispatchQueue: " + self.response.result) // <------- This is not printing anyvalue
return self.response.result // <------ This is not
}
You are dispatching whatever work you are doing in your create request method asynchronously, therefor your create data function will not wait for this work to be done to continue its execution, it just calls it and keeps its execution and thats why your value is not modified when you reach your "This is response outside DispatchQueue: " statement.
It will be modified in the capture block that you have created, thats why you need to create an #escaping completion block like they mentioned before, to only return your value when the work you did to obtain it is finished.
func createData(request:Crudpb_CreateRequest, with completion: #escaping (String) -> Void) {
DispatchQueue.main.async {
self.response = try! self.client.create(request)
completion(self.response)
}
}
This is the way I use #escaping closures: You don't need a return value in the function, given that the execution of the calling function may end before the closure is finished. You also need to specify a dataType in the #escaping parameter (Bool in this case)... also, Function types cannot have argument labels, so you must use "_"
self.fetchStuff(onCompletion: { (success) in
if success {
// Do something
}
})
func fetchStuff(onCompletion: #escaping (_ success: Bool) -> Void) {
// Do some asynch stuff
onCompletion(true)
}

Swift 4: Escaping closures can only capture inout parameters explicitly by value

I have been migrating my code from Swift 2 to Swift 4. I have the following code that worked fine in Swift 2:
func fetchUserThumbnailAvatar(_ task : inout URLSessionTask?, completion : #escaping (_ image : UIImage?) -> ()) {
fetchUserAvatar(Session.currentUser?.avatar?.thumbnailURL as URL? ?? URL(string:"")!, externalUrl: URL(string: thumbnailAvatar) ?? URL(string:"")!, &task, completion: completion)
}
fileprivate func fetchUserAvatar(_ internalUrl : URL, externalUrl : URL,_ task : inout URLSessionTask?, completion : #escaping (_ image : UIImage?) -> ()) {
fetchImage(externalUrl, task: &task, completion: { image in
if image == nil {
self.fetchImage(internalUrl, task: &task, completion: completion)
} else {
self.cache.removeObject(forKey: (internalUrl.path as AnyObject?)! )
completion(image)
}
})
}
However, after the conversion I get the following error:
Escaping closures can only capture inout parameters explicitly by value
at line:
if image == nil {
self.fetchImage(internalUrl, task: &task, completion: completion)
}
It would be great if someone could help me. Thanks.
The error is described in detail in this answer.
The problem with your code is that the first closure
fileprivate func fetchUserAvatar(_ internalUrl : URL, externalUrl : URL,_ task : inout URLSessionTask?, completion : #escaping (_ image : UIImage?) -> ()) {
fetchImage(externalUrl, task: &task, completion: { image in // <-- HERE --
if image == nil {
is an escaping closure. So when the code
if image == nil {
self.fetchImage(internalUrl, task: &task, completion: completion) // <-- HERE --
} else {
tries to write to the task variable, the original fetchUserAvatar call has already completed.
Note: I have written comments like this <-- HERE -- into the snippets, to clarify which line I am talking about. Also, please make sure to check out the answer that I linked above, because it will clarify everything..
The bad news is that you will have to refactor the code a bit to fix the error. You'll need to change the signatures of both fetchUserThumbnailAvatar, as well as fetchUserAvatar for that, and that will break callers; so the callers have to be changed as well. Therefore I cannot fix it for you, because the fix depends on code that I don't have.

swift 3 from working swift 2.3 : #escaping with optionals and calls needing error chain management

I have optionals (NSError?) that are flagged by Xcode/Swift as non-escaping. These are called within a function (that has #escaping on its closure) by a second function (also with #escaping on its closure). The problem seems to be that the errors are not captured in the closure of either function and so Xcode/Swift is seeing them as potentially escaping.
Previous stack overflow posts had noted 'withoutActuallyEscaping' as a workaround. That no longer seems to be supported (Xcode version 8.2.1).
Refactoring to try to keep everything local to the function has not worked. I've also tried moving all of the NSError? to Error (via local enum), but that also hasn't worked.
The core problem is how to enable an error returned (via success/failure) from one function to the calling statement to be either captured locally and retained or tagged as escaping so that Swift/XCode can compile.
Again, this was working in Swift 2.3, so I'm looking for suggestions on how to refactor or what to look at for correctly handling these calls with NSError or Error.
This is part of a large code stack (about 25K lines). I'm posting part of the stack below (given Hamish's comment) to try and make the question clearer. Currently there are about 17-different variations of this error that are present in the code-stack.
public func fetchMostRecentSamples(ofTypes types: [HKSampleType], completion: #escaping HMTypedSampleBlock)
{
let group = DispatchGroup()
var samples = [HKSampleType: [MCSample]]()
let updateSamples : ((MCSampleArray, CacheExpiry) -> Void, (MCError) -> Void, HKSampleType, [MCSample], MCError) -> Void = {
(success, failure, type, statistics, error) in
guard error == nil else {
failure(error)
return
}
guard statistics.isEmpty == false else {
failure(error)
return
}
samples[type] = statistics
success(MCSampleArray(samples: statistics), .never)
}
let onStatistic : ((MCSampleArray, CacheExpiry) -> Void, (MCError) -> Void, HKSampleType) -> Void = { (success, failure, type) in
self.fetchMostRecentSample(type) { (samples, error) in
guard error == nil else {
failure(error)
return
}
Then fetchMostRecentSample has this header:
public func fetchMostRecentSample(_ sampleType: HKSampleType, completion: #escaping HMSampleBlock)
and the error message on the failure is "Closure use of non-escaping parameter 'failure' may allow it to escape" : "Parameter 'failure' is implicitly non-escaping"
Note that the let updateSamples is fine (not calling another function), but that the onStatistic with the failure (having error codes) is where the problem with escaping/non-escaping is coming from. MCError is an enum with the error codes (refactored from NSError? in Swift 2.3 version).
Major credits to Hamish for helping me to see into this. In case it helps others coming across this on their Swift 3 conversions, the other place that I stumbled was in assuming that all of our third-party pods were complete if they stated ready for Swift 3. In this case I had to update the AwesomeCache code to handle the errors correctly:
open func setObject(forKey key: String, cacheBlock: #escaping (#escaping(CacheBlockClosure), #escaping(ErrorClosure)) -> Void, completion: #escaping (T?, Bool, NSError?) -> Void) {
if let object = object(forKey: key) {
completion(object, true, nil)
} else {
let successBlock: CacheBlockClosure = { (obj, expires) in
self.setObject(obj, forKey: key, expires: expires)
completion(obj, false, nil)
}
let failureBlock: ErrorClosure = { (error) in
completion(nil, false, error)
}
cacheBlock(successBlock, failureBlock)
}
}
This is simply adding two more #escaping beyond what was in the github master. Otherwise, as Hamish pointed out, it comes down to paying attention to where #escaping needs to be added on the function calls. The update for AwesomeCache came from code like this:
aggregateCache.setObjectForKey(key, cacheBlock: { success, failure in
let doCache : ([MCAggregateSample], NSError?) -> Void = { (aggregates, error) in
guard error == nil else {
failure(error)
return
}
success(MCAggregateArray(aggregates: aggregates), .Date(self.getCacheExpiry(period)))
}
where the success, failure will be flagged as potentially escaping without the code changes on the AwesomeCache.

Calling a function that has a TypeAlias as a parameter?

So I've written a little practice program that has to do with closures. I'm trying to better understand how the asynchronous concept works. When I try to call request(), I get conversion errors as seen below:
import UIKit
let correctPasscode = "3EyX"
typealias CompletionHandler = (result: AnyObject?, error: String?) -> Void
func request(passcode: String, completionHandler: CompletionHandler) {
sendBackRequest(passcode) {(result, error) -> Void in
if error != nil {
print(error)
}
else {
print(result)
}}
}
func sendBackRequest(passCode: String, completionHandler: CompletionHandler) {
if passCode == correctPasscode {
completionHandler(result: "Correct. Please proceed", error: nil)
} else {
completionHandler(result: nil, error: "There was an error signing in")
}
}
request(correctPasscode, completionHandler: CompletionHandler) // Error happens here
Type alias is there to tell you what actual type you need to pass. In this case, the type is a closure of type
(result: AnyObject?, error: String?) -> Void
You pass it like this:
request(correctPasscode, completionHandler:{
(result: AnyObject?, error: String?) in
print("Inside the handler...")
// Do some useful things here
})
or even shorter -
request(correctPasscode) {
(result: AnyObject?, error: String?) in
print("Inside the handler...")
// Do some useful things here
}
or even shorter - (the types are known via the func declaration) -
request(correctPasscode) { result, error in
print("Inside the handler...")
// Do some useful things here
}
I'm not sure what you try to accomplish, but this line:
request(correctPasscode, completionHandler: CompletionHandler)
doesn't compile because you are not passing a CompletionHandler closure to the function but rather a type object representing the type of that completion handler.
Hence the error: Cannot convert value of 'CompletionHandler.*Type*'.
A valid call would be:
request(correctPasscode) { result, error in
print("result was \(result), and error was \(error)")
}
But then your request function doesn't do anything with the closure which is passed in. It is a little hard to say what you want ...