Literals in Swift generics - ios

I'm trying to implement a generic sign function in Swift so that it can be used with any floating-point type:
func sign<T> (value:T) -> T {
if value < 0.0 {
return -1.0
}
if value > 0.0 {
return 1.0
}
return 0.0
}
But this error stands in the way:
Binary operator '<' cannot be applied to operands of type 'T' and
'Double'

If you constrain T to SignedNumberType, you'll be able to do everything you want. It inherits from Comparable and can convert from integer literals, the two things you need there.
func sign<T: SignedNumberType> (value:T) -> T {
if value < 0 {
return -1
}
if value > 0 {
return 1
}
return 0
}

You can create a type constraint (MyFloats below) to which your floating point types conform. You let this type constraint itself conform to Comparable, so that you may make use of the less than binary infix operator < when comparing the values of your generics. Also, for your example given above, the MyFloats type constraint need contain only a single blueprint; an initializer for a Double argument. This initalizer already exists for Double, Float and CGFloat types, but since a protocol cannot know which types that conforms to it, you need to include this blueprint.
protocol MyFloats : Comparable {
init(_ value: Double)
}
extension Double : MyFloats { }
extension Float : MyFloats { }
extension CGFloat : MyFloats { }
func sign<T: MyFloats> (value:T) -> T {
if value < T(0.0) {
return T(-1.0)
}
if value > T(0.0) {
return T(1.0)
}
return T(0.0)
}

Look at this answer. For your use case you need to create a new protocol that extends Comparable and extend the Float andDouble Swift types by using an extension.
Finally you'll be able to use this protocol as condition of the generic parameter.
func sign<T : MyCustomNumberProtocol> (value:T) -> T

Related

Swift 4: Array for all types of Ints and Floating point numbers

I have a program that working with all kind of Ints and Floating pointer value types
Is there is a way to create array that will holds all kind of those values in it?
I have tried to do this via protocol and extend Int and Double with them but no luck because of protocol equability limitation in Swift(or something like this)
protocol ArrayStructValue: Comparable,Equatable { }
extension Double: ArrayStructValue{}
extension Int:ArrayStructValue {}
If you want two different types in one array, why don't you use a union, e.g. using an enum:
enum MyNumber {
case integer(Int)
case double(Double)
}
let numbers: [MyNumber] = [.integer(10), .double(2.0)]
However, in most situations it would be probably better to just convert all Int into Double and just have a [Double] array.
As already mentioned by #vadian both types Int and Double already conforms to Strideable which inherits from Comparable which inherits from Equatable so there is no need to constrain your protocol to them. So all you need is to make Int and `Double inherit from your protocol:
protocol ArrayStructValue { }
extension Double: ArrayStructValue { }
extension Int: ArrayStructValue { }
var f: [ArrayStructValue] = []
f.append(1)
f.append(2.0)
type(of: f[0]) // Int.Type
type(of: f[1]) // Double.Type

Why CustomStringConvertible protocol not working for Int?

public func +<T: CustomStringConvertible>(lhs: T, rhs: T)->String{
return lhs.description+rhs.description
}
let a:String = "A"
let i:Int = 0
print(a+i)
I am overloading '+' operator for CustomStringConvertible types. String and Int both confirms CustomStringConvertible protocol but it gives an error: "binary operator '+' cannot be applied to operands of type 'String' and 'Int' print(a+i)". It working fine when I apply it to 'String'+'NSNumber'.
don't know what is going behind the scene. why it is not working?
The problem is firstly (believe it or not) String doesn't conform to CustomStringConvertible. You'll therefore want to conform it yourself in order for it to return self for description (probably easier than writing another overload to deal with strings independently).
extension String:CustomStringConvertible {
public var description: String {
return self
}
}
Secondly, you need two generic parameters for your + overload, in order to allow it to accept different types for both parameters, while ensuring that both parameters conform to CustomStringConvertible:
public func +<T: CustomStringConvertible, U:CustomStringConvertible>(lhs: T, rhs: U)->String{
return lhs.description+rhs.description
}

Comparing type of Generic associated type in Swift

I have the next code:
protocol Flyable {
var airspeedVelocity: Double { get }
}
func topSpeed<T: CollectionType where T.Generator.Element == Flyable>(collection: T) -> Double {
return collection.map { $0.airspeedVelocity }.reduce(0) { max($0, $1) }
}
I understood, by reading the Swift Documentation that:
You write a where clause by placing the where keyword immediately after the list of type parameters, followed by constraints for associated types or equality relationships between types and associated types.
This is the example given on the docs:
func allItemsMatch<C1: Container, C2: Container where
C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, _ anotherContainer: C2) -> Bool {
// code
}
Note that when expressing that the associated type ItemType of the C1 type must conform to Equatable protocol you use : and not ==, why is that not the case in my example, where I have to use == to state that the Element associated type of T.Generator must conform with Flyable?
When changing == with : the compiler complains with this:
error: cannot invoke 'topSpeed' with an argument list of type '([Flyable])' note: expected an argument list of type '(T)'
You can achieve this without using Generics.
protocol Flyable {
var airspeedVelocity: Double { get }
}
func topSpeed(collection: [Flyable]) -> Double {
return collection.map { $0.airspeedVelocity }.reduce(0) { max($0, $1) }
}
class Airplane: Flyable {
var airspeedVelocity: Double = 10
}
class Rocket: Flyable {
var airspeedVelocity: Double = 50
}
topSpeed([Airplane(),Rocket()]) //50
I've found the reason here. The where clause has the next grammar:
conformance-requirement → type-identifier­:­type-identifier­
same-type-requirement → type-identifier­==­type­
So, when you want to state that your type parameter conforms to certain protocol you use :. BUT, when you want your type to be exactly of a certain type, you use ==. In my example, I wanted to take as a parameter a collection of Flyable. So, when trying to use : the compiler complained because Flyable doesn't conform to Flyable. Flyable IS Flyable, thus I must use == (or do something like what #Rahul Katariya answered)

How to adhere to protocol method with a Raw type in method argument?

protocol Measurement {
mutating func convert(#toUnit: String)
}
enum MassUnit : String {
case Milligram = "mg"
}
enum VolumeUnit : String {
case Milliliter = "ml"
}
struct Mass : Measurement {
mutating func convert(#toUnit: MassUnit)
// Build error: Does not adhere to 'Measurement'
}
struct Volume : Measurement {
mutating func convert(#toUnit: VolumeUnit)
// Build error: Does not adhere to 'Measurement'
}
func +<T: Measurement> (left:T, right:T) -> Measurement {
let newRightValue = right.convert(toUnit: left.unit)
return T(quantity: left.quantity + newRightValue.quantity , unit: left.unit)
}
How can I make Mass adhere correctly to Measurement? What change in the Measurement protocol is needed to get it to work with enum of type String?
Question updated with more information about why the convert method signature should say something about the argument given. The code is part of an open source Unit framework i'm building called Indus Valley
You probably confuse enum MassUnit : String with inheritance.
Contrary to class ChildClass : ParentClass which denotes that ChildClass inherits from ParentClass, enum MassUnit : String has a slightly different meaning, telling that the rawType of the enum is String, not that the enum inherits the String type.
So MassUnit is not of type String. You MassUnit's rawValues are of type String, but to access that you need to call the rawValue property of the enum to get that String equivalent.
Therefore, mutating func convert(#toUnit: String) and mutating func convert(#toUnit: MassType) are not compatible, as MassType is not a String itself. Only its rawValue is.
When dealing with your unit conversions, I advise not trying to use Strings to represent the units when converting the way you've got setup above. That's going to make your code complicated with checking the String can be converted to its respective enum every time you want to make a conversion. Also, what if you want to use the a MassUnit/VolumeUnit instead of a String?
I would recommend using the a setup similar to what I've outlined below. It references my previous answer - How to represent magnitude for mass in Swift?
(Note - I've excluded anything to do with the volume because it's basically the same as the implementation for the mass)
I'd make the units like so:
protocol UnitProtocol {
var magnitude: Int { get }
init?(rawValue: String)
}
// Taken from my previous answer.
enum MassUnit: String, UnitProtocol, Printable {
case Milligram = "mg"
case Gram = "g"
var magnitude: Int {
let mag: Int
switch self {
case .Milligram: mag = -3
case .Gram : mag = 0
}
return mag
}
var description: String {
return rawValue
}
}
// Not making this a method requirement of `UnitProtocol` means you've only got to
// write the code once, here, instead of in every enum that conforms to `UnitProtocol`.
func ordersOfMagnitudeFrom<T: UnitProtocol>(unit1: T, to unit2: T) -> Int {
return unit1.magnitude - unit2.magnitude
}
Then I'd make the masses/volumes like so:
protocol UnitConstruct {
typealias UnitType: UnitProtocol
var amount: Double { get }
var unit : UnitType { get }
init(amount: Double, unit: UnitType)
}
struct Mass : UnitConstruct {
let amount: Double
let unit : MassUnit
}
Now for the converting function! Using a global function means you don't need to rewrite the code for every type than conforms to UnitConstruct.
func convert<T: UnitConstruct>(lhs: T, toUnits unit: T.UnitType) -> T {
let x = Double(ordersOfMagnitudeFrom(lhs.unit, to: unit))
return T(amount: lhs.amount * pow(10, x), unit: unit)
}
// This function is for converting to different units using a `String`,
// as asked in the OP.
func convert<T: UnitConstruct>(lhs: T, toUnits unit: String) -> T? {
if let unit = T.UnitType(rawValue: unit) {
return convert(lhs, toUnits: unit)
}
return nil
}
You can then use the previous code like so:
let mass1 = Mass(amount: 1.0, unit: .Gram)
let mass2 = convert(mass1, toUnits: .Milligram) // 1000.0 mg
// Or, converting using Strings:
let right = convert(mass1, toUnits: "mg") // Optional(1000.0 mg)
let wrong = convert(mass1, toUnits: "NotAUnit") // nil

How to covert NSMutableOrderedSet to generic array?

I have this for loop, p is a NSManagedObject, fathers is a to-many relationship, so I need to cast NSMutableOrderedSet to [Family] but it does not work, why?
for f in p.fathers as [Family] {
}
You can obtain an array representation of the set via the array property - then you can downcast it to the proper type and assign to a variable:
let families = p.fathers.array as [Family]
but of course you can also use it directly in the loop:
for f in p.fathers.array as [Family] {
....
}
Update
A forced downcast is now required using the ! operator, so the code above should be:
let families = p.fathers.array as! [Family]
The simple solution by Antonio should be used in this case. I'd just like to discuss this a bit more. If we try to enumerate an instance of 'NSMutableOrderedSet' in a 'for' loop, the compiler will complain:
error: type 'NSMutableOrderedSet' does not conform to protocol
'SequenceType'
When we write
for element in sequence {
// do something with element
}
the compiler rewrites it into something like this:
var generator = sequence.generate()
while let element = generator.next() {
// do something with element
}
'NS(Mutable)OrderedSet' doesn't have 'generate()' method i.e. it doesn't conform to the 'SequenceType' protocol. We can change that. First we need a generator:
public struct OrderedSetGenerator : GeneratorType {
private let orderedSet: NSMutableOrderedSet
public init(orderedSet: NSOrderedSet) {
self.orderedSet = NSMutableOrderedSet(orderedSet: orderedSet)
}
mutating public func next() -> AnyObject? {
if orderedSet.count > 0 {
let first: AnyObject = orderedSet.objectAtIndex(0)
orderedSet.removeObjectAtIndex(0)
return first
} else {
return nil
}
}
}
Now we can use that generator to make 'NSOrderedSet' conform to 'SequenceType':
extension NSOrderedSet : SequenceType {
public func generate() -> OrderedSetGenerator {
return OrderedSetGenerator(orderedSet: self)
}
}
'NS(Mutable)OrderedSet' can now be used in a 'for' loop:
let sequence = NSMutableOrderedSet(array: [2, 4, 6])
for element in sequence {
println(element) // 2 4 6
}
We could further implement 'CollectionType' and 'MutableCollectionType' (the latter for 'NSMutableOrderedSet' only) to make 'NS(Mutable)OrderedSet' behave like Swift's standard library collections.
Not sure if the above code follows the best practises as I'm still trying to wrap my head around details of all these protocols.

Resources