I've had a fair few warnings and errors in my Swift code since updating to the latest Xcode 6 DP3. Most have been resolved by adopting the newly changed syntax however there is one error which seems strange.
The following code gives the error Type 'NSDictionary?' does not conform to protocol 'Equatable':
if (launchOptions != nil && launchOptions![UIApplicationLaunchOptionsRemoteNotificationKey] != nil) {
Does anyone have a solution? I'm probably overlooking something simple here..!
Thanks
There is a regression in Beta 3 causing that Optional<T> cannot be compared with nil if T is not Equatable or Comparable.
It's a bug caused by the removal of the _Nil type for which the equality operators were defined. nil is now a literal. The bug has been confirmed by Chris Lattner on Apple Dev Forums
Some workarounds:
You can still use .getLogicValue()
if launchOptions.getLogicValue() && ... {
or directly
if launchOptions && ... { //calls .getLogicValue()
or you can use the "Javascript object to boolean" solution
var bool = !!launchOptions
(first ! calls the getLogicValue and negates, the second ! negates again)
or, you can define those equality operators by yourself until they fix it:
//this is just a handy struct that will accept a nil literal
struct FixNil : NilLiteralConvertible {
static func convertFromNilLiteral() -> FixNil {
return FixNil()
}
}
//define all equality combinations
func == <T>(lhs: Optional<T>, rhs: FixNil) -> Bool {
return !lhs.getLogicValue()
}
func != <T>(lhs: Optional<T>, rhs: FixNil) -> Bool {
return lhs.getLogicValue()
}
func == <T>(lhs: FixNil, rhs: Optional<T>) -> Bool {
return !rhs.getLogicValue()
}
func != <T>(lhs: FixNil, rhs: Optional<T>) -> Bool {
return rhs.getLogicValue()
}
Example:
class A {
}
var x: A? = nil
if x == nil {
println("It's nil!")
}
else {
println("It's not nil!")
}
However, this workaround might cause other subtle problems (it probably works similarily to the _Nil type in Beta 2 which was removed because it was causing problems...).
The release notes of XCode 6 Beta 5 state the following:
Optionals no longer conform to the BooleanType (formerly LogicValue)
protocol, so they may no longer be used in place of boolean
expressions (they must be explicitly compared with v != nil). This
resolves confusion around Bool? and related types, makes code more
explicit about what test is expected, and is more consistent with the
rest of the language.
Note that ImplicitlyUnwrappedOptional still includes some BooleanType
functionality. This !issue will be resolved in a future beta.
(17110911)!
This means your previous approach should work now without any issues, just go back to it:
if (launchOptions != nil && launchOptions![UIApplicationLaunchOptionsRemoteNotificationKey] != nil) {
// some code
}
As #Sulthan figured out, this is a bug in the current beta release of the Swift compiler.
But note that an optional is itself a LogicValue that can be tested for its
boolean value. So you can simply write
if launchOptions && launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] {
// ...
}
without comparing to nil.
This compiles for me but I'm not sure if it works as intended:
if launchOptions![UIApplicationLaunchOptionsRemoteNotificationKey] !== nil {}
This is most likely a side-effect by the change of nil to a literal in beta 3:
• nil is now a literal in the language, not a global constant of _Nil
type. This change resolved a number of problems with nil; e.g. nil
in a collection, nil converting to Any, etc. Types can now indicate
that they are nil compatible by conforming to the
NilLiteralConvertible protocol. (16951729)
For some reason it just complains when its an optional returned from indexing a dictionary, I have a feeling this will be fixed in the future. Submit a bug report though!
Related
In the code below, I'm casting away the nullability by using the bang operator.
void main() {
String? foo;
foo!.toLowerCase();
}
But when I use assert just before calling the function, I get an error.
void main() {
String? foo;
assert(foo != null);
foo.toLowerCase(); // Error
}
Is bang operator not doing the same thing behind the scene?
Note: I'm not looking for a solution to how to make it work, there are many ways, even the flow statement ones, if (foo == null) return;
(Adding this an answer instead of a comment.)
The point of asserts is that they can be disabled and that they will not incur any runtime penalty. It's been some matter of debate, but the current philosophy is that this means that asserts will not perform type promotion, so even if you do:
bool foo(Object object) {
assert(object is String);
// error: The getter 'isEmpty' isn't defined for the type 'Object'
return object.isEmpty;
}
For the same reason, assert(someLocalVariable != null) will not promote someLocalVariable to a non-nullable type:
bool bar(String? string) {
assert(string != null);
// error: The property 'isEmpty' can't be unconditionally accessed
// because the receiver can be null.
return string.isEmpty;
}
As of Dart 2.12 with null-safety enabled, however, you can get the desired effect by just performing the cast directly. That will promote the type and throw a runtime exception if the cast fails:
bool foo(Object object) {
object as String;
return object.isEmpty;
}
bool bar(String? string) {
string!;
return string.isEmpty;
}
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.
A coworker of mine can't compile the project (I think he's on an older version of Xcode, I'm on 11.2.1). But for some reason the code below has been working and compiling for me? I would think I should be getting syntax errors because of missing return statements?
override func serviceType() -> ServiceType {
.base
}
override func operationType() -> NQOType {
.Post
}
override func doesSupportOffline() -> Bool {
false
}
Look at here: https://docs.swift.org/swift-book/LanguageGuide/Functions.html
Functions With an Implicit Return: If the entire body of the function
is a single expression, the function implicitly returns that
expression. For example, both functions below have the same behavior:
func greeting(for person: String) -> String {
"Hello, " + person + "!"
}
The entire definition of the greeting(for:) function is the greeting
message that it returns, which means it can use this shorter form.
This works now as Swift 5.1 supports implicit return statements. If the body of a function is a single statement, the return keyword can be safely omitted as this statement is considered the return statement. This also applies for closures. More info can be found at the Swift Documentation
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.
After the update of iOS 9.3 and the new update for Swift 2.2 (at least that was stated in the Xcode update changes) the code below doesn't work for me anymore.
if context[indexPath.row].dynamicType.className() == "MyClass"
it throws an exception which states:
Value of type 'AnyObject.Type' has no member 'className'
I am going through the changes which are described in swift.org but I can't find the answer of it.
Why bother with Strings when you have true compiler support in the type system itself? I recommend instead:
if context[indexPath.row].dynamicType == MyClass.Type.self {
// ...
}
Also, it's quite likely that you really mean:
if let cell = context[indexPath.row] as? MyClass {
// ...
}
unless you're intent on cutting out derived classes forever.
I don't know what className() does. It's not a known method on AnyClass.
There are a few ways you can fix this.
Use description() to get a String description of the class name:
if context[indexPath.row].dynamicType.description() == "MyClass" {
// ...
}
Or if you're using Objective-C types you can use NSClassFromString to return an AnyClass from a String:
if context[indexPath.row].dynamicType == NSClassFromString("MyClass") {
// ...
}
Swift 3:
dynamicType has been renamed to type(of:). Usage:
type(of: context[indexPath.row]) == MyClass.self
or
context[indexPath.row] is MyClass