I recently came across a question from a developer that inspired me to write this tutorial. The developer ran into the following compiler error.

Argument type String expected to be an instance of a class or class-constrained type

For someone new to Swift, this error can be quite challenging to resolve. Let's reproduce the error and track down the root cause. Once we understand the root cause, the solution is surprisingly simple.

Reproducing the Error

Fire up Xcode and create a playground. Declare a struct with name Foo. The struct defines one method, print(_:), with parameter message of type AnyObject. The body of the method is irrelevant for this tutorial.

struct Foo {

    func print(_ message: AnyObject) {
		// ...
    }

}

The type of the message parameter is essential to understand what follows. If you want to learn everything there is to know about the AnyObject protocol, then visit What Is AnyObject in Swift. Let me explain in a nutshell what AnyObject is.

In Swift, AnyObject is a protocol that represents an instance of any class type.

In other words, the print(_:) method requires that the value of the message parameter is an instance of a class. Keep this in mind. Let's continue with the example.

We create a Foo object and store it in a constant with name foo. We then invoke the print(_:) method, passing a string literal as the argument.

struct Foo {

    func print(_ message: AnyObject) {
		// ...
    }

}

let foo = Foo()
foo.print("Hello World")

If you are following along with me, then you should see the following compiler error pop up.

Argument Type String Expected to Be an Instance of a Class or Class-Constrained Type

Understanding the Error

To understand what the error means, we need to revisit the definition of AnyObject. The message parameter of the print(_:) method is of type AnyObject, which means that the argument we pass to the print(_:) method is required to be an instance of a class type. That isn't true, though.

In the example, we pass a string literal to the print(_:) method. The String struct is a value type, not a class type (reference type). In other words, the string literal we pass to the print(_:) method doesn't meet the AnyObject requirement. It isn't an instance of a class type. It is a value type. That explains the error.

Resolving the Error

There are several ways to resolve the error. How you resolve the error depends on the context and the implementation of the method, so your solution may differ from mine. Let me share two possible solutions with you.

Removing AnyObject with String

In this example, it may not be necessary for the message parameter to be of type AnyObject. It may make more sense to change the type of the message parameter to String, not AnyObject. That change resolves the error.

struct Foo {

    func print(_ message: String) {

    }

}

let foo = Foo()
foo.print("Hello World")

Replacing AnyObject with Any

If we want to keep the print(_:) method flexible by allowing any type to be passed in, then we can replace AnyObject with Any. That change also resolves the error without compromising the flexibility and versatility of the print(_:) method.

struct Foo {

    func print(_ message: Any) {

    }

}

let foo = Foo()
foo.print("Hello World")

If you'd like to learn more about the meaning of Any, then visit What Is Any in Swift. In that tutorial, we take a close look at Any and its meaning.

The gist is that Any can represent an instance of any type at all, including function types. Unlike AnyObject, Any isn't limited to class types. That is why it is fine to pass a string literal, a value type, to the print(_:) method.

What's Next?

Which solution works for you depends on the context and the implementation of the method. It is true that Any and AnyObject may be more flexible at the call site, but it also means your code is less type-safe. Type safety is an essential aspect of the Swift programming language so being more specific is the better option in most scenarios.