I recently updated to Xcode 6.3 with Swift 1.2, and received over 300 errors, most of which involved adding an exclamation point. One type of issue that will not go away, however, is the error that "'_' is not convertible to 'StringLiteralConvertable'". This appears multiple times, in these situations:
PFCloud.callFunctionInBackground("modifyUser", withParameters: ["objectId":user.objectId, "key":"won", "value":won as AnyObject],block:nil)
PFCloud.callFunctionInBackground("modifyUser", withParameters: ["objectId":friend.objectId, "key":"parties", "value":played], block:nil)
PFCloud.callFunctionInBackground("modifyUser", withParameters: ["objectId":creator.objectId, "key":"left", "value" :left], block: {
(error) in
self.remainingPotatos = PFUser.currentUser()["left"] as! Int
})
The error appears to be on the strings that are the keys of the dictionary, however, they are by definition StringLiteralConvertable. What gives?
UPDATE
This appears to be another case of Xcode detecting an error but not telling which one. After adding '!' after 'objectId', the error switched to being about the data type of the arguments. The error message is:
Cannot invoke 'callFunctionInBackground' with an argument list of type '(String, withParameters:NSDictionary, block(_)->(_)'
Which part is the problem? (Also, changing error to 'error:NSError?' gives a similar result.
UPDATE
Looking at the Parse documentation, the block signature should be
^(id result, NSError *error)
I tried changing the block to
{
(result, error) in
//code
}
But still receive the same error
Possibly not the only problem, but you appear to have a key in one of your dictionaries of "value"! which isn’t valid in 1.2 (and would have compiled, but not made much sense, in 1.1)
import Foundation
let left = "blah" as NSString
let dict = ["value"!:left]
results in error: '_' is not convertible to StringLiteralConvertible
This was a problem with Xcode detecting the wrong error at the wrong place. Adding an '!' after "currentUser()" fixed the problem, and the lines with nil blocks lost their errors with a clean.
Related
While the Swift compiler (Xcode 7.2) seems perfectly correct in diagnosing an error for some source text equivalent to the following, it took long to detect the actual error made. Reason: the programmer needs to look not at the text marked, but elsewhere, thus mislead, wondering why an optional string and a non-optional string can not be operands of ??...
struct Outer {
var text : String
}
var opt : String?
var context : Outer
context = opt ?? "abc"
Obviously, the last line should have had context.text as the variable to be assigned. This is diagnosed:
confusion2.swift:9:19: error: binary operator '??' cannot be applied\
to operands of type 'String?' and 'String'
context = opt ?? "abc"
~~~ ^ ~~~~~
The message is formally correct. (I am assuming that type checking the left hand side establishes an expected type (Outer) for the right hand side, and this, then, renders the expression as not working, type-wise.) Taken literally, though, the diagnosis is wrong, as is seen when fixing the left hand side: ?? can be applied to operands of type String? and String.
Now, if this is as good as it gets, currently, in terms of compiler messages, what are good coping strategies? Is remembering
Type inference!
Context!
…
a start? Is there a more systematical approach? A check list?
Update (I'm adding to the list as answers come in. Thanks!)
break statements apart, so as to have several lines checked separately (#vacawama)
Beware of optionals (such as values got from dictionaries), see testSwitchOpt below
Another one
enum T {
case Str(String)
case Integer(Int)
}
func testSwitchOpt(x : T?) -> Int {
switch x {
case .Integer(let r): return r
default: return 0
}
}
The compiler says
optandswitch.swift:8:15: error: enum case 'Integer' not found in type 'T?'
case .Integer(let r): return r
A fix is to write switch x! (or a more cautious let), so as to make type checking address the proper type, I guess.
I could, perhaps should, file some report at Apple, but the issue seems to represent a recurring subject—I have seen this with other compilers—and I was hoping for some general and re-usable hints, if you don't mind sharing them.
Swift's type inference system is great in general, but it can lead to very confusing to outright wrong error messages.
When you get one of these Swift error messages that makes no sense, a good strategy is to break the line into parts. This will allow Swift to return a better error message before it goes too far down the wrong path.
For example, in your case, if you introduce a temporary variable, the real problem becomes clear:
// context = opt ?? "abc"
let temp = opt ?? "abc"
context = temp
Now the error message reads:
Cannot assign value of type 'String' to type 'Outer'
I was wondering if there is a way to turn off/avoid 'yellow' warnings in xcode on if let...NSUserDefaults constructs where the key is of a known value.
For example:
if let x = NSUserDefaults.standardUserDefaults().integerForKey("myKey") as? Int {...}
Because of the if let I have to use as?. However, as I am using a known value type (in this case integer) the as? Int is effectively redundant - which is why I am getting the 'yellow warning'.
Thoughts? Is there a better way to code these types of constructs?
My suggestion would be to address the issue instead of silencing the warnings. :)
NSUserDefaults.standardUserDefaults().integerForKey("myKey") does not return an Optional, and the type is known, so you don't need neither optional binding with if let nor type casting with as?.
Just this:
let x = NSUserDefaults.standardUserDefaults().integerForKey("myKey")
will suffice, since .integerForKey just returns 0 if it can't get the actual value.
If you don't like this behavior of getting a default value (I don't), then don't use .integerForKey and use objectForKey with optional binding and type casting instead. Like you were doing first but with .objectForKey replacing .integerForKey. That way you'll get an actual nil if the value for the key is unreachable, not a default value.
if let x = NSUserDefaults.standardUserDefaults(). objectForKey("myKey") as? Int {...}
First of all check always the signature:
⌥-click on the symbol integerForKey: or look at Quick Help.
You will see:
func integerForKey(_ defaultName: String) -> Int
It reveals the return value is a non optional.
Non optionals can retrieved directly as described in Eric's answer without any type casting, optional binding causes an error.
That's one of the essential semantics in Swift.
After updating Xcode to 6.3 today I finally was able to totally remove sqlite.swift and reinstall it. And after having fixed about 50 errors caused by something changing I am down to about 15 errors remaining and all of them have something to do with the new sqlite.swift.
I have searched for new docs to cover the syntax changes to no avail. Some errors I have found other posts about and was able to fix.
So this function that used to work now complains about the ? after the delete()?... The error message is "Optional chain has no effect, expression already produces Int?'. The recommendation is to remove the ?
func delete(id: Int) {
let rows = db[schema.tableName]
rows.filter(schema.id == id).delete()?
}
If I remove the ? after delete() then it tells me "cannot invoke 'delete' with no argument". I searched the source code and the code completion, all of which does not show any arguments.
Also on update statements I now get this error:
Example code:
rows.filter(schema.id == id)
.update(schema.acctID <- acctID, schema.accessCode <- accessCode, schema.status <- 0)
Error: cannot invoke 'update' with an argument list of type '(Setter, Setter, Setter)'
Swift 1.2 removed the ability to coerce using a trailing ?. You can use ! if the statement shouldn't fail:
func delete(id: Int) {
let rows = db[schema.tableName]
rows.filter(schema.id == id).delete()!
}
Or you can chain the delete() call to a tuple member, instead:
rows.filter(schema.id == id).delete().changes
This has been a continual support issue, so the interface may change in the near future.
The update() call needs to be fixed the same way:
rows.filter(schema.id == id)
.update(
schema.acctID <- acctID,
schema.accessCode <- accessCode,
schema.status <- 0)! // or .changes
I just updated Xcode to 6.3 and my parse sdk to 1.7.1. I knew from the apple docs that I was going to spend some time fixing some of my swift code on my iOS app. Xcode is happy with all my fixes except for one func that gets a parse class by id. I've dredged parse, apple, stackoverflow, and google for a fix, but I've been banging my head against this last fix for hours and desperately need some help from one of you super smart guys. I apologize in advance if this turns out to be a stupid question. I've been programming for a really long time, but I'm a noob to iOS.
Below is a simple example of what I'm trying to do, which worked before I updated Xcode and parse sdk. Now Xcode gives me errors for each of the lines where I am trying to get the variables out from the PFObject. Errors for each line is like this: "'AnyObject?' is not convertible to 'String'" and "Cannot assign to immutable value of type 'String". Any insight would be greatly appreciated.
func getFakeObject(objectId: String) -> FakeObject {
var fakeObject: FakeObject = FakeObject()
var query = PFQuery(className:"FakeObject")
var loadedFakeObject = query.getObjectWithId(objectId)
if loadedFakeObject != nil {
fakeObject.objectId = objectId
fakeObject.isActive = loadedFakeObject["isActive"] as! Bool
fakeObject.desc = loadedFakeObject["desc"] as! String
fakeObject.purchasePrice = loadedFakeObject["purchasePrice"] as! Double
fakeObject.purchaseDate = loadedFakeObject["purchaseDate"] as! NSDate
fakeObject.labels = loadedFakeObject["labels"] as [String]
}
return fakeObject
}
Swift 1.2 is a lot stricter about syntax and tries to prevent you from unknowingly doing dangerous things.
Where you write:
var loadedFakeObject = query.getObjectWithId(objectId)
if loadedFakeObject != nil {
Try instead:
if let loadedFakeObject = query.getObjectWithId(objectId) {
In the first case, you are getting an optional (there might be no such object), and then checking it its nil, but then still using the optional. Because it’s optional, you need to write loadedFakeObject!["isActive"] as! Bool. But there’s really no need because instead you can use if let to both check and unwrap the optional, meaning within the if statement, it won’t be optional any more.
You might also want to consider switching to as? instead of as! in various places, but this is probably less the cause of your problem, more of a runtime safety thing.
I am currently learning Swift and I am experimenting with Core Data where I would like to save a linked list. To add an element at the end of the list I have a while-loop like this:
var curr = start
while (curr.nextElem != nil) {
curr = curr.nextElem
}
The problem is though that curr.next != nil seems to be invalid, following error is shown: 'ListElem' is not convertible to UInt8
I noticed this error a few times before but always found a way around comparing but how can this be done in this case?
Your ListElem.nextElem property seems to be returning an actual ListElem, so it can never be nil. For it to be able to be nil, it has to be of optional type (ListElem?).
Also, try the Xcode 6.3 beta — most of the error messages where Swift 1.1 said "I dunno what you're doing, so I'll just say you can't convert it to UInt8" have been replaced with better diagnostics in Swift 1.2.