هذه المقالة هي تحديث للمقال
الحصول على البيانات عن بعد في iOS ، كتب في نوفمبر 2015 باستخدام Objective-C وبالتالي عفا عليه الزمن أخلاقيا. الآن ، سيتم إعطاء الرمز الذي تمت إعادة كتابته إلى Swift 3 و iOS 10 (أحدث إصدار هو Swift 4.1 و iOS 11 ، ولكن جهاز الكمبيوتر الخاص بي لم يعد يدعمهما).
نظرية موجزة
تنسيق عنوان Url
http://www.google.com/?q=Hello&safe=off
- http هو بروتوكول يحدد بأي معيار يتم تقديم الطلب. المزيد من الخيارات: https ، بروتوكول نقل الملفات ، ملف
www.google.com
- اسم المجال- / - دليل حيث توجد الموارد التي نحتاجها.
- بعد علامة الاستفهام (؟) اذهب إلى المعلمات q = Hello & safe = off. تتكون من أزواج القيمة الرئيسية.
- يشير الطلب أيضًا إلى طريقة تخبرنا عن كيفية تعامل الخادم مع هذا الطلب. بشكل افتراضي ، هذه طريقة GET.
يمكن قراءة عنوان url هذا من المثال على النحو التالي: يتم إرسال طلب http مع طريقة GET إلى google.com ، في الدليل الجذر / ، مع معلمتين q مع Hello وآمنة مع إيقاف التشغيل.
رأس http
يقوم المستعرض بتحويل سلسلة عنوان url إلى رأس ونص الطلب. بالنسبة لطلب http ، يكون النص فارغًا ، والعنوان كما يلي
GET /?q=Hello&safe=off HTTP/1.1 Host: google.com Content-Length: 133 // //
مخطط طلب الخادم
أولاً ، يتم إنشاء طلب ، ثم يتم إنشاء اتصال وإرسال طلب وتلقي استجابة.
مندوبو الجلسة
يتم تنفيذ جميع عمليات واجهة المستخدم (المرتبطة بواجهة المستخدم) في سلسلة المحادثات الرئيسية. لا يمكنك فقط أخذ هذا الموضوع وإيقافه أثناء إجراء نوع من العمليات التي تتطلب موارد مكثفة. لذلك ، كان أحد الحلول لهذه المشكلة هو إنشاء المندوبين. وبالتالي ، تصبح العمليات غير متزامنة ، ويتم تشغيل الخيط الرئيسي بدون توقف. عند اكتمال العملية اللازمة ، سيتم استدعاء طريقة التفويض المقابلة. الحل الثاني هو إنشاء خيط تنفيذ جديد.
كما هو الحال في الكتاب الأصلي ، نستخدم مندوبًا بحيث يتم تقسيم العمليات بشكل أوضح بين الأساليب. على الرغم من أن الرمز أكثر إحكاما من خلال الكتل.
وصف أنواع مفوضي الجلسة
نستخدم
NSURLSessionDownloadDelegate وننفذ طريقة
URLSession: downloadTask: didFinishDownloadingToURL :. هذا ، في الواقع ، نقوم بتنزيل البيانات بنكتة في مخزن مؤقت ، وعندما يكتمل التنزيل ، فإننا نسمي طريقة المفوض للمعالجة.
الانتقال إلى التيار الرئيسي
لا يتم تحميل البيانات إلى وحدة التخزين المؤقتة في الدفق الرئيسي ، ولكن من أجل استخدام هذه البيانات لتغيير واجهة المستخدم ، سننتقل إلى الدفق الرئيسي.
إغلاق هارب (@ escaping)
نظرًا لأنه بسبب تنفيذ الكود ، فإن الإغلاق الذي نمرره إلى طريقة تحميل البيانات من عنوان url سيبقى على قيد الحياة الطريقة نفسها ، بالنسبة لـ Swift 3 ، من الضروري تعيينه صراحة @ escaping ، وجعله غير مملوك بحيث لا يتم التقاط الرابط الذاتي والاحتفاظ به في هذا الإغلاق. لكن هذه هي الفروق الدقيقة في تنفيذ لغة Swift نفسها ، وليست تقنية تلقي البيانات بواسطة API.
عمليات إعادة التوجيه (عمليات إعادة التوجيه)
في بعض الحالات ، تحدث عمليات إعادة التوجيه. على سبيل المثال ، إذا كان لدينا بعض عناوين URL القصيرة ، فعندما ندخلها في شريط البحث في المتصفح ، ينتقل المتصفح أولاً إلى الخادم ، حيث يتم فك تشفير عنوان URL القصير وإرساله إلينا ، ثم ننتقل إلى الخادم الهدف باستخدام عنوان URL الكامل هذا. إذا لزم الأمر ، يمكننا التحكم في عمليات إعادة التوجيه هذه باستخدام
NSURLSessionTaskDelegate ، ولكن بشكل افتراضي ، تتعامل
NSURLSession مع جميع التفاصيل.
مخطط التسلسل
التسلسل هو عملية نقل البيانات من نوع تخزين إلى آخر ، دون فقدان المحتوى. على سبيل المثال ، يتم تخزين البيانات في شكل ثنائي لاستهلاك مساحة أقل ، وعند إرسالها عبر شبكة ، يتم تحويلها إلى تنسيق JSON (JavaScript Object Notation) عالمي ، والذي نقوم بالفعل بفك تشفيره وترجمته إلى كائنات في بيئة البرمجة الخاصة بنا.
مثال على JSON:
{ "name": "Martin Conte Mac Donell", "age": 29, "username": "fz" }
تشير الأقواس المتعرجة إلى القاموس ، ويتم تمثيل الكائنات داخل القاموس بأزواج قيمة المفتاح.
API (واجهة برمجة التطبيقات)
في حالتنا ، يتم تمثيل واجهة برمجة التطبيقات (API) بالعنوان الذي سنتلقى منه النكات العشوائية وتنسيقات استجابة JSON ، والتي نحتاج إلى تحليلها في هياكل ملائمة للتلاعب
http:
مثال لواجهة برمجة تطبيقات icndb:
{ "type": "success", "value": { "id": 201, "joke": "Chuck Norris was what Willis was talkin' about" } }
ممارسة الآن
يتم تنفيذ المشروع بأكمله ، مثل آخر مرة ، في التعليمات البرمجية ، دون استخدام لوحة مصورة. كل التعليمات البرمجية مكتوبة في 3 ملفات:
AppDelegate.swift و
MainViewController.swift و
HTTPCommunication.swift . يحتوي AppDelegate.swift على التكوين العام للتطبيق. يقوم HTTPCommunication.swift بتكوين الاتصال (طلب ، جلسة) ويستقبل البيانات. في MainViewController.swift ، يتم إجراء تسلسل لهذه البيانات للإخراج ، وتحتوي أيضًا على رمز واجهة المستخدم.
إنشاء مشروع فارغ. من أجل البساطة ، نكتب تطبيقًا فقط لـ
iPhone . نحذف
ViewController.swift و
Main.storyboard و
Info.plist نحذف أيضًا الرابط إلى لوحة العمل ، أي
اسم قاعدة ملف القصة المصورة الرئيسي - String - Main .
بشكل افتراضي ، يحظر
أمان نقل التطبيقات على iOS التنزيلات من الإنترنت باستخدام http العادي (وليس https) ، لذلك نقوم بإجراء تغييرات على
Info.plist ، كما هو موضح أدناه. للقيام بذلك ، افتح
Info.plist كرمز
مصدر ، ثم أضف التعليمات البرمجية التالية:
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <false/> <key>NSExceptionDomains</key> <dict> <key>api.icndb.com</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSIncludesSubdomains</key> <true/> </dict> </dict> </dict>
نحظر بشكل افتراضي التنزيلات العشوائية: مفتاح NSAllowsArbitraryLoads خطأ. ولكننا نضيف كاستثناء مجالنا مع النكات وجميع المجالات الفرعية: القيم الرئيسية NSExceptionDomains.
الآن في
AppDelegate.swift نعيد كتابة
التطبيق (_: didFinishLaunchingWithOptions :) على النحو التالي:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds)
قم
بإنشاء ملف
HTTPCommunication.swift . ونكتب الرمز التالي فيه.
import UIKit // NSObject, (conform) NSObjectProtocol, // URLSessionDownloadDelegate , // , . class HTTPCommunication: NSObject { // completionHandler - , // // . var completionHandler: ((Data) -> Void)! // retrieveURL(_: completionHandler:) // url func retrieveURL(_ url: URL, completionHandler: @escaping ((Data) -> Void)) { } } // , NSObject // (conforms) URLSessionDownloadDelegate, // // . extension HTTPCommunication: URLSessionDownloadDelegate { // // . func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { } }
الآن سنكتب رمز هذه الوظائف.
انسخ كود
الاسترجاع URL (_ url:، finishHandler :) // , // @escaping. func retrieveURL(_ url: URL, completionHandler: @escaping ((Data) -> Void)) { self.completionHandler = completionHandler let request: URLRequest = URLRequest(url: url) let session: URLSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) let task: URLSessionDownloadTask = session.downloadTask(with: request) // , // . task.resume() }
انسخ رمز
وظيفة Func Code (_ الجلسة: ، downloadTask: ، didFinishDownloadingTo :) func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { do { // // . // , try, // do {} catch {} let data: Data = try Data(contentsOf: location) // completionHandler . // , // , // , . DispatchQueue.main.async(execute: { self.completionHandler(data) }) } catch { print("Can't get data from location.") } }
نقوم بإنشاء ملف
MainViewController.swift ونسخ الشفرة التالية ، التي تنشئ الواجهة اللازمة:
import UIKit class MainViewController: UIViewController { lazy var jokeLabel: UILabel = { let label: UILabel = UILabel(frame: CGRect.zero) label.lineBreakMode = .byWordWrapping label.textAlignment = .center label.numberOfLines = 0 label.font = UIFont.systemFont(ofSize: 16) label.sizeToFit() self.view.addSubview(label) return label }() // . var jokeID: Int = 0 // ActivityView , // , . lazy var activityView: UIActivityIndicatorView = { let activityView: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray) activityView.hidesWhenStopped = true activityView.startAnimating() view.addSubview(activityView) return activityView }() lazy var stackView: UIStackView = { let mainStackView: UIStackView = UIStackView(arrangedSubviews: [self.jokeLabel]) // mainStackView.spacing = 50 mainStackView.axis = .vertical mainStackView.distribution = .fillEqually self.view.addSubview(mainStackView) return mainStackView }() override func viewDidLoad() { super.viewDidLoad() self.title = "Chuck Norris Jokes" // stackView activityView, // . // stackView // label. self.configConstraints() // (E.2) // // . self.retrieveRandomJokes() // (E.3) } func retrieveRandomJokes() { } } extension MainViewController { func configConstraints() { // autoresizingMask (constraints) // false, // self.stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ self.stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), self.stackView.leadingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.leadingAnchor), self.stackView.trailingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.trailingAnchor) ]) self.activityView.translatesAutoresizingMaskIntoConstraints = false // (constraints) activityView, // label: X Y // label X Y. NSLayoutConstraint.activate([ self.activityView.centerXAnchor.constraint(equalTo: self.jokeLabel.centerXAnchor), self.activityView.centerYAnchor.constraint(equalTo: self.jokeLabel.centerYAnchor) ]) } }
اكتشفنا الواجهة ، يمكنك الآن ملء الوظائف.
هنا هو رمز
reveevRandomJokes () func retrieveRandomJokes() { let http: HTTPCommunication = HTTPCommunication() // url , force unwrap // url , let url: URL = URL(string: "http://api.icndb.com/jokes/random")! http.retrieveURL(url) { // self , weak self [weak self] (data) -> Void in // json // , , . // json , // . guard let json = String(data: data, encoding: String.Encoding.utf8) else { return } // : JSON: { "type": "success", "value": // { "id": 391, "joke": "TNT was originally developed by Chuck // Norris to cure indigestion.", "categories": [] } } print("JSON: ", json) do { let jsonObjectAny: Any = try JSONSerialization.jsonObject(with: data, options: []) // , Any // , . guard let jsonObject = jsonObjectAny as? [String: Any], let value = jsonObject["value"] as? [String: Any], let id = value["id"] as? Int, let joke = value["joke"] as? String else { return } // , // . self.activityView.stopAnimating() self.jokeID = id self.jokeLabel.text = joke } catch { print("Can't serialize data.") } } }
الآن قم بتشغيل التطبيق والحصول على النتيجة التالية.
بينما ننتظر نكتة من الموقع.

وأخيرًا ، يتم تحميل النكتة وعرضها.

في المقالة التالية ، سنلقي نظرة على الجزء الثاني من التطبيق الذي تمت إعادة كتابته بسرعة ، والذي يسمح لك بتلقي النكات الجديدة دون إعادة تشغيل البرنامج ، بالإضافة إلى التصويت للنكات.