DEV Community

Anis Ali Khan
Anis Ali Khan

Posted on

Tutorial 34: Working with SQLite and Third-Party Databases in iOS πŸš€

Featuring: Fart Button 2: Farts on the Map πŸŒπŸ’¨


Welcome back, master developer! πŸ§™β€β™‚οΈβœ¨ Today we're pushing our skills even further:

  • SQLite
  • Third-party database libraries
  • MapKit integration

All wrapped inside the beautifully absurd Fart Button App, now upgraded to log and map every fart around the world! 🌍🎺πŸ’₯


πŸ“š What You'll Learn

  • Setting up SQLite manually
  • Using a third-party wrapper (like GRDB or SQLite.swift)
  • Saving and querying custom data
  • Displaying fart locations on a map πŸŒŽπŸ’¨

πŸ›  Project Setup

  1. Create a new Xcode project.
  2. Add MapKit.
  3. Install SQLite.swift (or GRDB.swift) using Swift Package Manager:

πŸ’Ύ Why Use SQLite?

  • Blazing fast πŸš€
  • Lightweight πŸ“¦
  • Cross-platform 🌎
  • Great for apps that don't need full Core Data complexity

You can even inspect your database manually with any SQLite viewer!


πŸ’¨ Designing the Fart Table

We'll store:

Column Type Description
id Int Primary Key
soundName String The fart sound filename
latitude Double Fart GPS latitude
longitude Double Fart GPS longitude
timestamp Date When it happened

Epic. πŸŒ‹


✨ Creating the Fart Database

import SQLite
import MapKit

class FartDatabase {
    static let shared = FartDatabase()
    private var db: Connection?

    let farts = Table("farts")
    let id = Expression<Int64>("id")
    let soundName = Expression<String>("soundName")
    let latitude = Expression<Double>("latitude")
    let longitude = Expression<Double>("longitude")
    let timestamp = Expression<Date>("timestamp")

    private init() {
        do {
            let documentDirectory = try FileManager.default.url(
                for: .documentDirectory,
                in: .userDomainMask,
                appropriateFor: nil,
                create: true
            )
            let fileUrl = documentDirectory.appendingPathComponent("farts").appendingPathExtension("sqlite3")
            db = try Connection(fileUrl.path)

            try db?.run(farts.create(ifNotExists: true) { table in
                table.column(id, primaryKey: true)
                table.column(soundName)
                table.column(latitude)
                table.column(longitude)
                table.column(timestamp)
            })
            print("πŸ“¦ Database ready!")
        } catch {
            print("😱 Database error: \(error)")
        }
    }

    func addFart(sound: String, coordinate: CLLocationCoordinate2D) {
        let insert = farts.insert(
            soundName <- sound,
            latitude <- coordinate.latitude,
            longitude <- coordinate.longitude,
            timestamp <- Date()
        )
        do {
            try db?.run(insert)
            print("πŸ’¨ Fart saved!")
        } catch {
            print("😱 Failed to insert fart: \(error)")
        }
    }

    func getAllFarts() -> [(sound: String, location: CLLocationCoordinate2D)] {
        var fartList = [(sound: String, location: CLLocationCoordinate2D)]()
        do {
            for fart in try db!.prepare(farts) {
                let location = CLLocationCoordinate2D(
                    latitude: fart[latitude],
                    longitude: fart[longitude]
                )
                fartList.append((sound: fart[soundName], location: location))
            }
        } catch {
            print("😱 Failed to fetch farts: \(error)")
        }
        return fartList
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ—Ί Building the MapKit UI

import SwiftUI
import MapKit

struct ContentView: 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 farts: [(sound: String, location: CLLocationCoordinate2D)] = []

    var body: some View {
        VStack {
            Map(coordinateRegion: $region, annotationItems: farts, annotationContent: { fart in
                MapMarker(coordinate: fart.location, tint: .green)
            })
            .edgesIgnoringSafeArea(.all)

            Button(action: recordFart) {
                Text("πŸ’¨ Drop a Fart πŸ’¨")
                    .font(.title)
                    .padding()
                    .background(Color.green)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }
            .padding()
        }
        .onAppear(perform: loadFarts)
    }

    private func recordFart() {
        let randomOffset = Double.random(in: -0.02...0.02)
        let newCoordinate = CLLocationCoordinate2D(
            latitude: region.center.latitude + randomOffset,
            longitude: region.center.longitude + randomOffset
        )
        FartDatabase.shared.addFart(sound: "RandomFart.mp3", coordinate: newCoordinate)
        loadFarts()
    }

    private func loadFarts() {
        farts = FartDatabase.shared.getAllFarts()
    }
}
Enter fullscreen mode Exit fullscreen mode

Boom. Now every fart is plotted on a map. Historical. πŸ˜‚


🧠 Key Concepts You Used

  • SQLite database setup πŸ“¦
  • Creating and managing tables πŸ› 
  • Saving records πŸ’Ύ
  • Querying data 🧹
  • MapKit for visualizing 🌍
  • SwiftUI for fun interactions 🧑

🎯 Challenges to Try

  • Add fart categories (Epic, Squeaky, Ninja).
  • Allow fart playback when tapping a pin. 🎧
  • Show a list of recent farts below the map.
  • Save user preferences for fart sounds!
  • Animate fart explosions when a marker is tapped. πŸ’₯

You've built the Fart Mapping Revolution! πŸ’¨πŸ—ΊπŸ‘‘

Not only can you save local data with SQLite β€” you can now map the gaseous trail of glory across the globe. πŸŒπŸ’š

Keep coding, keep laughing, and keep shipping hilarious apps! πŸš€πŸ’¨

Top comments (0)