NSUserDefaultsは、iOS、watchOS、tvOS、macOSにおけるキー-バリューデータストレージであり、アプリケーションの設定や構成を保存するために設計されています。データはアプリケーションのサンドボックス内のplistファイルに保存され、NSUbiquitousKeyValueStoreを介して自動的にiCloudと同期されます。公式ドキュメントによるとApple Developer、2025、NSUserDefaultsはプリミティブ型の保存をサポートしています:String、Int、Bool、Float、Double、Data、Date、Array、Dictionary。このクラスはSwift 3以降UserDefaultsに名称変更されましたが、Objective-Cでの名前NSUserDefaultsはコードベースやAppleのドキュメントで広く使用され続けています。
重要ポイント
NSUserDefaults(SwiftではUserDefaults)は、plist形式でキー-バリューペアを保存するためのAppleの組み込みメカニズムです。iOS、iPadOS、watchOS、tvOS、macOSのすべてのAppleプラットフォームで利用できます。主な目的は、ユーザー設定、インターフェースの状態、初回起動フラグ、選択オプション、その他のアプリケーション再起動後も持続する単純なデータを保存することです。
各iOSアプリケーションには隔離されたサンドボックスがあり、NSUserDefaultsはこのサンドボックス内のLibrary/PreferencesディレクトリにBundle Identifierという名前のファイルで保存されます。plistファイルにはキーと値のペアが含まれ、キーは文字列、値はサポートされている型のいずれかです。ファイルサイズに制限はありませんが、AppleはUserDefaultsには設定のみを保存し、大量のデータは保存しないことを推奨しています。
iOS 8以降、NSUserDefaultsはApp Groupsをサポートし始めました。これは同じデベロッパーのアプリケーションとその拡張機能(ウィジェット、watchOSコンパニオンアプリ)間の共有ストレージです。これにはApp Group識別子を持つinit?(suiteName:)イニシャライザを使用します。これにより、例えばTodayウィジェットが保存ロジックを複製することなくメインアプリケーションから設定を読み取ることができます。
物理的には、NSUserDefaultsはバイナリplistファイルに保存されます:{Sandbox}/Library/Preferences/com.example.myapp.plist。ファイルはコンパクトさと読み取り速度のためにバイナリplist形式(NSPropertyListBinaryFormat_v1_0)を使用しています。macOSでは、互換性のためにファイルがXML形式の場合もあります。AndroidのSharedPreferencesとは異なり、UserDefaultsのplistファイルはDictionaryとArrayを介してネストされた構造を含むことができます。
NSUserDefaultsファイルはデフォルトでは暗号化されていません。データは平文で保存され、デバイスへの物理アクセスやバックアップを介して読み取られる可能性があります。機密データ(パスワード、トークン、暗号化キー)を保存するには、Appleはオペレーティングシステムレベルで自動的にデータを暗号化するKeychainの使用を強く推奨しています。
NSUserDefaultsは、メモリキャッシュと定期的なディスク同期の原則で動作します。標準のUserDefaults.standardインスタンスへの最初のアクセス時に、システムはplistファイルをDictionaryとしてRAMにロードします。後続のすべての読み取りはメモリから実行されます。書き込みも最初にメモリで行われ、ディスクへの同期はバックグラウンドスレッドで定期的に行われます。
書き込み操作はset(_:forKey:)メソッドを使用します。このメソッドはオプショナルなAny?型の値を受け入れます。値はnilにすることもできます。これはキーを削除するために使用されます。以前は即時ディスク書き込みのためにsynchronize()メソッドが使用されていましたが、iOS 7およびOS X 10.9以降は不要になりました。システムが定期的に自動でデータを同期します。Appleは公式ドキュメントでsynchronize()を冗長であると宣言しています。
NSUserDefaultsは値の検索を整理するためにレジスタ(ドメイン)システムを使用します。アプリケーションがキーによって値を要求すると、UserDefaultsは特定の順序でドメインを順次チェックします:最初にNSArgumentDomain(コマンドライン引数)、次にアプリケーションドメイン、次にNSGlobalDomain(システム設定)、次に言語固有のドメイン、最後にNSRegistrationDomain(register(defaults:)を介して登録されたデフォルト値)。
import Foundation
// UserDefaultsの標準インスタンス
let defaults = UserDefaults.standard
// 値の書き込み
defaults.set("アンナ・ペトロワ", forKey: "username")
defaults.set(28, forKey: "age")
defaults.set(true, forKey: "isLoggedIn")
// デフォルト値の登録
defaults.register(defaults: [
"theme": "system",
"fontSize": 14
])
// デフォルト値の戻り値を伴う読み取り
let theme = defaults.string(forKey: "theme") ?? "system"
let fontSize = defaults.integer(forKey: "fontSize")
NSRegistrationDomainはプログラム上のドメインであり、メモリにのみ存在し、ディスクには保存されません。アプリケーションがアプリケーションドメインに独自の値を書き込むまで適用されるデフォルト値を設定するために使用されます。これにより、開発中に集中管理できるデフォルト設定の単一構成ポイントを作成できます。
NSUserDefaultsはデータの読み書きのための型付きメソッドのセットを提供します:string(forKey:)、integer(forKey:)、bool(forKey:)、float(forKey:)、double(forKey:)、data(forKey:)、array(forKey:)、dictionary(forKey:)、object(forKey:)。各読み取りメソッドには、保存される値の自動型推論を行う対応する書き込みメソッドset(_:forKey:)があります。UserDefaultsのSwiftバージョンは強い型付けを使用しますが、Objective-Cバージョンはidを受け入れ、返します。
| 読み取りメソッド (Swift) | データ型 | デフォルト値 |
|---|---|---|
| string(forKey:) | String? | nil |
| integer(forKey:) | Int | 0 |
| bool(forKey:) | Bool | false |
| float(forKey:) | Float | 0.0 |
| double(forKey:) | Double | 0.0 |
| data(forKey:) | Data? | nil |
NSUserDefaultsのsynchronize()メソッドは、すべての変更をメモリからディスクに強制的に書き込みます。初期のiOSバージョンでは、データの永続性を保証するために毎回の書き込み後にこのメソッドを呼び出す必要がありました。iOS 7以降、システムはバックグラウンドスレッドで自動的にUserDefaultsを同期し、Appleは公式にsynchronize()を冗長と宣言しました。このメソッドを呼び出してもエラーにはなりませんが、追加の永続性の保証はありません。
変更を監視するために、NSUserDefaultsはUserDefaults.didChangeNotification通知とKVO観察メソッドaddObserver(_:forKeyPath:options:context:)を提供します。SwiftUIでは、@AppStorageプロパティラッパーが利用可能で、UserDefaultsの値をUIの更新と自動的に同期します。@AppStorageはUserDefaultsと同じ型をサポートし、SwiftUIアプリケーションで設定を操作するための推奨方法です。
// KVOによる変更の監視
class SettingsViewModel: NSObject {
override func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?
) {
guard let keyPath else { return }
print("キーが変更されました:\(keyPath)")
}
}
// SwiftUI - AppStorage
struct SettingsView: View {
@AppStorage("theme") private var theme: String = "system"
var body: some View {
Picker("テーマ", selection: $theme) {
Text("システム").tag("system")
Text("ライト").tag("light")
Text("ダーク").tag("dark")
}
}
}
App Groups(アプリケーションとその拡張機能間の共有ストレージ)で作業するには、App Group識別子を持つUserDefaults(suiteName:)イニシャライザを使用します。例:“group.com.example.myapp”。このインスタンスに書き込まれたデータは、メインアプリケーション、ウィジェット、watchOSコンパニオンアプリ、および同じApp Groupに属する他の拡張機能からアクセス可能です。各suiteインスタンスは個別のplistファイルに保存されます。
その便利さとシンプルさにもかかわらず、NSUserDefaultsはiOS上のすべてのデータタイプに対するユニバーサルなストレージソリューションではありません。ボリューム、重要度、セキュリティ要件に応じて、Appleはそれぞれ特定のユースケースに最適化された複数の代替手段を提供しています。
| ソリューション | 使用すべき場合 | 制限事項 |
|---|---|---|
| NSUserDefaults | インターフェース設定と構成 | 大規模データや機密情報には不適切 |
| Keychain | パスワード、トークン、暗号化キー | 使用が複雑、低速 |
| CoreData | リレーションシップを持つ構造化データ | 10–20の設定には過剰 |
| FileManager | ドキュメント、画像、バイナリデータ | 手動ファイル管理が必要 |
| CloudKit | デバイス間のクラウド同期 | iCloudアカウントとネットワーク接続が必要 |
Keychainは機密データのためのAppleの安全なストレージです。NSUserDefaultsとは異なり、Keychain内のすべてのデータは、対応するデバイスではSecure Enclaveからのハードウェア暗号化を使用してオペレーティングシステムレベルで暗号化されます。Keychainはデバイスとともに自動的にロックおよびロック解除され、Keychain Access Groupsを介して同じデベロッパーのアプリケーション間でのアクセス共有をサポートします。
Keychainの主な欠点はAPIの複雑さです。単に文字列を保存するだけでも、クラス(kSecClassGenericPassword)、サービス(kSecAttrService)、アカウント(kSecAttrAccount)、実際のデータ(kSecValueData)の属性を指定してSecItemAddクエリを作成する必要があります。Keychainの操作を簡素化するために、KeychainAccessやSwiftKeychainWrapperなどのサードパーティラッパーが存在し、UserDefaultsと同様の便利なキー-バリューインターフェースを提供しています。
実用的な例を考えてみましょう。NSUserDefaultsを使用してiOSアプリケーションでオンボーディング状態(ウェルカム画面)を保存および復元します。初回起動時にユーザーはオンボーディング画面を表示し、完了後にUserDefaultsにフラグが保存されます。以降の起動ではオンボーディングはスキップされます。SwiftUIでは@AppStorageが使用され、UIKitではUserDefaults.standardへの直接アクセスが使用されます。
オンボーディングステータスを保存するためにUserDefaultsとの連携をカプセル化するOnboardingManagerを作成しましょう。マネージャーは状態を確認するためのisOnboardingCompletedプロパティとフラグを設定するmarkOnboardingCompletedメソッドを提供します。タイプミスを防ぐためにストレージキーは定数に抽出されています。ユニットテストのために、マネージャーはUserDefaultsProtocolを使用し、実際のストレージをMockUserDefaultsに置き換えることができます。
class OnboardingManager {
private let defaults: UserDefaults
private let hasSeenKey = "has_seen_onboarding"
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
}
var isOnboardingCompleted: Bool {
defaults.bool(forKey: hasSeenKey)
}
func markOnboardingCompleted() {
defaults.set(true, forKey: hasSeenKey)
}
func resetOnboarding() {
defaults.removeObject(forKey: hasSeenKey)
}
}
// アプリケーションでの使用
let onboardingManager = OnboardingManager()
if !onboardingManager.isOnboardingCompleted {
showOnboarding()
} else {
showMainScreen()
}
構造化されたProfileオブジェクトなど、より複雑な設定を保存するには、CodableプロトコルとJSONEncoder/JSONDecoderの使用をお勧めします。オブジェクトはJSONEncoderを介してDataにシリアライズされ、set(_:forKey:)を介して保存され、読み取り時にJSONDecoderを介してDataからオブジェクトにデシリアライズされます。このアプローチにより、型安全性を失うことなくUserDefaultsに複雑な構造を保存できます。
struct UserProfile: Codable {
let name: String
let age: Int
let preferences: [String: String]
}
extension UserDefaults {
func save<T: Codable>(_ value: T, forKey key: String) {
if let data = try? JSONEncoder().encode(value) {
set(data, forKey: key)
}
}
func load<T: Codable>(_ type: T.Type, forKey key: String) -> T? {
guard let data = data(forKey: key) else { return nil }
return try? JSONDecoder().decode(type, from: data)
}
}
// 使用
let profile = UserProfile(name: "アンナ", age: 28, preferences: ["theme": "dark"])
UserDefaults.standard.save(profile, forKey: "user_profile")
let loaded = UserDefaults.standard.load(UserProfile.self, forKey: "user_profile")
NSUserDefaultsは大量のデータを保存するように設計されていないことを覚えておくことが重要です。Appleは保存するデータを数十キロバイト程度に制限することを推奨しています。大きなオブジェクト(画像、ドキュメント、シリアライズされたモデル)を保存するには、Documentsディレクトリを持つFileManagerまたはCoreDataを使用してください。さらに、UserDefaultsはデータスキーマのバージョニングをサポートしていません。Codableモデルの構造が変更されると、古いデータがデシリアライズに失敗する可能性があり、これはアプリケーションコードで処理する必要があります。
よくある質問
どちらもキー-バリューストアですが、NSUserDefaultsはより多くの型(Data、Date、Array、Dictionary)をサポートし、自動的にiCloudと同期します。SharedPreferencesはデータをXMLに保存し、NSUserDefaultsはplist形式を使用します。NSUserDefaultsにはカスケード検索を持つドメインシステムがありますが、SharedPreferencesはファイル名を持つ単純なフラット構造を使用します。
いいえ、NSUserDefaultsはデータを暗号化なしで平文で保存します。パスワード、トークン、暗号化キーには、Secure Enclaveレベルでデータを暗号化するKeychainを使用してください。Keychainは秘密を読み取る前に生体認証(Face ID / Touch ID)などのアクセス属性もサポートしています。
同じユーザーのデバイス間での同期には、NSUbiquitousKeyValueStore—iCloudクラウドキー-バリューストレージを使用します。あるデバイスでこのサービスに書き込まれたデータは、同じiCloudアカウントを持つ他のすべてのデバイスに自動的に表示されます。最大容量:アプリケーションあたり1 MB、1024キー。
すべてのデータを削除するには、アプリケーションのBundle Identifierを指定してremovePersistentDomain(forName:)メソッドを呼び出します。個別の値を削除するにはremoveObject(forKey:)を使用します。完全な設定リセット:UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)。すべての削除は即座にインメモリキャッシュに適用されます。
AppleはNSUserDefaultsのサイズに厳格な制限を設けていませんが、保存する全データの総量は100 KBを超えないことが推奨されています。より大容量の場合はCoreDataまたはFileManagerを使用してください。1 MBを超えるデータを保存すると、アプリケーション起動時の読み取りパフォーマンスが著しく低下する可能性があります。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。