Building an iOS App with Go for CRUD Operations

Here’s a complete end-to-end example of a CRUD app using Go for the backend and Swift for the iOS frontend. The app will manage a simple list of tasks.


1. Backend: Go

Setup

  • Install Go: https://go.dev/
  • Install dependencies:
    Use the Gin framework for HTTP and GORM for database interactions. Install via:
    go get -u github.com/gin-gonic/gin
    go get -u gorm.io/gorm
    go get -u gorm.io/driver/sqlite

Code: main.go

Here’s the complete code for a simple CRUD API:

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

type Task struct {
	ID          uint   `json:"id" gorm:"primaryKey"`
	Title       string `json:"title"`
	Description string `json:"description"`
}

var db *gorm.DB

func main() {
	// Initialize database
	var err error
	db, err = gorm.Open(sqlite.Open("tasks.db"), &gorm.Config{})
	if err != nil {
		panic("Failed to connect to database!")
	}
	db.AutoMigrate(&Task{})

	// Initialize Gin
	r := gin.Default()

	// Routes
	r.GET("/tasks", getTasks)
	r.GET("/tasks/:id", getTask)
	r.POST("/tasks", createTask)
	r.PUT("/tasks/:id", updateTask)
	r.DELETE("/tasks/:id", deleteTask)

	// Start server
	r.Run(":8080")
}

// Handlers
func getTasks(c *gin.Context) {
	var tasks []Task
	db.Find(&tasks)
	c.JSON(http.StatusOK, tasks)
}

func getTask(c *gin.Context) {
	id := c.Param("id")
	var task Task
	if err := db.First(&task, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Task not found"})
		return
	}
	c.JSON(http.StatusOK, task)
}

func createTask(c *gin.Context) {
	var task Task
	if err := c.ShouldBindJSON(&task); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	db.Create(&task)
	c.JSON(http.StatusCreated, task)
}

func updateTask(c *gin.Context) {
	id := c.Param("id")
	var task Task
	if err := db.First(&task, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Task not found"})
		return
	}

	if err := c.ShouldBindJSON(&task); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	db.Save(&task)
	c.JSON(http.StatusOK, task)
}

func deleteTask(c *gin.Context) {
	id := c.Param("id")
	if err := db.Delete(&Task{}, id).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.Status(http.StatusNoContent)
}

Run the Server

go run main.go

The server will run on http://localhost:8080.


2. Frontend: Swift

Setup

  • Open Xcode and create a new SwiftUI App project.
  • Name it something like TaskManager.

Networking Code

Create a Swift file called API.swift for the networking layer.

import Foundation

struct Task: Codable, Identifiable {
    var id: Int
    var title: String
    var description: String
}

class API {
    static let baseURL = "http://localhost:8080"

    static func fetchTasks(completion: @escaping ([Task]) -> Void) {
        guard let url = URL(string: "\(baseURL)/tasks") else { return }
        
        URLSession.shared.dataTask(with: url) { data, _, _ in
            if let data = data {
                let tasks = try? JSONDecoder().decode([Task].self, from: data)
                DispatchQueue.main.async {
                    completion(tasks ?? [])
                }
            }
        }.resume()
    }

    static func createTask(_ task: Task, completion: @escaping (Bool) -> Void) {
        guard let url = URL(string: "\(baseURL)/tasks") else { return }
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let jsonData = try? JSONEncoder().encode(task)
        request.httpBody = jsonData
        
        URLSession.shared.dataTask(with: request) { _, response, _ in
            let success = (response as? HTTPURLResponse)?.statusCode == 201
            DispatchQueue.main.async {
                completion(success)
            }
        }.resume()
    }
}

SwiftUI Views

Edit the ContentView.swift file to build the UI.

import SwiftUI

struct ContentView: View {
    @State private var tasks: [Task] = []
    @State private var newTitle: String = ""
    @State private var newDescription: String = ""
    
    var body: some View {
        NavigationView {
            VStack {
                List(tasks) { task in
                    VStack(alignment: .leading) {
                        Text(task.title)
                            .font(.headline)
                        Text(task.description)
                            .font(.subheadline)
                            .foregroundColor(.gray)
                    }
                }
                
                Divider()
                
                VStack {
                    TextField("Task Title", text: $newTitle)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                    TextField("Task Description", text: $newDescription)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                    
                    Button("Add Task") {
                        let newTask = Task(id: 0, title: newTitle, description: newDescription)
                        API.createTask(newTask) { success in
                            if success {
                                newTitle = ""
                                newDescription = ""
                                API.fetchTasks { tasks in
                                    self.tasks = tasks
                                }
                            }
                        }
                    }
                    .padding()
                }
                .padding()
            }
            .navigationBarTitle("Tasks")
            .onAppear {
                API.fetchTasks { tasks in
                    self.tasks = tasks
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

3. Running the App

  1. Backend:

    • Ensure the Go backend is running.
  2. iOS App:

    • Run the app in the simulator or on a real device.
    • Replace localhost in API.swift with your machine's local IP if testing on a real device.

This setup gives you a fully functional CRUD app where:

  • Tasks can be listed, created, and updated in real-time between the backend and the iOS app.

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete