استخدام البروكسي في Swift: لماذا تحتاجه وكيف تُطبّقه
إذا كنت تُطوّر تطبيقات iOS أو macOS تعتمد على جلب البيانات من واجهات برمجية (APIs) أو مواقع ويب، فستواجه حتمًا قيودًا جغرافية أو حظرًا لعناوين IP الخاصة بمراكز البيانات. استخدام البروكسي في Swift عبر URLSession هو الحل العملي لتجاوز هذه القيود والحفاظ على موثوقية طلباتك. في هذا الدليل، سنغطي كل شيء من التكوين الأساسي وحتى أنماط الإنتاج المتقدمة مثل إعادة المحاولة والبرمجة غير المتزامنة.
السياق التقني: لماذا يوجد هذا التحدي
يعتمد URLSession على إطار CFNetwork من Apple، والذي يوفّر قاموس connectionProxyDictionary داخل URLSessionConfiguration لتكوين إعدادات البروكسي. المشكلة أن مفاتيح المصادقة مثل kCFProxyUsernameKey و kCFProxyPasswordKey غير موثوقة في URLSession على العديد من إصدارات iOS و macOS، مما يستلزم حلولاً بديلة للمصادقة. يمكنك مراجعة توثيق Apple الرسمي لفهم القيود بشكل أعمق.
أنواع البروكسي: أيهما تختار؟
قبل الغوص في الكود، من المهم فهم الفروقات بين أنواع البروكسي الرئيسية:
| النوع | السرعة | الموثوقية | الاستخدام الأمثل |
|---|---|---|---|
| سكني (Residential) | متوسط (~200ms) | عالية جدًا — يبدو كمستخدم حقيقي | تتبّع SERP، مراقبة الأسعار، تجاوز الحظر الجغرافي |
| مركز بيانات (Datacenter) | سريع جدًا (~50ms) | متوسطة — يُكتشف بسهولة | الطلبات عالية الأداء على واجهات لا تحظر |
| محمول (Mobile) | بطيء نسبيًا (~300ms) | عالية جدًا — أعلى ثقة | تطبيقات الهاتف المحمول، شبكات اجتماعية |
البروكسي السكني هو الخيار الأفضل لجلب البيانات من نقاط النهاية التي تحظر عناوين مراكز البيانات أو تقفل المحتوى حسب المنطقة الجغرافية. توفّر ProxyHat شبكة سكنية تغطي أكثر من 150 دولة — يمكنك استكشاف المواقع المتاحة لمعرفة التغطية.
تكوين البروكسي الأساسي عبر connectionProxyDictionary
أول خطوة هي إنشاء URLSessionConfiguration مع قاموس البروكسي الذي يُحدّد عنوان البوابة والمنفذ. سنستخدم gate.proxyhat.com على المنفذ 8080 لبروتوكول HTTP و HTTPS:
import Foundation
func createProxySession() -> URLSession {
let config = URLSessionConfiguration.default
config.connectionProxyDictionary = [
kCFNetworkProxiesHTTPEnable: 1,
kCFNetworkProxiesHTTPProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPPort: 8080,
kCFNetworkProxiesHTTPSEnable: 1,
kCFNetworkProxiesHTTPSProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPSPort: 8080
]
return URLSession(configuration: config)
}
هذا التكوين يوجّه جميع طلبات URLSession عبر بوابة ProxyHat. لاحظ أننا فعّلنا البروكسي لكل من HTTP و HTTPS، وهو ما تحتاجه معظم التطبيقات التي تتعامل مع واجهات برمجية مختلطة.
المصادقة والاستهداف الجغرافي: حل Proxy-Authorization
بما أن مفاتيح kCFProxyUsernameKey و kCFProxyPasswordKey غير موثوقة في URLSession، هناك طريقتان للمصادقة:
- ترويسة Proxy-Authorization: إضافة ترويسة
Basicيدويًا إلى الطلب. - URLSessionDelegate: تنفيذ
urlSession(_:didReceive:)للتعامل مع تحدي 407.
الطريقة الأولى أبسط وأكثر موثوقية. يمكنك ترميز الاستهداف الجغرافي والجلسة مباشرة في اسم المستخدم:
import Foundation
func makeRequest(
url: URL,
country: String,
city: String,
session: String
) async throws -> Data {
let username = "user-country-\(country)-city-\(city)-session-\(session)"
let password = "your_password"
let token = Data("\(username):\(password)".utf8).base64EncodedString()
var request = URLRequest(url: url)
request.setValue("Basic \(token)", forHTTPHeaderField: "Proxy-Authorization")
let config = URLSessionConfiguration.default
config.connectionProxyDictionary = [
kCFNetworkProxiesHTTPEnable: 1,
kCFNetworkProxiesHTTPProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPPort: 8080,
kCFNetworkProxiesHTTPSEnable: 1,
kCFNetworkProxiesHTTPSProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPSPort: 8080
]
let urlSession = URLSession(configuration: config)
let (data, response) = try await urlSession.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw URLError(.badServerResponse)
}
return data
}
في المثال أعلاه، اسم المستخدم user-country-US-city-newyork-session-abc123 يُحدّد الدولة والمدينة ومعرّف الجلسة. هذا يسمح بطلبات لاصقة (sticky) تحافظ على نفس عنوان IP طوال الجلسة.
الطريقة البديلة: URLSessionDelegate لتحدي 407
إذا كنت تفضّل التعامل مع التحدي بشكل تفاعلي، استخدم URLSessionDelegate:
import Foundation
class ProxyAuthDelegate: NSObject, URLSessionDelegate {
let username: String
let password: String
init(username: String, password: String) {
self.username = username
self.password = password
}
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition,
URLCredential?) -> Void
) {
guard challenge.protectionSpace.authenticationMethod ==
URLAuthenticationMethod.proxyAuthentication else {
completionHandler(.performDefaultHandling, nil)
return
}
let credential = URLCredential(
user: username,
password: password,
persistence: .forSession
)
completionHandler(.useCredential, credential)
}
}
تكوين SOCKS5 على المنفذ 1080
يدعم connectionProxyDictionary أيضًا بروتوكول SOCKS5، والذي قد يكون مفيدًا للتطبيقات التي تحتاج إلى نفق أكثر شمولًا. استخدم المنفذ 1080 مع مفاتيح kCFNetworkProxiesSOCKS*:
import Foundation
func createSOCKS5Session() -> URLSession {
let config = URLSessionConfiguration.default
config.connectionProxyDictionary = [
kCFNetworkProxiesSOCKSEnable: 1,
kCFNetworkProxiesSOCKSProxy: "gate.proxyhat.com",
kCFNetworkProxiesSOCKSPort: 1080
]
return URLSession(configuration: config)
}
ملاحظة: مع SOCKS5، ستحتاج أيضًا إلى ترويسة Proxy-Authorization أو URLSessionDelegate للمصادقة، تمامًا كما في HTTP. راجع توثيق MDN لترويسة Proxy-Authorization لفهم البروتوكول بشكل أفضل.
مثال عملي: جلب البيانات بشكل متزامن مع async/await و TaskGroup
في التطبيقات الحقيقية، غالبًا ما تحتاج إلى جلب بيانات من عدة مصادر بشكل متزامن. البروكسي السكني ضروري هنا لأن نقاط النهاية التي تحظر عناوين مراكز البيانات ستُعيد خطأ 403 أو 429. إليك مثال كامل يستخدم async/await و TaskGroup مع فك ترميز Codable:
import Foundation
struct Product: Codable {
let id: Int
let name: String
let price: Double
}
func fetchProducts(urls: [URL], country: String) async throws -> [Product] {
let sessionId = String(UUID().uuidString.prefix(8))
let username = "user-country-\(country)-session-\(sessionId)"
let password = "your_password"
let token = Data("\(username):\(password)".utf8).base64EncodedString()
let config = URLSessionConfiguration.default
config.connectionProxyDictionary = [
kCFNetworkProxiesHTTPEnable: 1,
kCFNetworkProxiesHTTPProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPPort: 8080,
kCFNetworkProxiesHTTPSEnable: 1,
kCFNetworkProxiesHTTPSProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPSPort: 8080
]
config.timeoutIntervalForRequest = 30
config.timeoutIntervalForResource = 60
let delegate = ProxyAuthDelegate(username: username, password: password)
let session = URLSession(configuration: config, delegate: delegate, delegateQueue: nil)
return try await withThrowingTaskGroup(of: Product?.self) { group in
for url in urls {
group.addTask {
do {
var request = URLRequest(url: url)
request.setValue("Basic \(token)", forHTTPHeaderField: "Proxy-Authorization")
let (data, response) = try await session.data(for: request)
guard let http = response as? HTTPURLResponse,
(200...299).contains(http.statusCode) else {
return nil
}
return try JSONDecoder().decode(Product.self, from: data)
} catch {
return nil
}
}
}
var results: [Product] = []
for try await product in group {
if let p = product {
results.append(p)
}
}
return results
}
}
هذا النمط يسمح بـ 100 جلسة متزامنة أو أكثر، مع الحفاظ على جلسة لاصقة لكل مجموعة طلبات عبر معرّف الجلسة الفريد. هذا مهم جدًا في سيناريوهات مثل جلب البيانات من الويب و تتبّع نتائج البحث.
نصائح الإنتاج: TLS وإعادة المحاولة و ATS
التعامل مع TLS عبر URLSessionDelegate
عند استخدام البروكسي، قد تحتاج إلى التحقق من شهادات TLS بشكل صريح. إليك URLSessionDelegate يتعامل مع ServerTrust:
import Foundation
class TLSPinningDelegate: NSObject, URLSessionDelegate {
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition,
URLCredential?) -> Void
) {
guard challenge.protectionSpace.authenticationMethod ==
NSURLAuthenticationMethodServerTrust else {
completionHandler(.performDefaultHandling, nil)
return
}
if let trust = challenge.protectionSpace.serverTrust {
completionHandler(.useCredential, URLCredential(trust: trust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
إعادة المحاولة مع التراجع الأسي
الطلبات عبر البروكسي قد تفشل لأسباب عابرة. تنفيذ إعادة المحاولة مع التراجع الأسي (exponential backoff) ضروري. وفقًا لـ RFC 7231، رمز الحالة 429 يعني أن العميل يرسل طلبات أكثر من المسموح. إليك تنفيذ عملي:
import Foundation
func fetchWithRetry(url: URL, maxRetries: Int = 3) async throws -> Data {
var delay: UInt64 = 1_000_000_000 // 1 ثانية بالنانو ثانية
for attempt in 0..<maxRetries {
do {
let config = URLSessionConfiguration.default
config.connectionProxyDictionary = [
kCFNetworkProxiesHTTPEnable: 1,
kCFNetworkProxiesHTTPProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPPort: 8080,
kCFNetworkProxiesHTTPSEnable: 1,
kCFNetworkProxiesHTTPSProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPSPort: 8080
]
var request = URLRequest(url: url)
let token = Data("user-country-US:your_password".utf8).base64EncodedString()
request.setValue("Basic \(token)", forHTTPHeaderField: "Proxy-Authorization")
let session = URLSession(configuration: config)
let (data, response) = try await session.data(for: request)
if let http = response as? HTTPURLResponse {
if (200...299).contains(http.statusCode) {
return data
}
if http.statusCode == 429 || http.statusCode >= 500 {
throw URLError(.badServerResponse)
}
}
return data
} catch {
if attempt == maxRetries - 1 {
throw error
}
try await Task.sleep(nanoseconds: delay)
delay *= 2
}
}
throw URLError(.timedOut)
}
أمان نقل التطبيقات (ATS) وخصوصية الجهاز
يفرض iOS و macOS App Transport Security (ATS) افتراضيًا، والذي يتطلب TLS 1.2 أو أعلى. عند استخدام بروكسي HTTP على المنفذ 8080، قد تحتاج إلى إضافة استثناء في Info.plist إذا كان الهدف النهائي يستخدم HTTP غير مشفّر. ومع ذلك، إذا كان الهدف النهائي يستخدم HTTPS، فلن تحتاج إلى أي استثناء لأن البروكسي يُمرّر الاتصال المشفّر كما هو.
من ناحية الخصوصية، تذكّر أن URLSession يخزّن ملفات تعريف الارتباط وبيانات الاعتماد محليًا. استخدم URLSessionConfiguration.ephemeral إذا كنت تريد تجنّب أي تخزين على الجهاز:
let config = URLSessionConfiguration.ephemeral
config.connectionProxyDictionary = [
kCFNetworkProxiesHTTPEnable: 1,
kCFNetworkProxiesHTTPProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPPort: 8080,
kCFNetworkProxiesHTTPSEnable: 1,
kCFNetworkProxiesHTTPSProxy: "gate.proxyhat.com",
kCFNetworkProxiesHTTPSPort: 8080
]
الاعتبارات الأخلاقية والقانونية
استخدام البروكسي لجلب البيانات العامة أمر قانوني في معظم الولايات القضائية، لكن يجب الالتزام بالحدود التالية:
- قانون الاحتيال وإساءة استخدام الكمبيوتر (CFAA) في الولايات المتحدة يحظر الوصول غير المصرّح به إلى الأنظمة المحمية. تجنّب تجاوز مصادقة أو اختراق أنظمة الحماية.
- اللائحة العامة لحماية البيانات (GDPR) في الاتحاد الأوروبي تحكم معالجة البيانات الشخصية. إذا كنت تجمع بيانات مستخدمين أوروبيين، تأكّد من الامتثال.
- إرشادات متجر التطبيقات (App Store Guidelines) تتطلب أن تطبيقك لا يجمع بيانات بدون موافقة المستخدم. وضّح سياسة الخصوصية بوضوح.
- يفضّل دائمًا استخدام واجهات برمجية رسمية عند توفّرها. راجع توثيق ProxyHat لمعرفة المزيد عن الاستخدام المسؤول.
استخدام SDK الخاص بـ ProxyHat
بينما يغطي هذا الدليل تكوين URLSession مباشرة، توفّر ProxyHat أيضًا SDKs لـ Python و Node.js تستخدم نفس البوابة gate.proxyhat.com. إذا كان مشروعك يتضمن خادمًا خلفيًا (backend)، يمكنك استخدام SDK على الخادم وترك تطبيق iOS يتصل بخادمك مباشرة. هذا النمط شائع لأنه يبسّط إدارة البروكسي ويقلّل من الحمل على التطبيق. يمكنك الاطّلاع على خطط الأسعار لاختيار الحزمة المناسبة.
نقاط رئيسية
- استخدم
connectionProxyDictionaryمعkCFNetworkProxiesHTTPEnableوkCFNetworkProxiesHTTPSEnableلتكوين البروكسي فيURLSession.- المصادقة عبر ترويسة
Proxy-Authorization: Basicأكثر موثوقية من مفاتيحkCFProxyUsernameKeyعلىURLSession.- الاستهداف الجغرافي يُرمّز في اسم المستخدم:
user-country-US-city-newyork-session-abc123.- البروكسي السكني ضروري لنقاط النهاية التي تحظر عناوين مراكز البيانات أو تقفل المحتوى حسب المنطقة.
- استخدم
TaskGroupمعasync/awaitللتزامن، وطبّق التراجع الأسي لإعادة المحاولة.- البروكسي السكني يوفّر نسبة نجاح تتجاوز 99% مقارنة بـ 60-70% لبروكسي مراكز البيانات في المواقع المحمية.
- احترم CFAA و GDPR وإرشادات متجر التطبيقات، وفضّل الواجهات البرمجية الرسمية عند توفّرها.
الأسئلة الشائعة
ما هو استخدام البروكسي في Swift؟
استخدام البروكسي في Swift يعني تكوين URLSession لتوجيه طلبات HTTP و HTTPS عبر خادم وسيط (مثل gate.proxyhat.com) بدلاً من الاتصال المباشر بالخادم الهدف. يُستخدم لتجاوز الحظر الجغرافي، إخفاء عنوان IP الأصلي، وجلب البيانات من نقاط نهاية تحظر عناوين مراكز البيانات.
لماذا يهم استخدام البروكسي في Swift لمستخدمي البروكسي؟
لأن URLSession لا يدعم المصادقة على البروكسي بشكل موثوق عبر مفاتيح القاموس القياسية. فهم كيفية إعداد connectionProxyDictionary واستخدام ترويسة Proxy-Authorization أو URLSessionDelegate ضروري لضمان عمل الطلبات بشكل صحيح مع بروكسيات تتطلب مصادقة.
أي نوع بروكسي يعمل بشكل أفضل مع URLSession في Swift؟
البروكسي السكني هو الأفضل لمعظم حالات الاستخدام على iOS و macOS، لأنه يوفر عناوين IP تبدو كمستخدمين حقيقيين، مما يقلل من احتمال الحظر. البروكسي عبر مركز البيانات أسرع لكنه يُكتشف بسهولة. البروكسي المحمول يوفّر أعلى ثقة لكنه أبطأ. اختر حسب حالة الاستخدام: السكني للتجميع، ومركز البيانات للأداء العالي.
كيف تتجنّب الحظر عند تنفيذ استخدام البروكسي في Swift؟
استخدم جلسات لاصقة (sticky sessions) مع معرّف جلسة فريد لكل مهمة، وطبّق التراجع الأسي عند استلام خطأ 429 أو 5xx، ووزّع الطلبات على فترات زمنية عشوائية، واستخدم الاستهداف الجغرافي المناسب. تجنّب إرسال أكثر من 5 طلبات في الثانية من نفس الجلسة، وفضّل البروكسي السكني للمواقع المحمية.
هل يعمل SOCKS5 مع URLSession على iOS؟
نعم، يمكنك استخدام مفاتيح kCFNetworkProxiesSOCKSEnable و kCFNetworkProxiesSOCKSProxy و kCFNetworkProxiesSOCKSPort في connectionProxyDictionary. استخدم المنفذ 1080 لـ SOCKS5 مع gate.proxyhat.com. ستحتاج أيضًا إلى المصادقة عبر ترويسة Proxy-Authorization أو URLSessionDelegate.






