How to pass a class type to a function?

Hi,


I know I can type "if person is Person" to determine if an object (person) is a type of a class (Person).


Now I want to write the same thing within a function that accepts both "person" and "Person" as parameters.


I know I can declare the object as being of type "Any", but I don't know what type to use for the class.


I tried "AnyClass", but it doesn't work; the "is" expression won't compile (the compiler says "use of undeclared type".


What's the right way to do this?

Replies

AnyClass
is the right type, but this won’t work with
is
because the right side of
is
must be a specific type. There’s a couple of ways you can approach this:
  • If you’re relying on the Objective-C runtime, you can use

    -isKindOfClass:
    from Swift. For example:
    func isOfType(_ o: NSObject, _ c: AnyClass) -> Bool {
        if o.isKind(of: c) {
            return true
        } else {
            return false
        }
    }
    print(isOfType(Person(), Person.self)) // -> true
    print(isOfType(Group(), Person.self))  // -> false

    In this example

    Person
    and
    Group
    must be Objective-C classes.
  • If not, you can often solve this problem by making the function generic in that type. It’s hard to craft a good example without knowing more about what you’re doing based on the result of the

    is
    test — for example, you might want to use
    as?
    to check for a protocol and then use that protocol on the value — but the following is a direct translation of the above:
    func isOfType<C>(_ a: Any, _ c: C.Type) -> Bool {
        if a is C {
            return true
        } else {
            return false
        }
    }

    Here there’s no requirement that

    Person
    and
    Group
    be Objective-C classes.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks. I had thought to use isKind, but my object wasn't an NSObject.


It's never been quite clear to me whether Swift classes should extend NSObject, or something else, or nothing. It does seem as though if they don't extend NSObject, they lose a lot of valuable functionality. Is there some advantage to not extending NSObject?


Frank

It's never been quite clear to me whether Swift classes should extend

NSObject

This definitely varies by context:

  • Some classes must inherit from

    NSObject
    (either directly or indirectly). View controllers, views, operations (in the
    NSOperation
    sense), managed objects (in the Core Data sense), and classes that implement Objective-C protocols are all good examples of this.
  • Some techniques require that you inherit from

    NSObject
    . For example, key-value coding (KVC) is only available to
    NSObject
    subclasses.

    Note Swift has it’s own key path mechanism that is not dependent on

    NSObject
    .
  • Non-Apple platforms don’t support the Objective-C runtime, so if you think that your code might eventually run on Linux or whatnot, it’s best to avoid

    NSObject
    .

Personally I follow these rules:

  • I prefer structs over classes.

  • I try to only inherit from

    NSObject
    where it’s required by frameworks.
  • In situations where I might previously have reached for Objective-C runtime’s dynamism, I try to find other, type-safe alternatives.

  • I don’t waste too much time on the previous point. If I can’t find a solution quickly, I take the pragmatic path and lean on the Objective-C runtime.

However, the best policy will vary by circumstances. For example, I was recently working on a playground that emulates certain aspects of a Raspberry Pi on iOS. This must use the Objective-C runtime in some places — like the

UIView
that emulates a set of LEDs connected to the Raspberry Pi — but it must avoid it in others. Specifically, I had to makes sure that the core code didn’t rely on the Objective-C runtime so that I could run that code on the Raspberry Pi itself.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Why structs over classes? Isn't it more efficient to pass by reference than by value? I've always thought of structs as appropriate for very small things only, like CGSize or NSRange.

Why structs over classes?

Because structs typically have value semantics [1], and I generally prefer value semantics over reference semantics.

IMPORTANT This is a general preference, not a strict rule. Some things are best modelled by reference semantics, and I use a class for those. For example, a TCP connection has its own independent identity — you can’t copy a TCP connection like you would, say, an

Int
— and thus I’d use a class for that.

Isn’t it more efficient to pass by reference than by value?

That depends. In many cases the Swift compiler can optimise away the copy, passing the struct by reference. In cases where it doesn’t, you can use a technique called copy on write (COW) to minimise the cost of copies that aren’t specifically mutated. However, I view COW as an optimisation, and I would only deploy it if I was absolutely certain (for example, I had hard evidence from the profiler) that it was necessary.

I've always thought of structs as appropriate for very small things only, like

CGSize
or
NSRange
.

That’s certainly the standard approach for Objective-C code, but Swift is not Objective-C and there are substantial benefits to be had by exploiting Swift’s features. Structs are just one of those features, and that’s not even the most important one IMO. I find that Swift’s enums have had the biggest impact on how I program.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] It is possible to create a struct that doesn’t have value semantics, although that’s not something I’d recommend. It’s also possible to create a class that does have value semantics, but only if you make it immutable.