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

Time:11-28

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

CodePudding user response:

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 } ?? []
    }
}

CodePudding user response:

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"
  • Related