วิธีแยกลอจิกการโต้ตอบระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์ในแอปพลิเคชัน iOS
เผยแพร่แล้ว: 2022-03-11ทุกวันนี้ แอปพลิเคชั่นมือถือส่วนใหญ่อาศัยการโต้ตอบระหว่างไคลเอนต์กับเซิร์ฟเวอร์เป็นอย่างมาก สิ่งนี้ไม่เพียงแค่หมายความว่าพวกเขาสามารถถ่ายโอนงานหนักส่วนใหญ่ไปยังเซิร์ฟเวอร์แบ็คเอนด์ได้ แต่ยังช่วยให้แอปพลิเคชั่นมือถือเหล่านี้นำเสนอคุณสมบัติและฟังก์ชั่นทุกประเภทที่สามารถใช้ได้ผ่านอินเทอร์เน็ตเท่านั้น
โดยปกติแล้ว เซิร์ฟเวอร์ส่วนหลังได้รับการออกแบบเพื่อให้บริการผ่าน RESTful API สำหรับแอปพลิเคชันที่ง่ายกว่า เรามักจะรู้สึกอยากได้รับโดยการสร้างโค้ดปาเก็ตตี้ โค้ดผสมที่เรียกใช้ API ด้วยตรรกะของแอปพลิเคชันที่เหลือ อย่างไรก็ตาม เนื่องจากแอปพลิเคชันมีความซับซ้อนและจัดการกับ API มากขึ้นเรื่อยๆ การโต้ตอบกับ API เหล่านี้ในลักษณะที่ไม่มีโครงสร้างและไม่ได้วางแผนอาจกลายเป็นเรื่องน่ารำคาญ
บทความนี้กล่าวถึงแนวทางสถาปัตยกรรมสำหรับการสร้างโมดูลเครือข่ายไคลเอ็นต์ REST ที่สะอาดหมดจดสำหรับแอปพลิเคชัน iOS ที่ช่วยให้คุณแยกตรรกะการโต้ตอบระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์ทั้งหมดออกจากโค้ดแอปพลิเคชันส่วนที่เหลือ
แอปพลิเคชันไคลเอนต์ - เซิร์ฟเวอร์
การโต้ตอบระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์โดยทั่วไปมีลักษณะดังนี้:
- ผู้ใช้ดำเนินการบางอย่าง (เช่น แตะที่ปุ่มบางปุ่มหรือทำท่าทางอื่นๆ บนหน้าจอ)
- แอปพลิเคชันเตรียมและส่งคำขอ HTTP/REST เพื่อตอบสนองต่อการกระทำของผู้ใช้
- เซิร์ฟเวอร์ประมวลผลคำขอและตอบสนองตามแอปพลิเคชัน
- แอปพลิเคชันได้รับการตอบกลับและอัปเดตส่วนต่อประสานผู้ใช้ตามนั้น
เมื่อมองแวบเดียว กระบวนการโดยรวมอาจดูเรียบง่าย แต่เราต้องคิดถึงรายละเอียด
แม้ว่า API ของเซิร์ฟเวอร์แบ็กเอนด์จะทำงานตามที่โฆษณาไว้ (ซึ่ง ไม่ได้ เป็นเช่นนั้นเสมอไป!) ก็มักจะได้รับการออกแบบมาไม่ดี ทำให้ไม่มีประสิทธิภาพหรือใช้งานยาก สิ่งที่น่ารำคาญอย่างหนึ่งก็คือการเรียกใช้ API ทั้งหมดต้องการให้ผู้เรียกให้ข้อมูลเดียวกันซ้ำซ้อน (เช่น วิธีจัดรูปแบบข้อมูลคำขอ โทเค็นการเข้าถึงที่เซิร์ฟเวอร์สามารถใช้เพื่อระบุผู้ใช้ที่ลงชื่อเข้าใช้อยู่ในปัจจุบัน เป็นต้น)
แอปพลิเคชันมือถืออาจต้องใช้เซิร์ฟเวอร์แบ็คเอนด์หลายเครื่องพร้อมกันเพื่อวัตถุประสงค์ที่แตกต่างกัน ตัวอย่างเช่น เซิร์ฟเวอร์หนึ่งอาจใช้สำหรับการรับรองความถูกต้องของผู้ใช้ ในขณะที่อีกเซิร์ฟเวอร์หนึ่งเกี่ยวข้องกับการรวบรวมการวิเคราะห์เท่านั้น
นอกจากนี้ ไคลเอนต์ REST ทั่วไปจะต้องทำมากกว่าแค่เรียกใช้ API ระยะไกล ความสามารถในการยกเลิกคำขอที่รอดำเนินการ หรือแนวทางการจัดการข้อผิดพลาดที่ชัดเจนและจัดการได้ เป็นตัวอย่างของฟังก์ชันการทำงานที่จำเป็นต้องสร้างขึ้นในแอปพลิเคชันมือถือที่มีประสิทธิภาพ
ภาพรวมของสถาปัตยกรรม
แกนหลักของไคลเอนต์ REST ของเราจะสร้างจากส่วนประกอบต่อไปนี้:
- โมเดล: คลาสที่อธิบายโมเดลข้อมูลของแอปพลิเคชันของเรา ซึ่งสะท้อนถึงโครงสร้างของข้อมูลที่ได้รับหรือส่งไปยังเซิร์ฟเวอร์แบ็กเอนด์
- Parsers: รับผิดชอบในการถอดรหัสการตอบสนองของเซิร์ฟเวอร์และสร้างโมเดลวัตถุ
- ข้อผิดพลาด: ออบเจ็กต์ที่แสดงถึงการตอบสนองของเซิร์ฟเวอร์ที่ผิดพลาด
- ไคลเอนต์: ส่งคำขอไปยังเซิร์ฟเวอร์แบ็กเอนด์และรับการตอบกลับ
- บริการ: จัดการการดำเนินการที่เชื่อมโยงทางตรรกะ (เช่น การพิสูจน์ตัวตน การจัดการข้อมูลที่เกี่ยวข้องกับผู้ใช้ การวิเคราะห์ ฯลฯ)
นี่คือวิธีที่แต่ละองค์ประกอบเหล่านี้จะโต้ตอบกัน:
ลูกศร 1 ถึง 10 ในภาพด้านบนแสดงลำดับการดำเนินการในอุดมคติระหว่างแอปพลิเคชันที่เรียกใช้บริการและบริการส่งคืนข้อมูลที่ร้องขอเป็นวัตถุแบบจำลองในที่สุด แต่ละองค์ประกอบในโฟลว์นั้นมีบทบาทเฉพาะทำให้มั่นใจได้ถึงการแยกข้อกังวลภายในโมดูล
การดำเนินการ
เราจะใช้ไคลเอ็นต์ REST ของเราเป็นส่วนหนึ่งของแอปพลิเคชันเครือข่ายสังคมในจินตนาการ ซึ่งเราจะโหลดรายชื่อเพื่อนของผู้ใช้ที่เข้าสู่ระบบอยู่ในปัจจุบัน เราจะถือว่าเซิร์ฟเวอร์ระยะไกลของเราใช้ JSON สำหรับการตอบกลับ
ให้เราเริ่มต้นด้วยการนำโมเดลและ parsers ของเราไปใช้
จาก Raw JSON สู่ Model Objects
โมเดลแรกของเรา User กำหนดโครงสร้างของข้อมูลสำหรับผู้ใช้โซเชียลเน็ตเวิร์ก เพื่อให้ง่ายขึ้น เราจะรวมเฉพาะฟิลด์ที่จำเป็นสำหรับบทช่วยสอนนี้เท่านั้น (ในการใช้งานจริง โครงสร้างมักจะมีคุณสมบัติมากกว่านั้นมาก)
struct User { var id: String var email: String? var name: String? } เนื่องจากเราจะได้รับข้อมูลผู้ใช้ทั้งหมดจากเซิร์ฟเวอร์ส่วนหลังผ่าน API เราจึงต้องการวิธีแยกวิเคราะห์การตอบสนอง API เป็นวัตถุ User ที่ถูกต้อง ในการทำเช่นนี้ เราจะเพิ่มคอนสตรัคเตอร์ให้กับ User ที่ยอมรับอ็อบเจกต์ JSON ที่แยกวิเคราะห์ ( Dictionary ) เป็นพารามิเตอร์ เราจะกำหนดวัตถุ JSON ของเราเป็นประเภทนามแฝง:
typealias JSON = [String: Any] จากนั้นเราจะเพิ่มฟังก์ชันตัวสร้างให้กับโครงสร้าง User ของเราดังนี้:
extension User { init?(json: JSON) { guard let id = json["id"] as? String else { return nil } self.id = id self.email = json["email"] as? String self.name = json["name"] as? String } } เพื่อรักษาคอนสตรัคเตอร์เริ่มต้นดั้งเดิมของ User เราเพิ่มคอนสตรัคเตอร์ผ่านส่วนขยายในประเภท User
ต่อไป เพื่อสร้างวัตถุ User จากการตอบสนอง API ดิบ เราจำเป็นต้องดำเนินการสองขั้นตอนต่อไปนี้:
// Transform raw JSON data to parsed JSON object using JSONSerializer (part of standard library) let userObject = (try? JSONSerialization.jsonObject(with: data, options: [])) as? JSON // Create an instance of `User` structure from parsed JSON object let user = userObject.flatMap(User.init)การจัดการข้อผิดพลาดที่คล่องตัว
เราจะกำหนดประเภทเพื่อแสดงข้อผิดพลาดต่างๆ ที่อาจเกิดขึ้นเมื่อพยายามโต้ตอบกับเซิร์ฟเวอร์แบ็กเอนด์ เราสามารถแบ่งข้อผิดพลาดดังกล่าวทั้งหมดออกเป็นสามประเภทพื้นฐาน:
- ไม่มีการเชื่อมต่ออินเทอร์เน็ต
- ข้อผิดพลาดที่รายงานเป็นส่วนหนึ่งของการตอบสนอง (เช่น ข้อผิดพลาดในการตรวจสอบ สิทธิ์การเข้าถึงไม่เพียงพอ ฯลฯ)
- ข้อผิดพลาดที่เซิร์ฟเวอร์ไม่สามารถรายงานเป็นส่วนหนึ่งของการตอบสนอง (เช่น เซิร์ฟเวอร์ขัดข้อง หมดเวลาตอบสนอง ฯลฯ)
เราสามารถกำหนดวัตถุข้อผิดพลาดของเราเป็นประเภทการแจงนับ และในขณะที่เรากำลังดำเนินการอยู่ ควรทำให้ประเภท ServiceError ของเราสอดคล้องกับโปรโตคอล Error ซึ่งจะทำให้เราใช้และจัดการค่าความผิดพลาดเหล่านี้ได้โดยใช้กลไกมาตรฐานที่ Swift จัดเตรียมไว้ให้ (เช่น การใช้ throw to throw an error)
enum ServiceError: Error { case noInternetConnection case custom(String) case other } ไม่เหมือนกับ noInternetConnection และข้อผิดพลาด other ข้อผิดพลาดที่กำหนดเองมีค่าที่เกี่ยวข้อง ซึ่งจะทำให้เราใช้การตอบสนองข้อผิดพลาดจากเซิร์ฟเวอร์เป็นค่าที่เกี่ยวข้องสำหรับข้อผิดพลาดนั้นเอง ซึ่งจะทำให้ข้อผิดพลาดมีบริบทมากขึ้น
ตอนนี้ มาเพิ่มคุณสมบัติ errorDescription ให้กับ ServiceError enumartion เพื่อให้ข้อผิดพลาดมีรายละเอียดมากขึ้น เราจะเพิ่มข้อความฮาร์ดโค้ดสำหรับ noInternetConnection และข้อผิดพลาด other และใช้ค่าที่เกี่ยวข้องเป็นข้อความสำหรับข้อผิดพลาด custom
extension ServiceError: LocalizedError { var errorDescription: String? { switch self { case .noInternetConnection: return "No Internet connection" case .other: return "Something went wrong" case .custom(let message): return message } } } มีอีกสิ่งหนึ่งที่เราต้องนำไปใช้ในการแจงนับ ServiceError ของเรา ในกรณีของข้อผิดพลาด custom เราจำเป็นต้องแปลงข้อมูล JSON ของเซิร์ฟเวอร์ให้เป็นวัตถุข้อผิดพลาด ในการทำเช่นนี้ เราใช้แนวทางเดียวกันกับที่ใช้ในกรณีของแบบจำลอง:
extension ServiceError { init(json: JSON) { if let message = json["message"] as? String { self = .custom(message) } else { self = .other } } }เชื่อมช่องว่างระหว่างแอปพลิเคชันและเซิร์ฟเวอร์แบ็กเอนด์
ส่วนประกอบไคลเอนต์จะเป็นตัวกลางระหว่างแอปพลิเคชันและเซิร์ฟเวอร์ส่วนหลัง เป็นองค์ประกอบสำคัญที่จะกำหนดวิธีที่แอปพลิเคชันและเซิร์ฟเวอร์จะสื่อสารกัน แต่จะไม่รู้อะไรเกี่ยวกับโมเดลข้อมูลและโครงสร้างของพวกเขา ลูกค้าจะต้องรับผิดชอบในการเรียกใช้ URL เฉพาะด้วยพารามิเตอร์ที่ให้ไว้และส่งคืนข้อมูล JSON ขาเข้าที่แยกวิเคราะห์เป็นออบเจ็กต์ JSON
enum RequestMethod: String { case get = "GET" case post = "POST" case put = "PUT" case delete = "DELETE" } final class WebClient { private var baseUrl: String init(baseUrl: String) { self.baseUrl = baseUrl } func load(path: String, method: RequestMethod, params: JSON, completion: @escaping (Any?, ServiceError?) -> ()) -> URLSessionDataTask? { // TODO: Add implementation } }ลองดูว่าเกิดอะไรขึ้นในโค้ดด้านบนนี้…
ขั้นแรก เราประกาศประเภทการแจงนับ RequestMethod ซึ่งอธิบายวิธี HTTP ทั่วไปสี่วิธี นี่เป็นวิธีการที่ใช้ใน REST API

คลาส WebClient มีคุณสมบัติ baseURL ซึ่งจะใช้เพื่อแก้ไข URL ที่เกี่ยวข้องทั้งหมดที่ได้รับ ในกรณีที่แอปพลิเคชันของเราต้องการโต้ตอบกับเซิร์ฟเวอร์หลายเครื่อง เราสามารถสร้าง WebClient ได้หลายอินสแตนซ์ โดยแต่ละรายการมีค่าต่างกันสำหรับ baseURL
ไคลเอ็นต์มีเมธอดเดียว load ซึ่งใช้พาธที่สัมพันธ์กับ baseURL เป็นพารามิเตอร์ วิธีการขอ พารามิเตอร์คำขอ และการปิดเสร็จสิ้น การปิดเสร็จสิ้นถูกเรียกใช้ด้วย JSON และ ServiceError ที่แยกวิเคราะห์เป็นพารามิเตอร์ สำหรับตอนนี้ วิธีการข้างต้นยังไม่มีการนำไปปฏิบัติ ซึ่งเราจะทำในไม่ช้านี้
ก่อนใช้วิธีการ load เราต้องการวิธีสร้าง URL จากข้อมูลทั้งหมดที่มีให้กับเมธอด เราจะขยายคลาส URL เพื่อจุดประสงค์นี้:
extension URL { init(baseUrl: String, path: String, params: JSON, method: RequestMethod) { var components = URLComponents(string: baseUrl)! components.path += path switch method { case .get, .delete: components.queryItems = params.map { URLQueryItem(name: $0.key, value: String(describing: $0.value)) } default: break } self = components.url! } }ที่นี่เราเพียงแค่เพิ่มเส้นทางไปยัง URL ฐาน สำหรับเมธอด GET และ DELETE HTTP เรายังเพิ่มพารามิเตอร์การสืบค้นลงในสตริง URL
ต่อไป เราต้องสามารถสร้างอินสแตนซ์ของ URLRequest จากพารามิเตอร์ที่กำหนดได้ ในการดำเนินการนี้ เราจะทำบางสิ่งที่คล้ายกับที่เราทำกับ URL :
extension URLRequest { init(baseUrl: String, path: String, method: RequestMethod, params: JSON) { let url = URL(baseUrl: baseUrl, path: path, params: params, method: method) self.init(url: url) httpMethod = method.rawValue setValue("application/json", forHTTPHeaderField: "Accept") setValue("application/json", forHTTPHeaderField: "Content-Type") switch method { case .post, .put: httpBody = try! JSONSerialization.data(withJSONObject: params, options: []) default: break } } } ที่นี่ ก่อนอื่นเราสร้าง URL โดยใช้ตัวสร้างจากส่วนขยาย จากนั้น เราเริ่มต้นอินสแตนซ์ของ URLRequest ด้วย URL นี้ ตั้งค่าส่วนหัว HTTP บางส่วนตามความจำเป็น จากนั้นในกรณีของวิธี POST หรือ PUT HTTP ให้เพิ่มพารามิเตอร์ลงในเนื้อหาคำขอ
ตอนนี้เราได้ครอบคลุมข้อกำหนดเบื้องต้นทั้งหมดแล้ว เราสามารถใช้วิธีการ load ได้:
final class WebClient { private var baseUrl: String init(baseUrl: String) { self.baseUrl = baseUrl } func load(path: String, method: RequestMethod, params: JSON, completion: @escaping (Any?, ServiceError?) -> ()) -> URLSessionDataTask? { // Checking internet connection availability if !Reachability.isConnectedToNetwork() { completion(nil, ServiceError.noInternetConnection) return nil } // Adding common parameters var parameters = params if let token = KeychainWrapper.itemForKey("application_token") { parameters["token"] = token } // Creating the URLRequest object let request = URLRequest(baseUrl: baseUrl, path: path, method: method, params: params) // Sending request to the server. let task = URLSession.shared.dataTask(with: request) { data, response, error in // Parsing incoming data var object: Any? = nil if let data = data { object = try? JSONSerialization.jsonObject(with: data, options: []) } if let httpResponse = response as? HTTPURLResponse, (200..<300) ~= httpResponse.statusCode { completion(object, nil) } else { let error = (object as? JSON).flatMap(ServiceError.init) ?? ServiceError.other completion(nil, error) } } task.resume() return task } } วิธีการ load ด้านบนดำเนินการตามขั้นตอนต่อไปนี้:
- ตรวจสอบความพร้อมใช้งานของการเชื่อมต่ออินเทอร์เน็ต หากไม่มีการเชื่อมต่ออินเทอร์เน็ต เราจะเรียกการปิดการดำเนินการทันทีโดยไม่มีข้อผิดพลาด
noInternetConnectionเป็นพารามิเตอร์ (หมายเหตุ: ความสามารถในการReachabilityในโค้ดเป็นคลาสที่กำหนดเอง ซึ่งใช้หนึ่งในวิธีทั่วไปในการตรวจสอบการเชื่อมต่ออินเทอร์เน็ต) - เพิ่มพารามิเตอร์ทั่วไป . ซึ่งอาจรวมถึงพารามิเตอร์ทั่วไป เช่น โทเค็นของแอปพลิเคชันหรือ ID ผู้ใช้
- สร้างวัตถุ
URLRequestโดยใช้ตัวสร้างจากส่วนขยาย - ส่งคำขอไปยังเซิร์ฟเวอร์ เราใช้วัตถุ
URLSessionเพื่อส่งข้อมูลไปยังเซิร์ฟเวอร์ - แยกวิเคราะห์ข้อมูลที่เข้ามา เมื่อเซิร์ฟเวอร์ตอบสนอง ก่อนอื่นเราจะแยกวิเคราะห์ payload การตอบสนองเป็นวัตถุ JSON โดยใช้
JSONSerializationจากนั้นเราจะตรวจสอบรหัสสถานะของการตอบกลับ หากเป็นรหัสความสำเร็จ (เช่น ในช่วงระหว่าง 200 ถึง 299) เราจะเรียกการปิดสำเร็จด้วยวัตถุ JSON มิฉะนั้น เราจะแปลงวัตถุ JSON เป็นวัตถุServiceErrorและเรียกการปิดเสร็จสมบูรณ์ด้วยวัตถุข้อผิดพลาดนั้น
การกำหนดบริการสำหรับการดำเนินการที่เชื่อมโยงเชิงตรรกะ
ในกรณีของแอปพลิเคชันของเรา เราต้องการบริการที่จะจัดการกับงานที่เกี่ยวข้องกับเพื่อนของผู้ใช้ สำหรับสิ่งนี้ เราสร้างคลาส FriendsService ตามหลักการแล้ว ชั้นเรียนในลักษณะนี้จะรับผิดชอบการดำเนินงาน เช่น การรับรายชื่อเพื่อน เพิ่มเพื่อนใหม่ การลบเพื่อน การจัดกลุ่มเพื่อนเป็นหมวดหมู่ เป็นต้น เพื่อความง่ายในบทช่วยสอนนี้ เราจะใช้วิธีเดียว :
final class FriendsService { private let client = WebClient(baseUrl: "https://your_server_host/api/v1") @discardableResult func loadFriends(forUser user: User, completion: @escaping ([User]?, ServiceError?) -> ()) -> URLSessionDataTask? { let params: JSON = ["user_id": user.id] return client.load(path: "/friends", method: .get, params: params) { result, error in let dictionaries = result as? [JSON] completion(dictionaries?.flatMap(User.init), error) } } } คลาส FriendsService มีคุณสมบัติ client ประเภท WebClient เริ่มต้นด้วย URL พื้นฐานของเซิร์ฟเวอร์ระยะไกลซึ่งมีหน้าที่ในการจัดการเพื่อน ดังที่กล่าวไว้ก่อนหน้านี้ ในคลาสบริการอื่นๆ เราสามารถมีอินสแตนซ์ที่แตกต่างกันของ WebClient ที่เริ่มต้นด้วย URL อื่นได้หากจำเป็น
ในกรณีของแอปพลิเคชันที่ทำงานกับเซิร์ฟเวอร์เดียวเท่านั้น คลาส WebClient สามารถกำหนดคอนสตรัคเตอร์ที่เริ่มต้นด้วย URL ของเซิร์ฟเวอร์นั้น:
final class WebClient { // ... init() { self.baseUrl = "https://your_server_base_url" } // ... } เมื่อเรียกใช้เมธอด loadFriends จะเตรียมพารามิเตอร์ที่จำเป็นทั้งหมดและใช้อินสแตนซ์ของ WebClient ของ FriendService เพื่อส่งคำขอ API หลังจากที่ได้รับการตอบกลับจากเซิร์ฟเวอร์ผ่าน WebClient มันจะแปลงวัตถุ JSON เป็นโมเดล User และเรียกการปิดที่เสร็จสมบูรณ์โดยใช้วัตถุเหล่านี้เป็นพารามิเตอร์
การใช้งาน FriendService โดยทั่วไปอาจมีลักษณะดังนี้:
let friendsTask: URLSessionDataTask! let activityIndicator: UIActivityIndicatorView! var friends: [User] = [] func friendsButtonTapped() { friendsTask?.cancel() //Cancel previous loading task. activityIndicator.startAnimating() //Show loading indicator friendsTask = FriendsService().loadFriends(forUser: currentUser) {[weak self] friends, error in DispatchQueue.main.async { self?.activityIndicator.stopAnimating() //Stop loading indicators if let error = error { print(error.localizedDescription) //Handle service error } else if let friends = friends { self?.friends = friends //Update friends property self?.updateUI() //Update user interface } } } } ในตัวอย่างข้างต้น เราถือว่าฟังก์ชัน friendsButtonTapped ถูกเรียกใช้เมื่อใดก็ตามที่ผู้ใช้แตะปุ่มที่ต้องการแสดงรายการเพื่อนในเครือข่าย เรายังเก็บการอ้างอิงถึงงานในคุณสมบัติ friendsTask เพื่อให้เราสามารถยกเลิกคำขอได้ตลอดเวลาโดยเรียก friendsTask?.cancel()
ซึ่งช่วยให้เราควบคุมวงจรชีวิตของคำขอที่รอดำเนินการได้ดียิ่งขึ้น ทำให้เราสามารถยุติคำขอได้เมื่อเราพิจารณาแล้วว่าคำขอเหล่านี้ไม่เกี่ยวข้อง
บทสรุป
ในบทความนี้ ฉันได้แชร์สถาปัตยกรรมแบบง่ายของโมดูลเครือข่ายสำหรับแอปพลิเคชัน iOS ของคุณ ซึ่งทั้งการใช้งานเพียงเล็กน้อยและสามารถปรับให้เข้ากับความต้องการด้านเครือข่ายที่ซับซ้อนของแอปพลิเคชัน iOS ส่วนใหญ่ได้ อย่างไรก็ตาม ประเด็นสำคัญจากสิ่งนี้คือไคลเอ็นต์ REST และส่วนประกอบที่ออกแบบมาอย่างเหมาะสมซึ่งแยกจากตรรกะแอปพลิเคชันที่เหลือของคุณ สามารถช่วยให้รหัสการโต้ตอบระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์ของแอปพลิเคชันของคุณเรียบง่าย แม้ว่าแอปพลิเคชันเองจะมีความซับซ้อนมากขึ้น .
ฉันหวังว่าคุณจะพบว่าบทความนี้มีประโยชน์ในการสร้างแอปพลิเคชัน iOS ตัวต่อไปของคุณ คุณสามารถค้นหาซอร์สโค้ดของโมดูลเครือข่ายนี้ได้บน GitHub ตรวจสอบรหัส แยกมัน เปลี่ยนมัน เล่นกับมัน
หากคุณพบว่าสถาปัตยกรรมอื่นๆ เหมาะสมกว่าสำหรับคุณและโครงการของคุณ โปรดแบ่งปันรายละเอียดในส่วนความคิดเห็นด้านล่าง
