Can ChatGPT create an iOS app in SwiftUI - part 1

Artificial Intelligence is trending at the moment with applications like DALL-E and Canva able to create images in certain styles and ChatGPT able to generate text. This article explores if ChatGPT can do more than childrens homework and see can ChatGPT create an iOS task app in SwiftUI. So I asked it to create a task manager app.

The ChatGPT site can get busy and you may frequently get a message that "ChatGPT is at capacity right now".


Related articles on ChatGPT and SwiftUI:


Tips on using chatGPT to generate code

  • Use version control and commit the code when it compiles
  • Compile the code after every change from chatGPT
  • Use 'Regenerate response' if not happy with the code generated
  • Feedback compile errors to chatGPT for a possible solution
  • Be prepared to roll back to a previous version and edit the request to chatGPT from there and try again


Ask chatGPT to create an app

It is necessary to create an account with ChatGPT and log in to start a conversation. I simply ask chatGPT to create an app in SwiftUI.

First prompt to create the app

Create task management app in SwiftUI using MVVM design for iPhone

Response from chatGPT

Sure, I can guide you on how to create a task management app in SwiftUI using the MVVM design pattern for iPhone. Here are the steps to follow:
Step 1: Create a new SwiftUI project in Xcode.
Step 2: Create a new file for the model, Task.swift.

Model

1struct Task: Identifiable {
2    var id = UUID()
3    var title: String
4    var completed: Bool = false
5}

Step 3: Create a new file for the ViewModel, TaskViewModel.swift.

ViewModel

1class TaskViewModel: ObservableObject {
2    @Published var tasks = [
3        Task(title: "Task 1"),
4        Task(title: "Task 2"),
5        Task(title: "Task 3")
6    ]
7}

Step 4: Create a new file for the View, ContentView.swift.

View

 1struct ContentView: View {
 2    @ObservedObject var viewModel = TaskViewModel()
 3    
 4    var body: some View {
 5        NavigationView {
 6            List(viewModel.tasks) { task in
 7                Text(task.title)
 8            }
 9            .navigationTitle("Tasks")
10        }
11    }
12}

Step 5: Run the app on an iPhone simulator to see the list of tasks.

Step 6: Add a button to mark a task as completed in the ContentView.

View

 1struct ContentView: View {
 2    @ObservedObject var viewModel = TaskViewModel()
 3    
 4    var body: some View {
 5        NavigationView {
 6            List(viewModel.tasks) { task in
 7                Button(action: {
 8                    if let index = self.viewModel.tasks.firstIndex(where: { $0.id == task.id }) {
 9                        self.viewModel.tasks[index].completed.toggle()
10                    }
11                }) {
12                    HStack {
13                        Image(systemName: task.completed ? "checkmark.circle.fill" : "circle")
14                        Text(task.title)
15                    }
16                }
17            }
18            .navigationTitle("Tasks")
19        }
20    }
21}

Step 7: Run the app again to see the checkmarks appearing when tasks are completed.

Step 8: Add a new task button to the ContentView.

View

 1struct ContentView: View {
 2    @ObservedObject var viewModel = TaskViewModel()
 3    
 4    var body: some View {
 5        NavigationView {
 6            List(viewModel.tasks) { task in
 7                Button(action: {
 8                    if let index = self.viewModel.tasks.firstIndex(where: { $0.id == task.id }) {
 9                        self.viewModel.tasks[index].completed.toggle()
10                    }
11                }) {
12                    HStack {
13                        Image(systemName: task.completed ? "checkmark.circle.fill" : "circle")
14                        Text(task.title)
15                    }
16                }
17            }
18            .navigationTitle("Tasks")
19            .navigationBarItems(trailing: Button(action: {
20                self.viewModel.tasks.append(Task(title: "New Task"))
21            }) {
22                Image(systemName: "plus")
23            })
24        }
25    }
26}

Step 9: Run the app again to see the new task button appearing in the navigation bar.

That's it! You have successfully created a task management app in SwiftUI using the MVVM design pattern for iPhone.


Initial task app from SwiftUI code generated by ChatGPT
Initial task app from SwiftUI code generated by ChatGPT



Add more details to a Task

Well, so far so good, but the Task model just has a title. Let's see if we can add more details to the Tasks.

Next prompt to chatGPT

add a due date to the task and a task status field

Response from chatGPT

Sure, here are the steps to add a due date and a task status field to the task management app:

Step 1: Update the Task model in Task.swift to include the due date and the task status fields.

Task.swift

1struct Task: Identifiable {
2    var id = UUID()
3    var title: String
4    var completed: Bool = false
5    var dueDate: Date? // new field for due date
6    var status: String? // new field for task status
7}

Step 2: Update the TaskViewModel class in TaskViewModel.swift to include the due date and the task status fields.

TaskViewModel.swift

1class TaskViewModel: ObservableObject {
2    @Published var tasks = [
3        Task(title: "Task 1", dueDate: Date(), status: "In progress"),
4        Task(title: "Task 2", dueDate: Date().addingTimeInterval(86400), status: "Not started"),
5        Task(title: "Task 3", dueDate: nil, status: "Complete")
6    ]
7}

Step 3: Update the ContentView to display the due date and the task status in the list.

ContentView.swift

 1struct ContentView: View {
 2    @ObservedObject var viewModel = TaskViewModel()
 3    
 4    static let taskDateFormat: DateFormatter = {
 5        let formatter = DateFormatter()
 6        formatter.dateStyle = .medium
 7        return formatter
 8    }()
 9    
10    var body: some View {
11        NavigationView {
12            List(viewModel.tasks) { task in
13                VStack(alignment: .leading) {
14                    Text(task.title)
15                    if let dueDate = task.dueDate {
16                        Text("Due date: \(dueDate, formatter: Self.taskDateFormat)")
17                            .font(.caption)
18                    }
19                    if let status = task.status {
20                        Text("Status: \(status)")
21                            .font(.caption)
22                    }
23                }
24                .foregroundColor(task.completed ? .gray : .primary)
25                .opacity(task.completed ? 0.5 : 1)
26                .onTapGesture {
27                    if let index = self.viewModel.tasks.firstIndex(where: { $0.id == task.id }) {
28                        self.viewModel.tasks[index].completed.toggle()
29                    }
30                }
31            }
32            .navigationTitle("Tasks")
33            .navigationBarItems(trailing: Button(action: {
34                self.viewModel.tasks.append(Task(title: "New Task", dueDate: Date(), status: "Not started"))
35            }) {
36                Image(systemName: "plus")
37            })
38        }
39    }
40}

Step 4: Run the app on an iPhone simulator to see the due date and the task status appearing in the list.

Step 5: Update the new task button to include the due date and the task status fields.

Step 6: Run the app again to see the new task button creating tasks with the due date and the task status fields.

That's it! You have successfully added a due date and a task status field to the task management app.


Updated tasks to include a due date and a status - code generated by ChatGPT
Updated tasks to include a due date and a status - code generated by ChatGPT



Add unit tests

Now that I have the beginings of an app, I decide to go mad and ask chatGPT to add unit tests.

Next prompt to chatGPT

Add unit tests

Response from chatGPT

Sure, adding unit tests to the task management app is a great way to ensure that the code is working as expected and to catch any issues before they reach production. Here are the steps to add unit tests to the app:

Step 1: Create a new file called TaskViewModelTests.swift in the same directory as TaskViewModel.swift.

Step 2: In TaskViewModelTests.swift, import XCTest and the app's code.

TaskViewModelTests.swift

1import XCTest
2@testable import TaskManager

Step 3: Define a class for the unit tests and a variable for the TaskViewModel.

TaskViewModelTests.swift

1class TaskViewModelTests: XCTestCase {
2    var viewModel: TaskViewModel!
3}

Step 4: Override the setUp() method to create a new instance of TaskViewModel.

TaskViewModelTests.swift

1override func setUp() {
2    viewModel = TaskViewModel()
3}

Step 5: Write a unit test for the add task functionality.

TaskViewModelTests.swift

1func testAddTask() {
2    let initialCount = viewModel.tasks.count
3    viewModel.tasks.append(Task(title: "New Task", dueDate: Date(), status: "Not started"))
4    let finalCount = viewModel.tasks.count
5    XCTAssertEqual(finalCount, initialCount + 1)
6}

Step 6: Write a unit test for the toggle completed functionality.

TaskViewModelTests.swift

1func testToggleCompleted() {
2    let task = Task(title: "Task", dueDate: Date(), status: "In progress")
3    viewModel.tasks.append(task)
4    viewModel.tasks[0].completed.toggle()
5    XCTAssertTrue(viewModel.tasks[0].completed)
6}

Step 7: Write a unit test for the due date and the task status fields.

TaskViewModelTests.swift

1func testDueDateAndStatus() {
2    let task = Task(title: "Task", dueDate: Date(), status: "In progress")
3    viewModel.tasks.append(task)
4    XCTAssertNotNil(viewModel.tasks[0].dueDate)
5    XCTAssertEqual(viewModel.tasks[0].status, "In progress")
6}

Step 8: Override the tearDown() method to set the TaskViewModel to nil.

TaskViewModelTests.swift

1override func tearDown() {
2    viewModel = nil
3}

Step 9: Run the unit tests and ensure that they all pass.

That's it! You have successfully added unit tests to the task management app. You can continue to add more unit tests as needed to ensure that the code is working as expected.


Added unit tests to Task Manager app - code generated by ChatGPT
Added unit tests to Task Manager app - code generated by ChatGPT



Change Task Status to be ENUM

The Status property in Task was created as an optional string, it would be better if this was an enum.

Next prompt to chatGPT

change task status to be one of "not started" "in progress" or "complete"

Response from chatGPT


Sure, here are the steps to change the task status to be one of "not started", "in progress", or "complete" in the task management app:

Step 1: Update the Task model in Task.swift to use an enumeration for the task status.

Task.swift

 1enum TaskStatus: String, CaseIterable {
 2    case notStarted = "Not started"
 3    case inProgress = "In progress"
 4    case complete = "Complete"
 5}
 6
 7struct Task: Identifiable {
 8    var id = UUID()
 9    var title: String
10    var completed: Bool = false
11    var dueDate: Date?
12    var status: TaskStatus = .notStarted // update status field to use enumeration
13}

Step 2: Update the TaskViewModel class in TaskViewModel.swift to use the TaskStatus enumeration and to initialize the task status field to .notStarted.

TaskViewModel.swift

1class TaskViewModel: ObservableObject {
2    @Published var tasks = [
3        Task(title: "Task 1", dueDate: Date(), status: .inProgress),
4        Task(title: "Task 2", dueDate: Date().addingTimeInterval(86400), status: .notStarted),
5        Task(title: "Task 3", dueDate: nil, status: .complete)
6    ]
7}

Step 3: Update the ContentView to use the TaskStatus enumeration and to update the task status based on user selection.

ContentView.swift

 1struct ContentView: View {
 2    @ObservedObject var viewModel = TaskViewModel()
 3    
 4    static let taskDateFormat: DateFormatter = {
 5        let formatter = DateFormatter()
 6        formatter.dateStyle = .medium
 7        return formatter
 8    }()
 9    
10    var body: some View {
11        NavigationView {
12            List(viewModel.tasks) { task in
13                VStack(alignment: .leading) {
14                    Text(task.title)
15                    if let dueDate = task.dueDate {
16                        Text("Due date: \(dueDate, formatter: Self.taskDateFormat)")
17                            .font(.caption)
18                    }
19                    Text("Status:")
20                    Picker("Task Status", selection: $viewModel.tasks[viewModel.tasks.firstIndex(of: task)!].status) {
21                        ForEach(TaskStatus.allCases, id: \.self) { status in
22                            Text(status.rawValue).tag(status)
23                        }
24                    }
25                    .pickerStyle(SegmentedPickerStyle())
26                }
27                .foregroundColor(task.completed ? .gray : .primary)
28                .opacity(task.completed ? 0.5 : 1)
29                .onTapGesture {
30                    if let index = self.viewModel.tasks.firstIndex(where: { $0.id == task.id }) {
31                        self.viewModel.tasks[index].completed.toggle()
32                    }
33                }
34            }
35            .navigationTitle("Tasks")
36            .navigationBarItems(trailing: Button(action: {
37                self.viewModel.tasks.append(Task(title: "New Task", dueDate: Date(), status: .notStarted))
38            }) {
39                Image(systemName: "plus")
40            })
41        }
42    }
43}

Step 4: Run the app on an iPhone simulator to see the updated task status options in the list and the ability to update the task status.

Step 5: Update the unit tests to ensure that the new task status enumeration is being used correctly.

That's it! You have successfully changed the task status to be one of "not started", "in progress", or "complete" in the task management app.


Next prompt to chatGPT

modify Task to conform to Equatable

Response from chatGPT


Sure, here's how you can modify the Task struct in Task.swift to conform to the Equatable protocol:

Task.swift

 1struct Task: Identifiable, Equatable {
 2    var id = UUID()
 3    var title: String
 4    var completed: Bool = false
 5    var dueDate: Date?
 6    var status: TaskStatus = .notStarted
 7    
 8    static func == (lhs: Task, rhs: Task) -> Bool {
 9        return lhs.id == rhs.id
10    }
11}

In this modified implementation, we added Equatable protocol conformance to the Task struct by adding the protocol name after Identifiable, separated by a comma. Then we implemented the == operator function, which compares two Task instances for equality based on their id properties.


Added due date and status to tasks - code generated by ChatGPT
Added due date and status to tasks - code generated by ChatGPT



Allow Tasks to be changed

The app can now display a list of tasks with a title, due Date and a status and new tasks can be added to the list. The next step is to make the Tasks editable.


Next prompt to chatGPT

make a task editable

Response from chatGPT


Sure, here's how you can make a task editable in the task management app:

Step 1: Add an isEditing property to the TaskViewModel class in TaskViewModel.swift.

TaskViewModel.swift

1class TaskViewModel: ObservableObject {
2    @Published var tasks = [
3        Task(title: "Task 1", dueDate: Date(), status: .inProgress),
4        Task(title: "Task 2", dueDate: Date().addingTimeInterval(86400), status: .notStarted),
5        Task(title: "Task 3", dueDate: nil, status: .complete)
6    ]
7    @Published var isEditing = false // add isEditing property
8}

Step 2: Add an Edit button to the ContentView that toggles the isEditing property.

ContentView.swift

 1struct ContentView: View {
 2    @ObservedObject var viewModel = TaskViewModel()
 3    
 4    static let taskDateFormat: DateFormatter = {
 5        let formatter = DateFormatter()
 6        formatter.dateStyle = .medium
 7        return formatter
 8    }()
 9    
10    var body: some View {
11        NavigationView {
12            List {
13                ForEach(viewModel.tasks) { task in
14                    if viewModel.isEditing {
15                        EditableTaskView(task: $viewModel.tasks[viewModel.tasks.firstIndex(of: task)!])
16                    } else {
17                        TaskView(task: task)
18                    }
19                }
20                .onDelete { indexSet in
21                    self.viewModel.tasks.remove(atOffsets: indexSet)
22                }
23            }
24            .navigationTitle("Tasks")
25            .navigationBarItems(
26                trailing: HStack {
27                    if viewModel.isEditing {
28                        Button("Done") {
29                            self.viewModel.isEditing.toggle()
30                        }
31                    } else {
32                        Button(action: {
33                            self.viewModel.tasks.append(Task(title: "New Task", dueDate: Date(), status: .notStarted))
34                        }) {
35                            Image(systemName: "plus")
36                        }
37                        Button("Edit") {
38                            self.viewModel.isEditing.toggle()
39                        }
40                    }
41                }
42            )
43        }
44    }
45}

TaskView.swift

 1struct TaskView: View {
 2    let task: Task
 3    
 4    var body: some View {
 5        VStack(alignment: .leading) {
 6            Text(task.title)
 7            if let dueDate = task.dueDate {
 8                Text("Due date: \(dueDate, formatter: ContentView.taskDateFormat)")
 9                    .font(.caption)
10            }
11            Text("Status: \(task.status.rawValue)")
12        }
13        .foregroundColor(task.completed ? .gray : .primary)
14        .opacity(task.completed ? 0.5 : 1)
15    }
16}

EditableTaskView.swift

 1struct EditableTaskView: View {
 2    @Binding var task: Task
 3    
 4    var body: some View {
 5        VStack(alignment: .leading) {
 6            TextField("Title", text: $task.title)
 7            if let dueDate = task.dueDate {
 8                DatePicker("Due date", selection: $task.dueDate, displayedComponents: .date)
 9            }
10            Picker("Task Status", selection: $task.status) {
11                ForEach(TaskStatus.allCases, id: \.self) { status in
12                    Text(status.rawValue).tag(status)
13                }
14            }
15            .pickerStyle(SegmentedPickerStyle())
16        }
17        .foregroundColor(task.completed ? .gray : .primary)
18        .opacity(task.completed ? 0.5 : 1)
19    }
20}

Step 3: Create two new views, TaskView and EditableTaskView, that will display the task information in read-only and editable mode, respectively. In EditableTaskView, use a Binding to allow the task to be edited and modified in place.

Step 4: Update the ContentView to display the EditableTaskView when the isEditing property is true


Error in the EditableTaskView on initial code to make a task editable - code generated by ChatGPT
Error in the EditableTaskView on initial code to make a task editable - code generated by ChatGPT



Fix Data Binding Error in EditableTaskView

The code generated to make the Task editable does not compile. There are a number of options with charGPT, the easiest of which is to just regenerate the code from the previous request. Alternatively, we can prompt chatGPT with the error message to see if it can provide a solution.


Next prompt to chatGPT

Cannot convert value of type 'Binding<Date?>' to expected argument type
'Binding<Date>'

Response from chatGPT


The error message "Cannot convert value of type 'Binding<Date?>' to expected argument type 'Binding'" occurs because a Binding<Date?> cannot be directly assigned to a Binding.

Here's how you can fix the error in the EditableTaskView:

Step 1: Declare a new Date property in EditableTaskView that will be used to store the current value of the due date.

EditableTaskView.swift

1struct EditableTaskView: View {
2    @Binding var task: Task
3    @State private var dueDate: Date? // Add new property
4    
5    var body: some View {
6        // ...
7    }
8}

Step 2: In the initializer of EditableTaskView, assign the value of the task's due date to the dueDate property.

EditableTaskView.swift

 1struct EditableTaskView: View {
 2    @Binding var task: Task
 3    @State private var dueDate: Date?
 4    
 5    init(task: Binding<Task>) {
 6        _task = task
 7        _dueDate = State(initialValue: task.wrappedValue.dueDate) // Assign initial value to dueDate
 8    }
 9    
10    var body: some View {
11        // ...
12    }
13}

Step 3: Use the dueDate property to create a Binding in the DatePicker.

EditableTaskView.swift

 1struct EditableTaskView: View {
 2    @Binding var task: Task
 3    @State private var dueDate: Date?
 4    
 5    init(task: Binding<Task>) {
 6        _task = task
 7        _dueDate = State(initialValue: task.wrappedValue.dueDate)
 8    }
 9    
10    var body: some View {
11        VStack(alignment: .leading) {
12            TextField("Title", text: $task.title)
13            if let dueDate = dueDate {
14                DatePicker("Due date", selection: Binding<Date>(
15                    get: { dueDate },
16                    set: { newValue in self.dueDate = newValue }
17                ), displayedComponents: .date)
18            }
19            Picker("Task Status", selection: $task.status) {
20                ForEach(TaskStatus.allCases, id: \.self) { status in
21                    Text(status.rawValue).tag(status)
22                }
23            }
24            .pickerStyle(SegmentedPickerStyle())
25        }
26        .foregroundColor(task.completed ? .gray : .primary)
27        .opacity(task.completed ? 0.5 : 1)
28    }
29}

With these changes, the dueDate property is used as an intermediary between the Binding<Date?> and the Binding, allowing you to modify the due date value in place.


SwiftUI App with an Editable list of Tasks - code generated by ChatGPT
SwiftUI App with an Editable list of Tasks - code generated by ChatGPT



Task App broke

Now that there have been a number of changes to tasks, I asked chatGPT to update the unit tests.

Update unit tests

I will not detail the back and forth here as there were unit tests added that did not match the app. The resoltion of these compile errors led to modifying the structs and classes such as the addition of a TaskListViewModel and then some confucion on what was in a TaskViewModel and TaskListViewModel. I eventually got the app to compile again, but the ContentView had changed too much and the app had lost some functionality. In addition clicking on a Task seemed to navigate to a subview withthe same list of tasks and son on recursively. I did not spend too much time debugging and instead chose to roll back the code to when it was last working.


Task App broke after resolving errors on unit tests on ViewModel - code generated by ChatGPT
Task App broke after resolving errors on unit tests on ViewModel - code generated by ChatGPT


Add unit tests to Task

It is possible to "Regenerate response" for every response in ChatGPT with an option to toggle between the different responses. It is also possible to go back to a previous request and edit it and then save and submit. This removes the previous response and any subsequent request and responses.

After getting so lost with resolving compile errors with the updated unit tests and the introduction of new ViewModels and ultimately loosing functionality in the app I went back to the request to update the unit tests. This time, I specified to just add unit tests for Task. I also rolled back the code from git. Even adding these unit tests took a couple of attempts.


Next prompt to chatGPT

Add unit tests for Task

Response from chatGPT


Sure! Here's an example of how you could write some unit tests for the Task struct.

TaskTests.swift

 1import XCTest
 2@testable import TaskManager
 3
 4class TaskTests: XCTestCase {
 5    
 6    // Test task creation
 7    func testTaskCreation() {
 8        let title = "Test Task"
 9        let dueDate = Date()
10        let status: TaskStatus = .notStarted
11        
12        let task = Task(title: title, dueDate: dueDate, status: status)
13        
14        XCTAssertEqual(task.title, title)
15        XCTAssertEqual(task.dueDate, dueDate)
16        XCTAssertEqual(task.status, status)
17        XCTAssertFalse(task.completed)
18    }
19    
20    // Test task completion
21    func testTaskCompletion() {
22        var task = Task(title: "Test Task", dueDate: nil, status: .notStarted)
23        
24        XCTAssertFalse(task.completed)
25        
26        task.completed = true
27        
28        XCTAssertTrue(task.completed)
29    }
30    
31    // Test task status updates
32    func testTaskStatusUpdates() {
33        var task = Task(title: "Test Task", dueDate: nil, status: .notStarted)
34        
35        XCTAssertEqual(task.status, .notStarted)
36        
37        task.status = .inProgress
38        
39        XCTAssertEqual(task.status, .inProgress)
40        
41        task.status = .complete
42        
43        XCTAssertEqual(task.status, .complete)
44    }
45    
46    // Test task equality
47    func testTaskEquality() {
48        let task1 = Task(title: "Task 1", dueDate: nil, status: .notStarted)
49        let task2 = Task(title: "Task 2", dueDate: nil, status: .notStarted)
50        let task3 = Task(title: "Task 1", dueDate: nil, status: .notStarted)
51        
52        XCTAssertNotEqual(task1, task2)
53        XCTAssertEqual(task1, task3)
54    }
55}

In this example, we have four test methods that cover some basic functionality of the Task struct:

  • testTaskCreation: tests that a task is created with the expected properties and that completed is initially set to false.
  • testTaskCompletion: tests that completed can be set to true and that the value is stored correctly.
  • testTaskStatusUpdates: tests that the status property can be updated to each of the three possible values (notStarted, inProgress, and complete) and that the value is stored correctly.
  • testTaskEquality: tests the implementation of Equatable by creating three tasks with different titles but the same values for dueDate, status, and completed. The test asserts that the two tasks with the same title are considered equal, while the one with a different title is not.

Of course, you could write additional tests to cover other scenarios or edge cases, but this should give you a good starting point.


ChatGPT allows you to Regenerate response with a mechanism to toggle between the responses
ChatGPT allows you to Regenerate response with a mechanism to toggle between the responses



Fix Failing unit test

One of the unit tests fails. Let's feed that back to ChatGPT to see what it recommends.


Next prompt to chatGPT

test testTaskEquality fails because task1 is not equal to task3

Response from chatGPT


I apologize for the mistake in my previous response. You are correct that the testTaskEquality test case should fail because the Task instances with different titles should not be considered equal. Here's the corrected test case:

TaskTests.swift

1func testTaskEquality() {
2    let task1 = Task(title: "Task 1", dueDate: nil, status: .notStarted)
3    let task2 = Task(title: "Task 2", dueDate: nil, status: .notStarted)
4    let task3 = Task(title: "Task 1", dueDate: nil, status: .notStarted)
5    
6    XCTAssertNotEqual(task1, task2)
7    XCTAssertNotEqual(task1, task2)
8    XCTAssertEqual(task1, task3)
9}

In this corrected test case, we expect task1 and task3 to be equal because they have the same values for dueDate, status, and completed, even though their title properties are different. The assertion XCTAssertNotEqual(task1, task2) ensures that tasks with different titles are not considered equal, while the assertion XCTAssertEqual(task1, task3) ensures that tasks with the same values for the other properties are considered equal, regardless of their titles.


Unit Test is still failing.


Next prompt to chatGPT

test testTaskEquality still fails because the tasks have different id

Response from chatGPT


I apologize again for the mistake in my previous response. You are correct that the Task instances in the test cases have different id properties, which will cause the equality check to fail even if all other properties are equal.

To fix the testTaskEquality test case, you can create the Task instances with explicit id values that are the same for the two tasks with the same title:

TaskTests.swift

 1func testTaskEquality() {
 2    let id1 = UUID()
 3    let id2 = UUID()
 4    
 5    let task1 = Task(id: id1, title: "Task 1", dueDate: nil, status: .notStarted)
 6    let task2 = Task(id: id2, title: "Task 2", dueDate: nil, status: .notStarted)
 7    let task3 = Task(id: id1, title: "Task 1", dueDate: nil, status: .notStarted)
 8    
 9    XCTAssertNotEqual(task1, task2)
10    XCTAssertNotEqual(task2, task3)
11    XCTAssertEqual(task1, task3)
12}

In this version of the test case, we create two UUIDs (id1 and id2) and use id1 for both task1 and task3, so they will have the same id value. We also modify the assertions to use XCTAssertNotEqual for both task1 and task2 and XCTAssertEqual for task1 and task3, to ensure that tasks are only considered equal if they have the same id and all other properties are equal.


Unit tests on Task struct passing - code generated by ChatGPT
Unit tests on Task struct passing - code generated by ChatGPT



Editing tasks does not work

The App is now stable and it displays a list of tasks. Functionality like swipe to delete works as well as adding new tasks. However, editing the tasks appears to work until the done button is selected. Selecting edit again does show the previous edits, but these are not persisted back to the model.

I'll continue this in the next article.


Edits to the Tasks are not shown in the task list - code generated by ChatGPT

Edits to the Tasks are not shown in the task list - code generated by ChatGPT



Conclusion

I was pleasantly surprised by how much code chatGPT can generate. ChatGPT or other artificial intelligence tools are not likely to replace developers in the near future. However, I think a lot of work can be done by combining the artificial intelligence in a tool like chatGPT with the human ability to problem solve and design.

I think everyone should have a go at generating something with chatGPT. Perhaps this is the next version of auto complete, but it seems so much more as it is generating text or code and can be used to help solve technical problems

It is likely that we will get to the stage where there are those who use and work with Artificial Intelligence and those who don't and the advantage will be with those who embrace its use and learn to work with it. Of course, there are still a number of open questions with regard to copyright infringements and the law has not yet caught up all the technology we use. Is using code from chatGPT any different than searching for code on the Internet or GitHub and using that to help develop an application?

Working with chatGPT is very exciting and frightening at the same time. It has certainly given me pause to writing technical articles on solving issues that chatGPT could just spit out the answer for you whenever you need it. Although, the answers at the moment are not always right.

To answer the question "Can ChatGPT create an iOS app in SwiftUI?"

Yes and No

  • Yes chatGPT can generate code to create an App.
  • No because the user experience of using the App will not be good and the code may not work.

I would say currently that using chatGPT to help you write an app takes more time that writing the code yourself. As other people have stated chatGPT will give an answer with confidence, even when that answer is incorrect.

I am curious to continue working with chatGPT to further develop this TaskManager app. First to see how chatGPT suggests resolving the editing issue and then to persist the task data when the app is closed.

Source code for TasksAppWithChatGpt is available on GitHub.