Does Swift support implicit conversion? - ios

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"

Related

Using variable as lower bound for arc4random - explicit type/strideable?

)
I have updated a "Workout Object" to have both a minimum and maximum number of reps.
When I've been hardcoding the lower bound in a playground, I've been using :
let numberOfExercises = Int(arc4random_uniform(4) + 3)
When I try to use variables in a function/with a class object I get an error of "+'is unavailable: Please use explicit type conversions or Strideable methods for mixed-type arithmetics" e.g. here ...
class ExerciseGeneratorObject: Object {
#objc dynamic var name = ""
#objc dynamic var minReps = 0
#objc dynamic var maxReps = 0
convenience init(name: String, minReps: Int, maxReps: Int) {
self.init()
self.name = name
self.minReps = minReps
self.maxReps = maxReps
}
func generateExercise() -> WorkoutExercise {
return WorkoutExercise(
name: name,
//get error on this line...
reps: Int(arc4random_uniform(UInt32(maxReps))+minReps)
)
}
}
There is an answer here + is unavailable: Please use explicit type conversions or Strideable methods for mixed-type arithmetics but that method is already using so don't see how it's applicable here.
Also here '+' is deprecated: Mixed-type addition is deprecated in Swift 3.1 but again think this is a different problem
'+' is unavailable: Please use explicit type conversions or Strideable methods for mixed-type arithmetics.
Example:
let a: UInt32 = 4
let b = 3
let result = a + b //error
Basically means you can't add mixed-types.
In your case when you do arc4random_uniform(UInt32(maxReps)) + minReps, arc4random_uniform() returns a UInt32 which cannot be added to minReps because that's an Int.
Solution:
Update your parenthesis:
let numberOfExercises = Int(arc4random_uniform(UInt32(maxReps))) + minReps
Here Int(arc4random_uniform(UInt32(maxReps))) gives an Int that we can add to the minReps Int.
BTW, the following works out-of-the-box:
let numberOfExercises = Int(arc4random_uniform(4) + 3)
Because of Swift's automatic type inference. Basically it just went ahead with UInt32 without bothering you. That is... until you give it explicit mixed types.

Swift String from imported unsigned char 2D array

I am using a 3rd party C library in my iOS application, which I am in the process of converting from Objective-C to Swift. I hit an obstacle when attempting to read one of the structs returned by the C library in Swift.
The struct looks similar to this:
typedef unsigned int LibUint;
typedef unsigned char LibUint8;
typedef struct RequestConfiguration_ {
LibUint8 names[30][128];
LibUint numberNames;
LibUint currentName;
} RequestConfiguration;
Which is imported into Swift as a Tuple containing 30 Tuples of 128 LibUint8 values. After a long time of trial and error using nested withUnsafePointer calls, I eventually began searching for solutions to iterating a Tuple in Swift.
What I ended up using is the following functions:
/**
* Perform iterator on every children of the type using reflection
*/
func iterateChildren<T>(reflectable: T, #noescape iterator: (String?, Any) -> Void) {
let mirror = Mirror(reflecting: reflectable)
for i in mirror.children {
iterator(i.label, i.value)
}
}
/**
* Returns a String containing the characters within the Tuple
*/
func libUint8TupleToString<T>(tuple: T) -> String {
var result = [CChar]()
let mirror = Mirror(reflecting: tuple)
for child in mirror.children {
let char = CChar(child.value as! LibUint8)
result.append(char)
// Null reached, skip the rest.
if char == 0 {
break;
}
}
// Always null terminate; faster than checking if last is null.
result.append(CChar(0))
return String.fromCString(result) ?? ""
}
/**
* Returns an array of Strings by decoding characters within the Tuple
*/
func libUint8StringsInTuple<T>(tuple: T, length: Int = 0) -> [String] {
var idx = 0
var strings = [String]()
iterateChildren(tuple) { (label, value) in
guard length > 0 && idx < length else { return }
let str = libUint8TupleToString(value)
strings.append(str)
idx++
}
return strings
}
Usage
func handleConfiguration(config: RequestConfiguration) {
// Declaration types are added for clarity
let names: [String] = libUint8StringsInTuple(config.names, config.numberNames)
let currentName: String = names[config.currentName]
}
My solution uses reflection to iterate the first Tuple, and reflection to iterate the second, because I was getting incorrect strings when using withUnsafePointer for the nested Tuples, which I assume is due to signage. Surely there must be a way to read the C strings in the array, using an UnsafePointer alike withUsafePointer(&struct.cstring) { String.fromCString(UnsafePointer($0)) }.
To be clear, I'm looking for the fastest way to read these C strings in Swift, even if that involves using Reflection.
Here is a possible solution:
func handleConfiguration(var config: RequestConfiguration) {
let numStrings = Int(config.numberNames)
let lenStrings = sizeofValue(config.names.0)
let names = (0 ..< numStrings).map { idx in
withUnsafePointer(&config.names) {
String.fromCString(UnsafePointer<CChar>($0) + idx * lenStrings) ?? ""
}
}
let currentName = names[Int(config.currentName)]
print(names, currentName)
}
It uses the fact that
LibUint8 names[30][128];
are 30*128 contiguous bytes in memory. withUnsafePointer(&config.names)
calls the closure with $0 as a pointer to the start of that
memory location, and
UnsafePointer<CChar>($0) + idx * lenStrings
is a pointer to the start of the idx-th subarray. The above code requires
that each subarray contains a NUL-terminated UTF-8 string.
The solution suggested by Martin R looks good to me and, as far as I can see from my limited testing, does work. However, as Martin pointed out, it requires that the strings be NUL-terminated UTF-8. Here are two more possible approaches. These follow the principle of handling the complexity of C data structures in C instead of dealing with it in Swift. Which of these approaches you choose depends on what specifically you are doing with RequestConfiguration in your app. If you are not comfortable programming in C, then a pure Swift approach, like the one suggested by Martin, might be a better choice.
For the purposes of this discussion, we will assume that the 3rd party C library has the following function for retrieving RequestConfiguration:
const RequestConfiguration * getConfig();
Approach 1: Make the RequestConfiguration object available to your Swift code, but extract names from it using the following C helper function:
const unsigned char * getNameFromConfig(const RequestConfiguration * rc, unsigned int nameIdx)
{
return rc->names[nameIdx];
}
Both this function's signature and the RequestConfiguration type must be available to the Swift code via the bridging header. You can then do something like this in Swift:
var cfg : UnsafePointer<RequestConfiguration> = getConfig()
if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig(cfg, cfg.memory.currentName)))
{
print(s)
}
This approach is nice if you need the RequestConfiguration object available to Swift in order to check the number of names in multiple places, for example.
Approach 2: You just need to be able to get the name at a given position. In this case the RequestConfiguration type does not even need to be visible to Swift. You can write a helper C function like this:
const unsigned char * getNameFromConfig1(unsigned int idx)
{
const RequestConfiguration * p = getConfig();
return p->names[idx];
}
and use it in Swift as follows:
if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig1(2)))
{
print(s)
}
This will print the name at position 2 (counting from 0). Of course, with this approach you might also want to have C helpers that return the count of names as well as the current name index.
Again, with these 2 approaches it is assumed the strings are NUL-terminated UTF-8. There are other approaches possible, these are just examples.
Also please note that the above assumes that you access RequestConfiguration as read-only. If you also want to modify it and make the changes visible to the 3rd party library C code, then it's a different ballgame.

How to cast AnyObject to an enum [duplicate]

I'm in AppDelegate, trying to pass a reply to a WatchKit Extension Request. I cannot use an array of enums as the value in a Dictionary whose values are typed as AnyObject. Experimenting in a Playground shows this:
enum E : Int {
case a = 0
case b
}
var x : AnyObject = [0, 1] // OK
var y : AnyObject = [E.a, E.b] // [E] is not convertible to AnyObject
Of course I can work around this by converting my enums to strings or numbers, but why is this a type error in Swift?
AnyObject exists for compatibility with Objective-C. You can only put objects into an [AnyObject] array that Objective-C can interpret. Swift enums are not compatible with Objective-C, so you have to convert them to something that is.
var x: AnyObject = [0, 1] works because Swift automatically handles the translation of Int into the type NSNumber which Objective-C can handle. Unfortunately, there is no such automatic conversion for Swift enums, so you are left to do something like:
var y: AnyObject = [E.a.rawValue, E.b.rawValue]
This assumes that your enum has an underlying type that Objective-C can handle, like String or Int.
Another example of something that doesn't work is an optional.
var a: Int? = 17
var b: AnyObject = [a] // '[Int?]' is not convertible to 'AnyObject'
See Working with Cocoa Data Types for more information.

Is Float, Double, Int an AnyObject?

I read inline documentation of Swift and I am bit confused.
1) Any is a protocol that all types implicitly conform.
2) AnyObject is a protocol to which all classes implicitly conform.
3) Int, Float, Double are structs
Here is a sample code:
import UIKit
func passAnyObject(param: AnyObject) {
print(param)
}
class MyClass {}
struct MyStruct {}
let a: Int = 1
let b = 2.0
let c = NSObject()
let d = MyClass()
let e = MyStruct()
passAnyObject(a)
passAnyObject(b)
passAnyObject(c)
passAnyObject(d)
//passAnyObject(e) // Argument type 'MyStruct' does not conform to expected type 'AnyObject'
if a is AnyObject { // b, d, e is also AnyObject
print("\(a.dynamicType) is AnyObject")
}
What I don't understand is why Int, Double, Float are AnyObjects? Why compiler doesn't say anything? Those types are declared as structs. Struct MyStruct cannot be passed to the method on the top because it does not conform to AnyObject.
Could you help me understand why Int, Double and Float are AnyObject or why compiler thinks they are?
Because you have Foundation imported, Int, Double, and Float get converted to NSNumber when passed to a function taking an AnyObject. Type String gets converted to NSString. This is done to make life easier when calling Cocoa and Cocoa Touch based interfaces. If you remove import UIKit (or import Cocoa for OS X), you will see:
error: argument type 'Int' does not conform to expected type 'AnyObject'
when you call
passAnyObject(a)
This implicit conversion of value types to objects is described here.
Update for Swift 3 (Xcode 8 beta 6):
Passing an Int, Double, String, or Bool to a parameter of type AnyObject now results in an error such as Argument of type 'Int' does not conform to expected type 'AnyObject'.
With Swift 3, implicit type conversion has been removed. It is now necessary to cast Int, Double, String and Bool with as AnyObject in order to pass it to a parameter of type AnyObject:
let a = 1
passAnyObject(a as AnyObject)
Good find! UIKit actually converts them to NSNumber - also mentioned by #vacawama. The reason for this is, sometimes you're working with code that returns or uses AnyObject, this object could then be cast (as!) as an Int or other "structs".
class Test {
static func test() {
let anyObjectsValues: [AnyObject] = [1, "Two", 3, "Four"] as [AnyObject]
anyObjectsValues.forEach { (value) in
switch value {
case is Int:
print("\(value) is an Int!")
case is String:
print("\(value) is a String!")
default:
print("\(value) is some other type!")
}
}
}
}
I have not imported UIKit or Foundation frameworks. Why compiler is not giving any error? Even it printing the result.
Output:
1 is an Int!
Two is a String!
3 is an Int!
Four is a String!
Does anybody have an idea?

Create a .toString() extension for all numbers in Swift

I'm trying to create a .toString method extension for numbers using Swift just for fun. It works if I specify Int, or Double, etc. It doesn't work right out of the box with NSNumber since it is an object in itself, right? Is there a way to 'catch all' ints floats doubles etc? Some base number class of some sort?
I see this other answer, but I don't want to create a custom type/protocol, just plug this on any 'number'.
Generic type constraint for numerical type only
extension Int {
func toString () -> String {
return String (self)
}
}
You don't need to reinvent the wheel. You can use Swift native description property for Double, Int, and NSNumbers:
Int(1).description // "1"
Double(2.0).description // "2.0"
NSNumber(double: 3.0).doubleValue.description // "3.0"
let x : Int = 33
let stringValue = "\(x)"
or
let stringValue = String(x)
or
let stringValue = Int(x).description

Resources