I am using Firebase on iOS with Swift 3.
When I use
FIRDatabase.database().reference().child("child").setValue("value") {
(error: Error?, databaseReference: FIRDatabaseReference) in
print("Error while setting value \(error)")
}
The app crashes on runtime with the following log:
*** Terminating app due to uncaught exception 'InvalidFirebaseData', reason: '(nodeFrom:priority:) Cannot store object of type _SwiftValue
at . Can only store objects of type NSNumber, NSString, NSDictionary,
and NSArray.'
I tried to use the same function but without the trailing closure and for some reason, it works!
FIRDatabase.database().reference().child("child").setValue("value",
withCompletionBlock: {
(error: Error?, databaseReference: FIRDatabaseReference) in
print("Error while setting value \(error)")
})
Is there something special about trailing closures and Swift 3?
tl;dr: Firebase provides a setValue(_ value: Any?, andPriority priority: Any?) which is incorrectly matched when using a trailing closure with setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void).
Solution: When using an API that has many varieties, avoid using trailing closures. In this case, prefer setValue(myValue, withCompletionBlock: { (error, dbref) in /* ... */ }); do not use setValue(myValue) { (error, dbref) in /* ... */ }.
Explanation
This appears to be a Swift bug. As in other languages, such as Java, Swift generally chooses the most specific overload. E.g.,
class Alpha {}
class Beta : Alpha {}
class Charlie {
func charlie(a: Alpha) {
print("\(#function)Alpha")
}
func charlie(a: Beta) {
print("\(#function)Beta")
}
}
Charlie().charlie(a: Alpha()) // outputs: charlie(a:)Alpha
Charlie().charlie(a: Beta() as Alpha) // outputs: charlie(a:)Alpha
Charlie().charlie(a: Beta()) // outputs: charlie(a:)Beta
However, when overloaded functions match a trailing closure, Swift (at least, sometimes) selects the more general type. E.g.,
class Foo {
func foo(completion: () -> Void) {
print(#function)
}
func foo(any: Any?) {
print(#function)
}
}
func bar() {}
Foo().foo(completion: bar) // outputs: foo(completion:)
Foo().foo(any: bar) // outputs: foo(any:)
Foo().foo() { () in } // outputs: foo(any:)
// ^---- Here lies the problem
// Foo().foo(bar) will not compile; can't choose between overrides.
Any? is a more general type than () -> Void -- i.e., "anything, even null" is more broad than "a function receiving 0 parameters and returning something of type Void". However, the trailing closure matches Any?; this is the opposite of what you would expect from a language that matches the most specific type.
While there is an accepted answer, which provides some clarity, explaining that it's a Swift bug is not really accurate. That being said, the explanation is accurate but not for this issue.
Allowing the closure to be added to setValue in the first place is the real bug.
A more accurate answer is that there is no completion block/closure for the setValue function, which is why it fails.
The specific function -setValue: does NOT have a closure, which is why it's crashing. i.e. it's an incorrect implementation in your code. Per the docs:
func setValue(_ value: Any?)
note that the setValue function does NOT have a closure and if you add one the function will have no idea what to do with that data.
To use a completion block/closure, you must call the correct function which is
-setValue:withCompletionBlock:
Bottom line is you can't randomly add a parameter or call to a function that's not designed to accept it.
This is obviously not valid but conceptually it's the same error.
let a = "myString"
a.append("x") { (error: Error?) }
In this case the compiler knows the the string.append function doesn't have a closure option and catches it before compiling.
So go a tad further, this code complies & runs but also generates an error
ref.child("child").setValue("value") { }
Again, setValue doesn't have a closure so this code is improperly implemented.
To clarify, given a class
class MyClass {
var s = ""
var compHandler = {}
func setValue(aString: String) {
self.s = aString
}
func setValue(aString: String, someCompletionHandler: completionHandler) {
self.s = aString
self.compHandler = someCompletionHandler
}
}
Note that setValue:aString is a totally different function than setValue:aString:someCompletionHandler
The only parameter that can be based to setValue:aString is a String as the first and only parameter.
The setValue:aString:someCompletionHandler will be passed two parameters, a String in the first position and a completionHandler in the second position.
The actual completion block is the second parameter passed.
param1, param2
------, ---------------
string, completionBlock
This is why
setValue(value) {}
is improperly formatted whereas
setValue(value, withBlock: {})
is properly formatted.
Related
Between these 2 codes
( returns -> Void)
static func dropboxWorkoutList ( userCompletionHandler: #escaping ([String]) -> Void) {
let client = DropboxClientsManager.authorizedClient
var files: [String] = []
_ = client?.files.listFolder(path: "/workouts")
.response { response, error in
if let result = response {
for entry in result.entries {
if let file = entry as? Files.FileMetadata {
let ext = (file.name as NSString).pathExtension
switch ext {
case "txt", "mrc", "zwo":
// print("filename:\(file.name) ext:\(ext) path:\(file.pathLower)")
files.append(file.pathLower!)
default:
continue
}
}
}
files = files.sorted(by: { $0 < $1 } )
// print("Contents of Workout Folder:\(files)")
userCompletionHandler(files)
} else if let error = error {
print(error)
}
}
}
calling it
dropboxFunc.dropboxWorkoutList() { (value) in
print("value:\(value)")
print("value[0] : \(value[0])")
print("value.count : \(value.count)")
}
results in:
value:["/1-01.txt", "/1-17.txt"]
value[0] : /1-01.txt
value.count : 5
however when changing it
from Return -> Void
to Return -> Any
trying to execute the below will have swift telling me:
"Missing return in a closure expected to return 'Any'"
dropboxFunc.dropboxWorkoutList() { (value) in
print("value:\(value)")
print("value[0] : \(value[0])")
print("value.count : \(value.count)")
}
I can only allowed to execute 1 print statement. Just want to understand the difference.
Note: Asked this
Return list of files from function call
and was given this as possible answer
How I can return value from async block in swift
Specifying a closure of ([String]) -> Any means that the closure is going to return something, and it is of type Any. But in your examples, (a) your closures are not returning anything at all; and (b) the dropboxWorkoutList does not appear to need/use an object returned by the closure supplied by the caller, anyway. This is a “completion handler closure” pattern, and completion handler closures almost always have a Void return type.
I actually want to use the return values from dropboxWorkoutList to populate a tableview which I've not coded yet
OK, then I think you may be conflating the closure parameter (what dropboxWorkoutList will be supplying back to the caller) and the closure’s return value (the far less common scenario, where the caller needs to supply dropboxWorkoutList some value based upon the closure’s parameter). In this case, you want the former (the closure parameter), not the latter (the closure’s return value).
You likely do not want to change the closure to be ([String]) -> Any, at all, but rather leave it as ([String]) -> Void. The caller should just take the parameter of the closure and use that. For example:
dropboxFunc.dropboxWorkoutList { values in
self.strings = values // update whatever model object your table view data source is using
self.tableview.reloadData() // and tell the table view to reload the data
}
In short, your question here (and in that other post) was, effectively, “how do I return a value using a closure?”, to which the answer is that you should not think of it as “returning” a value. Rather, in asynchronous routines with completion handler closures, results are supplied as parameter(s) to the closure. Essentially, dropboxWorkoutList is calling a function (in this case, a closure) to say “hey, here is your data”. It is providing data to that closure, not returning data from that closure.
this is my first question and I hope you guys can help me out.
Lets assume I've written a Framework in Swift that does stuff purely with native Swift Types. This is our interface:
class Foo {
#discardableResult
public func perform(query: String) -> [String: Any]? {
return [:]
}
}
Note that our result is discardable.
I would now like to support other Libraries optionally. I know we have Codable and stuff but lets assume that I want to support SwiftyJSON optionally by creating an extension that only compiles when SwiftyJSON can be imported.
#if canImport(SwiftyJSON)
import SwiftyJSON
extension Foo {
#discardableResult
func perform(query: String) -> JSON? {
// Call internal func => cast JSON => return JSON
return JSON()
}
}
#endif
Without SwiftyJSON (can not import) this compiles:
let bar = Foo().perform(query: "")
With SwiftyJSON it does not because its ambiguous.
Return type has to be explicitly defined like:
let baz: [String: Any]? = Foo().perform(query: "")
// or
let bar: JSON? = Foo().perform(query: "")
For calls where I want the result thats fine.
BUT: Since we have a discardableResult calls like
Foo().perform(query: "")
will always be ambiguous.
The following calls work but are not very nice imho.
Foo().perform(query: "") as JSON?
Foo().perform(query: "") as [String: Any]?
What is the best way to deal with this problem? Should the method just not be overloaded and have a different name instead? Or am I overlooking something that makes the calls non ambiguous in a nice way?
Edit:
A comment suggested to remove discardableResult. I really do not wanna do that since that would lead to a lot of calls that look like this:
_ = Foo().perform(query: "")
The more I think about the problem it occurs to me that there might just not be a good solution for this..
Actually, removing #discardableResult wouldn't work, and _ = Foo().perform(query: "") wouldn't compile, it would still have the same ambiguity: it's not that the compiler is being nitpicky, but it has literally no way of knowing which of your two functions to call!
It might be interesting to think about what behavior you'd expect from this line, and why.
You seem to want to have a function based on an existing function which:
Returns a different type;
Has the same name and parameters;
Has a discardable result.
Unfortunately, it looks like it's a classic "pick any two" type of scenario…
Well, let's do just that! Let's see what happens if we stop insisting on any one of the three conditions.
Let me use a simpler example:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomething() -> Int {
return 1
}
doSomething() // won't compile
let x:String
x = doSomething() // "a"
We have a function overloaded with two return types (String and Int), and we see that we cannot discard its result as it would result in unresolvable ambiguity.
1. Single return type: use an enum with a payload
Let's eliminate the first condition, and try using a single return type. This is possible:
enum ReturnType {
case text(_ payload:String)
case number(_ payload:Int)
}
#discardableResult func doSomething() -> ReturnType {
if ... // some condition
{
return .text("a")
} else {
return .number(1)
}
}
doSomething() // works
let x = doSomething() // ReturnType.text("a") or ReturnType.number(1)
Here we cannot (easily) extend the functionality via extensions; rather, we'd need to touch the enum and function code with all the possible options every time we want to add a new return type. (Of course advanced APIs can also be created that help third parties easily write extensions… If it's worth the effort.)
We would also need a way to determine which return type to choose: it can be a function parameter, or a value stored elsewhere, etc. Here's an example with a parameter:
#discardableResult func doSomething(useText:Bool = false) -> ReturnType {
if useText {
return .text("a")
} else {
return .number(1)
}
}
doSomething() // works
doSomething(useText:true) // works
let x = doSomething() // ReturnType.number(1)
let x2 = doSomething(useText:false) // ReturnType.number(1)
let x3 = doSomething(useText:true) // ReturnType.text("a")
Note: Here, we lose the convenience of having the compiler infer the type from the call site; since the type is now an opaque wrapper, it's no longer the compiler's business to make sure that the wrapped type is correct. This may be a high cost for simply maintaining "result discardability," but then it may also allow us to abstract away some details, only unwrapping the "payload" when needed, which can have its own benefits.
2. Change the name or parameters of the function
This is quite easy:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomethingElse() -> Int {
return 1
}
doSomething() // works
doSomethingElse() // works
let x = doSomething() // "a"
let y = doSomethingElse() // 1
We can also use this in an extension. All we lose is the shared name.
Changing the parameters is also possible but it would be pretty silly in this already-silly example:
#discardableResult func doSomething() -> String {
return "a"
}
#discardableResult func doSomething(thinkOfAnyNumber:Int) -> Int {
return 1
}
doSomething() // "a"
doSomething(thinkOfAnyNumber:42) // 1
Note: I'd obviously only do this if the additional parameters actually make sense.
Note 2: The parameter configuration is different between the two functions, not the parameter values like in the previous case.
3. Do not make the result discardable
Note that simply removing the #discardableResult attribute won't make it impossible to try to discard the result: you'll still be able to try the _ = assignment, or simply ignore the function result (and the compiler warning). Both will ultimately fail to compile, so it will be up to the user of the API to avoid doing either.
Hopefully, your function does some other things (side effects) than provide a return value: in that case, there may be little use in discarding the result of a function that does nothing but provide that result, and one may probably be better off not calling it in the first place.
If the side effects are identical between the two overloaded functions, you can factor them out into a single function:
func doTheActualWork() {
// ...
}
func doSomething() -> String {
doTheActualWork()
return "a"
}
func doSomething() -> Int {
doTheActualWork()
return 1
}
doSomething() // won't compile
doTheActualWork() // use this instead
let z:String = doSomething() // "a"
Note that this can also be done via extensions as long as they re-use existing functions and only overload them with different signatures and/or return types.
Now if the side effects (i.e. doTheActualWork() implementations) are also different in the two cases… Then I give up. But that can be handled as well.
Well, this is what I've been able to gather on this interesting problem. Now I may be dead wrong, and there may be something better out there… We'll find out soon enough.
I migrated my project to Swift 3 and am down to 4 errors in an open source framework. The open source code is https://github.com/blackmirror-media/BMInputBox. It was working fine in Swift 2.2. It migrated fairly smoothly, with the exception of these errors. The errors are all the same and state:
.../Pod/Classes/BMInputBox.swift:430:26:
Cannot call value of non-function type '((AnyObject...) -> Void)!'
Here's the code it relates to:
/// Closure executed when user submits the values.
open var onSubmit: ((_ value: AnyObject...) -> Void)!
/// As tuples are not supported in Objc, this is a method, which is called as well, but instead an array of values are returned
open var onSubmitObjc: ((_ value: [AnyObject]) -> Void)!
/// Closure executed when user cancels submission
open var onCancel: (() -> Void)!
/// Closure executed when the value changes in the field. The caller can modify the value and return it
open var onChange: ((_ value: String) -> String)?
func cancelButtonTapped () {
if self.onCancel != nil {
self.onCancel()
}
self.hide()
}
func submitButtonTapped () {
// Submitting the form if valid
if self.validateInput() {
if self.onSubmit != nil {
let valueToReturn: String? = self.textInput!.text
if let value2ToReturn = self.secureInput?.text {
*Error> self.onSubmit(valueToReturn, value2ToReturn)
}
else {
*Error> self.onSubmit(valueToReturn)
}
}
if self.onSubmitObjc != nil {
let valueToReturn: String? = self.textInput!.text
if let value2ToReturn = self.secureInput?.text {
*Error> self.onSubmitObjc([valueToReturn!, value2ToReturn])
}
else {
*Error> self.onSubmitObjc([valueToReturn!])
}
}
self.hide()
}
// Shaking the validation label if not valid
else {
self.animateLabel()
}
}
I'm new to Swift and am having trouble figuring out what this is actually supposed to do. Without knowing that, it's hard to figure out what Swift 3 doesn't like about it.
What does this line actually do?
open var onSubmit: ((_ value: AnyObject...) -> Void)!
To my inexperienced eyes, it looks like it's trying to pass a tuple to a function that is declared as a variable (rather than a function) and that doesn't do anything or return anything. I'm obviously missing something. Can anyone explain why it is declared as a variable and why there is no logic defined in it? If anyone knows why Swift 3 is complaining, that would be a bonus!
onSubmit is a variable holding a closure as an implicitly unwrapped optional. But the error message is misleading, the actual reason is that strings (numbers, etc) are not automatically bridged to AnyObject anymore in Swift 3.
Replacing AnyObject by Any (the protocol to which all types conform) in those closure definitions should solve the problem.
I have been converting over to the new swift syntax. I'm having trouble tweaking a third party SQLITE party into Swift 3.0. It appears there has been an update to closures: https://github.com/apple/swift-evolution/blob/master/proposals/0103-make-noescape-default.md
I cannont figure out for the life of me how to pass the variable "task" to my function to be added to the GCD queue Here's what my code looks like when I call my function "put on thread."
let foo: ()->Void = {
let interesting = "confusing"
print("Swift 3.0 Conversion has been " + interesting + " with some parts!")
}
/// Compiler error: Cannot convert value of type () -> Void to expected argument SwiftData
putOnThread(foo)
///I have also tried this and I get a different compiler error:
SwiftData.putOnThread(foo)
//Compiler error: Use an instance member 'putOnThread' on type 'SwiftData'. Did you mean to use a value type of SwiftData instead?
Here's what the putOnThread method looks like:
extension SwiftData {
public func putOnThread(closure: #escaping ()->Void) {
SQLiteDB.sharedInstance.queue.sync {
closure()
}
}
}
Note: SwiftData is a struct. Any help would be greatly appreciated! Maybe once I'm finished, I can happily commit my Swift 3.0 changes back to github!
I'm writing some Swift code that i'd like to use in Objective-C. The headers are all generated automatically but when using closures (that become blocks in objective-c), the variable names are missing.
For Example:
#objc public func doSomething(success: (result: String) ->())
becomes
-(void)doSomething:(NSString * _Nonnull)success;
Where i would have expected it to be:
-(void)doSomething:(NSString * result)success;
Is this an Xcode bug or is there a way to specify what the variable should be named?
function and closure in Swift are the same type, you can try something like
// what you have
func boo(mf: String->Void) { mf("alfa") }
boo { (str) -> Void in
print(str)
} // "alfa"
// try this
func foo(str: String) { print(str + " beta") }
// and see the signature in Objective C
boo(foo)
/*
alfa
alfa beta
*/