質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.64%

UserDefaultsで配列を保存したいのですが、どうしてもエラーになってしまいます

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 788

MoonDDDD

score 9

 前提・実現したいこと

Userクラスの中のlikeという配列をUserDefaultsで使おうとした時、likeのデコード処理でnilが入ってしまい、このようなエラーが発生してしまいます。この対処法を教えてもらえませんか?

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

 ソースコード(データクラス)

import Foundation

class User:NSObject, NSCoding {

    var UserName: String?
    var Email: String?
    var Password: String?
    var UserImage: NSData?
    var lives: String?
    var sex: String?
    var age: String?
    var colleage: String?
    var event: String?
    var eventName: String?
    var like = [String]()

    override init() {
    }

    //デコード処理
    required init?(coder aDecoder: NSCoder) {
        UserName = aDecoder.decodeObject(forKey: "username") as? String
        Email = aDecoder.decodeObject(forKey: "email") as? String
        Password = aDecoder.decodeObject(forKey: "password") as? String
        UserImage = aDecoder.decodeObject(forKey: "userimage") as? NSData
        lives = aDecoder.decodeObject(forKey: "lives") as? String
        sex = aDecoder.decodeObject(forKey: "sex") as? String
        age = aDecoder.decodeObject(forKey: "age") as? String
        colleage = aDecoder.decodeObject(forKey: "colleage") as? String
        like = aDecoder.decodeObject(forKey: "like") as! [String]
        event = aDecoder.decodeObject(forKey: "event") as? String
        eventName = aDecoder.decodeObject(forKey: "eventname") as? String
    }

    //エンコード処理
    func encode(with aCoder: NSCoder){
        aCoder.encode(UserName, forKey: "username")
        aCoder.encode(Email, forKey: "email")
        aCoder.encode(Password, forKey: "password")
        aCoder.encode(UserImage, forKey: "userimage")
        aCoder.encode(lives, forKey: "lives")
        aCoder.encode(sex, forKey: "sex")
        aCoder.encode(age,forKey: "age")
        aCoder.encode(colleage, forKey:"colleage")
        aCoder.encode(like, forKey: "like")
        aCoder.encode(event, forKey: "event")
        aCoder.encode(eventName, forKey: "eventname")
    }
}

//現在のユーザーデータと指定している本のデータを格納するシングルトン

final class NowUser{
    static let shared = NowUser()
    var nowuser = User()
    var tapuser = User()
    var searchSwich: Bool = true
    private init() {}
}

 ソースコード(HomeViewController)

import UIKit

class HomeViewController: UIViewController ,UICollectionViewDelegate, UICollectionViewDataSource{

    var searchUsers = [User]()
    var user = User()
    var collectionView: UICollectionView!

    override func viewWillAppear(_ animated: Bool) {
        LoginCheck()
        searchUsers.removeAll()
        tabBarController?.tabBar.isHidden = false

        //検索用のsearchUsersと全てのユーザーを表示するusersをsearchSwichで分ける処理
        if NowUser.shared.searchSwich == false {
            let searchUserDefaults = UserDefaults.standard
            if let storedSearchusers = searchUserDefaults.object(forKey: "searchusers") as? Data {
                if let unarchivesearchusers = NSKeyedUnarchiver.unarchiveObject(with: storedSearchusers) as? [User] {
                    searchUsers.append(contentsOf: unarchivesearchusers)
                }
            }
            NowUser.shared.searchSwich = true
        }else{
            let userDefaults = UserDefaults.standard
            if let storedusers = userDefaults.object(forKey: "users") as? Data {
                if let unarchiveusers = NSKeyedUnarchiver.unarchiveObject(with: storedusers) as? [User] {
                    searchUsers.append(contentsOf: unarchiveusers)
                }
            }
            self.navigationItem.title = "検索"
        }
        collectionView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white 
        //NavigationBarの設定
        let searchImageButton: UIBarButtonItem = UIBarButtonItem(image: UIImage(named:"search1"), style: UIBarButtonItemStyle.plain, target: self, action: #selector(Search(_:)))
        self.navigationItem.leftBarButtonItem = searchImageButton

        //CollectionViewの設定
        let flowLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        flowLayout.itemSize = CGSize(width: 90, height: 120)
        flowLayout.minimumInteritemSpacing = 20.0
        flowLayout.minimumLineSpacing = 20.0
        flowLayout.sectionInset = UIEdgeInsetsMake(20,20,0,20)
        collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: flowLayout)
        collectionView.backgroundColor = UIColor.white
        collectionView.register(HomeCustomCell.self, forCellWithReuseIdentifier: "HomeCustomCell")
        collectionView.delegate = self
        collectionView.dataSource = self
        self.view.addSubview(collectionView)
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return searchUsers.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCustomCell", for: indexPath) as! HomeCustomCell
        cell.userNameLabel.text   = searchUsers[indexPath.row].UserName
        cell.userImageView.image  = UIImage(data: (searchUsers[indexPath.row].UserImage! as Data))?.resize(size: CGSize(width: 50, height: 50))
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        NowUser.shared.tapuser = searchUsers[indexPath.row]
        navigationController?.pushViewController(ProfileViewController(), animated: true)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @objc func LoginCheck(){
        if NowUser.shared.nowuser.Email == nil{
            let nc = UINavigationController(rootViewController: LoginViewController())
            self.present(nc, animated: true, completion: nil)
        }
    }

    @objc func Search(_ sender: UIButton){
     self.present(SearchViewController(), animated: true, completion: nil)
    }
}


class HomeCustomCell: UICollectionViewCell {

    let userImageView: UIImageView = {
        let imageView = UIImageView()
        return imageView
    }()

    let userNameLabel: UILabel = {
        let label = UILabel()
        label.textColor = .white
        label.textAlignment = NSTextAlignment.center
        return label
    }()

    override func draw(_ rect: CGRect) {
        self.layer.cornerRadius = 10
        self.layer.masksToBounds = true

        let viewHeight10 = self.contentView.frame.height / 10
        let viewWidth22 = self.contentView.frame.width / 22

        self.contentView.addSubview(userImageView)
        self.contentView.addSubview(userNameLabel)

        self.selectedBackgroundView = UIView(frame: self.bounds)
        self.selectedBackgroundView?.backgroundColor = .red

        userNameLabel.translatesAutoresizingMaskIntoConstraints = false
        userNameLabel.topAnchor.constraint(equalTo:contentView.topAnchor,constant: viewHeight10 * 7).isActive = true
        userNameLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        userNameLabel.widthAnchor.constraint(equalToConstant: viewWidth22 * 22).isActive = true
        userNameLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

        userImageView.translatesAutoresizingMaskIntoConstraints = false
        userImageView.topAnchor.constraint(equalTo:contentView.topAnchor).isActive = true
        userImageView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        userImageView.widthAnchor.constraint(equalToConstant: viewWidth22 * 22).isActive = true
        userImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    }
}

 ここを読み込む時にUserクラスを参照してエラーが起こりました

let userDefaults = UserDefaults.standard
            if let storedusers = userDefaults.object(forKey: "users") as? Data {
                if let unarchiveusers = NSKeyedUnarchiver.unarchiveObject(with: storedusers) as? [User] {       //ここでUserクラスを参照した時にエラーが起こった
                    searchUsers.append(contentsOf: unarchiveusers)
                }
            }


searchUsersはUser型の配列です!( var searchUsers = [User]() )

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • takabosoft

    2018/11/07 11:35 編集

    「UserDefaultsで使おうとした時」とありますが、その前にNSKeyedArchiverか何かでUser↔NSDataに変換していて、問題はその辺で起こっているわけですよね?そのあたりのコードを載せてもらえますか。

    キャンセル

  • MoonDDDD

    2018/11/07 11:53

    そうです!今、問題のコードをあげました!お願いします!!

    キャンセル

回答 1

checkベストアンサー

0

再現を試みているのですが、手元ではエラーはおきません。
なにか他に条件はありませんか?

class User: NSObject, NSCoding {

    var UserName: String?
    var Email: String?
    var Password: String?
    var UserImage: NSData?
    var like = [String]()

    override init() {
    }

    //デコード処理
    required init?(coder aDecoder: NSCoder) {
        UserName = aDecoder.decodeObject(forKey: "username") as? String
        Email = aDecoder.decodeObject(forKey: "email") as? String
        Password = aDecoder.decodeObject(forKey: "password") as? String
        UserImage = aDecoder.decodeObject(forKey: "userimage") as? NSData
        like = aDecoder.decodeObject(forKey: "like") as! [String]          //ここでエラーが発生する
    }

    //エンコード処理
    func encode(with aCoder: NSCoder){
        aCoder.encode(UserName, forKey: "username")
        aCoder.encode(Email, forKey: "email")
        aCoder.encode(Password, forKey: "password")
        aCoder.encode(UserImage, forKey: "userimage")
        aCoder.encode(like, forKey: "like")
    }
}



class ViewController: UIViewController {

    private func save() {
        var users = [User]()

        let test = User()
        //test.like.append("hoge")

        users.append(test)

        let data = NSKeyedArchiver.archivedData(withRootObject: users)
        UserDefaults.standard.set(data, forKey: "users")
    }

    private func load() {
        let userDefaults = UserDefaults.standard
        if let storedusers = userDefaults.object(forKey: "users") as? Data {
            if let unarchiveusers = NSKeyedUnarchiver.unarchiveObject(with: storedusers) as? [User] {
                print(unarchiveusers)
                //searchUsers.append(contentsOf: unarchiveusers)
            }
        }
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        save()
        load()
    }


}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/07 22:48

    なるほど。エラー起こらなかったんですか。。
    実はマッチんングアプリもどきを作製していて、そこのデータクラスと一番初めに出るHomeViewController.swiftを載せておきます!!
    どうしても解決できなかったのでよろしくお願いします泣

    キャンセル

  • 2018/11/07 22:51

    投げやりな形になってすみません!!

    キャンセル

  • 2018/11/08 09:49

    せめて私が載せたような、容易に「再現する最低限のコード」に絞って載せてもらえませんか?ダミーデータで保存、それを読み込んでエラーが発生する、というシンプルなプログラムを作ってください。それができなければ解決は難しいと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る