บทความนี้จะพาคุณเชื่อมต่อแอป iOS กับ AI API แบบ Streaming โดยใช้ภาษา Swift ตั้งแต่ขั้นตอนแรกจนถึงขั้นตอนสุดท้าย อธิบายทุกอย่างแบบละเอียดเหมือนกำลังคุยกับเพื่อน ไม่ต้องกังวลว่าจะไม่เข้าใจเพราะเราจะไม่ใช้คำศัพท์เทคนิคยากๆ
HTTP Streaming คืออะไร ทำไมต้องรู้
ปกติเวลาคุณถาม AI แล้วรอ มันจะคิดนานมากแล้วค่อยตอบทีเดียว แต่ Streaming คือการที่ AI จะส่งคำตอบมาทีละคำเหมือนพิมพ์สด ทำให้ผู้ใช้รู้สึกว่าได้คำตอบเร็วและน่าตื่นเต้นกว่าเดิมมาก
เตรียมตัวก่อนเริ่ม
สิ่งที่คุณต้องมี:
- Mac ที่ติดตั้ง Xcode แล้ว (ดาวน์โหลดฟรีจาก App Store)
- บัญชี API จาก สมัครที่นี่ — ราคาถูกกว่า OpenAI ถึง 85% รองรับ WeChat และ Alipay มีความหน่วงต่ำกว่า 50ms
- ความรู้พื้นฐาน Swift ระดับอ่านโค้ดได้
ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่
เปิด Xcode แล้วไปที่ File > New > Project เลือก iOS > App แล้วกด Next ตั้งชื่อโปรเจกต์ว่า AISteamDemo เลือก Swift เป็นภาษาหลัก กด Create เป็นอันเสร็จ
ขั้นตอนที่ 2: สร้างไฟล์สำหรับเรียกใช้ API
ในโฟลเดอร์โปรเจกต์ ให้สร้างไฟล์ใหม่ชื่อ AIService.swift โดยคลิกขวาที่โฟลเดอร์ > New File > Swift File
ขั้นตอนที่ 3: เขียนโค้ดเรียก API แบบ Streaming
คัดลอกโค้ดด้านล่างไปวางในไฟล์ AIService.swift ของคุณได้เลย:
import Foundation
class AIService {
// ตั้งค่าตามที่ได้จากการสมัคร HolySheep
private let apiKey = "YOUR_HOLYSHEEP_API_KEY"
private let baseURL = "https://api.holysheep.ai/v1"
func sendMessageStreaming(
prompt: String,
onChunkReceived: @escaping (String) -> Void,
onComplete: @escaping () -> Void,
onError: @escaping (Error) -> Void
) {
// สร้าง URL สำหรับ chat completion
guard let url = URL(string: "\(baseURL)/chat/completions") else {
onError(NSError(domain: "InvalidURL", code: 400, userInfo: nil))
return
}
// สร้าง request และตั้งค่า header
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("text/event-stream", forHTTPHeaderField: "Accept")
// สร้าง body ตามรูปแบบที่ API ต้องการ
let body: [String: Any] = [
"model": "gpt-4.1",
"messages": [
["role": "user", "content": prompt]
],
"stream": true
]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: body)
} catch {
onError(error)
return
}
// เริ่ม stream connection
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
onError(error)
return
}
guard let data = data, let text = String(data: data, encoding: .utf8) else {
onError(NSError(domain: "NoData", code: 500, userInfo: nil))
return
}
// แยกวิเคราะห์ข้อมูล Streaming
let lines = text.components(separatedBy: "\n")
for line in lines {
if line.hasPrefix("data: ") {
let jsonString = String(line.dropFirst(6))
if jsonString == "[DONE]" {
onComplete()
return
}
// ดึงข้อความจาก JSON
if let content = self.extractContent(from: jsonString) {
DispatchQueue.main.async {
onChunkReceived(content)
}
}
}
}
onComplete()
}
task.resume()
}
// ฟังก์ชันดึงข้อความจาก JSON format
private func extractContent(from jsonString: String) -> String? {
guard let data = jsonString.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let choices = json["choices"] as? [[String: Any]],
let firstChoice = choices.first,
let delta = firstChoice["delta"] as? [String: Any],
let content = delta["content"] as? String else {
return nil
}
return content
}
}
ขั้นตอนที่ 4: นำไปใช้ในหน้าจอแอป
คัดลอกโค้ดด้านล่างไปวางใน ViewController.swift หรือ SwiftUI View ของคุณ:
import UIKit
class ViewController: UIViewController {
private let aiService = AIService()
private var responseLabel = UILabel()
private var activityIndicator = UIActivityIndicatorView()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.backgroundColor = .white
// สร้างปุ่มสำหรับทดสอบ
let button = UIButton(type: .system)
button.setTitle("ถาม AI", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
button.frame = CGRect(x: 20, y: 100, width: 200, height: 50)
button.addTarget(self, action: #selector(askAI), for: .touchUpInside)
view.addSubview(button)
// สร้าง label แสดงคำตอบ
responseLabel.frame = CGRect(x: 20, y: 170, width: view.bounds.width - 40, height: 500)
responseLabel.numberOfLines = 0
responseLabel.font = UIFont.systemFont(ofSize: 16)
responseLabel.text = "กดปุ่มเพื่อถาม AI..."
view.addSubview(responseLabel)
// สร้าง loading indicator
activityIndicator.frame = CGRect(x: 230, y: 100, width: 50, height: 50)
activityIndicator.hidesWhenStopped = true
view.addSubview(activityIndicator)
}
@objc private func askAI() {
responseLabel.text = "กำลังคิด..."
activityIndicator.startAnimating()
aiService.sendMessageStreaming(
prompt: "อธิบายเรื่อง AI แบบเข้าใจง่าย",
onChunkReceived: { chunk in
// ต่อข้อความทีละส่วน
self.responseLabel.text = (self.responseLabel.text ?? "") + chunk
},
onComplete: {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
}
},
onError: { error in
DispatchQueue.main.async {
self.responseLabel.text = "เกิดข้อผิดพลาด: \(error.localizedDescription)"
self.activityIndicator.stopAnimating()
}
}
)
}
}
ขั้นตอนที่ 5: ทดสอบและรันแอป
กดปุ่ม Run (⌘+R) เพื่อทดสอบแอป ถ้าทุกอย่างถูกต้อง คุณจะเห็นข้อความปรากฏทีละตัวแบบ Streaming สวยงาม
ราคาและค่าใช้จ่าย
หลายคนกังวลเรื่องค่าใช้จ่าย เลือกใช้ HolySheep AI ประหยัดกว่ามาก เพราะอัตราแลกเปลี่ยน ¥1=$1 คิดเป็นเงินไทยก็ถูกมาก:
- GPT-4.1 — $8 ต่อพันโทเคน (OpenAI คิด $60+)
- Claude Sonnet 4.5 — $15 ต่อพันโทเคน (Anthropic คิด $90+)
- Gemini 2.5 Flash — $2.50 ต่อพันโทเคน
- DeepSeek V3.2 — $0.42 ต่อพันโทเคน (ราคาถูกที่สุด)
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
กรณีที่ 1: แสดงข้อความ "401 Unauthorized"
ข้อผิดพลาดนี้เกิดจาก API Key ไม่ถูกต้องหรือหมดอายุ ให้ไปที่หน้า API Keys ในบัญชี HolySheep แล้วคัดลอก Key ใหม่มาแทนที่
// ❌ ผิด - Key ไม่ถูกต้อง
private let apiKey = "sk-wrong-key-here"
// ✅ ถูกต้อง - ใส่ Key ที่คัดลอกมาจาก HolySheep
private let apiKey = "YOUR_HOLYSHEEP_API_KEY"
กรณีที่ 2: แสดงข้อความ "No Data" หรือข้อความว่างเปล่า
อาจเกิดจากการตั้งค่า Accept Header ผิดพลาด หรือ Network ไม่ได้เชื่อมต่อ ให้ตรวจสอบว่าแอปมีสิทธิ์เข้าถึง Network หรือไม่ และเปลี่ยน URLSession configuration
// เพิ่ม configuration สำหรับ allow arbitrary loads
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 60
config.timeoutIntervalForResource = 120
// ใช้ configuration นี้แทน default
let task = URLSession(configuration: config).dataTask(with: request) { ... }
กรณีที่ 3: Streaming หยุดกลางคันแล้วไม่ต่อ
ปัญหานี้มักเกิดจากการ parse JSON ผิดพลาด ให้เพิ่มการตรวจสอบและการจัดการ error ที่ดีขึ้น รวมถึงการ reconnection เมื่อ connection หลุด
private func extractContent(from jsonString: String) -> String? {
// ข้ามบรรทัดว่าง
if jsonString.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return nil
}
guard let data = jsonString.data(using: .utf8) else {
return nil
}
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let choices = json["choices"] as? [[String: Any]],
let firstChoice = choices.first,
let delta = firstChoice["delta"] as? [String: Any],
let content = delta["content"] as? String {
return content
}
} catch {
print("Parse error: \(error)")
}
return nil
}
กรณีที่ 4: แอป crash เมื่อกดปุ่ม
ตรวจสอบว่า Info.plist มีการอนุญาต Network แล้วหรือยัง และดูว่าโค้ดอยู่ใน main thread หรือไม่ เพราะ UI ต้องอัพเดตบน main thread เท่านั้น
// ใช้ DispatchQueue.main.async สำหรับทุก UI update
@objc private func askAI() {
DispatchQueue.main.async {
self.responseLabel.text = "กำลังคิด..."
self.activityIndicator.startAnimating()
}
aiService.sendMessageStreaming(
prompt: "อธิบายเรื่อง AI",
onChunkReceived: { chunk in
DispatchQueue.main.async {
self.responseLabel.text = (self.responseLabel.text ?? "") + chunk
}
},
onComplete: {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
}
},
onError: { error in
DispatchQueue.main.async {
self.responseLabel.text = "ผิดพลาด: \(error.localizedDescription)"
self.activityIndicator.stopAnimating()
}
}
)
}
สรุป
การเชื่อมต่อ Swift iOS กับ AI API แบบ Streaming ไม่ได้ยากอย่างที่คิด แค่รู้พื้นฐา