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!
Related
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.
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'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
*/
How to convert following block from Objective-C to Swift. Am using Objective-C files in Swift using bridge header. But small confusion in block conversion
Objective-C Block:
+ (void) while:(id)obj name:(void(^)(type*))callback;
Sample output:
[Sub while:keeper viewControllerChanged:^(NSString* newNickname) {
NSLog(#"\nVC2- Now screen is in: %#", newNickname);
}];
How to convert this in swift ?
EDIT:
Swift block error is
Sub.while(obj: AnyObject!, viewControllerChanged: ((String!) -> Void)!)
When you define :
class func while1(obj:AnyObject, callback name:((newNickname:NSString?) -> Void)) {
}
And when call function :
self.while1(self) { (newNickname) -> Void in
print("\nVC2- Now screen is in:" + "\(newNickname)")
}
EDIT :
Okay, Then you just want to call it from swift..right..? Then use this statement :
ClassName.while1(obj as AnyObject) { (nickName:String!) -> Void in
print(nickName)
}
But first make sure that in your definition statement "type" indicates for what DataType, so please define there actual DataType
+ (void)while:(id)obj name:(void(^)(type*))callback;
to --> For example :
+ (void)while1:(id)obj name:(void(^)(NSString *))callback;
And one more thing to note that while in built in keyword, please do not use it if possible.
You can call like this:
YourClassName.while2(Yourparameter , name: {(nickName : String) -> Void in
})
I hope this help.
I'm not sure if you are asking how to write blocks in swift or if I'm not completely getting your question. But if it's the first then...
(void(^)(type*))callback
becomes
callback: (param:type*)->(returnType*)
i.e.
func doSomething(callback:((number:Int)->()))
is called like
doSomething(callback:{number in
print("\(number)")
})
Due to Swift's lack of covariance, I needed some workaround. I'm coming from Java world, so I instinctively tried to create constraint from one type to other generic type.
So I wrote the following class:
class Factory<T: AnyObject> {
let factoryClosure: () -> T
init(closure: () -> T) {
factoryClosure = closure
}
init<CHILD: T>(childFactory: Factory<CHILD>) {
factoryClosure = { () -> T in
return otherFactory.create()
}
}
func create() -> T {
return factoryClosure()
}
}
I expected this to work just fine. I have the T defined and CHILD should be a subclass of T. Swift compiler however disagrees and shows the following error on the line with init<CHILD: T>.
Inheritance from non-protocol, non-class type 'T'
I tried the generic parameter inheritance in different scenario as well. Adding the following method into the class (and removing the init that was causing the compile error).
func to<OTHER where OTHER: AnyObject, T: OTHER>() {
}
This yields basically the same output.
Type 'T' constrained to non-protocol type 'OTHER'
Anything I though may work did not and ended with similar error message. Is this a bug in Swift? Or am I missing something? Or is it a feature of Swift and will never work as I thought?
If you want to pass any Factory<T> where T is of type AnyObject you just have to write:
init(childFactory: Factory<T>) {
factoryClosure = { () -> T in
return otherFactory.create()
}
}
because T is automatically constrained by your class.