When I tried to migrate to Swift 4 an error occurred with my generic typealias for closures if you give it Void as an input. This worked fine in Swift 3 but not so much now, anybody know why, and is this a bug or a feature?
There is a workaround by defying another closure that explicit handles this case. But would be nice to not have to use this workaround and to understand why this error arose in Swift 4.
typealias Handler<T> = (T) -> Void
func foo(completion: Handler<String>) {
completion("fooing")
}
// This worked in Swift 3, but not in Swift 4
func bar(completion: Handler<Void>) {
completion() // Missing argument for parameter #1 in call
}
It seems that this is not a bug but how Swift works now. You cannot omit an associated value of type Void anymore.
I have found two passable workarounds:
The first one just explicitly just pass the value.
func bar(completion: Handler<Void>) {
completion(())
}
The second solution is to declare another typealias for void cases.
typealias VoidHandler = () -> Void
func barbar(completion: VoidHandler) {
completion()
}
The Swift 3 compiler is warning me of an unused result, even though I have marked the function with #discardableResult.
It is only occurring when calling the function on an optional variable, using the ? syntax.
To simplify the problem, I created this sample code. (I had to put it in a project because the warning didn't show in a playground.)
class Foo {
#discardableResult func bar() -> String? {
return "bar"
}
}
class Tester {
func doSomething() {
var foo: Foo?
foo = Foo()
foo?.bar() //Warning: Expression of type 'String?' is unused
foo!.bar() //No warning
}
}
It's a known bug (https://bugs.swift.org/browse/SR-1681), though the Swift gang seems to think it has been resolved. Maybe the fix hasn't made it into Xcode yet, or maybe they're just wrong.
UPDATE We got the official word: The fix will appear starting in Xcode 8.3.
The actual Xcode error i get is:
Cannot convert value of type '(AnyObject?, _) -> ()' to expected argument type 'FOSSAPIResponseHandler?'
The typealias I defined as FOSSAPIResponseHandler is:
public typealias FOSSAPIResponseHandler = ((_ responseObject: Any?, _ error: NSError?) -> Void)
The method where i use that is:
public func getAccount(responseHandler: FOSSAPIResponseHandler?) {
...
}
And the error appears when i use the call:
self.networkingClient.getAccount { (responseObject, error) in
}
The error in xcode appears at the line .getAccount { to in.
Any help would be appreciated as this is literally the last error im seeing in Xcode during this tumultuous time of converting to Swift 3.
I've tried the following code in a clean playground :
typealias FOSSAPIResponseHandler = ((_ responseObject: Any?, _ error: NSError?) -> Void)
class NetworkingClient: NSObject {
func getAccount(responseHandler: FOSSAPIResponseHandler?) {
guard responseHandler != nil else { return }
responseHandler!("Cool", nil)
}
}
let networkingClient = NetworkingClient()
networkingClient.getAccount { (responseObject, error) in
print (responseObject!)
}
It works perfectly (printing Cool in the console). So it should be something else in your code, something we're not seeing in the few lines of code you've shared.
My first guess would be that you store the response handler somewhere in your NetworkingClient class, something similar with this :
var responseHandler: FOSSAPIResponseHandler?
Could you please verify that this var is using the same FOSSAPIResponseHandler typelias and not something else ?
PS. In either case, please add to my playground more and more of your code until you get the error and share it with us please.
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()
}
I'm trying to copy the all function from python to swift, starting off with checking for any nill items in a list, but I'm having a tough time checking optional items. For some reason I can send a optional string (string for example) and even though it says it's nil it still passes thru an if statement, which it doesn't outside of the function. Any advice about how to deal with this or another way of doing it? Thanks!
func `all`<T>(array: [T]) -> Bool {
for item in array {
if item as Any? {
println(item) // Says Nil >.<
}
var test: T? = item
if test {
println("Broken") // Prints broken :(
}
}
return true
}
var t: String?
all([t])
It's unclear to me exactly what you're trying to test, but maybe this will help.
The parameter to the function should be an Array of optionals [T?]
It may also be beneficial to directly compare elements to nil. The comparison could be abstracted to a closure much like the filter function uses.
func all<T>(array: [T?]) -> Bool {
for element in array {
if element==nil {
return false
}
}
return true
}
I know this is old, but it still being searched for. Just spent couple of hours looking for a solution and I think I have finally found one.
if (item as AnyObject) is NSNull {
//value is nil
}