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.
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.