Type of expression is ambiguous without more context when replacing constant literal value with constant variable - ios

I was wondering, what is a good way to fix the following compiler error?
class A {
var x: Int32
init() {
x = -1
}
}
let a: A? = nil
// No issue.
let ok: Int = Int(a?.x ?? -1)
// Type of expression is ambiguous without more context
let CONSTANT: Int = -1
let not_ok: Int = Int(a?.x ?? CONSTANT)
Any idea why we are getting Type of expression is ambiguous without more context if we use CONSTANT instead of -1?
What is a good way to fix the compiler error (retain same class, same type and same CONSTANT), yet still retain 1 liner?

It's for the same reason that you can say this:
let d = 3.0
let d2 = d + 1
But not this:
let i = 1
let d3 = d + i
Swift will cast the literal 1 to the required numeric type (here, Double), but it won't cast a variable away from its type.
So the answer is: make the types match.

The ?? operator is declared like this:
func ?? <T>(optional: T?, defaultValue: #autoclosure () throws -> T)
rethrows -> T
In the expression Int(a?.x ?? -1), the compiler needs to infer T for the
?? operator. It successfully infers T == Int32, because
the expression a?.x has the type Int32?.
it is okay for ?? to return Int32 here, because Int has an initialiser that takes Int32.
most relevantly, the literal -1 can be converted to Int32, because Int32 conforms to ExpressibleByIntegerLiteral.
If you instead do Int(a?.x ?? CONSTANT), however, that last point wouldn't work. ExpressibleByIntegerLiteral only works with literals, not any constant. The compiler sees CONSTANT as an expression of type Int, and tries very hard to find a type for the expression a?.x ?? CONSTANT, but it can't, and spits out a not-so-useful error message.
I suppose the error message is not very useful because it needs to consider quite a lot of things in this case (return type, the two parameters, overload resolution for Int.init), and it could be rather hard to pinpoint exactly where went wrong.
Anyway, to fix this, you can just convert the constant to Int32:
Int(a?.x ?? Int32(CONSTANT))
Or if you don't want to convert CONSTANT to an Int32 then back to Int again, you can rewrite this as:
a.map { Int($0.x) } ?? CONSTANT
"Map a to its x as an Int if a is not nil, otherwise CONSTANT".

Related

Cannot assign value of type 'String?' to type 'Int'

I am getting the error message Cannot assign value of type 'String?' to type 'Int'
I have browsed through other questions like this but it still shows the error.
if sunscreenName.text != nil && reapplyTime.text != nil {
sunscreen = sunscreenName.text!
reApplyTime = reapplyTime.text
//Some sort of message such as Progress hud
}
Thanks in advance!
I got your problem, actually what happens here Swift is is type safe langauge
So what you are doing is is to store a String value in Int which will not happen automatically you need to convert it to Int
like this
Int(sunscreenName.text)
But there is a catch there not all string are convertible to Int type, fo e.g.
let name = "roshan"
if you try to convert it to Int it will give you a nil
let a = Int(name)
So its better you do a optional Binding here provided by Swift
if let sunValue = Int(sunscreenName.text),let reApplyValue = Int(reapplyTime.text) {
sunscreen = sunValue
reApplyTime = reApplyValue
}
I recommend reading through The Swift Programming Language to get a better understanding of Swift and its fundamental concepts, since this question is fairly basic.
You make several mistakes:
if sunscreenName.text != nil && reapplyTime.text != nil {
This is wrong. In Swift, if you plan to use the value later, you should use if let rather than comparing to nil. Comparing to nil leaves the values optional, but if let unwraps them. So, do this instead:
if let sunscreenText = sunscreenName.text, let reapplyText = reapplyTime.text {
Now you have the sunscreenText and reapplyText variables, which are typed String, not String? (i.e. they are not optional).
Now, there's these two lines.
sunscreen = sunscreenName.text!
reApplyTime = reapplyTime.text
You don't say which one is giving the error, but the issue is the same in either case. First, use our unwrapped sunscreenText and reapplyText variables instead of sunscreenName.text! and reapplyTime.text. Next, if one of these is meant to be an Int instead of a String, cast it. Swift is not like JavaScript, in that it won't automatically convert values from one type to another, so if something is a string and we need an integer, we have to convert it ourselves.
(assuming reapplyTime was the line that was giving the error:)
if let reapplyInt = Int(reapplyText) {
reapplyTime = reapplyInt
}
The reason we have to unwrap is because Int(String) can return nil if the string is something that can't be converted to an integer. Alternately, we could just provide a default value:
reapplyTime = Int(reapplyText) ?? 0 // sets to 0 if it can't parse the string as an integer

Constant 'spacesLeft' inferred to have type '()', which may be unexpected Swift

I am building a Tic Tac Toe game with an AI using Xcode 8 and Swift. Here are the relevant variables I am using that are contributing to the error:
var allSpaces: Set<Int> = [1,2,3,4,5,6,7,8,9]
var playerOneMoves = Set<Int>()
var playerTwoMoves = Set<Int>()
var nextMove: Int? = nil
Inside a function defining how the AI will play there are these variables:
var count = 0
let spacesLeft = allSpaces.subtract(PlayerOneMoves.union(playerTwoMoves))
The latter results in the compiler warning:
Constant 'spacesLeft" inferred to have type '()', which may be unexpected
There is an if statement just below that says:
if allSpaces.subtract(playerOneMoves.union(playerTwoMoves)).count > 0 {
nextMove = spacesLeft[spacesLeft.startIndex.advancedBy(Int(arc4random_uniform(UInt32(spacesLeft.count))))]
}
The condition gives the following error:
Value of tuple type '()' has no member 'count'
The statement gives the following error:
Type '()' has no subscript members
I am struggling to find a solution.
subtract modifies Set in place and doesn't return a value, you want to use subtracting
For the first warning, subtract returns Void, so use subtracting:
let spacesLeft = allSpaces.subtracting(playerOneMoves.union(playerTwoMoves))
For the second error, advancedBy is deprecated, you may change like this:
if spacesLeft.count > 0 {
nextMove = spacesLeft[spacesLeft.index(spacesLeft.startIndex, offsetBy: Int(arc4random_uniform(UInt32(spacesLeft.count))))]
}
Set.subtract is a mutating function, so it modifies the Set in place and its return value is Void, which is just a type alias for an empty tuple, (), hence the warning.
You should call Set.substracting, which is the non-mutating version of subtract and returns Set<Set.Element>.
The subtract(_:) function is a mutating function so it will mutate the Set your using to call the function.
From Apple Docs:
subtract(_:)
Removes the elements of the given set from this set.
The reason you're getting the errors is because this function returns Void which in Swift is a typealias for an empty tuple(from Swift's source code). Since Void has no subscripts nor count property/variable you get those errors.
Maybe you should take a look at the subtracting(_:) function, which returns a different Set.
From Apple Docs:
subtracting(_:)
Returns a new set containing the elements of this set that do not occur in the given set.

Swift3 error 'cannot be applied to operands of type

Getting the following error:
Binary operator '==' cannot be applied to operands of type 'UInt16' and '() -> UInt16'
in this section of code:
let array: [UInt16] = [3,4, 7]
let x = NSNumber(value: 4)
let option = array.filter { (test) -> Bool in
return test == x.uint16Value // compiler complains here
}
print(option) // "[4]"
Normally this type of error means two values of separate class are being compared. Here the compiler thinks I'm comparing a uint16 value with a function that returns a uint16 value.
What I mean to do is call NSNumber's uint16 property getter.
// defined in NSNumber
open var uint16Value: UInt16 { get }
I'm told to instead make a function call by adding parenthesis (e.g .uint16Value()). But this leads to the follow up error:
Cannot call value of non-function type 'UInt16'
I have dropped this exact code into a playground and it runs fantastically.
Has anyone else run into this? Or seen this type of behavior? Have you beaten it?
Update:
When setting to a local variable, no difference is made. I have recently run the migration tool from Swift 2.2 or 2.3 up to swift 3 in Xcode 8.0. It doesn't seem like there is anything wrong with syntax or migration.
let y = x.uint16Value
return test == y // compiler complains here

Swift: assign optional value into non-optional var [duplicate]

I would like to simplify the constant need to do
if let firstName = firstName {
self.name = firstName
}
A possible custom, generic operator to do this would be
infix operator ?= {}
func ?= <T>(inout left: T, right: T?) {
if let right = right {
left = right
}
}
to simplify the previous example to
self.name ?= firstName
This creates an issue where if firstName's value is nil, then Swift will wrap the value in an optional.
var name: String? = "Bob"
var firstName: String? = nil
self.name ?= firstName
print(self.name)
/*
prints "nil" since it is wrapping firstName in an optional when
passed in.
E.g. Optional<nil> gets unwrapped to nil in function and assigned
*/
Any possible fix to the custom operator? I attempted to limit the left-hand parameter to not be an optional but that is not possible with generic's type constraints.
The problem (as you've correctly identified) is that because the left hand side argument is of type T, when you pass an optional into it T will be inferred to be Optional<Whatever>. Because the right hand side argument is T? (and because types can be freely promoted to optionals), it will infer the type to be Optional<Optional<Whatever>>, leading to the confusing double wrapping you're observing.
The solution is to add an overload to deal with the situation when the left hand side argument is also an optional.
infix operator ?= {}
func ?= <T>(inout left: T, right: T?) {
if let right = right {
left = right
}
}
// overload to deal with an optional left handed side
func ?= <T>(inout left: T?, right: T?) {
if let right = right {
left = right
}
}
(Note that in Swift 3, inout should appear before the parameter type)
Now if you use this operator with an optional as the left handed argument, Swift will use the overloaded version instead of the original version, as it'll always favour the more type-specific signature. This means that the right hand side won't get wrapped in a double optional, as it's now of the exact same type as the left hand side argument.
var name: String? = "Bob"
var firstName: String? = nil
name ?= firstName
print(name) // prints: Optional("Bob")
Note that this is similar to what the ?? does, it has two definitions to deal with one side being optional, one side being non-optional & both sides being optional in order to avoid the generation of double wrapped optionals:
#warn_unused_result
public func ??<T>(optional: T?, #autoclosure defaultValue: () throws -> T) rethrows -> T
#warn_unused_result
public func ??<T>(optional: T?, #autoclosure defaultValue: () throws -> T?) rethrows -> T?
I found a interesting way to reduce the overhead of if let by Nil Coalescing Operator
The nil coalescing operator (a ?? b) unwraps an optional a if it
contains a value, or returns a default value b if a is nil. The
expression a is always of an optional type. The expression b must
match the type that is stored inside a.
In short -
nonOptional = optional ?? someDefaultValue
E.g. -
let defaultColorName = "red"
var userDefinedColorName: String? // optional variable
var colorNameToUse = userDefinedColorName ?? defaultColorName
So, here if userDefinedColorName is nil then defaultColorName will be get assighed to non-optional variable colorNameToUse.
For complete reference check this Swift documentation

Does Swift support implicit conversion?

For example, I have the following code:
let numberOfBlocks = 3
let blockWidth = SKSpriteNode(imageNamed: "image.png").size.width
let padding = 20.0
let offsetX : Float = (self.frame.size.width - (blockWidth * numberOfBlocks + padding * (numberOfBlocks-1))) / 2
I got the error:
'Double' is not convertible to 'UInt8'
Is there a way to implicitly convert the data type (maybe only for primitive data type)?
Edit:
I know how to do the explicit conversion by using constructor of particular type as Iducool suggested. But it's not a big help to my question because we even don't know where to add the conversions. I simplified my expression in playground:
The problem is in "padding" variable, the error message is
'Double' is not convertible to 'UInt8'.
So I did the conversion:
Then the problem is in "blockWidth" variable now.
I added the conversion again:
And error message is:
Type 'UInt8' does not conform to protocol 'FloatLiteralCovertible'
The final working expression is:
Is it simple and swift? I don't think so.
There is no implicitly cast in Swift.
Easy way of conversion in swift is using constructor of particular type.
Like if you want to get Float from double then you can use Float(doubleValue) and Same way if you want to convert float to integer then you can use Int(floatValue).
In your case:
let intValue = UInt8(doubleValue)
Beware that you will lose any value after the decimal point. So, choose a better way. Above conversion is just to help you in understanding.
Note that Swift always chooses Double (rather than Float) when inferring the type of floating-point numbers.
Swift doesn't support implicitly cast anymore in Xcode6 GM. Following answer only apply to Xcode6 beta version.
I don't want to talk about implicitly cast is good or bad, but you can have it if you really want with __conversion()
e.g. If you need UInt8 and Int be able to convert from Double
extension Double {
func __conversion() -> UInt8 { return UInt8(self) }
func __conversion() -> Int { return Int(self) }
// add more if you need to
}
xcrun swift
Welcome to Swift! Type :help for assistance.
1> extension Double {
2. func __conversion() -> UInt8 { return UInt8(self) }
3. }
4> var d = 1.0
d: Double = 1
5> var u8 : UInt8 = d
u8: UInt8 = 1
6>
Note: I won't put this in my production code. I only want to point out it if possible but not recommending it.
using bridgeToObjectiveC() method you can call the methods provided in Objective - C to convert from one primitive data type to another for e.g.
variable_name.bridgeToObjectiveC().intValue
will convert that variable named variable_name to integer
Implicit conversion is possible but with literals only and some conversions are available from the box e.g. Int -> Double:
let a = 3 // Int
let b = 100.5 // Double
// Doesn't work with variables
let c = a * b // Error: Binary operator '*' cannot be applied to operands of type 'Int' and 'Double'
// But this works, because Int(3) literal converts to Double(3.0) implicitly
let d = 3 * b // 301.5
If you want to make backward conversion Double -> Int you should extend Int with ExpressibleByFloatLiteral:
extension Int: ExpressibleByFloatLiteral {
public init(floatLiteral value: Double) {
self.init(value)
}
}
// Double(100.5) converts to Int(100)
let e = a * 100.5 // 300
Even more it's possible to implicitly convert to any type from literals, for instance String -> URLRequest:
extension URLRequest: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self.init(url: URL(string: value)!)
}
}
let request: URLRequest = "https://www.google.com"

Resources