ChatGPTを使用し、1時間でiPhoneアプリを作ってみた(swiftUI)
今回はChatGPTでiPhoneアプリを作っていこうと思います!
結構前にstory boredを使用し、少しだけアプリを作ってみたことがありますが、swiftUIは使用したことがなかったため、とりあえずVStackとナビゲーション遷移の仕方を勉強して、早速ChatGPTでコーティングしてみることにしました!
プロンプト
クイズ画面を表示したい
クイズ問題はCSVファイルを読み込んで、クイズを表示する
クイズの内容は英単語を答えるクイズとする
クイズは4択とする
問題数は10問とする
CSVファイルの問題はサンプルの問題をいくつか作成してください
ChatGPTの回答
question,correct,option1,option2,option3,option4
What is the English word for "犬"?,dog,cat,mouse,bird
What is the English word for "猫"?,cat,dog,mouse,bird
What is the English word for "鳥"?,bird,cat,mouse,dog
What is the English word for "ネズミ"?,mouse,cat,bird,dog
What is the English word for "馬"?,horse,cow,sheep,goat
What is the English word for "牛"?,cow,horse,sheep,goat
What is the English word for "羊"?,sheep,horse,cow,goat
What is the English word for "山羊"?,goat,horse,cow,sheep
What is the English word for "ライオン"?,lion,tiger,leopard,cheetah
What is the English word for "トラ"?,tiger,lion,leopard,cheetah
import SwiftUI
import Foundation
struct QuizQuestion: Identifiable {
let id = UUID()
let question: String
let correct: String
let options: [String]
}
class QuizViewModel: ObservableObject {
@Published var questions: [QuizQuestion] = []
@Published var currentQuestionIndex = 0
@Published var score = 0
init() {
loadCSV()
}
func loadCSV() {
guard let path = Bundle.main.path(forResource: "quiz_questions", ofType: "csv") else {
print("CSV file not found")
return
}
do {
let data = try String(contentsOfFile: path)
let rows = data.components(separatedBy: "\n")
for row in rows.dropFirst() {
let columns = row.components(separatedBy: ",")
if columns.count == 6 {
let question = columns[0]
let correct = columns[1]
let options = Array(columns[2...5])
let quizQuestion = QuizQuestion(question: question, correct: correct, options: options.shuffled())
questions.append(quizQuestion)
}
}
questions.shuffle()
} catch {
print("Error reading CSV file: \(error)")
}
}
func answerQuestion(with answer: String) {
if questions[currentQuestionIndex].correct == answer {
score += 1
}
currentQuestionIndex += 1
}
func restartQuiz() {
score = 0
currentQuestionIndex = 0
questions.shuffle()
}
}
struct QuizView: View {
@ObservedObject var viewModel = QuizViewModel()
var body: some View {
NavigationView {
VStack {
if viewModel.currentQuestionIndex < viewModel.questions.count {
let question = viewModel.questions[viewModel.currentQuestionIndex]
Text(question.question)
.font(.title)
.padding()
ForEach(question.options, id: \.self) { option in
Button(action: {
viewModel.answerQuestion(with: option)
}) {
Text(option)
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding(.horizontal)
}
} else {
Text("Quiz Completed!")
.font(.largeTitle)
.padding()
Text("Your score: \(viewModel.score)/\(viewModel.questions.count)")
.font(.title)
.padding()
Button(action: {
viewModel.restartQuiz()
}) {
Text("Restart Quiz")
.padding()
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
.navigationTitle("Quiz")
.padding()
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: QuizView()) {
Text("Start Quiz")
.foregroundColor(.white)
.frame(width: UIScreen.main.bounds.width * 0.9)
.padding()
.background(Color.blue)
.cornerRadius(10)
}
.padding()
}
.navigationTitle("Main View")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
動作確認
多少バグもありましたが、1時間程度で画面表示することができました!
(csvのカラム数は5個だが、6個のロジックとなっていたり。。)
if columns.count == 6 { // ロジック不備。CSVのカラム数は5個
let question = columns[0]
let correct = columns[1]
let options = Array(columns[2...5]) // ロジック不備。
let quizQuestion = QuizQuestion(question: question, correct: correct, options: options.shuffled())
questions.append(quizQuestion)
}
ChatGPTにコードの内容を教えてもらう
無事画面を表示することができましたので、次にロジックの各ステップについてChatGPTに教えてもらおうと思います。
質問データを格納する構造体
struct QuizQuestion: Identifiable {
let id = UUID() // 一意のIDを生成
let question: String // クイズの質問
let correct: String // 正しい答え
let options: [String] // 選択肢のリスト
}
Identifiableプロトコル
Identifiableプロトコルを付けないと、ForEachやListでエラーとなる
質問データを管理するクラス
変数の変更を検知しビューを自動更新(ObservableObjectプロトコル)
class QuizViewModel: ObservableObject {
@Published var questions: [QuizQuestion] = []
@Published var currentQuestionIndex = 0
@Published var score = 0
init() {
loadCSV()
}
func loadCSV() {
guard let path = Bundle.main.path(forResource: "quiz_questions", ofType: "csv") else {
print("CSV file not found")
return
}
do {
let data = try String(contentsOfFile: path)
let rows = data.components(separatedBy: "\n")
for row in rows.dropFirst() {
let columns = row.components(separatedBy: ",")
if columns.count == 6 {
let question = columns[0]
let correct = columns[1]
let options = Array(columns[2...5])
let quizQuestion = QuizQuestion(question: question, correct: correct, options: options.shuffled())
questions.append(quizQuestion)
}
}
questions.shuffle()
} catch {
print("Error reading CSV file: \(error)")
}
}
func answerQuestion(with answer: String) {
if questions[currentQuestionIndex].correct == answer {
score += 1
}
currentQuestionIndex += 1
}
func restartQuiz() {
score = 0
currentQuestionIndex = 0
questions.shuffle()
}
}
ObservableObjectプロトコルと@Publishedプロパティをつけないと、ビューが自動的に更新されない
CSVファイルの読み込み処理
CSVのファイルパスの取得
guard let path = Bundle.main.path(forResource: "quiz_questions", ofType: "csv") else {
print("CSV file not found")
return
}
CSVファイルの読み込み処理
do {
let data = try String(contentsOfFile: path)
let rows = data.components(separatedBy: "\n")
for row in rows.dropFirst() {
print("loop")
let columns = row.components(separatedBy: ",")
print(columns.count)
if columns.count == 5 {
let question = columns[0]
let correct = columns[1]
let options = Array(columns[1...4])
let quizQuestion = QuizQuestion(question: question, correct: correct, options: options.shuffled())
questions.append(quizQuestion)
}
}
questions.shuffle()
} catch {
print("Error reading CSV file: \(error)")
}
配列の要素をシャッフルする
questions.shuffle()
質問文を表示するビュー
let question = viewModel.questions[viewModel.currentQuestionIndex]
Text(question.question)
.font(.title)
.padding()
ForEach(question.options, id: \.self) { option in
Button(action: {
viewModel.answerQuestion(with: option)
}) {
Text(option)
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding(.horizontal)
}
各種コードの説明その1
ループで質問の選択肢を4つ表示する
ForEach内で使用されるid: \.selfは、配列の各要素を一意に識別するために使用(struct QuizQuestion: Identifiableのidとは関係なし)
選択肢(ボタン)を押下すると、ボタン押下時用の処理が発火
各種コードの説明その2
withは、Swiftのメソッド宣言において引数ラベルを指定するためのキーワード
func answerQuestion(with answer: String) {
if questions[currentQuestionIndex].correct == answer {
score += 1
}
currentQuestionIndex += 1
}
おわりに
最後まで読んで頂き、ありがとうございます!
ChatGPTに動くサンプルコードを教えてもらえ、わからないロジックはすぐにChatGPTが答えてくれるので、効率よく学習することができました!
今後もChatGPTを使用し、色々なiPhoneを作ってみようと思います。
おまけ
最近、ChatGPTを使用し、色々なことを模索しています。
もしよければ、以下の記事も見て頂けると嬉しいです!
この記事が気に入ったらサポートをしてみませんか?