
قبل بضع سنوات كنت أعمل في مشروع كان من المفترض أن يكون له ميزات مماثلة مع Snapchat وفي الوقت نفسه تطبيق تشبه Instagram المواضيعي. تم تطوير المشروع فقط لمنصة واحدة - دائرة الرقابة الداخلية. بطبيعة الحال ، أثناء تطوير الميزة الرئيسية - نشر الصور ، أراد العميل فجأة إضافة القدرة على تحديد المكان الذي تم التقاط الصورة فيه. في معظم الحالات ، يتذكر الكثيرون على الفور واجهة برمجة تطبيقات الأماكن من Google و Facebook ، ولكن العميل لم يكن راضيًا عن حقيقة أن هذه الحلول لها حدود معينة. لذلك ، بعد مراجعة إضافية ، تم العثور على بديل آخر من Apple -
CLPlacemark ، والذي كان مجانيًا ، وفي ذلك الوقت في الوثائق لم يكن هناك ذكر لحدود الاستخدام اليومي. نظرًا لأن تطوير الأنظمة الأساسية الأخرى لم يكن مخططًا له أصلاً على الإطلاق ، فقد بدا هذا كخيار مناسب جدًا.
توضح وثائق Apple أن CLPlacemark يمكنها تقديم الكثير من التفاصيل حول هذه النقطة ، وأن
CLGeocoder لديها أيضًا
طريقة تتيح لك بسهولة إرجاع صفيف CLPlacemark بالبيانات المطلوبة حسب اسم الموقع. كما اتضح ، كل شيء لا يعمل وردية جدا.
بدا مصدر الكود مثل هذا:
import CoreLocation let geocoder = CLGeocoder() func findPlace(name: String) { geocoder.geocodeAddressString(name) { placemarks, error in print(placemarks) } } findPlace(name: “New”)
في هذا السيناريو البسيط ، يعرض الكود الجغرافي دومًا صفيف CLPlacemark ، لكن المهم هو أن هذه المصفوفة لا تحتوي أبدًا على أكثر من عنصر واحد. نتيجةً لذلك ، على الشاشة بأكملها ، حيث كان من المتوقع وجود قائمة كبيرة من المواضع مثل: نيويورك ، ونيوزيلندا ، ونيو بالانس ستور ، وما إلى ذلك ، تلقيت عنصرًا واحدًا فقط لا يرتبط دائمًا بما أدخلته.
بعد صراع غير ناجح مع CLGeocoder ، أخبرني زميلي: "ألا تفكر في محاولة البحث ، فربما تملك MapKit فرصة مماثلة؟" كما اتضح فيما بعد ، يحتوي MapKit على
MKLocalSearch ، حيث يمكننا الحصول على مجموعة
MKPlacemark ، الموروثة من CLPlacemark. بدا أن الدائرة تعمل بشكل جيد ، لذلك بدأت في تجربة هذا النهج:
import MapKit let request = MKLocalSearchRequest() var localSearch: MKLocalSearch? func findPlace(name: String) { request.naturalLanguageQuery = text localSearch = MKLocalSearch(request: request) localSearch?.start { (searchResponse, _) in guard let items = searchResponse?.mapItems else { return } print(items) } } findPlace(name: “New)
في هذه الحالة ، تلقيت صفيفًا به 10 عناصر CLPlacemark كرد فعل. هذه النتيجة بدت أكثر قبولا ، لأنه نتيجة لذلك ، تم تقديم قائمة كافية. ولكن بعيدًا عن دائمًا ، عندما تبدأ في إدخال اسم أي من المؤسسات الموجودة في مكان قريب ، فإنها تظهر على الفور النتيجة المرجوة. على سبيل المثال ، دومينوز بيتزا بجواري. أود ذلك عندما أقوم بإدخال مثل هذا الاستعلام في سطر ، أولاً وقبل كل شيء ، يجب أن أحصل على المؤسسات على مقربة مني قدر الإمكان.
بدأت الدراسة على أساس تشكيل الصفيف وكيف يمكن تحسينه. لقد حددت العديد من الأشياء التي يمكن أن تؤثر على اختيار المعلمات:
- عنوان IP الذي تم إرسال الطلب منه إلى Apple. عندما تم تشغيل VPN ، كانت الكائنات الموجودة في النتائج أقرب بالفعل إلى موقع خادم VPN.
- الموقع الحالي للمستخدم. إذا تم إرسال إحداثيات المستخدم الحالي إلى الطلب ، فستكون النتائج أكثر دقة.
- لغة نظام الجهاز.
أمثلة VPNنيويورك
تورونتو
كييف
لندن
فرانكفورت
من الممكن وجود عوامل أخرى يمكن أن تؤثر على نتائج البحث ، ولكن هذا كان كافياً لتحقيق النتيجة المرجوة.
كانت دورة التطوير الإضافية هي استخدام الموقع الحالي للجهاز.
import UIKit import MapKit import CoreLocation final class ViewController: UIViewController, CLLocationManagerDelegate { private let locationManager = CLLocationManager() private let request = MKLocalSearch.Request() private var localSearch: MKLocalSearch? private var region = MKCoordinateRegion() override func viewDidLoad() { super.viewDidLoad() if CLLocationManager.locationServicesEnabled() { locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() } } func searchPlace(_ place: String) { localSearch?.cancel() request.naturalLanguageQuery = place request.region = region localSearch = MKLocalSearch(request: request) localSearch?.start { [weak self] response, error in let mapItems = response.mapItems
يؤديالموقع الحالي للجهاز - مدريد ، مزود خدمة الإنترنت فودافون ES

في طريقة delagate didUpdateLocations ، نقوم بإنشاء
MKCoordinateSpan . إذا فهمت بشكل صحيح وثائق Apple ، فكلما قلت القيمة التي حددناها لخط العرض / خط الطول دلتا ، ستتم الإشارة إلى منطقتنا الحالية الضيقة (والأكثر دقة) ، نظرًا لأنها نوع من التكبير في الإحداثيات الحالية في MapKit.
بعد ذلك ، في الواقع ، تغيرت أولوية التسليم وأظهرت لي في المقام الأول تلك الأماكن التي بجواري.
يبقى فقط لجعل الأسماء في القائمة أكثر جمالا. نظرًا لأن بعض خصائص CLPlacemark قد يكون لها نفس الاسم في بعض الأحيان ، في النهاية لن تبدو لطيفة جدًا: نيويورك ، نيويورك ، نيويورك. للقيام بذلك ، تحتاج إلى إنشاء بنية منفصلة ، والتي ستشكل اسمًا جميلًا في القائمة.
import Foundation import MapKit struct Placemark { let location: String init(item: MKMapItem) { var locationString: String = "" if let name = item.name { locationString += "\(name)" } if let locality = item.placemark.locality, locality != item.name { locationString += ", \(locality)" } if let administrativeArea = item.placemark.administrativeArea, administrativeArea != item.placemark.locality { locationString += ", \(administrativeArea)" } if let country = item.placemark.country, country != item.name { locationString += ", \(country)" } location = locationString } }
بعد ذلك ، استجابةً بالفعل للبحث ، يمكننا بسهولة تعيين CLPlacemark في الهيكل الذي تم إنشاؤه ونقله إلى القائمة.
localSearch?.start { [weak self] searchResponse, error in guard let items = searchResponse?.mapItems else { return }
الآن تبدو النتائج أكثر أناقة ويمكن استخدامها بالفعل في المشروع لتمييز المواقع التي تمت زيارتها.
أحد العيوب الرئيسية هو أنه لا يمكنك استخدام هذا الحل إلا في حالة شحذ المشروع لنظام التشغيل iOS / Mac OS. إذا كان المشروع يتضمن تطويرًا لأنظمة أساسية أخرى ، فإنني أوصي باستخدام حل من
Google أو
Facebook . أيضا ، ليست كل المواقع محددة بشكل مثالي في جميع المناطق.
يمكنك رؤية الكود النهائي للمشروع في
المستودع .