Swift: send type as argument and compare a variable's type with it - swift

I want to implement a function like this one:
protocol Base {
var value: Int { get set }
}
class ObjectTypeA: Base {
var value: Int = 0
}
class ObjectTypeB: Base {
var value: Int = 1
}
var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
objects.filter({ $0 is type }).forEach { // <<< ERROR IS HERE
var object = $0
object.value = value
}
}
updatePropertyForType(ObjectTypeB.self, value: 10)
But there is an error:
'type' is not a type
Please, help me fix it.

See this answer:
protocol Base: AnyObject {
var value: Int { get set }
}
class ObjectTypeA: Base {
var value: Int = 0
}
class ObjectTypeB: Base {
var value: Int = 1
}
var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
objects.filter({let item = $0; return type === item.dynamicType }).forEach {
$0.value = value
}
}
call it with:
updatePropertyForType(ObjectTypeA.self, value: 3)

As the other answers have eluded to, you cannot use meta-types with is. However, a nicer solution would be to simply use generics. This will allow Swift to infer the type you pass into the function, allowing you to write it as:
protocol Base : class {
var value: Int { get set }
}
class ObjectTypeA: Base {
var value: Int = 0
}
class ObjectTypeB: Base {
var value: Int = 1
}
var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updateElements<T:Base>(ofType type: T.Type, withValue value: Int) {
objects.filter{ $0 is T }.forEach{ $0.value = value }
}
updateElements(ofType: ObjectTypeB.self, withValue: 10)
You'll also want to make your Base protocol class bound (: class) in order to allow Swift to treat Base typed instances as reference types (allowing you to do $0.value = value).
Your previous code of:
var object = $0
object.value = value
would have worked for reference types, but not for value types – as object here is simply a copy of $0, so any mutations won't be reflected in the array. Therefore you should annotate your protocol to make it clear that your protocol isn't designed to be used with value types.
If you do want to be able to handle value types, you may want to consider using map instead:
func updateElements<T:Base>(ofType type: T.Type, withValue value: Int) {
objects = objects.map {
var object = $0
if object is T {
object.value = value
}
return object
}
}

But this code works just fine.
protocol Base {
var value: Int { get set }
}
class ObjectTypeA: Base {
var value: Int = 0
}
class ObjectTypeB: Base {
var value: Int = 1
}
var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
objects.filter({ object in
let result = object.dynamicType == type
return result
}).forEach {
var object = $0
object.value = value
}
}
updatePropertyForType(ObjectTypeB.self, value: 10)
print(objects)

Related

Swift extension with class: how to make a function to return an object's real type?

I have code like this:
class A{}
class B: A{
var val = 1
}
class C: A{
var num = 5
}
extension Optional where Wrapped == [B?]{
var vals: [B]{
var result = [B]()
if let arr = self{
for part in arr{
if let val = part{
result.append(val)
}
}
}
return result
}
}
extension Optional where Wrapped == [C?]{
var vals: [C]{
var result = [C]()
if let arr = self{
for part in arr{
if let val = part{
result.append(val)
}
}
}
return result
}
}
var one: [B?]? = [B()]
var two: [C?]? = [C(), nil]
print(one.vals.count)
print(two.vals.count)
Here is the optimized one:
Combined into one, for B ( A's subclass ) & C ( A's subclass )
extension Optional where Wrapped: Collection{
var vals: [A]{
var result = [A]()
if let arr = self{
for part in arr{
if let val = part as? A{
result.append(val)
}
}
}
return result
}
}
Now question comes,
for case like the follwing,
how to go on the optimization?
print(one.vals.first?.val ?? "")
print(two.vals.first?.num ?? "")
I guess, I need a function to return an object's real type
PS: I know , to handle data , struct is perfect with protocol
While it's a company project, & I'm a new one
You need to introduce an extra type variable to say that the extension works on Optionals where Wrapped.Element is another Optional of any type. You have to express the "any type" part with another type variable, but you cannot add this type variable in the extension's declaration (though this feature is being proposed), or the property's declaration. What you can do instead, is to make vals a function:
func vals<T>() -> [T] where Wrapped.Element == T? {
var result = [T]()
if let arr = self{
for part in arr{
if let val = part{
result.append(val)
}
}
}
return result
}
Note that this can be simplified to:
extension Optional where Wrapped: Sequence {
func vals<T>() -> [T] where Wrapped.Element == T? {
self?.compactMap { $0 } ?? []
}
}
Just for fun. Another possible approach to keep it as a computed property instead of a generic method is to create an AnyOptional protocol with an associatedtype Wrapped and conform Optional to it. Then you can create a computed property to return an array of its Wrapped Element Wrapped type:
protocol AnyOptional {
associatedtype Wrapped
var optional: Optional<Wrapped> { get }
}
extension Optional: AnyOptional {
var optional: Optional<Wrapped> { self }
}
extension AnyOptional where Wrapped: Sequence, Wrapped.Element: AnyOptional {
var elements: [Wrapped.Element.Wrapped] {
optional?.compactMap(\.optional) ?? []
}
}
print(one.elements) // "[B]\n"
print(two.elements) // "[C]\n"
print(one.elements.first?.val ?? "") // "1\n"
print(two.elements.first?.num ?? "") // "5\n"

Extension optional Array with Optional Element. Is it even possible?

I have a protocol FooProtocol. and a class Bar<Foo:FooProtocol>. Inside a class an Array var mess: [Foo?]? to keep [foo1, foo2, nil, foo3...] or nil
And I try to make extension for this array to count new Foo object. I prefer to have protocols, because Foos could be very different objects delivered from outer world.
protocol FooProtocol {
....
init(from heaven: Int)
}
extension Optional where
Wrapped: Collection,
Wrapped.Element == Optional,
Wrapped.Element.Wrapped: FooProtocol // 'Wrapped' is not a member type of 'Wrapped.Element'
{
var united: Wrapped.Element.Wrapped { // Nope
let i = ...
return Wrapped.Element.Wrapped(from: i) // Nope
}
}
class Bar<Foo:FooProtocol> {
var mess: [Foo?]?
init (with mess: [Foo?]?) {
self.mess = mess
}
var important: Foo {
return mess.united
}
}
Any ideas? I'm blocked.
Edit 1:
After Leo suggestions I changed some parts of my code. But still stucked. This time more code from Playgrounds.
Any object that could be converted into '[Double]'. Could be color (as RGBA), Bezier curve, square, whatever...
public protocol FooProtocol {
var atomized: () -> [Double] {get}
static var count: Int {get}
init(_ array:[Double])
init()
}
public extension Array where Element: FooProtocol {
var average: Element {
var resultAtoms: [Double] = []
let inputAtoms = self.map {$0.atomized()}
for i in 0..<Element.count {
let s = inputAtoms.reduce(into: 0.0, {$0 += $1[i]}) / Double (Element.count)
resultAtoms.append(s)
}
return Element(resultAtoms)
}
}
extension Optional where
Wrapped: Collection,
Wrapped.Element == Optional<FooProtocol>
{
typealias Foo = Wrapped.Element.Wrapped // Doesn't work. How to get class?
var average: Foo { // I cannot use Wrapped.Element, it's Optional
if let thatsList = self {
let withOptionals = Array(thatsList) // OK, its [Optional<FooProtocol>]
let withoutOptionals = thatsList.compactMap({$0}) // OK, its [FooProtocol]
// This is funny, called from class works and makes 'bingo'.
return withoutOptionals.average // Error: Value of protocol type 'FooProtocol' cannot conform to 'FooProtocol'; only struct/enum/class types can conform to protocols
} else {
return Foo() // Hello? init Wrapped? Foo? How to get Foo()?
}
}
}
class Bar<Foo:FooProtocol> {
var mess: [Foo?]?
init (with mess: [Foo?]?) {
self.mess = mess
}
func workOn() {
let z:Foo = mess.average // OK, I can make 'mess.average ?? Foo()' but prefer not do it
}
// Thats OK
func workHard() { // To prove 'Array extension where Element: FooProtocol' works
if let messExist = mess {
let withoutOptionals = messExist.compactMap({$0})
let bingo = withoutOptionals.average //It's OK
}
}
}
class SomeFoo : FooProtocol {
static var count = 3
required init() {
a = 0
b = 0
c = 0
}
required init(_ array: [Double]) {
self.a = Int(array[0])
self.b = Float(array[1])
self.c = array[2]
}
var atomized: () -> [Double] {
return {return [Double(self.a), Double(self.b), self.c]}
}
var a: Int
var b: Float
var c: Double
}
let aFoo = SomeFoo([1, 2, 3])
let bFoo = SomeFoo([7, 9, 1])
let cFoo = SomeFoo([2, 6, 5])
let barData = [nil, aFoo, nil, bFoo, cFoo]
let barWithData = Bar(with: barData)
let barWithoutData = Bar<SomeFoo>(with: nil)
Maybe I should forget about extending array and make some functions inside a class (I'm almost sure I will need those functions somewhere else)
Edit 2
Even if I try to simplify and to make extension for Array I found troubles.
extension Array where
Element == Optional<FooProtocol>
{
func averageNils <Foo: FooProtocol>() -> Foo {
let withOptionals = Array(self) // OK, its [Optional<FooProtocol>]
let withoutOptionals = self.compactMap({$0}) // OK, its [FooProtocol]
return withoutOptionals.average as! Foo // Error: Value of protocol type 'FooProtocol' cannot conform to 'FooProtocol'; only struct/enum/class types can conform to protocols
}
}
From my understanding, it should work as you did, but one never knows what happens in the swift compiler world (and especially it's error messages).
Anyway, you can circumvent digging deeper into Wrapped.Element.Wrapped by specifyig the Wrapped.Element more precisely to be an Optional<FooProtocol>:
protocol FooProtocol {}
class Foo : FooProtocol {}
extension Optional where
Wrapped: Collection, //OK
Wrapped.Element == Optional<FooProtocol> // still good
{
var unfied: Wrapped.Element // Should be 'Foo' if self is '[Foo?]?' {
{
return 1 == 0 ? nil : Foo()
}
}

declare generic class with a generic class as a parameter in swift

I wrote these simple generic classes and it worked great:
class LinkedListNode <T> {
var value: T
var next: LinkedListNode<T>?
weak var prev: LinkedListNode<T>?
init(value: T) {
self.value = value
self.next = nil
}
}
class LinkedList<T> {
var first: LinkedListNode<T>? = nil
var last: LinkedListNode<T>? = nil
var count = 0
#discardableResult func append(_ value: T) -> LinkedListNode<T> {
let new = LinkedListNode(value: value)
new.prev = last
last?.next = new
count += 1
last = new
if first == nil {
first = new
}
return new
}
}
And I used it like:
let list = LinkedList<Int>()
list.append(3)
let lastNode = list.append(5)
Now I realized there are some cases when I need to have a customized node: CustomNode<T>, subclass of LinkedListNode<T>. So I would like to be able to pass the class to be used as the node as:
let list = LinkedList<CustomNode<Int>>()
list.append(3)
let customNode = list.append(5)
How can I declare my class to have it like this or something similar?
I have tried the following declaration but weird errors rise. Is this even possible?
class LinkedList<Node<T>: LinkedListNode<T>> { ...
Update 2019/07/26.
Even with Kamran's approach this method does not compile. I am not sure if this is doable without a protocol. See my comment on Kamran's answer.
func remove(node: LinkedListNode<T>) { // change to `func remove(node: U)`
node.next?.prev = node.prev
node.prev?.next = node.next
if node === first {
first = first?.next
}
if node === last {
last = last?.prev // Error here: "Cannot assign value of LinkedListNode<T>? to U?"
}
}
The syntax you are trying can be achieved as below,
class LinkedListNode <T> {
var value: T
var next: LinkedListNode<T>?
weak var prev: LinkedListNode<T>?
required init(value: T) {
self.value = value
self.next = nil
}
}
class GenericCustomNode<T>: LinkedListNode<T> {
required init(value: T) {
super.init(value: value)
}
}
class NonGenericCustomNode: LinkedListNode<Int> {
required init(value: Int) {
super.init(value: value)
}
}
class LinkedList<T, U: LinkedListNode<T>> {
var first: U? = nil
var last: U? = nil
var count = 0
#discardableResult func append(_ value: T) -> U {
let new = U(value: value)
new.prev = last
last?.next = new
count += 1
last = new
if first == nil {
first = new
}
return new
}
func remove(node: U) {
node.next?.prev = node.prev
node.prev?.next = node.next
if node === first {
first = first?.next as? U
}
if node === last {
last = last?.prev as? U
}
}
}
Usage:
let list = LinkedList<Int, LinkedListNode<Int>>()
list.append(5)
print(list.first?.value)
let someCustom = LinkedList<Int, GenericCustomNode<Int>>()
someCustom.append(15)
print(someCustom.first?.value)
let otherCustom = LinkedList<Int, NonGenericCustomNode>()
otherCustom.append(2)
print(otherCustom.first?.value)
Output:
Optional(5)
Optional(15)
Optional(2)
You will need to define a protocol with an associated type:
protocol Node: class {
associatedtype Value
var value: Value {get set}
var next: Self? {get set}
var prev: Self? {get set}
init(value: Value)
}
final class BasicNode<Value>: Node {
var value: Value
var next: BasicNode<Value>?
weak var prev: BasicNode<Value>?
init(value: Value) {
self.value = value
}
}
final class CustomNode<Value>: Node {
// customize however you want
var value: Value
var next: BasicNode<Value>?
weak var prev: BasicNode<Value>?
init(value: Value) {
self.value = value
}
}
class LinkedList<N: Node> {
var first: N? = nil
var last: N? = nil
var count = 0
#discardableResult
func append(_ value: N.Value) -> N {
let new = N(value: value)
new.prev = last
last?.next = new
count += 1
last = new
if first == nil {
first = new
}
return new
}
}
However, this will require using your basic linked list in an annoying way all the time:
let list = LinkedList<BasicNode<Int>>()
Depending on how you need to customize the node, I would consider finding a way to customize the behavior in the LinkList class itself using dependency injection.

How do you implement a generic class factory in Swift?

I want to "make" a number of class instances similar to a base class but different in underlying type. (Not quite the same as the typical "Animal" class factory examples seen all over the net!)
The code below is close to working but it requires the user to "upcast" the make result, as in:
var f1 = FOO.make(version: FOO.Ver.f1) as! FOO1_32
I do not want the user to know about the specific class type other than it is a FOO. I have seen other proposals and they all indicate that the solution is to define the make with a generic type that conforms to the protocol as in:
make<T: FOOProtocol>(version: Ver = .f1) -> T
However this gives me the error "generic parameter 'T' could not be inferred" on the call to FOO.make(version: FOO.Ver.f1)
Anyone know how to do this? My Playground code follows.
protocol FOOProtocol
{
associatedtype FOOtype
var value: FOOtype {get set}
}
class FOO
{
enum Ver
{
case f1
case f2
}
class func make(version: Ver = .f1) -> FOO
{
print("FOO make")
switch version
{
case .f1:
return FOO1_32()
case .f2:
return FOO2_64()
}
}
}
class FOO1_32: FOO, FOOProtocol
{
typealias FOOtype = UInt32
private var fooVal: UInt32 = 0
var value: UInt32
{
get { return self.fooVal }
set { self.fooVal = newValue }
}
override init()
{
print("FOO1_32 init")
self.fooVal = 132
}
}
class FOO2_64: FOO, FOOProtocol
{
typealias FOOtype = UInt64
private var fooVal: UInt64 = 0
var value: UInt64
{
get { return self.fooVal }
set { self.fooVal = newValue }
}
override init()
{
print("FOO2_64 init")
self.fooVal = 264
}
}
var f1 = FOO.make(version: FOO.Ver.f1) // requires: as! FOO1_32
let f1v = f1.value
print("\(f1v)")
var f2 = FOO.make(version: FOO.Ver.f2) // requires: as! FOO2_64
let f2v = f2.value
print("\(f2v)")

get the type/class of a property from its name in swift

Lets say I have this class:
class Node {
var value: String
var children: [Node]?
}
If I have the name of one of its properties (for example "children") how can I get its type? (In this case [Node]?)
I imagine having a global function like below will solve my needs:
func typeOfPropertyWithName(name: String, ofClass: AnyClass) -> AnyClass? {
//???
}
// Example usage:
var arrayOfNodesClass = typeOfPropertyWithName("children", Node.self)
Swift 2 (Note: Reflection changed):
import Foundation
enum PropertyTypes:String
{
case OptionalInt = "Optional<Int>"
case Int = "Int"
case OptionalString = "Optional<String>"
case String = "String"
//...
}
extension NSObject{
//returns the property type
func getTypeOfProperty(name:String)->String?
{
let type: Mirror = Mirror(reflecting:self)
for child in type.children {
if child.label! == name
{
return String(child.value.dynamicType)
}
}
return nil
}
//Property Type Comparison
func propertyIsOfType(propertyName:String, type:PropertyTypes)->Bool
{
if getTypeOfProperty(propertyName) == type.rawValue
{
return true
}
return false
}
}
custom class:
class Person : NSObject {
var id:Int?
var name : String?
var email : String?
var password : String?
var child:Person?
}
get the type of the "child" property:
let person = Person()
let type = person.getTypeOfProperty("child")
print(type!) //-> Optional<Person>
property type checking:
print( person.propertyIsOfType("email", type: PropertyTypes.OptionalInt) ) //--> false
print( person.propertyIsOfType("email", type: PropertyTypes.OptionalString) //--> true
or
if person.propertyIsOfType("email", type: PropertyTypes.OptionalString)
{
//true -> do something
}
else
{
//false -> do something
}
Reflection is achieved in Swift using the global reflect() function. When passing an instance of some type to reflect() it returns a MirrorType, which has a range of properties allowing you to analyze your instance:
var value: Any { get }
var valueType: Any.Type { get }
var objectIdentifier: ObjectIdentifier? { get }
var count: Int { get }
var summary: String { get }
var quickLookObject: QuickLookObject? { get }
var disposition: MirrorDisposition { get }
subscript(i: Int) -> (String, MirrorType) { get }
This seems to work:
func getTypeOfVariableWithName(name: String, inInstance instance: Any) -> String? {
let mirror = reflect(instance)
var variableCollection = [String: MirrorType]()
for item in 0..<mirror.count {
variableCollection[mirror[item].0] = mirror[item].1
}
if let type = variableCollection[name] {
let longName = _stdlib_getDemangledTypeName(type.value)
let shortName = split(longName, { $0 == "."}).last
return shortName ?? longName
}
return nil
}
Here's some example code on SwiftStub.
Edit:
The result for optional values is only "Optional".
The result for arrays is only "Array".
The result for dictionaries is only "Dictionary".
I'm not sure if it is possible to extract what kind of optional/array/dictionary it is. But I guess this would also be the case for custom data structures using generics.
Building on #PeterKreinz answer I needed to be able to check types of inherited properties as well so added a little to his above code:
extension NSObject {
// Returns the property type
func getTypeOfProperty (name: String) -> String? {
var type: Mirror = Mirror(reflecting: self)
for child in type.children {
if child.label! == name {
return String(child.value.dynamicType)
}
}
while let parent = type.superclassMirror() {
for child in parent.children {
if child.label! == name {
return String(child.value.dynamicType)
}
}
type = parent
}
return nil
}
}
Hope this may help someone.
Swift 3 update:
// Extends NSObject to add a function which returns property type
extension NSObject {
// Returns the property type
func getTypeOfProperty (_ name: String) -> String? {
var type: Mirror = Mirror(reflecting: self)
for child in type.children {
if child.label! == name {
return String(describing: type(of: child.value))
}
}
while let parent = type.superclassMirror {
for child in parent.children {
if child.label! == name {
return String(describing: type(of: child.value))
}
}
type = parent
}
return nil
}
}
The solution provided by #peter-kreinz using Swift's class Mirror works beautifully when you have an instance of a class, and want to know the types of the properties. However if you want to inspect the properties of a class without having an instance of it you might be interested in my solution.
I have a solution that finds the name and type of a property given any class that inherits from NSObject.
I wrote a lengthy explanation on StackOverflow here, and my project is available here on Github,
In short you can do something like this (but really check out the code Github):
public class func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>? {
var count = UInt32()
guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
var types: Dictionary<String, Any> = [:]
for i in 0..<Int(count) {
guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
let type = getTypeOf(property: property)
types[name] = type
}
free(properties)
return types
}