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

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

ただいまの
回答率

89.10%

オプショナル型の変数をアンラップした時にnilが出て、アプリが動かなくなってしまい困っています

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 454

初心者です。よろしくお願いいたします。

前提・実現したいこと

swift3を使って本を参考に簡単なクイズアプリを作成しています。
エラーが出ないようにしたいです。

発生している問題・エラーメッセージ

スタート画面から問題表示画面に移行するときに以下のエラーが出てしまう
以下ソースコードになります。

エラーメッセージ
unexpectedly found nil while unwrapping an Optional value

以下の部分でEXC_BAD_INSTRUCTION(code=EXC_INVOP,subcode=0x0)と表示され、アプリが停止してしまいます。

questionNoLabel.text = "Q.\(questionData.questionNo)"

該当のソースコード

問題画面とスタート画面とデータベースのコードを置いておきます。よろしくお願いいたします。

問題画面

import UIKit
import AudioToolbox


class QuestionViewController: UIViewController{

    var questionData: QuestionData!

    @IBOutlet weak var questionNoLabel: UILabel!
    @IBOutlet weak var questionTextView: UITextView!
    @IBOutlet weak var answer1Button: UIButton!
    @IBOutlet weak var answer2Button: UIButton!
    @IBOutlet weak var answer3Button: UIButton!
    @IBOutlet weak var answer4Button: UIButton!
    @IBOutlet weak var correctImageView: UIImageView!
    @IBOutlet weak var incorrectImageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        questionNoLabel.text = "Q.\(questionData.questionNo)"
        questionTextView.text = questionData.question
        answer1Button.setTitle(questionData.answer1,for: UIControlState.normal)
        answer2Button.setTitle(questionData.answer2,for: UIControlState.normal)
        answer3Button.setTitle(questionData.answer3,for: UIControlState.normal)
        answer4Button.setTitle(questionData.answer4,for: UIControlState.normal)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    @IBAction func tapAnswer1Button(_ sender: Any) {
        questionData.userChoiceAnswerNumber = 1
        goNextQuestionWithAnimation()
    }
    @IBAction func tapAnswer2Button(_ sender: Any){
        questionData.userChoiceAnswerNumber = 2
        goNextQuestionWithAnimation()
    }

    @IBAction func tapAnswer3Button(_ sender: Any) {
        questionData.userChoiceAnswerNumber = 3
        goNextQuestionWithAnimation()
    }

    @IBAction func tapAnswer4Button(_ sender: Any) {
        questionData.userChoiceAnswerNumber = 4
        goNextQuestionWithAnimation()

    }
    func goNextQuestionWithAnimation(){
        if questionData.isCorrect(){
            goNextQuestionWithCorrectAnimation()
        } else{
            goNextQuestionWithInCorrectAnimation()
        }
    }
    func goNextQuestionWithCorrectAnimation(){
        AudioServicesPlayAlertSound(1025)
        UIView.animate(withDuration: 2.0, animations:{
            self.correctImageView.alpha = 1.0
        }){(Bool) in self.goNextQuestion()
        }
    }
    func goNextQuestionWithInCorrectAnimation(){
        AudioServicesPlayAlertSound(1006)
        UIView.animate(withDuration: 2.0, animations:{
            self.correctImageView.alpha = 1.0
        }){(Bool) in self.goNextQuestion()
        }
    }
    func goNextQuestion(){
        guard let nextQuestion = QuestionDataManager.sharedInstance.nextQuestion() else{
            if let resultViewController = storyboard?.instantiateViewController(withIdentifier: "result") as? ResultViewController{
                present(resultViewController, animated: true, completion: nil)
            }
            return
        }
        if let nextQuestionViewController = storyboard?.instantiateViewController(withIdentifier: "question")
            as? QuestionViewController{

            nextQuestionViewController.questionData = nextQuestion
            present(nextQuestionViewController, animated: true,completion: nil)
        }
    }
}


スタート画面

import UIKit

class StartViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning(){

        super.didReceiveMemoryWarning()
    }

    override func prepare(for segue: UIStoryboardSegue, sender:Any?){

        QuestionDataManager.sharedInstance.loadQuestion()

        guard let nextViewController = segue.destination as?
            QuestionViewController else {
                return
        }

        guard let questionData = QuestionDataManager.sharedInstance.nextQuestion() else {
            return
        }
        nextViewController.questionData = questionData
    }

    @IBAction func goToTitle(_ segue: UIStoryboardSegue){
    }
}


データベース

import Foundation

class QuestionData {
    var question: String
    var answer1: String
    var answer2: String
    var answer3: String
    var answer4: String

    var correctAnswerNumber: Int

    var userChoiceAnswerNumber: Int?
    var questionNo: Int = 0

    init(questionSourceDataArray: [String]){
        question = questionSourceDataArray[0]
        answer1  = questionSourceDataArray[1]
        answer2 = questionSourceDataArray[2]
        answer3  = questionSourceDataArray[3]
        answer4  = questionSourceDataArray[4]
        correctAnswerNumber = Int(questionSourceDataArray[5])!
    }

    func isCorrect() -> Bool{
        if correctAnswerNumber == userChoiceAnswerNumber{
            return true
        }
        return false
    }
}

class QuestionDataManager{
        static let sharedInstance = QuestionDataManager()

        var questionDataArray = [QuestionData]()

        var nowQuestionIndex: Int = 0
    private init(){

    }

    func loadQuestion(){
        questionDataArray.removeAll()

        nowQuestionIndex = 0

        guard let csvFilePath = Bundle.main.path(forResource: "question",
            ofType: "csv") else {
            print("csvファイルが存在しません")
            return
        }
        do {
            let csvStringData = try String(contentsOfFile: csvFilePath,
                encoding: String.Encoding.utf8)
            csvStringData.enumerateLines(invoking:{ (line, stop) in
                let questionSourceDataArray = line.components(separatedBy: ",")
                let questionData = QuestionData (questionSourceDataArray:
                    questionSourceDataArray)
                self.questionDataArray.append(questionData)
                questionData.questionNo = self.questionDataArray.count
            })
        } catch let error{
            print("csvファイル読み込みエラーが発生しました:\(error)")
            return
        }
    }
    func nextQuestion() -> QuestionData? {
        if nowQuestionIndex < questionDataArray.count {
            let nextQuestion = questionDataArray[nowQuestionIndex]
            nowQuestionIndex += 1
            return nextQuestion
    }
        return nil
    }
}

試したこと

読み込み部分のコードでおかしい部分がないか確認しましたが間違えていないと思います。。

https://yutahand.com/coffeequiz/

またこのブログを参考に自分でナンバーを設定もしたのですおなじようなエラーが出てしまいます。
またこのブログではバグと書いてあり、再起動で治ると書いてありましたが再起動しても治りませんでした。

補足情報(FW/ツールのバージョンなど)

swift 3
xcode 8.2.1
参考にしている本:本気ではじめるiPhoneアプリ作り 黒帯エンジニアがしっかり教える基本テクニック Xcode8.X+Swift3.X対応

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

現在viewDidLoad()内に書かれている処理を含めて、questionDataへのアクセスはquestionDataの読み込みが完了してから実行するようにして下さい。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/12/07 17:30

    <UILabel: 0x7fc102e09330; frame = (16 20; 42 20.5); text = 'Q1'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60800009c340>>
    と表示されました。

    キャンセル

  • 2018/12/07 17:39

    回答を書き直しました。

    キャンセル

  • 2018/12/09 00:53

    再び回答ありがとうございます!読み込み後に実行する方法というのは初心者で実装するにはどのようにするのがおすすめでしょうか。
    NSNotificationなどを利用して実装しようと考えたのですが挫折してしまいました。。。

    キャンセル

0

画面が表示されたときに、questionDataが空なようです。
可能性を考えてみたので、以下をチェックしてみてください。

question.csvは作成しましたか?
question.csvに一つ以上の問題が記入されていますか?
question.csvはプロジェクトに追加しましたか?
「csvファイルが存在しません」「csvファイル読み込みエラーが発生しました」というエラーはログに表示されていませんか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/12/08 10:22

    回答ありがとうございます。
    csvは作成して3つの問題が記入されており、プロジェクトに追加されていますね、ログも表示されていないです。

    キャンセル

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

  • ただいまの回答率 89.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • iOSに関する質問
  • オプショナル型の変数をアンラップした時にnilが出て、アプリが動かなくなってしまい困っています