مقدمة: REST مقابل GraphQL — معركة قديمة في عالم Swift الجديد
منذ زمن بعيد، في عصر Objective-C وإرسال المهام عبر المحددات، لم يكن الاختيار بين REST و GraphQL حادًا بهذا الشكل. كان REST هو الملك، وكان JSON مرافقه المخلص. لكن الأوقات تغيرت. أصبح Swift لغة ذات نظام أنواع قوي، وظهرت async/await و Codable وأطر عمل قوية مثل Apollo iOS. الآن السؤال "REST أم GraphQL؟" يبدو مختلفًا: "ما الذي سيجلب لمشروع Swift الخاص بي فائدة أكبر؟".
في هذه المقالة، لن نعيد سرد ويكيبيديا. سنأخذ مثالًا حقيقيًا لتطبيق iOS لمنصة تعليمية (نعم، منصة PythonLib) ونرى كيف يتصرف كل نهج في كود Swift. ستتعرف على متى لا يزال REST هو المسيطر، ومتى يكون GraphQL هو المنقذ الحقيقي.
باختصار عن الشيء الرئيسي: كيف يختلف REST عن GraphQL
لأولئك الذين بدأوا للتو في هذا الموضوع — المفاهيم الأساسية.
- REST — هو نمط معماري حيث كل مورد (مستخدم، دورة، درس) له عنوان URL خاص به. تحصل على هيكل بيانات ثابت يقرر الخادم إرساله إليك. غالبًا — مجموعة كاملة من البيانات الزائدة.
- GraphQL — هي لغة استعلام. أنت نفسك تقرر الحقول التي تحتاجها. نقطة دخول واحدة (
/graphql) ويمكن لاستعلام واحد أن يحل محل عدة استعلامات REST.
على الورق، يبدو GraphQL كرصاصة فضية. لكن في مشاريع Swift هناك تفاصيل دقيقة سنقوم بتحليلها عمليًا الآن.
مثال 1: الحصول على ملف المستخدم ودوراته
نهج REST في Swift (URLSession + Codable)
لنفترض أن لدينا REST API كلاسيكي. لعرض الملف الشخصي وقائمة الدورات، نحتاج إلى طلبين:
// النماذجstruct UserProfile: Codable { let id: Int let name: String let email: String let avatarURL: String}
struct Course: Codable { let id: Int let title: String let progress: Double}
// طبقة الشبكاتfunc fetchUserProfile(userId: Int) async throws -> UserProfile { let url = URL(string: "https://api.pythonlib.ru/users/\\(userId)")! let (data, _) = try await URLSession.shared.data(from: url) return try JSONDecoder().decode(UserProfile.self, from: data)}
func fetchCourses(userId: Int) async throws -> [Course] { let url = URL(string: "https://api.pythonlib.ru/users/\\(userId)/courses")! let (data, _) = try await URLSession.shared.data(from: url) return try JSONDecoder().decode([Course].self, from: data)}
// الاستخدامlet profile = try await fetchUserProfile(userId: 42)let courses = try await fetchCourses(userId: 42)كل شيء بسيط ويمكن التنبؤ به. لكن هناك مشكلة: نحصل على جميع حقول الملف الشخصي، حتى لو كنا نحتاج فقط إلى الاسم والبريد الإلكتروني. وأيضًا — طلبين شبكيين. على اتصال بطيء، هذا مؤلم.
نهج GraphQL في Swift (Apollo iOS)
الآن نفس الشيء، ولكن عبر GraphQL. أولاً نحدد الاستعلام في ملف .graphql:
query GetUserWithCourses($userId: ID!) { user(id: $userId) { id name email courses { id title progress } }}يقوم Apollo بإنشاء كود Swift مصنف النوع تلقائيًا. في Xcode يبدو الأمر هكذا:
import Apollo
let apolloClient = ApolloClient(url: URL(string: "https://api.pythonlib.ru/graphql")!)
func fetchUserWithCourses(userId: Int) async throws -> GetUserWithCoursesQuery.Data { return try await withCheckedThrowingContinuation { continuation in apolloClient.fetch(query: GetUserWithCoursesQuery(userId: userId)) { result in switch result { case .success(let graphQLResult): if let data = graphQLResult.data { continuation.resume(returning: data) } else if let errors = graphQLResult.errors {