はじめに
もうネタがない...と思って色々漁っていたところ下記のようなものを見つけました
iOS13 で UserDefaults に 4194304 bytes 以上のデータを保存できない模様
検証
とりあえず検証してみました。
方法
- Macでスクショを撮る(画面サイズのやつ(13328104 bytes))
- Xcode のプロジェクトに追加する
- UserDefaults に画像を保存する(処理は下記)
let url = Bundle.main.url(forResource: "test", withExtension: "png")!
UserDefaults.standard.set(try? Data(contentsOf: url), forKey: "image")
結果
iOS13
コンソールに下記の警告が出ました。
[User Defaults] CFPrefsPlistSource<0x6000024e0800> (Domain: アプリ, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): Attempting to store >= 4194304 bytes of data in CFPreferences/NSUserDefaults on this platform is invalid. This is a bug in アプリ or a library it uses
[User Defaults] CFPrefsPlistSource<0x6000024e0800> (Domain: アプリ, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): Transitioning into direct mode
コンソールに何か出ましたが、下記の処理でデータは取れてるようでした。
if let data = UserDefaults.standard.data(forKey: "image") {
print(data.count)
imageView.image = UIImage(data: data)
}
print の表示も 13328104 なのでサイズはそのままでした。アプリを再起動しても取得できました。
その後、アプリを再起動して下記の処理を書くと再度コンソールに警告が出ました。
UserDefaults.standard.set("しんぐるとん", forKey: "Singleton")
[User Defaults] CFPrefsPlistSource<0x600000df4480> (Domain: アプリ, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: Yes): Attempting to store >= 4194304 bytes of data in CFPreferences/NSUserDefaults on this platform is invalid. This is a bug in アプリ or a library it uses
[User Defaults] CFPrefsPlistSource<0x600000df4480> (Domain: アプリ, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): Transitioning into direct mode
微妙に違う?? Contents Need Refresh: Yes
Yes になってる??
画像データも文字列も UserDefaults
から取得することができました.
iOS12.4.1
コンソールには何も表示されることなく読み書きできました
4194304 byte とは
4194304 byte ってどれくらい保存できるんだ?ってことなんですが下記のような struct
を作って保存すると(文字数にもよりますが...)49 byte でした。
let user = User(userID: "1234567890", userName: "SingleSingle") // 49 byte
UserDefaults.standard.write(user, key: .user)
struct User: Codable {
let userID: String
let userName: String
}
extension UserDefaults {
enum Key: String {
case user
}
@discardableResult
func write<T: Codable>(_ value: T?, key: Key) -> Bool {
let data = try? JSONEncoder().encode(value)
set(data, forKey: key.rawValue)
return synchronize()
}
func read<T: Codable>(_ type: T.Type, key: Key) -> T? {
guard let data = data(forKey: key.rawValue) else {
return nil
}
return try? JSONDecoder().decode(type, from: data)
}
}
だいたい 85598 件くらい上記のデータが保存できるみたいです(十分な容量がありそう)。
関係ないですが syncronize()
は公式ドキュメントに
this method is unnecessary and shouldn't be used.
とあるので必要なさそうですが担当のアプリでこれがないとバグったのでとりあえずまだ入れてます。
結論
ちょっと調べてみましたが公式のドキュメントは見当たりませんでした...
とりあえずは iOS12 でも iOS13 でも UserDefaults
で 4194304 bytes 以上のデータの読み書きはできるようでした。
しかし
Attempting to store >= 4194304 bytes of data in CFPreferences/NSUserDefaults on this platform is invalid.
という警告が出てる以上このまま使うのは良くない
大きなデータは他の方法で保存するよう修正した方がいいでしょう
下記のようにとにかく UserDefaults
以外の保存方法に書き換えるしかなさそう??
-
Data の
write(to:options:)
でファイルに書き出す - CoreData を使う
- Realm を使う
- その他 SQLite など使う
さいごに
そもそも UserDefaults
にそんなでかいデータ入れる実装ってどうなんだ?というのはあります