NSFontAttributedString worked before XCode 6.1 - swift

let timeFont = [NSFontAttributeName:UIFont(name: "Voyage", size: 20.0)]
var attrString3 = NSAttributedString("(Time)", attributes : timeFont); // <--- compiler error "Extra argument in call"
This code worked in xcode 6.0, but now that I've upgraded to xcode 6.1 it doesn't work anymore and I can't figure out what I need to get it back working. It says that there is an extra argument, but that's not correct. I believe that it has something to do with the new failable initializers, but everything that I've tried doesn't' work.

There are two reasons your code is failing to compile:
The initializer for NSAttributedString that you want to use now requires the explicit labeling of the string parameter
The UIFont initializer that you are using now returns an optional (i.e., UIFont?), which needs to be unwrapped before you pass it in the attributes dictionary.
Try this instead:
let font = UIFont(name: "Voyage", size: 20.0) ?? UIFont.systemFontOfSize(20.0)
let attrs = [NSFontAttributeName : font]
var attrString3 = NSAttributedString(string: "(Time)", attributes: attrs)
Note the use of the new coalescing operator ??. This unwraps the optional Voyage font, but falls back to the System Font if Voyage is unavailable (which seems to be the case in the Playground). This way, you get your attributed string regardless, even if your preferred font can't be loaded.

Xcode 6.1 comes with Swift 1.1 that supports constructors that can fail. UIFont initialisation can fail and return nil. Also use string: when creating NSAttributedString:
if let font = UIFont(name: "Voyage", size: 20.0) {
let timeFont = [NSFontAttributeName:font]
var attrString3 = NSAttributedString(string: "(Time)", attributes : timeFont)
}

Related

Swift Xcode warning / Expression implicitly coerced from '[NSAttributedString.Key : UIFont?]' to '[NSAttributedString.Key : Any]' [duplicate]

Someone else must have received this message while (or after) converting an iOS project to Swift 3, yet when I do a Google search, I get no relevant results.
Anyway, after converting to Swift 3, I have about 30 warnings that say:
Expression implicitly coerced from 'UIView?' to Any
But the warnings do not point to any specific line of code. They only reference the class where the warning exists.
Does anyone have an insight into this warning or how I might go about silencing them?
In my case it was an issue related to a dictionary without explicit type:
let dict = ["key": value]
Than I solved specifying the type:
let dict: [String: Any] = ["key": value]
In your case you can specify your value type:
let dict: [String: UIView] = ["key": value]
This will happen when the function you are calling has a parameter of type Any, and you are passing an optional.
For example:
let color: UIColor? = UIColor.red
UIBarButtonItem.appearance().setTitleTextAttributes([NSFontAttributeName: color], for: .normal)
Notice that color is of type UIColor? and that setTitleTextAttributes expects a dictionary of type [String: Any]?.
In order to avoid the warning you have to either force unwrap your optional, or cast it to Any.
UIBarButtonItem.appearance().setTitleTextAttributes([NSFontAttributeName: color!], for: .normal)
or
UIBarButtonItem.appearance().setTitleTextAttributes([NSFontAttributeName: color as Any], for: .normal)
Looks like a bug in the Swift compiler:
https://bugs.swift.org/browse/SR-2921
Currently, I'm seeing this with Xcode 8.1 and 8.2 beta 1.
In your case, the warning should identify the source file, but not the line - as you stated. You will have to hunt around for calls to functions with Any parameters.
Good new is that it appears fixed in an upcoming Swift toolchain.
I believe this is fixed in Xcode 8.3 beta 1 (but have not confirmed)
Problem
The expected type is Any but the type provided was UIView?
The problem is with the optional, just make sure an instance of UIView is passed and things would work.

Swift 5.5 AttributedString doesn't show in interface

Here's the Swift 5.5 way to make an attributed string:
let s = "Hello"
var attrib = AttributedString(s)
if let range = attrib.range(of: "ell") {
attrib[range].foregroundColor = .red
self.label.attributedText = NSAttributedString(attrib)
}
The problem is that the label doesn't show any red characters. In fact, I can print self.label.attributedText and I can see that the red color attribute didn't even make it into the NSAttributedString. What's going on?
By default, Swift thinks you're applying the SwiftUI attributes, which UIKit doesn't understand. You have to specify what kind of attributed string attribute this is:
attrib[range].uiKit.foregroundColor = .red
// ^^^^^

Swift Compiler thinks that a property is optional (tintColor in UINavigationBar), even though is defined as non-optional [duplicate]

This
NSAttributedString.Key.foregroundColor: view.tintColor
Triggers this warning
Expression implicitly coerced from 'UIColor?' to 'Any'
But shouldn't that warning be
Expression implicitly coerced from 'UIColor?' to 'UIColor'
Since this property
NSAttributedString.Key.foregroundColor
is of type UIColor?
Note: This only started happening after updating to Swift 5, Xcode 10.2.
Here's more context:
override func viewDidLoad() {
super.viewDidLoad()
UIBarButtonItem.appearance().setTitleTextAttributes(
[
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 40),
NSAttributedString.Key.foregroundColor: view.tintColor
], for: .normal)
}
This has nothing to do with .foregroundColor. It has everything to do with .tintColor and setTitleTextAttributes.
This parameter is of type [NSAttributedString.Key : Any]. It is not in any way considering the documentation for each key. It doesn't know or care that this should be a UIColor. If you passed "squid", this would compile without warning (it wouldn't work, but it would compile):
UIBarButtonItem.appearance().setTitleTextAttributes(
[
.font: UIFont.systemFont(ofSize: 40),
.foregroundColor: "squid",
], for: .normal)
All it's looking at is that you're assigning view.tintColor to a value of type Any.
The problem is that view.tintColor is not UIColor, it's UIColor!. It's not actually possible for .tintColor to be nil, but it's possible to set it to nil:
view.tintColor // r 0.0 g 0.478 b 1.0 a 1.0
view.tintColor = .red
view.tintColor // r 1.0 g 0.0 b 0.0 a 1.0
view.tintColor = nil
view.tintColor // r 0.0 g 0.478 b 1.0 a 1.0
That makes sense in ObjC, but the only way to express it in Swift is to use a ! type. When you assign ! types to other things, they become ? types. And that means that you're using UIColor? in a place that accepts Any (the value of the dictionary).
Using an optional as Any can be dangerous because it creates a lot of weird corner cases. For example, you can't round-trip an optional through Any; it gets squashed into its base type:
let x: Any = Optional(1)
x as? Int? // Cannot downcast from 'Any' to a more optional type 'Int?'
x as? Int // 1
There are a lot of these kinds of little sharp-edges when working with Any.
Of course you didn't mean to work with Any. It's not your fault. But this is why Swift is complaining.
There are several solutions, depending on what you like. You can use a !:
.foregroundColor: view.tintColor!
You can add as Any to silence the warning:
.foregroundColor: view.tintColor as Any
Personally I'd use as Any.
Or you can be elaborate and unload the value earlier (I don't recommend this):
let tintColor = view.tintColor ?? .blue
UIBarButtonItem.appearance().setTitleTextAttributes(
[
.font: UIFont.systemFont(ofSize: 40),
.foregroundColor: tintColor,
], for: .normal)
setTitleTextAttributes expects a dictionary of [NSAttributedString.Key : Any]
Everything can be treated as Any and that's why this warning doesn't appear in any other case. The only time it appears is when you do it with Optional. Compiler just wants you to make sure that you know what you're doing with your optional :)
You asked why it happens in Xcode 10.2 and Swift 5?
It always worked well for optionals that were declared this way:
let optionalNumber: Int? = 5
and never for optionals that were declared this way:
let implicitlyUnwrappedOptionalNumber: Int! = 5
look at your example:
view.tintColor
is
Why it didn't worked for implicitly unwrapped optionals? Because before Swift 5, ImplicitlyUnwrappedOptional and Optional were two different types! And as I wrote before: It always worked well for optionals (and didn't work for ImplicitlyUnwrappedOptional).
Right now they are the same type, but implicitly unwrapped optionals have special #_autounwrapped mechanism to differentiate the two.
Thay started removal of this type in Swift 4.2:
https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md
This proposal seeks to limit the adoption of IUOs to places where they
are actually required, and put the Swift language on the path to
removing implicitly unwrapped optionals from the system entirely when
other technologies render them unnecessary. It also completely
abolishes any notion of IUOs below the type-checker level of the
compiler, which will substantially simplify the compiler
implementation.
but apparently they completed it in Swift 5:
https://forums.swift.org/t/possible-misdiagnosis-of-se-0054/9546
ImplicitlyUnwrappedOptional isn’t going to be a type at all anymore.
We put the warnings specifically around the use of ! because that’s
easier to detect, but yes, using it in any position that isn’t the
top-level type of a variable, parameter, or return value is deprecated
and will be removed. (#rudkx has already done a lot of work to
actually do that removing in Swift 5, some of which will start showing
up even in Swift 4.1.)
Just do this:
guard let viewTint = view.tintColor else { return }
NSAttributedString.Key.foregroundColor: viewTint
Hope this helps!
Adding to what Rob Napier wrote in his reply, here is Apple's response to my Bug Report:
Engineering has provided the following information regarding this issue:
This is an unintended result of an intentional change in Swift 5.
tintColor is of type UIColor!, an implicitly unwrapped optional, and
it needs to be converted to Any so it can be added to the dictionary
passed to setTitleTextAttributes(_:for:). When faced with this
situation, Swift 4.2 would insert a force unwrap to convert the value
to UIColor, which would cause a difficult-to-debug crash if the value
was nil. Swift 5.0 instead converts the value to UIColor?, which never
crashes but emits a warning when implicitly converted to Any.
In your specific case, the tintColor property is documented to never
return nil, so it’s safe to use the force unwrapping operator by
writing “view.tintColor!” instead of “view.tintColor”. (This
workaround is specific to the tintColor property—in other situations
where you get this warning, force-unwrapping may crash your app at
runtime. Only force-unwrap when you are certain that the value can
never be nil.)
If your project has a strict style guide which absolutely forbids
force unwrapping, you could instead use the nil-coalescing operator by
writing something like “view.tintColor ?? UIColor.black”."

'coerced to Any' but property is of type UIColor

This
NSAttributedString.Key.foregroundColor: view.tintColor
Triggers this warning
Expression implicitly coerced from 'UIColor?' to 'Any'
But shouldn't that warning be
Expression implicitly coerced from 'UIColor?' to 'UIColor'
Since this property
NSAttributedString.Key.foregroundColor
is of type UIColor?
Note: This only started happening after updating to Swift 5, Xcode 10.2.
Here's more context:
override func viewDidLoad() {
super.viewDidLoad()
UIBarButtonItem.appearance().setTitleTextAttributes(
[
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 40),
NSAttributedString.Key.foregroundColor: view.tintColor
], for: .normal)
}
This has nothing to do with .foregroundColor. It has everything to do with .tintColor and setTitleTextAttributes.
This parameter is of type [NSAttributedString.Key : Any]. It is not in any way considering the documentation for each key. It doesn't know or care that this should be a UIColor. If you passed "squid", this would compile without warning (it wouldn't work, but it would compile):
UIBarButtonItem.appearance().setTitleTextAttributes(
[
.font: UIFont.systemFont(ofSize: 40),
.foregroundColor: "squid",
], for: .normal)
All it's looking at is that you're assigning view.tintColor to a value of type Any.
The problem is that view.tintColor is not UIColor, it's UIColor!. It's not actually possible for .tintColor to be nil, but it's possible to set it to nil:
view.tintColor // r 0.0 g 0.478 b 1.0 a 1.0
view.tintColor = .red
view.tintColor // r 1.0 g 0.0 b 0.0 a 1.0
view.tintColor = nil
view.tintColor // r 0.0 g 0.478 b 1.0 a 1.0
That makes sense in ObjC, but the only way to express it in Swift is to use a ! type. When you assign ! types to other things, they become ? types. And that means that you're using UIColor? in a place that accepts Any (the value of the dictionary).
Using an optional as Any can be dangerous because it creates a lot of weird corner cases. For example, you can't round-trip an optional through Any; it gets squashed into its base type:
let x: Any = Optional(1)
x as? Int? // Cannot downcast from 'Any' to a more optional type 'Int?'
x as? Int // 1
There are a lot of these kinds of little sharp-edges when working with Any.
Of course you didn't mean to work with Any. It's not your fault. But this is why Swift is complaining.
There are several solutions, depending on what you like. You can use a !:
.foregroundColor: view.tintColor!
You can add as Any to silence the warning:
.foregroundColor: view.tintColor as Any
Personally I'd use as Any.
Or you can be elaborate and unload the value earlier (I don't recommend this):
let tintColor = view.tintColor ?? .blue
UIBarButtonItem.appearance().setTitleTextAttributes(
[
.font: UIFont.systemFont(ofSize: 40),
.foregroundColor: tintColor,
], for: .normal)
setTitleTextAttributes expects a dictionary of [NSAttributedString.Key : Any]
Everything can be treated as Any and that's why this warning doesn't appear in any other case. The only time it appears is when you do it with Optional. Compiler just wants you to make sure that you know what you're doing with your optional :)
You asked why it happens in Xcode 10.2 and Swift 5?
It always worked well for optionals that were declared this way:
let optionalNumber: Int? = 5
and never for optionals that were declared this way:
let implicitlyUnwrappedOptionalNumber: Int! = 5
look at your example:
view.tintColor
is
Why it didn't worked for implicitly unwrapped optionals? Because before Swift 5, ImplicitlyUnwrappedOptional and Optional were two different types! And as I wrote before: It always worked well for optionals (and didn't work for ImplicitlyUnwrappedOptional).
Right now they are the same type, but implicitly unwrapped optionals have special #_autounwrapped mechanism to differentiate the two.
Thay started removal of this type in Swift 4.2:
https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md
This proposal seeks to limit the adoption of IUOs to places where they
are actually required, and put the Swift language on the path to
removing implicitly unwrapped optionals from the system entirely when
other technologies render them unnecessary. It also completely
abolishes any notion of IUOs below the type-checker level of the
compiler, which will substantially simplify the compiler
implementation.
but apparently they completed it in Swift 5:
https://forums.swift.org/t/possible-misdiagnosis-of-se-0054/9546
ImplicitlyUnwrappedOptional isn’t going to be a type at all anymore.
We put the warnings specifically around the use of ! because that’s
easier to detect, but yes, using it in any position that isn’t the
top-level type of a variable, parameter, or return value is deprecated
and will be removed. (#rudkx has already done a lot of work to
actually do that removing in Swift 5, some of which will start showing
up even in Swift 4.1.)
Just do this:
guard let viewTint = view.tintColor else { return }
NSAttributedString.Key.foregroundColor: viewTint
Hope this helps!
Adding to what Rob Napier wrote in his reply, here is Apple's response to my Bug Report:
Engineering has provided the following information regarding this issue:
This is an unintended result of an intentional change in Swift 5.
tintColor is of type UIColor!, an implicitly unwrapped optional, and
it needs to be converted to Any so it can be added to the dictionary
passed to setTitleTextAttributes(_:for:). When faced with this
situation, Swift 4.2 would insert a force unwrap to convert the value
to UIColor, which would cause a difficult-to-debug crash if the value
was nil. Swift 5.0 instead converts the value to UIColor?, which never
crashes but emits a warning when implicitly converted to Any.
In your specific case, the tintColor property is documented to never
return nil, so it’s safe to use the force unwrapping operator by
writing “view.tintColor!” instead of “view.tintColor”. (This
workaround is specific to the tintColor property—in other situations
where you get this warning, force-unwrapping may crash your app at
runtime. Only force-unwrap when you are certain that the value can
never be nil.)
If your project has a strict style guide which absolutely forbids
force unwrapping, you could instead use the nil-coalescing operator by
writing something like “view.tintColor ?? UIColor.black”."

swift 2.2: failable initializers in lazy properties

First very appreciate for your help. I just upgraded Xcode yesterday which contains swift 2.2. I've faced a few issues but I fixed them quickly by following the "what's new in swift 2.2" topics from Natashatherobot. But there is one issue I cannot fix. It's about failable initializers of UIFont which was introduced in swift 2.2. Attached is a simple piece of code that will report error in swift 2.2. It might not report the error immediately, until I cleaned the project.
lazy var somelabel: UILabel = {
let label = UILabel()
let font = UIFont(name: "somefont", size: 10) ?? UIFont.systemFontOfSize(10) //this line gave me error
label.font = font
label.text = "Calculating..."
return label
}()
Here is the screenshot of the error
The error is : (name: String, size: CGFloat) -> UIFont' is not convertible to '(name: String, size: CGFloat) -> UIFont?'
I can fix it in two ways:
Method 1: don't put this line:
let font = UIFont(name: "somefont", size: 10) ?? UIFont.systemFontOfSize(10)
in the 'lazy instantiation' closure. (Put it in computed properties reports no error)
Method 2: instead of using:
UIFont(name: "somefont", size: 10)
use the below instead( However I don't think this should be the right approach because it makes the initializer more "objc" style):
UIFont.init(name: "somefont", size: 10)
But I still don't understand why it would report me error in the lazy property closure. I will be very appreciated if someone can give me some explanations.
This might be a bug of the latest version xcode. Those whose project was set up before the upgrade might face this problem. Anyone who is luck enough to face such issue can try to start a new project in swift 2.2 environment.