Swift
NSCoding
iOS Development
Debugging
Error Handling

Got Unrecognized selector -replacementObjectForKeyedArchiver crash when implementing NSCoding in Swift

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The crash mentioning replacementObjectForKeyedArchiver usually appears when NSKeyedArchiver reaches an object that does not participate in Objective-C style archiving the way Foundation expects. In Swift, the most common cause is trying to archive a pure Swift class or nested property that is not compatible with NSCoding or NSSecureCoding.

Why This Selector Shows Up

NSKeyedArchiver is built on top of Foundation's Objective-C archiving system. As it walks the object graph, it may ask objects whether they want to substitute themselves with another archived representation. That internal mechanism is where the selector name comes from.

The important part is that the crash does not usually mean you forgot to write a method named replacementObjectForKeyedArchiver. It means the archiver was sent to an object that is not being bridged or archived correctly.

A Safe NSCoding Implementation Pattern

If you are still using NSCoding, the archived class should normally inherit from NSObject and implement both decoding and encoding consistently.

swift
1import Foundation
2
3final class Person: NSObject, NSCoding {
4    let name: String
5    let age: Int
6
7    init(name: String, age: Int) {
8        self.name = name
9        self.age = age
10    }
11
12    required init?(coder: NSCoder) {
13        guard let name = coder.decodeObject(forKey: "name") as? String else {
14            return nil
15        }
16
17        self.name = name
18        self.age = coder.decodeInteger(forKey: "age")
19    }
20
21    func encode(with coder: NSCoder) {
22        coder.encode(name, forKey: "name")
23        coder.encode(age, forKey: "age")
24    }
25}

The NSObject inheritance is not just tradition here. It matters because the Foundation archiver expects Objective-C runtime behavior around the archived object.

Nested Properties Must Also Be Archivable

Even if the top-level class looks correct, the crash can come from one of its stored properties. Every object you pass into coder.encode must itself be archivable.

For example, this pattern is risky if Address is a plain Swift class with no archiving support:

swift
1final class Employee: NSObject, NSCoding {
2    let name: String
3    let address: Address
4
5    init(name: String, address: Address) {
6        self.name = name
7        self.address = address
8    }
9
10    required init?(coder: NSCoder) {
11        guard
12            let name = coder.decodeObject(forKey: "name") as? String,
13            let address = coder.decodeObject(forKey: "address") as? Address
14        else {
15            return nil
16        }
17
18        self.name = name
19        self.address = address
20    }
21
22    func encode(with coder: NSCoder) {
23        coder.encode(name, forKey: "name")
24        coder.encode(address, forKey: "address")
25    }
26}

If Address is not archivable, the failure may surface only when the archiver reaches that property.

Prefer NSSecureCoding for New Code

In newer code, NSSecureCoding is the better choice because it makes decoding rules explicit and safer.

swift
1import Foundation
2
3final class Person: NSObject, NSSecureCoding {
4    static var supportsSecureCoding: Bool { true }
5
6    let name: String
7
8    init(name: String) {
9        self.name = name
10    }
11
12    required init?(coder: NSCoder) {
13        guard let name = coder.decodeObject(of: NSString.self, forKey: "name") as String? else {
14            return nil
15        }
16
17        self.name = name
18    }
19
20    func encode(with coder: NSCoder) {
21        coder.encode(name, forKey: "name")
22    }
23}

This does not remove the need for compatible nested objects, but it makes the archival contract clearer and reduces a class of unsafe decoding bugs.

Consider Codable If You Control the Format

If you are not required to interoperate with older NSCoding archives, Swift's Codable is often simpler and more natural. It avoids some of the Objective-C runtime assumptions that make keyed archiving crashes confusing in Swift-heavy codebases.

That said, if you are working with UserDefaults, older archives, Interface Builder integrations, or legacy iOS code, NSCoding and NSSecureCoding may still be the right tools.

Common Pitfalls

The biggest pitfall is making the top-level Swift class conform to NSCoding without inheriting from NSObject. That often works poorly because the Foundation archiver expects Objective-C-compatible objects.

Another common mistake is checking only the main class and forgetting that one nested property may be the real source of the crash. Audit every object passed to coder.encode, not just the top-level type.

Developers also introduce bugs by encoding one set of keys and decoding with different keys or different types. That mismatch may not produce this exact selector crash every time, but it leads to fragile archives and hard-to-trace failures.

Summary

  • The replacementObjectForKeyedArchiver crash usually points to an archive-compatibility problem, not a missing method you need to implement directly.
  • Classes archived with NSCoding should normally inherit from NSObject.
  • Every nested object passed into the coder must also be archivable.
  • 'NSSecureCoding is the safer modern form of the same pattern.'
  • If you control the storage format, Codable may be a cleaner alternative than legacy keyed archiving.

Course illustration
Course illustration

All Rights Reserved.