30 Days of SwiftUI - Day 26: SwiftUI Challenge Day: Networking, SwiftData, and More!
Hello Swift learners! Today, we're putting our knowledge to the test with some practical questions based on what we've learned in the past few days. Let's sharpen our skills and solidify our understanding of networking, SwiftData, and other advanced techniques.
Practical Tests
Test 1: Network Request with Form Validation
Question: Create a SwiftUI view that fetches user data from the JSONPlaceholder API. Include a form with username and email fields. Validate the form, and only enable the submit button when both fields are valid. When the submit button is pressed, print a message with the fetched user data.
Test 2: SwiftData Movie Editor with Filtering
Question: Enhance the SwiftData movie editor from Day 24 by adding a filter that allows users to view movies based on their release year. Include a Stepper
to dynamically change the filter year and update the @Query
results.
Test 3: SwiftData Movie Relationships and Deletion
Question: Extend the SwiftData movie app to include an Actor
model. Create a relationship between Movie
and Actor
. Implement a feature to delete a movie along with its associated actors.
Answers and Complete SwiftUI Code
Answer 1: Network Request with Form Validation
import SwiftUI
struct User: Codable, Identifiable {
let id: Int
let name: String
let username: String
let email: String
}
struct NetworkFormTest: View {
@State private var users = [User]()
@State private var username = ""
@State private var email = ""
var isFormValid: Bool {
!username.isEmpty && email.contains("@")
}
var body: some View {
VStack {
List(users) { user in
VStack(alignment: .leading) {
Text(user.name).font(.headline)
Text(user.email)
}
}
Form {
TextField("Username", text: $username)
TextField("Email", text: $email)
Button("Submit") {
if let user = users.first(where: {$0.username == username && $0.email == email}){
print("User data: \(user)")
} else {
print("User not found.")
}
}
.disabled(!isFormValid)
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else {
print("Invalid URL")
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let decodedUsers = try? JSONDecoder().decode([User].self, from: data) {
DispatchQueue.main.async {
self.users = decodedUsers
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct NetworkFormTest_Previews: PreviewProvider {
static var previews: some View {
NetworkFormTest()
}
}
Answer 2: SwiftData Movie Editor with Filtering
import SwiftUI
import SwiftData
@Model
class Movie {
var title: String
var director: String
var year: Int
init(title: String, director: String, year: Int) {
self.title = title
self.director = director
self.year = year
}
}
struct FilteredMovieEditor: View {
@Environment(\.modelContext) var modelContext
@State private var filterYear = 2000
@Query var movies: [Movie]
var filteredMovies: [Movie] {
movies.filter { $0.year > filterYear }
}
var body: some View {
VStack {
Stepper("Filter Year: \(filterYear)", value: $filterYear)
List(filteredMovies) { movie in
NavigationLink(destination: EditableMovieDetail(movie: movie)) {
Text("\(movie.title) (\(movie.year))")
}
}
}
}
}
struct EditableMovieDetail: View {
@Bindable var movie: Movie
var body: some View {
Form {
TextField("Title", text: $movie.title)
TextField("Director", text: $movie.director)
Stepper("Year: \(movie.year)", value: $movie.year)
}
.padding()
}
}
struct FilteredMovieEditor_Previews: PreviewProvider {
static var previews: some View {
FilteredMovieEditor()
.modelContainer(for: Movie.self)
}
}
Answer 3: SwiftData Movie Relationships and Deletion
import SwiftUI
import SwiftData
@Model
class Movie {
var title: String
@Relationship(deleteRule: .cascade) var actors: [Actor]? = []
}
@Model
class Actor {
var name: String
var age: Int
}
struct MovieActorDeletion: View {
@Environment(\.modelContext) var modelContext
@Query var movies: [Movie]
var body: some View {
List {
ForEach(movies) { movie in
VStack(alignment: .leading) {
Text(movie.title).font(.headline)
if let actors = movie.actors, !actors.isEmpty {
Text("Actors: \(actors.map { $0.name }.joined(separator: ", "))")
}
}
}
.onDelete(perform: deleteMovies)
}
}
func deleteMovies(at offsets: IndexSet) {
for index in offsets {
modelContext.delete(movies[index])
}
}
}
struct MovieActorDeletion_Previews: PreviewProvider {
static var previews: some View {
MovieActorDeletion()
.modelContainer(for: Movie.self, configurations: ModelConfiguration())
}
}
Key Takeaways
- Networking and Forms: We combined network requests with form validation, demonstrating how to handle external data and user input.
- SwiftData Filtering: We enhanced the SwiftData movie editor with dynamic filtering, showcasing how to manipulate data based on user input.
- SwiftData Relationships: We implemented movie relationships and deletion, reinforcing how to manage complex data structures.
By working through these practical tests, you've strengthened your understanding of key SwiftUI concepts. Keep practicing, and you'll be building more complex and engaging apps in no time!
Follow me on Linkedin: igatitech 🚀🚀🚀
Comments
Post a Comment