Difference between @objc and @nonobjc in Swift

Jul 25, 2023#swift#comparison

In Swift, @objc and @nonobjc are two attributes that affect the interoperability between Swift and Objective-C. This enables a gradual transition from Objective-C to Swift, promotes code reuse, and provides access to a vast array of libraries and APIs in both languages.

  • @objc is used to expose a method, property, or class to Objective-C
  • @nonobjc is used to explicitly hide an entity from Objective-C

@objc

The @objc attribute exposes a Swift declaration to Objective-C and the Objective-C runtime. This is useful when you want to interact with Objective-C frameworks, such as UIKit, or use features that require Objective-C, such as selectors, key-value coding, or dynamic dispatch.

To use @objc in Swift, you need to mark the class, method, property, or other declaration that you want to expose to Objective-C with the @objc attribute. For example:

// A Swift class that inherits from NSObject and is exposed to Objective-C
@objc class Person: NSObject {
  // A Swift property that is exposed to Objective-C
  @objc var name: String

  // A Swift method that is exposed to Objective-C
  @objc func sayHello() {
    print("Hello, I'm \(name)")
  }
}

You can also use @objcMembers to expose all members of a class to Objective-C without marking them individually. For example:

// A Swift class that inherits from NSObject and exposes all its members to Objective-C
@objcMembers class Person: NSObject {
  // A Swift property that is exposed to Objective-C implicitly
  var name: String

  // A Swift method that is exposed to Objective-C implicitly
  func sayHello() {
    print("Hello, I'm \(name)")
  }
}

Note that not all Swift declarations can be exposed to Objective-C. For example, Swift enums, structs, generics, tuples, and closures cannot be represented in Objective-C. If you try to mark them with @objc, you will get a compiler error.

@nonobjc

The @nonobjc attribute is used in Swift to indicate that a certain declaration should not be exposed to Objective-C. It is essentially the opposite of @objc. When you mark a declaration with @nonobjc, you’re specifying that it should not be accessible from Objective-C code.

class MySwiftClass: NSObject {
  // Exposed to Objective-C
  @objc func methodExposedToObjC() {
    print("This method is exposed to Objective-C.")
  }

  // Not exposed to Objective-C
  @nonobjc func methodNotExposedToObjC() {
    print("This method is not exposed to Objective-C.")
  }
}

You can use @nonobjc to suppress an implicit @objc attribute that is added by the compiler in some cases. For example, if you override a method or satisfy a requirement from a protocol that has the @objc attribute, the compiler will also add the @objc attribute to your declaration. You can use @nonobjc to prevent that and make your declaration unavailable in Objective-C.

Another use case for @nonobjc is to resolve name conflicts between Swift and Objective-C declarations. For example, if you have a Swift method that has the same name as an Objective-C method, but different parameters or return type, you can use @nonobjc to avoid a compiler error.

Note that you cannot use @nonobjc on protocols, because protocols are never implicitly exposed to Objective-C. You can only use @nonobjc on methods, properties, subscripts, or initializers.