I made this question because even though there are multiple ones with similar titles I was not able to find one with the same issue I have. I have a function that calls on a function that has a call back. This second function makes another call to another function that has a callback. My issue is, the third function that is being called is inside a .m file which through a bridge I am able to access it. This function returns nil if there is no error. The problem is that I am unable to allow nil in the callback function and it return the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I am new to swift and from what I have read using optionals is a good way but it still giving issues. Any ideas?
Code:
mainViewController.swift
func makeACall(){
var service:Service!
service = Service()
// error happens on this next line I believe is the error variable
service.lookup(item: itemTextField.text, callback: { (details:String?, error: serviceError?) -> Void in
if (error != nil){
//do something
}
})
}
Service.swift
func lookup(item:String!, callback: (_ details:String?, _ error: serviceError?) -> Void){
super.fetchItem(item, callback: { (details:String?, error: serviceError?) -> Void in
callback(details, error!)
}) // in objective C .m file fetchItem returns the call back as callback(details, nil) if no error
}
If your callback has a optional, why are you force-unwrapping it?
You didn't for details.
Both of these are optionals, yet you force unwrapped the error one. Use callback like this:
callback(details, error)
As for the text going in. Just do this before the fetch:
guard let itemText = itemTextField.text else { return }
Or if you want to call the function even with an empty string you can do this
let itemText = itemTextField.text ?? ""
And then use itemText like so:
service.lookup(item: itemText, callback: { (details:String?, error: serviceError?) -> Void in
if (error != nil){
//do something
}
})
Related
I am wanting to put a swift 3 do-catch inside a function rather than constantly writing it everywhere I need it; inside this function I wish to return a tuple with a boolean, and an optional error.
I am trying to return a tuple from the function and handle the result in my XCTest
However, I get an error saying:
Initializer for conditional binding must have Optional type, not '(Bool, Error?)' (aka '(Bool, Optional)')
My function is as follows;
public static func isValidPurchase(train: Train, player: Player) -> (Bool, Error?) {
do {
let result = try train.canBePurchased(by: player)
return (result, nil)
} catch let error {
return (false, error)
}
}
My canBePurchased code is a bit long, but it goes like this:
func canBePurchased(by player: Player) throws -> Bool {
if (!self.isUnlocked) {
throw ErrorCode.trainIsNotUnlocked(train: self)
}
// other if-statements and throws go here
}
And in my XCTest I call it as such:
if let result = TrainAPI.isValidPurchase(train: firstTrain, player: firstPlayer) as! (Bool, Error?) {
}
I've tried to force cast:
if let result: (Bool, Error?) ...
but this only demotes the compiler error to a warning.
The complier displays the error as noted above.
What am I doing wrong in terms of Initializer for conditional binding must have Optional type and how do I avoid it?
Thanks
The return type from isValidPurchase(train:player) is (Bool, Error?), which is not an optional (it is a tuple where the 2nd member happens to be an optional). Hence, there is no use for optional binding when capturing the return from a call to isValidPurchase(train:player). You simply assign the return value and study it's content (possible error etc) from there:
// e.g. using explicitly separate tuple members
let (result, error) = TrainAPI
.isValidPurchase(train: firstTrain, player: firstPlayer)
if let error = error { /* you have an error */ }
else { /* no error, proceed with 'result' */ }
Or, studying the return using a switch statement:
// result is a tuple of type (Bool, Error?)
let result = TrainAPI
.isValidPurchase(train: firstTrain, player: firstPlayer)
switch result {
case (_, let error?): print("An error occured!")
case (let result, _): print("Result = \(result)")
}
Just use optional casting instead of force casting. Using force casting result would have a non-optional value even if used without the if let statement.
if let result = TrainAPI.isValidPurchase(train: firstTrain, player: firstPlayer) as? (Bool, Error?) {
}
In a closure can we use the same name somehow inside as well as a value that is captured by the closure.
func load(withResource resource: Resource) {
var data: A?
var error: Error?
load(resource: resource) { (result, error) in
data = result // Ok!
error = error // error!
}
print("data: \(data), error: \(error)")
}
I am thinking if there is something like using self if we were talking about stored properties but these vars are are declared in the function scope.
The easiest way would just to rename error but I was wondering if there is another way.
First, If your load method is asynchronous, error and data will always be nil when it's printed.
The direct answer to your question is that within the scope of the closure, the value of "error" is the value from the parameter and there's no way to access the error from the function.
Obviously there are a lot of options to get around this, but one clean option would be to make your information into a Tuple:
func load(withResource resource: Resource) {
var closureData: (data: A?, error: Error?)?
load(resource: resource) { (result, error) in
closureData = (result, error)
}
//print(closureData) if you don't need to format it)
print("data: \(closureData.data), error: \(closureData.error)")
}
/* Checks Is Username Already Exists
Take - username
Return - Bool (True/False) */
func checkIsUserExists(username: String, completion: ((isUser: Bool?) -> Void)!) {
var isPresent: Bool = false;
let query: PFQuery = PFQuery(className: "your_class_name")
query.whereKey("your_key", equalTo:username)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if (objects!.count > 0) {
isPresent = true;
}
} else {
// Log details of the failure
println("Error: \(error) \(error!.userInfo!)")
}
completion(isUser: isPresent);
}
}
I found this function method that checks if the user exists in parse database but I dont get it what is the proper way to call it?
checkIsUserExists("user#gm.com, comletion.. ?)
It requires a closure that acts as a callback. So you'd call it in the following way:
checkIsUserExists("User Name"){isUser in if let user = isUser where user {
userFound()
}
else {
userNotFound()
}
}
Assuming you also had the following methods in place:
func userFound() {
// do something if the user was found
}
func userNotFound() {
// do something if the user was not found
}
The important keyword is in. Before the keyword, within the curly braces, I've named the value that is being captured isUser. It is a Bool as we can see from the method. And the closure is not required to return anything: (isUser: Bool?) -> Void)! hence the word Void. After the in keyword we can use the captured value to do whatever we wish with the optional true or false value. Here I call one of two functions.
In terms of code it is a very poor callback because while we know whether the user exists by the time the callback happens there might be several callbacks waiting in line and so we won't know which user exists because the user name is not passed to the closure.
You can find out plenty about closures in the Swift documentation.
got it!
The correct way is
checkIsUserExists(userEmail.text!)
{
(isUser: Bool?) in
print(isUser)
}
WOW! what a weird function call
I am trying to create a generic function that can take an optional argument.
Here's what I have so far:
func somethingGeneric<T>(input: T?) {
if (input != nil) {
print(input!);
}
}
somethingGeneric("Hello, World!") // Hello, World!
somethingGeneric(nil) // Errors!
It works with a String as shown, but not with nil.
Using it with nil gives the following two errors:
error: cannot invoke 'somethingGeneric' with an argument list of type '(_?)'
note: expected an argument list of type '(T?)'
What am I doing wrong and how should I correctly declare/use this function? Also, I want to keep the usage of the function as simple as possible (I don't want do something like nil as String?).
I guess the compiler can't figure out what T is just from nil.
The following works just fine though for example:
somethingGeneric(Optional<String>.None)
I believe you've overcomplicated the problem by requiring the ability to pass untyped nil (which doesn't really exist; even nil has a type). While the approach in your answer seems to work, it allows for the creation of ?? types due to Optional promotion. You often get lucky and that works, but I've seen it blow up in really frustrating ways and the wrong function is called. The problem is that String can be implicitly promoted to String? and String? can be implicitly promoted to String??. When ?? shows up implicitly, confusion almost always follows.
As MartinR points out, your approach is not very intuitive about which version gets called. UnsafePointer is also NilLiteralConvertible. So it's tricky to reason about which function will be called. "Tricky to reason about" makes it a likely source of confusing bugs.
The only time your problem exists is when you pass a literal nil. As #Valentin notes, if you pass a variable that happens to be nil, there is no issue; you don't need a special case. Why force the caller to pass an untyped nil? Just have the caller pass nothing.
I'm assuming that somethingGeneric does something actually interesting in the case that it is passed nil. If that's not the case; if the code you're showing is indicative of the real function (i.e. everything is wrapping in an if (input != nil) check), then this is a non-issue. Just don't call somethingGeneric(nil); it's a provable no-op. Just delete the line of code. But I'll assume there's some "other work."
func somethingGeneric<T>(input: T?) {
somethingGeneric() // Call the base form
if (input != nil) {
print(input!);
}
}
func somethingGeneric() {
// Things you do either way
}
somethingGeneric(input: "Hello, World!") // Hello, World!
somethingGeneric() // Nothing
Good question and answer. I have an Swift 4 update to contribute:
var str: String? = "Hello, playground"
var list: Array<String>? = ["Hello", "Coder256"]
func somethingGeneric<T>(_ input: T?) {
if (input != nil) {
print(input!);
}
}
func somethingGeneric(_ input: ExpressibleByNilLiteral?) {}
somethingGeneric("Hello, World!") // Hello, World!
somethingGeneric(nil) // *nothing printed*
somethingGeneric(nil as String?) // *nothing printed*
somethingGeneric(str) // Hello, playground
str = nil
somethingGeneric(str) // *nothing printed*
somethingGeneric(list) // ["Hello", "Coder256"]
list = nil
somethingGeneric(list) // *nothing printed*
I figured it out:
func somethingGeneric<T>(input: T?) {
if (input != nil) {
print(input!);
}
}
func somethingGeneric(input: NilLiteralConvertible?) {}
somethingGeneric("Hello, World!") // Hello, World!
somethingGeneric(nil) // *nothing printed*
somethingGeneric(nil as String?) // *nothing printed*
I think that you will never call somethingGeneric(nil) but mostly somethingGeneric(value) or somethingGeneric(function()) for which the compiler has enough info not to be stucked trying to guess the type:
func somethingGeneric<T>(input: T?) {
if let input = input {
print(input);
}
}
func neverString() -> String? {
return nil
}
let a: String? = nil
somethingGeneric("Hello, World!") // Hello, World!
somethingGeneric(a) // Nothing and no error
somethingGeneric(neverString()) // Nothing and no error
Also, I would use the if let syntax instead of if(value != nil).
Here is the solution I came up with that compiles on Swift 5, as many of the solutions here did not compile for me. It might be considered hacky as I use a stored variable to help things along. I was unable to come up with a Swift 5 version of the nil parameters that resolve to type T.
class MyClass {
func somethingGeneric<T>(input: T?) {
if let input = input {
print(input)
}
}
func somethingGeneric() {
somethingGeneric(Object.Nil)
}
}
final class Object {
static var Nil: Object? //this should never be set
}
Actually there is a way to do this, inspired by Alamofire's internal code.
You do not have to install Alamofire to use this solution.
Usage
Your problematic method definition
func someMethod<SomeGenericOptionalCodableType: Codable>(with someParam: SomeGenericOptionalCodableType? = nil) {
// your awesome code goes here
}
What works ✅
// invoke `someMethod` correctly
let someGoodParam1 = Alamofire.Empty.value
someMethod(with: someGoodParam1)
I think it is possible to use Alamofire.Empty.value as a default value in someMethod definition as a parameter.
What does not work ❌
// invoke `someMethod` incorrectly
let someBadParam1: Codable? = nil
let someBadParam2 = nil
someMethod(with: someBadParam1)
someMethod(with: someBadParam2)
Solution definition (source)
/// Type representing an empty value. Use `Empty.value` to get the static instance.
public struct Empty: Codable {
/// Static `Empty` instance used for all `Empty` responses.
public static let value = Empty()
}
Now I have the next function:
func processRequest(path: String, callback: ((json: JSON?, error: NSError?) -> Void)?) {
// Code ...
dispatch_async(dispatch_get_main_queue()) {
callback?(json: JSON(data), error: nil)
}
})
And how function is called:
processRequest("test") { (json: JSON?, error: NSError?) in
// Play with returned data
}
And my question is: What will happen if I don't return the callback?
This is bad, or nothing terrible will happen?
Example of situation:
func processRequest(path: String, callback: ((json: JSON?, error: NSError?) -> Void)?) {
// Code ...
if (condition) {
// What will happen if I am here and not return back "callback"
} else {
dispatch_async(dispatch_get_main_queue()) {
callback?(json: JSON(data), error: nil)
}
}
})
processRequest("test") { (json: JSON?, error: NSError?) in
// Play with returned data
}
When you pass a callback, you provide a block of code that processRequest decides when should be executed.
Given your implementation of processRequest, if you pass nil as callback param, simply no additional code is executed when processRequest decides that is time to invoke the callback function.
Infact the callback parameter is defined as Optional
callback: ((json: JSON?, error: NSError?) -> Void)?
and it's executed only if it is not nil
callback?(json: JSON(data), error: nil)
Update
As you noted you are not passing a nil callback, you are passing a callback with an empty body. Given the current definition of processRequest the result is similar: nothing happens.
More specifically an empty closure is enqueued to the main queue (the one that has the right to update the user interface). I imagine this will produce an infinitesimal overhead in the system because that empty block of code will be synchronously added to the main queue, later on dequeued and finally executed.
Again, short answer: nothing happen.