30 Days of SwiftUI - Day 28: Beyond the Basics: Custom Types, Maps, and Biometrics in SwiftUI
Hello Swift adventurers! Today, we're exploring some advanced SwiftUI topics, including custom type conformance, file system interaction, map integration, and biometric authentication. Let's dive in!
Custom Ordering: Adding Conformance to Comparable
for Custom Types
We'll learn how to make custom types comparable, allowing them to be sorted and compared.
Example: Making a Person
Struct Comparable
import SwiftUI
struct Person: Comparable, Identifiable {
let id = UUID()
let name: String
let age: Int
static func < (lhs: Person, rhs: Person) -> Bool {
lhs.age < rhs.age
}
static func == (lhs: Person, rhs: Person) -> Bool {
lhs.age == rhs.age && lhs.name == rhs.name
}
}
struct ComparableDemo: View {
let people = [
Person(name: "Alice", age: 30),
Person(name: "Bob", age: 25),
Person(name: "Charlie", age: 35)
].sorted()
var body: some View {
List(people) { person in
Text("\(person.name) - \(person.age)")
}
}
}
struct ComparableDemo_Previews: PreviewProvider {
static var previews: some View {
ComparableDemo()
}
}
Visual Representation:
- A list of people sorted by age.
File Storage: Writing Data to the Documents Directory
We'll learn how to save data to the app's documents directory.
Example: Saving Text to a File
import SwiftUI
struct FileStorage: View {
@State private var text = "Hello, World!"
var body: some View {
VStack {
TextField("Enter text", text: $text)
Button("Save") {
saveTextToFile()
}
}
.padding()
}
func saveTextToFile() {
let filename = "myText.txt"
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent(filename)
do {
try text.write(to: url, atomically: true, encoding: .utf8)
print("Text saved to \(url)")
} catch {
print("Error saving text: \(error)")
}
}
}
struct FileStorage_Previews: PreviewProvider {
static var previews: some View {
FileStorage()
}
}
Visual Representation:
- A text field and a button that saves the text to a file.
View States: Switching View States with Enums
We'll learn how to manage different view states using enums.
Example: Loading State View
import SwiftUI
enum LoadingState {
case loading, loaded(String), error
}
struct LoadingStateView: View {
@State private var state = LoadingState.loading
var body: some View {
VStack {
switch state {
case .loading:
ProgressView("Loading...")
case .loaded(let message):
Text(message)
case .error:
Text("Error loading data.")
}
Button("Load Data") {
state = .loaded("Data loaded successfully!")
}
}
}
}
struct LoadingStateView_Previews: PreviewProvider {
static var previews: some View {
LoadingStateView()
}
}
Visual Representation:
- A view that displays a loading indicator, a loaded message, or an error message based on the state.
Map Integration: Integrating MapKit with SwiftUI
We'll learn how to display maps in SwiftUI using MapKit
.
Example: Basic Map View
import SwiftUI
import MapKit
struct MapView: View {
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
var body: some View {
Map(coordinateRegion: $region)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
}
}
Visual Representation:
- A map centered on San Francisco.
Biometric Authentication: Using Touch ID and Face ID with SwiftUI
We'll learn how to use Touch ID and Face ID for authentication.
Example: Face ID Authentication
import SwiftUI
import LocalAuthentication
struct BiometricAuth: View {
@State private var isAuthenticated = false
var body: some View {
VStack {
if isAuthenticated {
Text("Authenticated!")
} else {
Button("Authenticate") {
authenticate()
}
}
}
}
func authenticate() {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Authenticate to unlock."
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
DispatchQueue.main.async {
isAuthenticated = success
}
}
} else {
print("Biometrics not available.")
}
}
}
struct BiometricAuth_Previews: PreviewProvider {
static var previews: some View {
BiometricAuth()
}
}
- requires physical iOS Device.
Visual Representation:
- A button that triggers biometric authentication.
User Locations: Adding User Locations to a Map
We'll learn how to add user locations to a map.
Example: Map with Annotations
import SwiftUI
import MapKit
struct LocationAnnotation: Identifiable {
let id = UUID()
let coordinate: CLLocationCoordinate2D
}
struct MapAnnotations: View {
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
@State private var annotations = [
LocationAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)),
LocationAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7858, longitude: -122.4065))
]
var body: some View {
Map(coordinateRegion: $region, annotationItems: annotations) { annotation in
MapMarker(coordinate: annotation.coordinate)
}
}
}
struct MapAnnotations_Previews: PreviewProvider {
static var previews: some View {
MapAnnotations()
}
}
Visual Representation:
- A map with annotations for user locations.
Improved Annotations: Improving Our Map Annotations
We'll learn how to customize map annotations.
Example: Custom Map Annotations
import SwiftUI
import MapKit
struct CustomAnnotationView: View {
var body: some View {
Image(systemName: "mappin.circle.fill")
.foregroundColor(.red)
.font(.title)
}
}
struct MapCustomAnnotations: View {
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
@State private var annotations = [
LocationAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)),
LocationAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7858, longitude: -122.4065))
]
var body: some View {
Map(coordinateRegion: $region, annotationItems: annotations) { annotation in
MapAnnotation(coordinate: annotation.coordinate) {
CustomAnnotationView()
}
}
}
}
Visual Representation:
- A map with custom annotations.
Annotation Interaction: Selecting and Editing Map Annotations
We'll learn how to select and edit map annotations.
Example: Interactive Map Annotations
import SwiftUI
import MapKit
struct InteractiveMapAnnotations: View {
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
@State private var annotations = [
LocationAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)),
LocationAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7858, longitude: -122.4065))
]
@State private var selectedAnnotation: LocationAnnotation?
var body: some View {
Map(coordinateRegion: $region, selection: $selectedAnnotation, annotationItems: annotations) { annotation in
MapMarker(coordinate: annotation.coordinate)
}
.sheet(item: $selectedAnnotation) { annotation in
Text("Selected: \(annotation.coordinate.latitude), \(annotation.coordinate.longitude)")
}
}
}
struct InteractiveMapAnnotations_Previews: PreviewProvider {
static var previews: some View {
InteractiveMapAnnotations()
}
}
Visual Representation:
- A map with annotations that can be selected, showing a sheet with annotation details.
Biometric UI Lock: Locking Our UI Behind Face ID
We'll learn how to lock our UI behind Face ID authentication.
Example: Face ID Protected View
import SwiftUI
import LocalAuthentication
struct FaceIDProtected: View {
@State private var isAuthenticated = false
var body: some View {
if isAuthenticated {
Text("Unlocked View")
} else {
VStack {
Text("Protected View")
Button("Unlock with Face ID") {
authenticate()
}
}
}
}
func authenticate() {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Authenticate to unlock."
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
DispatchQueue.main.async {
isAuthenticated = success
}
}
} else {
print("Biometrics not available.")
}
}
}
struct FaceIDProtected_Previews: PreviewProvider {
static var previews: some View {
FaceIDProtected()
}
}
- requires physical iOS Device.
Visual Representation:
- A view that requires Face ID authentication to unlock.
🔥 Conclusion
Day 28 has been a deep dive into advanced SwiftUI techniques. We've explored custom type conformance, file system interaction, map integration, and biometric authentication. These skills will empower you to build more sophisticated and secure apps. Keep experimenting and building!
Follow me on Linkedin: igatitech 🚀🚀🚀
Comments
Post a Comment