Swift هي لغة برمجة من Apple تجمع بين قوة Objective-C وأمان اللغات الحديثة. على عكس العديد من اللغات الأخرى، تقدم Swift تراكيب فريدة: أنواع القيم مع النسخ التلقائي، و Actors مع عزل على مستوى المترجم، و Property Wrappers لإعادة استخدام منطق الخصائص، والبرمجة الموجهة بالبروتوكولات كبديل للوراثة التقليدية. وفقًا لوثائق Apple Swift، هذه الميزات تجعل الكود أكثر أمانًا وأداءً.
النقاط الرئيسية
في Swift، struct هو نوع قيمة، بينما class هو نوع مرجعي. هذا الاختلاف الأساسي يحدد السلوك أثناء التعيين والتمرير للدوال وإدارة الذاكرة. يتم نسخ struct بالكامل عند كل تعيين، بينما يُمرر class بالمرجع — يتم نسخ المؤشر فقط إلى الكائن في heap.
يطبق struct تلقائيًا دلالات القيمة — كل مثيل مستقل. تغيير خاصية struct ممكن فقط من خلال var، ويجب وضع علامة mutating على الدوال التي تعدل الخصائص. لا يدعم struct الوراثة ولكنه يمكنه الامتثال للبروتوكولات. تستخدم المكتبة القياسية لـ Swift struct لـ String و Array و Dictionary و Int و Bool — جميع الأنواع الأساسية هي أنواع قيمة.
struct User {
let id: Int
var name: String
mutating func updateName(_ newName: String) {
name = newName
}
}
var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// نسخة كاملة — مثيلات مستقلة
يُخزن class في heap ويدعم المساواة المرجعية. يمكن لمتغيرات متعددة الإشارة إلى نفس الكائن، التغييرات عبر مرجع واحد تكون مرئية عبر جميع المراجع الأخرى. يدعم class الوراثة، ويسمح بتجاوز الدوال (override)، وله deinit لتنظيف الموارد. في Swift، يُستخدم class بشكل أقل من struct — توصي Apple بالبدء باستخدام struct والتحول إلى class فقط عند الحاجة إلى الوراثة أو دلالات المرجع.
class Car {
let model: String
var speed: Int
init(model: String, speed: Int) {
self.model = model
self.speed = speed
}
func accelerate(_ amount: Int) {
speed += amount
}
}
let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// نفس المرجع — التغييرات مرئية في كل مكان
يعتمد الاختيار بين struct و class على دلالات البيانات. لـ الإعدادات ونماذج البيانات والحالات استخدم struct — النسخ أكثر أمانًا وقابلية للتنبؤ. للكائنات ذات الهوية (جلسة المستخدم، اتصال قاعدة البيانات) وعند الحاجة إلى الوراثة، استخدم class.
Actor هو نوع مرجعي يحمي حالته القابلة للتغيير من الوصول المتزامن. يضمن مترجم Swift أن متغيرات actor يمكن الوصول إليها فقط من نفس سياق actor — أي وصول خارجي يسبب خطأ في التجميع. هذا يمنع سباقات البيانات على مستوى اللغة دون أقفال يدوية.
جميع خصائص وطرق actor معزولة افتراضيًا. استدعاء طريقة actor من الخارج يتطلب await — يقوم Swift بتبديل التنفيذ إلى actor والعودة. يقوم actor بمزامنة الوصول بشكل تسلسلي: يتم وضع طلبات من مهام مختلفة في قائمة انتظار. هذا يضمن حالة متسقة داخل actor. يمكن استدعاء الطرق غير المعزولة (nonisolated) بدون await ولكن لا يمكنها الوصول إلى الخصائص القابلة للتغيير.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func withdraw(amount: Double) throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
}
nonisolated func accountInfo() -> String {
return "حساب بنكي"
}
}
let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — ذري، بدون حالة سباق
Actor مفيد لـ مديري الحالة والتخزين المؤقت وخدمات الشبكة حيث تقرأ وتكتب مهام متعددة البيانات بشكل متزامن. على عكس المزامنة اليدوية عبر NSLock أو DispatchQueue، يتم التحقق من actor في وقت التجميع — من المستحيل ارتكاب خطأ. تظهر أخطاء العزل في وقت التجميع وليس في وقت التشغيل.
Property Wrapper هو تعليق توضيحي يضيف سلوكًا إلى خاصية دون تغيير تعريفها. توفر SwiftUI و Combine مغلفات مدمجة لإدارة حالة العرض. يولد المترجم كودًا يغلف الوصول إلى الخاصية، مضيفًا منطق المراقبة أو المزامنة أو التحويل.
@Published يُستخدم في الفئات التي تمتثل لـ ObservableObject لنشر التغييرات تلقائيًا. يتلقى مشتركو Combine إشعارات عند كل تحديث. @State هو حالة عرض SwiftUI المحلية. يعيد SwiftUI رسم العرض تلقائيًا عند تغيير @State. @Binding ينشئ اتصالًا ثنائي الاتجاه بين العرض الأب والتابع دون امتلاك البيانات.
import SwiftUI
import Combine
@main
struct CounterApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct ContentView: View {
@State private var greeting: String = "مرحبًا"
@StateObject private var viewModel = CounterViewModel()
var body: some View {
VStack {
CounterDisplay(count: $viewModel.count)
Text(greeting)
Button("زيادة", action: viewModel.increment)
}
}
}
struct CounterDisplay: View {
@Binding var count: Int
var body: some View {
Text("العدد: \(count)")
}
}
يتم إنشاء Property Wrappers المخصصة باستخدام @propertyWrapper وهيكل مع حقل wrappedValue. هذا يسمح بإعادة استخدام المنطق: التحقق، التطبيع، تخزين UserDefaults. تقلل Property Wrappers بشكل كبير من الكود المتكرر وتجعل نية المطور واضحة من خلال التعليق التوضيحي.
@propertyWrapper
struct Clamped<T: Comparable> {
private var value: T
let range: ClosedRange<T>
init(wrappedValue: T, range: ClosedRange<T>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: T {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct Settings {
@Clamped(range: 0...100) var volume: Int = 50
}
var s = Settings()
s.volume = 150
// s.volume === 100 — القيمة محدودة تلقائيًا
Generics هي آلية قوية في Swift لكتابة كود مرن دون فقدان أمان الأنواع. تعمل الدوال والأنواع العامة مع أي نوع يفي بالقيود. البرمجة الموجهة بالبروتوكولات (POP) هي نموذج Swift حيث يحل protocol + extension محل الوراثة التقليدية للفئات، مما يوفر تكوين السلوك.
يسمح Swift بتقييد المعلمات العامة بالبروتوكولات (جمل where). بروتوكول مع نوع مرتبط هو المكافئ العام للبروتوكولات: يتم تحديد النوع الملموس في وقت الامتثال. هذا هو الأساس للمجموعات والخوارزميات الآمنة الأنواع. تستخدم المكتبة القياسية لـ Swift generics بنشاط: Array و Dictionary و Optional — كلها أنواع عامة.
protocol Cacheable {
associatedtype Key: Hashable
associatedtype Value
func get(_ key: Key) -> Value?
mutating func set(_ value: Value, for key: Key)
}
struct MemoryCache<K: Hashable, V>: Cacheable {
private var storage: [K: V] = [:]
typealias Key = K
typealias Value = V
func get(_ key: K) -> V? {
return storage[key]
}
mutating func set(_ value: V, for key: K) {
storage[key] = value
}
}
func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
for (index, item) in array.enumerated() {
if item == target {
return index
}
}
return nil
}
let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — طريقة عامة واحدة لجميع أنواع Equatable
Extension البروتوكول هي آلية رئيسية لـ POP. في extension يمكن توفير تنفيذ افتراضي لطرق البروتوكول. تحصل الأنواع الممتثلة للبروتوكول على هذا التنفيذ مجانًا ويمكنها تجاوزه عند الحاجة. هذا يسمح ببناء تسلسلات هرمية للسلوك دون وراثة الفئات — بديل للوراثة المتعددة، غير الموجودة في Swift.
protocol Drivable {
var speed: Double { get set }
func drive()
}
extension Drivable {
func drive() {
if speed > 0 {
print("القيادة بسرعة \(speed) كم/س")
}
}
}
struct Bicycle: Drivable {
var speed: Double = 15
}
struct Plane: Drivable {
var speed: Double = 900
func drive() {
print("الطيران بسرعة \(speed) كم/س")
}
}
let bike = Bicycle()
bike.drive() // "القيادة بسرعة 15.0 كم/س" — افتراضي
let plane = Plane()
plane.drive() // "الطيران بسرعة 900.0 كم/س" — تجاوز
POP مع extension يتيح التكوين من خلال بروتوكولات متعددة — يمكن لنوع أن يممتثل لعدة بروتوكولات، ويحصل على تطبيقاتها. هذا أكثر مرونة من وراثة الفئة الواحدة حيث يكون التسلسل الهرمي بأكمله ثابتًا. المكتبة القياسية لـ Swift مبنية على POP: CustomStringConvertible و Equatable و Hashable و Codable — كلها بروتوكولات مع extensions.
Enum في Swift هو تركيب أكثر قوة بشكل ملحوظ من اللغات الأخرى. يمكن أن تحتوي كل حالة على قيم مرتبطة — مجموعة عشوائية من البيانات من أي نوع. بالاقتران مع extension و pattern matching، يصبح enum الأساس لنمذجة الحالات ونتائج العمليات وآلات الحالة المحدودة.
القيم المرتبطة تسمح لكل حالة بتخزين بيانات فريدة. على سبيل المثال، حالة loading لا تحتوي على بيانات، success تحتوي على النتيجة، failure تحتوي على خطأ. Pattern Matching عبر switch يستخرج القيم المرتبطة ويعالج كل حالة. يتحقق المترجم من التغطية الكاملة (exhaustive switch) — مما يلغي الحالات المنسية.
enum NetworkResult<T> {
case loading
case success(T)
case failure(Error)
}
enum MediaFile {
case image(url: URL, width: Int, height: Int)
case video(url: URL, duration: Double)
case audio(url: URL, bitrate: Int)
}
func handle(_ result: NetworkResult<String>) {
switch result {
case .loading:
print("جارٍ التحميل...")
case .success(let data):
print("تم الاستلام: \(data)")
case .failure(let error):
print("خطأ: \(error.localizedDescription)")
}
}
let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)
if case .video(let url, let duration) = file {
print("فيديو \(url) مدته \(duration) ثانية")
}
يسمح Swift بإضافة الدوال والخصائص المحسوبة والامتثال للبروتوكولات عبر extension لـ enum. هذا لا يتطلب تغيير التعريف الأصلي — يبقى الكود نظيفًا ومعياريًا. يمكن للدوال في extension العمل مع القيم المرتبطة وحساب البيانات المشتقة وتنفيذ منطق الأعمال.
extension MediaFile {
var fileName: String {
switch self {
case .image(let url, _, _),
.video(let url, _),
.audio(let url, _):
return url.lastPathComponent
}
}
var description: String {
switch self {
case .image(_, let w, let h):
return "صورة \(w)x\(h)"
case .video(_, let d):
return "فيديو \(d) ث"
case .audio(_, let b):
return "صوت \(b) كيلوبت/ث"
}
}
}
extension MediaFile: Equatable {
static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
return lhs.fileName == rhs.fileName
}
}
let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "فيديو 120.0s"
Enum مع القيم المرتبطة + extension هو بديل للفئات المختومة من Kotlin أو الاتحادات المميزة من لغات أخرى. تستخدم Swift هذه التركيبة لـ Result (نوع قياسي في المكتبة القياسية)، و Optional (Optional هو enum مع .none و .some) ومعالجة الحالة في SwiftUI. يضمن Pattern Matching معالجة جميع الحالات — الأخطاء في وقت التشغيل مستحيلة.
الأسئلة الشائعة
Struct — نوع قيمة، يُنسخ عند التعيين، لا يدعم الوراثة. Class — نوع مرجعي، يُمرر بالمرجع، يدعم الوراثة و deinit والتحقق من الهوية (===). توصي Apple بالبدء بـ struct والتحول إلى class فقط عند الحاجة إلى دلالات المرجع.
Actor — نوع مرجعي آمن للخيوط. يمنع المترجم الوصول المتزامن إلى الحالة القابلة للتغيير من سياقات مختلفة. استخدم Actor لمديري الحالة والتخزين المؤقت والخدمات التي تصل إليها مهام متعددة. استدعاء طرق actor يتطلب await.
الرئيسية: @State — حالة العرض المحلية؛ @Binding — اتصال بالبيانات دون امتلاك؛ @Published — نشر التغييرات في ObservableObject؛ @StateObject — امتلاك ObservableObject؛ @EnvironmentObject — تبعية من البيئة. يتم إنشاء المغلفات المخصصة عبر @propertyWrapper.
POP — نموذج حيث يحدد protocol + extension السلوك بدلاً من وراثة الفئة. يمتثل نوع لعدة بروتوكولات، ويحصل على تطبيقاتها مجانًا. هذا تكوين بدلاً من الوراثة — أكثر مرونة وأمانًا. المكتبة القياسية لـ Swift (Equatable, Codable) مبنية على POP.
Enum في Swift يدعم القيم المرتبطة — يمكن لكل حالة تخزين بيانات عشوائية من أنواع مختلفة. بالاقتران مع pattern matching (switch)، يوفر هذا نموذجًا قويًا للحالات والأخطاء والتصميم المدفوع بالمجال. Optional و Result هما enum قياسيان في Swift مع قيم مرتبطة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.