โญ๏ธ ReactExample6 – server + swift
๐๐ป ์ด์ ์ ํฌ์คํธํ React ๋ถ๋ถ์ Active,inActiove๋ฒํผ์ Swift์ฑ์ผ๋ก ๊ตฌํํ ๋ด์ฉ์
๋๋ค.
This is the implementation of the Active, inActive buttons from the React section I posted previously into a Swift app.
๐๐ป ์๋ฒ ๋ถ๋ถ์ ์ฝ๋๋ Vite+React์ Express์๋ฒ ๋ถ๋ถ๊ณผ ๊ฐ์ต๋๋ค.
The code for the server part is the same as the Express server part of Vite+React.
๐๐ป Nodejs Express Server
โ๏ธserver.js
const express = require("express");
const { Pool } = require("pg");
const cors = require("cors");
const app = express();
app.use(cors());
app.use(express.json());
const pool = new Pool({
user: "yourID", // ๋ณธ์ธ์ ๊ณ์ ๋ช
/ your account name
host: "localhost",
database: "postgres", // ๋ณธ์ธ์ DB๋ช
/ Your DB name
password: "", // ๋ณธ์ธ์ ๋น๋ฐ๋ฒํธ / your password
port: 5432,
});
// ํ์ฌ ์ํ ๊ฐ์ ธ์ค๊ธฐ
// Get current state
app.get("/status", async (req, res) => {
try {
const result = await pool.query(
"SELECT is_active FROM button_status WHERE id = 1",
);
res.json(result.rows[0]);
} catch (err) {
res.status(500).send(err.message);
}
});
// ์ํ ์
๋ฐ์ดํธํ๊ธฐ
// update status
app.post("/toggle", async (req, res) => {
const { isActive } = req.body;
try {
const result = await pool.query(
"UPDATE button_status SET is_active = $1 WHERE id = 1 RETURNING *",
[isActive],
);
res.json(result.rows[0]);
} catch (err) {
res.status(500).send(err.message);
}
});
app.listen(3000, () => console.log("Server running on http://localhost:3000"));
๐๐ป swift
โ๏ธContentView
1) @MainActor :
–UI ๋ณ๊ฒฝ์ ๋ฐ๋์ ๋ฉ์ธ ์ค๋ ๋์์ ํด์ผํฉ๋๋ค.
UI changes must be made on the main thread.
–@MainActor๊ฐ ์์ผ๋ฉด ์ด ํจ์๋ ๋ฐ๋์ ๋ฉ์ธ ์ค๋ ๋์์ ์คํ๋์ด์ผ ํ๋ค๋ ์๋ฏธ์
๋๋ค.
The presence of @MainActor means that this function must be executed on the main thread.
2)Thread
–์ฐ๋ ๋๋ ํ๋์ ํ๋ก๊ทธ๋จ ์์์ ์ฌ๋ฌ ์์
์ ๋์์ ์คํํ๊ธฐ ์ํ ์คํ ํ๋ฆ์ ์๋ฏธํฉ๋๋ค.
A thread is a flow of execution for executing multiple tasks simultaneously within a single program.
–์ฆ ์คํ ํ๋ฆ์ ๋ถ๋ฆฌํด์ ๋์์ ์งํํ ์ ์๊ฒ ๋ง๋๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
That means separating the execution flow so that it can proceed concurrently.
import SwiftUI
struct ContentView: View {
@State private var isActive: Bool = false
private let baseURL = "http://192.168.0.100:3000" // your ip address
var body: some View {
VStack(spacing: 20) {
Text("ํ์ฌ ์ํ / Current Status")
.font(.headline)
Text(isActive ? "๐ต ํ์ฑ / Active" : "๐ด ๋นํ์ฑ / Inactive")
.font(.title)
Button {
Task {
await toggleStatus()
}
} label: {
Text(isActive ? "๋นํ์ฑํํ๊ธฐ / InActivate"
: "ํ์ฑํํ๊ธฐ / Activate")
.padding()
.frame(width: 220)
.background(isActive ? Color.green : Color.red)
.foregroundColor(.white)
.cornerRadius(10)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.task { // ๋น๋๊ธฐ ์์ / asynchronous start
await fetchStatus()
}
}
}
//
// MARK: - Networking
//
// extension : ๊ธฐ์กด ํ์
์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ฑฐ๋,์ฝ๋๋ฅผ ์ญํ ๋ณ๋ก ๋๋๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ฌธ๋ฒ( ์ฝ๋ ๊ฐ๋
์ฑ์ ์ํด์ ์ฌ์ฉํจ.)
// Extension: A grammar used to add functionality to existing types or to divide code by role (used for code readability)
extension ContentView {
// GET ์์ฒญ(์กฐํ) / GET Request(Search)
@MainActor
func fetchStatus() async {
guard let url = URL(string: "\(baseURL)/status") else { return }
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decoded = try JSONDecoder().decode(StatusResponse.self, from: data)
isActive = decoded.is_active
} catch {
print("์ํ ๊ฐ์ ธ์ค๊ธฐ ์คํจ/Failed to get status:", error)
}
}
// POST ์์ฒญ(์ํ๋ณ๊ฒฝ) / POST Request(Status change)
@MainActor
func toggleStatus() async {
guard let url = URL(string: "\(baseURL)/toggle") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body = ["isActive": !isActive]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
do {
let (data, _) = try await URLSession.shared.data(for: request)
let decoded = try JSONDecoder().decode(StatusResponse.self, from: data)
isActive = decoded.is_active
} catch {
print("ํ ๊ธ ์คํจ/Toggle failed:", error)
}
}
}
// ์๋ฒ ์๋ต ๋ชจ๋ธ / Server response model
struct StatusResponse: Codable {
let is_active: Bool
}
#Preview {
ContentView()
}
โญ๏ธ ๋ง์ฝ ์ค์ ๋ก swift์ฑ์ ๋น๋ํ๋ ๊ฒฝ์ฐ ์๋ฒ๋ httpsํ๊ฒฝ์ผ๋ก ๋ฐ๊ฟ์ค์ผํฉ๋๋ค.
If you are actually building a Swift app, you will need to change the server to https.
httpํ๊ฒฝ์์ ์ฌ์ฉํ๋ ค๋ฉด xcode์์ ๋ณ๋์ ์ค์ ์ ํด์ค์ผํฉ๋๋ค.(์๋์ ์ด๋ฏธ์ง๋ฅผ ์ฐธ์กฐํ์ธ์)
To use it in an http environment, you need to make separate settings in Xcode. (See the image below)
โ๏ธ Xcode -> ํ๋ก์ ํธ์ ํ->TARGETS์ ํ -> info ์ ํ
Xcode ->Select project -> Select TARGETS -> Select info

โ๏ธ ๋ง์ฐ์ค ์ฐํด๋ฆญ -> Add Row ์ ํ
Right click -> Select Add Row

โ๏ธ App Transport Security Settings ์ ํ
Select App Transport Security Settings
โ๏ธ ์ข์ธก ํ์ดํ ์๋๋ก ํ์ฅ
Expand left arrow down
โ๏ธ ๋ง์ฐ์ค ์ฐํด๋ฆญํด์ Add Row์ ํ
Right-click and select Add Row

โ๏ธ Allow Arbitray Loads ์
๋ ฅ
Enter Allow Arbitray Loads

๐๐ป ์คํฌ๋ฆฐ์ท/ScreenShot
โ๏ธ Xcode Simulator


โ๏ธ Postgresql
