Tüm Yazılar
KategoriiOS
Okuma Süresi
20 dk
Yayın Tarihi
...
Kelime Sayısı
1.447kelime

Kahveni hazırla - bu içerikli bir makale!

URLSessionWebSocketTask ile real-time iletişim. Chat uygulaması, presence sistemi, reconnection stratejileri ve Combine entegrasyonu.

WebSocket ile Real-time iOS Uygulamaları: Chat ve Live Updates

Push notification ile 1-2 saniyelik gecikme kabul edilebilir. Ama chat, canlı skor, borsa takibi veya collaborative editing gibi uygulamalarda milisaniye önemli. WebSocket, HTTP'nin üzerine kurulu full-duplex iletişim protokolüdür — server ve client arasında sürekli açık bir kanal sağlar.

💡 Hızlı Not: iOS 13+ ile URLSessionWebSocketTask native WebSocket desteği geldi. Artık 3rd party kütüphane gerekmeden WebSocket kullanabilirsin.

İçindekiler

  1. WebSocket vs HTTP vs SSE
  2. URLSessionWebSocketTask
  3. Connection Management
  4. Message Protocol Tasarımı
  5. Chat Uygulaması
  6. Presence Sistemi
  7. Reconnection Stratejileri
  8. Combine ile WebSocket
  9. Scaling Considerations
  10. Best Practices

WebSocket vs HTTP vs SSE {#websocket-vs}

Özellik
HTTP
SSE
WebSocket
Yön
Client → Server
Server → Client
İki yönlü
Bağlantı
Her request yeni
Tek yönlü açık
İki yönlü açık
Protokol
HTTP/1.1, 2, 3
HTTP
ws:// / wss://
Overhead
Header her seferinde
Düşük
Çok düşük (2-6 byte frame)
Kullanım
REST API
Notifications, feeds
Chat, gaming, collab

URLSessionWebSocketTask {#urlsession-websocket}

swift
1class WebSocketManager: ObservableObject {
2 private var webSocketTask: URLSessionWebSocketTask?
3 @Published var messages: [ChatMessage] = []
4 @Published var isConnected = false
5 
6 func connect() {
7 let url = URL(string: "wss://api.example.com/ws")!
8 webSocketTask = URLSession.shared.webSocketTask(with: url)
9 webSocketTask?.resume()
10 isConnected = true
11 receiveMessage()
12 }
13 
14 func disconnect() {
15 webSocketTask?.cancel(with: .goingAway, reason: nil)
16 isConnected = false
17 }
18 
19 // Mesaj gönder
20 func send(_ text: String) {
21 let message = URLSessionWebSocketTask.Message.string(text)
22 webSocketTask?.send(message) { error in
23 if let error { print("Send error: \(error)") }
24 }
25 }
26 
27 // JSON mesaj gönder
28 func sendJSON<T: Encodable>(_ object: T) throws {
29 let data = try JSONEncoder().encode(object)
30 let message = URLSessionWebSocketTask.Message.data(data)
31 webSocketTask?.send(message) { error in
32 if let error { print("Send error: \(error)") }
33 }
34 }
35 
36 // Mesaj al (recursive)
37 private func receiveMessage() {
38 webSocketTask?.receive { [weak self] result in
39 switch result {
40 case .success(let message):
41 switch message {
42 case .string(let text):
43 DispatchQueue.main.async {
44 self?.handleTextMessage(text)
45 }
46 case .data(let data):
47 DispatchQueue.main.async {
48 self?.handleDataMessage(data)
49 }
50 @unknown default:
51 break
52 }
53 // Sonraki mesajı dinle
54 self?.receiveMessage()
55 case .failure(let error):
56 print("Receive error: \(error)")
57 self?.handleDisconnection()
58 }
59 }
60 }
61 
62 private func handleTextMessage(_ text: String) {
63 if let data = text.data(using: .utf8),
64 let message = try? JSONDecoder().decode(ChatMessage.self, from: data) {
65 messages.append(message)
66 }
67 }
68 
69 private func handleDataMessage(_ data: Data) {
70 if let message = try? JSONDecoder().decode(ChatMessage.self, from: data) {
71 messages.append(message)
72 }
73 }
74}

Connection Management {#connection}

swift
1// Ping/Pong heartbeat ile bağlantı canlılığı kontrol
2func startHeartbeat() {
3 Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] _ in
4 self?.webSocketTask?.sendPing { error in
5 if let error {
6 print("Ping failed: \(error)")
7 self?.handleDisconnection()
8 }
9 }
10 }
11}

Message Protocol Tasarımı {#protocol}

swift
1// Type-safe WebSocket message protocol
2enum WSMessageType: String, Codable {
3 case chat, typing, presence, read, system
4}
5 
6struct WSMessage: Codable {
7 let type: WSMessageType
8 let payload: AnyCodable // veya generic
9 
10 // Chat message
11 struct ChatPayload: Codable {
12 let id: UUID
13 let text: String
14 let senderId: String
15 let timestamp: Date
16 }
17 
18 // Typing indicator
19 struct TypingPayload: Codable {
20 let userId: String
21 let isTyping: Bool
22 }
23 
24 // Presence
25 struct PresencePayload: Codable {
26 let userId: String
27 let status: String // online, offline, away
28 }
29}

Chat Uygulaması {#chat}

swift
1struct ChatView: View {
2 @StateObject var wsManager = WebSocketManager()
3 @State private var messageText = ""
4 
5 var body: some View {
6 VStack {
7 ScrollView {
8 LazyVStack(alignment: .leading) {
9 ForEach(wsManager.messages) { message in
10 MessageBubble(message: message)
11 }
12 }
13 }
14 HStack {
15 TextField("Mesaj yaz...", text: $messageText)
16 .textFieldStyle(.roundedBorder)
17 Button("Gönder") {
18 wsManager.send(messageText)
19 messageText = ""
20 }
21 }
22 .padding()
23 }
24 .onAppear { wsManager.connect() }
25 .onDisappear { wsManager.disconnect() }
26 }
27}

Presence Sistemi {#presence}

Kullanıcının çevrimiçi/çevrimdışı durumunu takip et:

swift
1func updatePresence(status: String) {
2 let presence = WSMessage(type: .presence, payload: PresencePayload(userId: currentUserId, status: status))
3 try? sendJSON(presence)
4}
5 
6// App lifecycle'da presence güncelle
7NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: .main) { _ in
8 updatePresence(status: "away")
9}
10NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main) { _ in
11 updatePresence(status: "online")
12}

Reconnection Stratejileri {#reconnection}

swift
1// Exponential backoff ile otomatik reconnect
2func handleDisconnection() {
3 isConnected = false
4 reconnect(attempt: 0)
5}
6 
7private func reconnect(attempt: Int) {
8 let maxAttempts = 10
9 guard attempt < maxAttempts else { return }
10 
11 let delay = min(pow(2.0, Double(attempt)), 60.0) // Max 60 saniye
12 DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
13 self?.connect()
14 if self?.isConnected == false {
15 self?.reconnect(attempt: attempt + 1)
16 }
17 }
18}

Combine ile WebSocket {#combine}

swift
1import Combine
2 
3class ReactiveWebSocket {
4 private let messageSubject = PassthroughSubject<WSMessage, Error>()
5 var messagePublisher: AnyPublisher<WSMessage, Error> { messageSubject.eraseToAnyPublisher() }
6 
7 // View'da kullanım
8 // wsManager.messagePublisher
9 // .filter { $0.type == .chat }
10 // .receive(on: DispatchQueue.main)
11 // .sink { message in /* UI güncelle */ }
12}

Scaling Considerations {#scaling}

Teknik
Açıklama
Connection pooling
Tek WebSocket, multiplexed channels
Message compression
Per-message deflate
Binary protocol
JSON yerine Protobuf/MessagePack
Load balancing
Sticky sessions veya pub/sub (Redis)

Message Queue ve Offline Destek {#message-queue}

Baglanti koptugundan mesajlar kaybolmasin — local queue ile offline desteği:

swift
1class OfflineMessageQueue {
2 private var pendingMessages: [WSMessage] = []
3 private let storage = UserDefaults.standard
4 private let queueKey = "ws_pending_messages"
5 
6 init() {
7 // Onceki oturumdan kalan mesajlari yukle
8 if let data = storage.data(forKey: queueKey),
9 let messages = try? JSONDecoder().decode([WSMessage].self, from: data) {
10 pendingMessages = messages
11 }
12 }
13 
14 func enqueue(_ message: WSMessage) {
15 pendingMessages.append(message)
16 persist()
17 }
18 
19 func flushAll(via ws: WebSocketManager) {
20 let messagesToSend = pendingMessages
21 pendingMessages.removeAll()
22 persist()
23 
24 for message in messagesToSend {
25 try? ws.sendJSON(message)
26 }
27 }
28 
29 private func persist() {
30 if let data = try? JSONEncoder().encode(pendingMessages) {
31 storage.set(data, forKey: queueKey)
32 }
33 }
34 
35 var count: Int { pendingMessages.count }
36}
37 
38// Kullanim
39func send(_ text: String) {
40 let message = WSMessage(type: .chat, text: text, timestamp: Date())
41 if isConnected {
42 try? sendJSON(message)
43 } else {
44 offlineQueue.enqueue(message)
45 }
46}
47 
48// Baglanti gelince kuyruktakileri gonder
49func onReconnected() {
50 offlineQueue.flushAll(via: self)
51}

WebSocket Guvenlik {#security}

Guvenlik Onlemi
Aciklama
wss:// (TLS)
Tum trafik sifrelenir, MITM saldirisi engellenir
Token auth
Handshake sirasinda JWT token gonder
Rate limiting
Server tarafinda mesaj sayisi sinirlama
Input validation
Gelen mesajlari parse etmeden once validate et
Origin check
Server'da allowed origins kontrolu
swift
1// Auth token ile WebSocket baglantisi
2func connectWithAuth(token: String) {
3 var request = URLRequest(url: URL(string: "wss://api.example.com/ws")!)
4 request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
5 webSocketTask = URLSession.shared.webSocketTask(with: request)
6 webSocketTask?.resume()
7 isConnected = true
8 receiveMessage()
9}

Best Practices {#best-practices}

  1. wss:// kullan — güvenlik için TLS zorunlu
  2. Heartbeat — 30 saniyede bir ping/pong
  3. Reconnection — exponential backoff ile
  4. Message queue — offline mesajları queue'la
  5. Background — URLSession background task
  6. Auth token yenileme — token expire oldugunda reconnect ile yeni token gonder
  7. Message deduplication — ayni mesajin birden fazla islenmesini UUID ile onle
  8. Binary protocol — yuksek trafikte JSON yerine Protobuf veya MessagePack kullan

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Okuyucu Ödülü

Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var: **Kaynaklar:** - [Apple: URLSessionWebSocketTask](https://developer.apple.com/documentation/foundation/urlsessionwebsockettask) - [RFC 6455: The WebSocket Protocol](https://tools.ietf.org/html/rfc6455) - [Socket.IO](https://socket.io/)

Etiketler

#websocket#real-time#ios#chat#urlsessionwebsocket#networking#swift
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz