Can someone please explain why if, as it says in the import definition:
typealias SKColor = UIColor
I get the error 'UIColor!' is not identical to 'SKColor' when I do the following? I was about to say 'I know the difference between UIColor and UIColor!' but actually, maybe I don't truly understand!
import UIKit
import SpriteKit
func nColours(gradient: [SKColor]) -> Int {
return gradient.count
}
let gradient = [SKColor.redColor(), SKColor.magentaColor()]
nColours([SKColor.redColor(), SKColor.magentaColor()]) // 2, OK
nColours(gradient) // <<<<<< Error
// 'UIColor!' is not identical to 'SKColor'
experimenting, I tried this :
let gradient = [SKColor.redColor(), SKColor.magentaColor()]
let b = (gradient == [SKColor.redColor(), SKColor.magentaColor()]) // <<<<<< Error
// '[UIColor!]' is not convertible to '_ArrayCastKind'
The problem is that UIColor and UIColor! are technically different types - the ! stands for Implicitly Unwrapped Optional and is used where there is an optional value, but that optional should always have a value.
It seems that many objects returned from the system frameworks use implicitly optional types, though this usage should get less common as the frameworks are fully converted to Swift - there's no reason UIColor.redColor() would ever return a nil color, so its return type will probably change from SKColor! to SKColor in the future.
In your code example, if you change your let gradient declaration to explicitly declare the array as being of type [SKColor] (as opposed to SKColor!) the compiler happily carries on:
let gradient : [SKColor] = [SKColor.redColor(), SKColor.magentaColor()]
Related
I'm trying to convert height in feet with a decimal to height in feet and inches.
I'm trying to use the .round(.down) method to get the feet, and multiply the decimal by 12 for the inches. I'm getting all kinds of errors, like so:
var heightInFeet: Float = 5.45
let feetRounded = heightInFeet.round(.down) // feetRounded is "type ()." What is that?
percentToNextFoot = heightInFeet - feetRounded // Error: Binary operator '-' cannot be applied to operands of type 'Float' and '()'
I tried the following and got another error:
percentToNextFoot = heightInFeet - Float(feetRounded) // Cannot invoke initializer for type 'Float' with an argument list of type '(())'
I finally got it working by avoiding the .round() method, but I'm still really confused by the value type (). Can anyone explain what's going on here?:
var heightInFeet: Float = 5.45
var feet = Int(heightInFeet) // 5
var percentToNextFoot = heightInFeet - Float(feet) // 0.45
let heightInFeetAndInches = "\(feet)ft \(Int(percentToNextFoot * 12))in" // 5ft 5in
() is shorthand for void. It means "no value is possible here".
In this example, it means that the .round() method does not return anything - it is a mutating function called on its receiver. So assigning its void return to a var causes that var's type to be inferred to be void. Void vars can be useful, sometimes, rarely*, but not in this case.
Methods on value types often come in pairs: a verb like round, and a passive verb e.g. rounded. The first operates directly on, and modifies, its target; the second returns a modified version of its target. For another example, see sort() and sorted() on collections, or append(_) and appending(_) on strings, etc.
(* note: filter is an annoying exception; it means "filtered", and there is no handy "filter in place".)
To get the effect you were going for in the first example, rounded() is what you want.
--
(* To answer the tangential question in your title: how would one actually use a void variable? Well, here's a way I use them sometimes:
In an object with some setup that I would like to happen sometime after init, but guaranteed at most once per instance, I used to use Objective-C's dispatch_once. That's not available in Swift, so now I'll make a lazy void member like so:
class Foo {
lazy var setup: () = {
// do some complicated stuff I couldn't do in `init` for some reason
// this will only run once
}()
func doSomethingUseful() {
_ = setup // runs the setup initializer just the first time we get here
// do useful things that depend on setup having happened
}
}
I'll leave it to the comments to judge whether we're "meant to" use such a technique. :)
Welcome to stack overflow!
Double has two rounding methods:
Double.round(), which rounds a Double value by mutating it in-place. This is one you called. It doesn't return anything, which strictly speaking means it returns Void, a.k.a. (), the empty tuple.
Double.rounded(), which rounds a Double value by returning a new Double. This is the one you probably intended to call.
By calling the first, and trying to assign the value to a variable, you end up with a variable of type Void, whose value is ().
This is a common Swift convention: "object.foo" edits it in place. "object.fooed" returns a mutated copy.
That said, in your case, I would recommend doing this using the existing Measurement API:
import Foundation
extension Measurement where UnitType == UnitLength {
func toFeetAndInches() -> (feet: Measurement<UnitLength>, inches: Measurement<UnitLength>) {
let numberOfWholeFeet = self.converted(to: .feet).value.rounded(.towardZero)
return (
feet: Measurement(value: numberOfWholeFeet, unit: UnitLength.feet),
inches: Measurement(value: self.value - numberOfWholeFeet, unit: UnitLength.feet).converted(to: .inches)
)
}
}
let heightInFeet = Measurement(value: 5.5, unit: UnitLength.feet)
let (feet, inches) = heightInFeet.toFeetAndInches()
let mf = MeasurementFormatter()
mf.unitOptions = .providedUnit // force the use of feet/inches, rather than the unit appropriate for the user's locale.
mf.unitStyle = .medium
print(mf.string(for: feet)!, mf.string(for: inches)!) // => "5 ft. 6 in."
If you look at the reference for the round function of Float type, you will see that it returns nothing. It just mutate the float you called this method on.
You can do
var feetRounded = heightInFeet
feetRounded.round(.down)
Please take a look at the documentation. The method
mutating func round(_ rule: FloatingPointRoundingRule)
has no return value (aka Void aka ())
If you need a result you have to use rounded(_:) which has a return value
func rounded(_ rule: FloatingPointRoundingRule) -> Float
round changes the value in place; you use it like this:
var heightInFeet: Float = 5.45
heightInFeet.round(.down)
You notice that no value is returned; there is no = in the second line. We do not need to set anything to the result of the round call, because it has no result.
If, as in your code, you accidentally do capture the "result", it is expressed as type (). So () is the "result" type of a method call that has no result.
When we accidentally write this:
var heightInFeet: Float = 5.45
let x = heightInFeet.round(.down)
we get this error: "Constant 'x' inferred to have type '()', which may be unexpected." That is just a fancy way of saying, "You've taken a method call that has no result and captured its 'result'. You probably didn't mean to do that!" And indeed, you didn't.
According to Apple Doc,
The Any type represents values of any type, including optional types.
Swift gives you a warning if you use an optional value where a value
of type Any is expected. If you really do need to use an optional
value as an Any value, you can use the as operator to explicitly cast
the optional to Any, as shown below.
var things = [Any]()
things.append(3) // No warning
let optionalNumber: Int? = 3
things.append(optionalNumber) // Warning, even though Any also represents optional types.
things.append(optionalNumber as Any) // No warning
Why do we need to explicitly cast the optional to Any?
Every type can be implicitly promoted to an optional of that type. This means that when you cast T? to Any it is very hard to know whether it was originally T or originally T? (or even T?? or worse). Most confusing is that Any can be promoted to Any? and that Any? is of type Any, so telling the difference between Any, Any?, Any??, and Any??? (etc.) is very tricky, and sometimes impossible.
Any is a very tricky type in Swift and should almost never be used. Except for explicitly tricking the compiler (in some very fancy and fragile type-eraser), I don't know of any case where it really makes sense to have Any as a variable type, and definitely not in the form of [Any]. If you're created an [Any], you've gone down a bad path that isn't going to go well.
There are a very few cases where Any as a function parameter type makes sense (print() being the most famous), but they are extremely rare in app-level code. If you find yourself needing Any, you've probably done something wrong, and the compiler is going to fuss at you about it and often make you write extra as code to make sure you really mean the messy things you're saying.
Just to give some concrete versions of this, optionality tends to be lost when you enter Any. So consider this situation:
let number: Int = 3
let optionalNumber: Int? = 3
let nilNumber: Int? = nil
let anyNumber = number as Any
let anyOptional = optionalNumber as Any
let anyNil = nilNumber as Any
if anyNumber is Int { print("number is Int")} // yes
if anyOptional is Int { print("optional number is Int")} // yes
if anyNil is Int { print("nil is Int")} // no
if anyNil is Int? { print("nil is Int?")}
// -> Error: Cannot downcast from 'Any' to a more optional type 'Int?'
Rats.
We can't get our optional back the same way we put it in. We can promote it of course:
if (anyNil as Any?) is Int? { print("nil is Int?") } // yes
But we can promote anything that way, since everything is implicitly an optional of itself:
if (anyNumber as Any?) is Int? { print("number is Int?")} // also yes
So, um. Rats. We don't really know if it was originally optional or not. It's mess, and the compiler is warning you that it's going to be a mess if you go very far down this road. T->Any is a bit of magic. T->T? is also a bit of magic. Combine the two magics, and you had better know exactly what you're doing.
How does covariance work for Optionals in Swift?
Say I write the following code:
var nativeOptionalView: Optional<UIView>
let button = UIButton()
nativeOptionalView = .Some(button)
var nativeOptionalButton = Optional.Some(button)
nativeOptionalView = nativeOptionalButton
It compiles and works just fine. However if I define MyOptional as
enum MyOptional<T> {
case Some(T)
case None
}
And write the following:
var myOptionalView: MyOptional<UIView>
let button = UIButton()
myOptionalView = .Some(button)
var myOptionalButton = MyOptional.Some(button)
myOptionalView = myOptionalButton
I get the error:
error: cannot assign value of type 'MyOptional<UIButton>' to type 'MyOptional<UIView>'
I understand why this errors happens with MyOptional, what I don't understand is why it doesn't happen with Optional.
It doesn't. Swift does not support custom covariant generics for now.
The Swift type checker is per expression, not global (as in Haskell). This task is handled by the Semantic Analysis in lib/Sema. The constraint system then tries to match the types and special cases of covariance are then handled for collections, and optionals.
This was a language design decision. You should be able to do everything you need with the built-in collection types and optionals. If you aren't you should probably open a radar.
While I agree that there is probably some "compiler magic" going on, this can be accomplished in your custom implementation by casting the button to a UIView, e.g.
var myOptionalButton = MyOptional.Some(button as UIView)
or
var myOptionalButton: MyOptional<UIView> = .Some(button)
I will first explain what I'm trying to do and how I got to where I got stuck before getting to the question.
As a learning exercise for myself, I took some problems that I had already solved in Objective-C to see how I can solve them differently with Swift. The specific case that I got stuck on is a small piece that captures a value before and after it changes and interpolates between the two to create keyframes for an animation.
For this I had an object Capture with properties for the object, the key path and two id properties for the values before and after. Later, when interpolating the captured values I made sure that they could be interpolated by wrapping each of them in a Value class that used a class cluster to return an appropriate class depending on the type of value it wrapped, or nil for types that wasn't supported.
This works, and I am able to make it work in Swift as well following the same pattern, but it doesn't feel Swift like.
What worked
Instead of wrapping the captured values as a way of enabling interpolation, I created a Mixable protocol that the types could conform to and used a protocol extension for when the type supported the necessary basic arithmetic:
protocol SimpleArithmeticType {
func +(lhs: Self, right: Self) -> Self
func *(lhs: Self, amount: Double) -> Self
}
protocol Mixable {
func mix(with other: Self, by amount: Double) -> Self
}
extension Mixable where Self: SimpleArithmeticType {
func mix(with other: Self, by amount: Double) -> Self {
return self * (1.0 - amount) + other * amount
}
}
This part worked really well and enforced homogeneous mixing (that a type could only be mixed with its own type), which wasn't enforced in the Objective-C implementation.
Where I got stuck
The next logical step, and this is where I got stuck, seemed to be to make each Capture instance (now a struct) hold two variables of the same mixable type instead of two AnyObject. I also changed the initializer argument from being an object and a key path to being a closure that returns an object ()->T
struct Capture<T: Mixable> {
typealias Evaluation = () -> T
let eval: Evaluation
let before: T
var after: T {
return eval()
}
init(eval: Evaluation) {
self.eval = eval
self.before = eval()
}
}
This works when the type can be inferred, for example:
let captureInt = Capture {
return 3.0
}
// > Capture<Double>
but not with key value coding, which return AnyObject:\
let captureAnyObject = Capture {
return myObject.valueForKeyPath("opacity")!
}
error: cannot invoke initializer for type 'Capture' with an argument list of type '(() -> _)'
AnyObject does not conform to the Mixable protocol, so I can understand why this doesn't work. But I can check what type the object really is, and since I'm only covering a handful of mixable types, I though I could cover all the cases and return the correct type of Capture. Too see if this could even work I made an even simpler example
A simpler example
struct Foo<T> {
let x: T
init(eval: ()->T) {
x = eval()
}
}
which works when type inference is guaranteed:
let fooInt = Foo {
return 3
}
// > Foo<Int>
let fooDouble = Foo {
return 3.0
}
// > Foo<Double>
But not when the closure can return different types
let condition = true
let foo = Foo {
if condition {
return 3
} else {
return 3.0
}
}
error: cannot invoke initializer for type 'Foo' with an argument list of type '(() -> _)'
I'm not even able to define such a closure on its own.
let condition = true // as simple as it could be
let evaluation = {
if condition {
return 3
} else {
return 3.0
}
}
error: unable to infer closure type in the current context
My Question
Is this something that can be done at all? Can a condition be used to determine the type of a generic? Or is there another way to hold two variables of the same type, where the type was decided based on a condition?
Edit
What I really want is to:
capture the values before and after a change and save the pair (old + new) for later (a heterogeneous collection of homogeneous pairs).
go through all the collected values and get rid of the ones that can't be interpolated (unless this step could be integrated with the collection step)
interpolate each homogeneous pair individually (mixing old + new).
But it seems like this direction is a dead end when it comes to solving that problem. I'll have to take a couple of steps back and try a different approach (and probably ask a different question if I get stuck again).
As discussed on Twitter, the type must be known at compile time. Nevertheless, for the simple example at the end of the question you could just explicitly type
let evaluation: Foo<Double> = { ... }
and it would work.
So in the case of Capture and valueForKeyPath: IMHO you should cast (either safely or with a forced cast) the value to the Mixable type you expect the value to be and it should work fine. Afterall, I'm not sure valueForKeyPath: is supposed to return different types depending on a condition.
What is the exact case where you would like to return 2 totally different types (that can't be implicitly casted as in the simple case of Int and Double above) in the same evaluation closure?
in my full example I also have cases for CGPoint, CGSize, CGRect, CATransform3D
The limitations are just as you have stated, because of Swift's strict typing. All types must be definitely known at compile time, and each thing can be of only one type - even a generic (it is resolved by the way it is called at compile time). Thus, the only thing you can do is turn your type into into an umbrella type that is much more like Objective-C itself:
let condition = true
let evaluation = {
() -> NSObject in // *
if condition {
return 3
} else {
return NSValue(CGPoint:CGPointMake(0,1))
}
}
I'm very new to swift, but proficient in other languages like Java, JavaScript, C, ... I'm lost with Swift syntax when it comes to create expressions. Look at this basic example where I just try to find out if one string is contained into another by calling String.rangeOfString that returns an Optional Range (Range?)
This works as expected:
let LEXEMA:String="http://"
let longUrl:String="http://badgirls.many/picture.png"
let range=longUrl.rangeOfString(LEXEMA);
if (range? != nil) {
// blah
}
Now I'm trying to combine the expression inside the if, something like:
if (longUrl.rangeOfString(LEXEMA)? !=nil) {
// blah
}
But I always get syntax errors, the above yields a "Expected Separator" and can't understand why. Done some more tests:
if (absolutePath.rangeOfString(URL_LEXEMA) !=nil) { }
Expected Separator before "!"
if absolutePath.rangeOfString(URL_LEXEMA) !=nil { }
Braced block of statements is an unused closure
What am I doing wrong?
If you’re coming from other like Java, you might be thinking of optionals like pointers/references, and so used to equating them to nil and if non-nil, using them. But this is probably going to lead to more confusion. Instead, think of them like a container for a possible result, that you need to unwrap to use. if let combines the test and unwrapping operation.
With this in mind, here’s how you could adapt your code:
let LEXEMA: String="http://"
let longUrl: String="http://badgirls.many/picture.png"
if let range = longUrl.rangeOfString(LEXEMA) {
// use range, which will be the unwrapped non-optional range
}
else {
// no such range, perhaps log an error if this shouldn’t happen
}
Note, that ? suffixing behaviour you were using changes in Swift 1.2 so even the code in your question that compiles in 1.1 won’t in 1.2.
It’s possible that sometimes you are whether there was a value returned, but you don’t actually need that value, just to know it wasn’t nil. In that case, you can compare the value to nil without the let:
if longUrl.rangeOfString(LEXEMA) != nil {
// there was a value, but you don't care what that value was
}
That said, the above is probably better expressed as:
if longUrl.hasPrefix(LEXEMA) { }
For starters:
You don't need parenthesis with if statements unless you have nested parenthetical subexpressions that require it.
You don't need to specify the type on the left side of the = of a let or var declaration if Swift can figure it out from the right side of the =. Very often Swift can figure it out, and you can tell that Swift can figure it out, so you can avoid that redundant clutter.
You do need to specify the type if Swift cannot figure out the type from
the right side. Example:
For example, consider the following lines:
let LEXEMA = "http://"
let longUrl = "http://badgirls.many/picture.png"
Swift can figure out that they're strings.
Similarly for this function or class that returns a UIView:
var myView = ViewReturningClassOrFunc()
Consider this:
#IBOutlet var myView : UIView!
In the above line, Swift cannot figure out ahead of time it will be assigned a UIView, so you have to provide the type. By providing a ! at the end you've made it an implicitly unwrapped optional. That means, like ?, you're indicating that it can be nil, but that you are confident it will never be nil at the time you access it, so Swift won't require you to put a ! after it when you reference it. That trick is a time saver and big convenience.
You should NOT add the ? to the line:
if (longUrl.rangeOfString(URL_LEXEMA) !=nil) {
As another answer pointed out, you're missing the let.
if let longUrl.rangeOfString(URL_LEXEMA) {
println("What do I win? :-)")
}
swift is case sensitive language. you need to check about whitespaces as well
if longUrl.rangeOfString(LEXEMA) != nil {
//your condition
}
there should be space between statement != nil
Just add a space between != and nil like:
if longUrl.rangeOfString(LEXEMA) != nil {
// blah
}
I tested your code in playground, an error of Expected ',' separator reported.
And do not forget the rules that 1s and 0s and Airspeed Velocity said.