12/08/2018, 14:38

Genfencing with Core Location

Geofencing - giới hạn địa lý - thông báo tới ưng dụng khi thiết bị đặt vào hoặc rời khỏi khu vực địa lý mà bạn đã cài đặt trước. Nó cho phép bạn tạo ra những tính năng thú vị như : thông báo mỗi khi bạn rời khỏi nhà, hoặc có thể thống kê dữ liệu lần cuối cùng cũng như lần chi mua nhiều nhất của ...

Geofencing - giới hạn địa lý - thông báo tới ưng dụng khi thiết bị đặt vào hoặc rời khỏi khu vực địa lý mà bạn đã cài đặt trước. Nó cho phép bạn tạo ra những tính năng thú vị như : thông báo mỗi khi bạn rời khỏi nhà, hoặc có thể thống kê dữ liệu lần cuối cùng cũng như lần chi mua nhiều nhất của người dùng với những cửa hàng yêu thích. Trong bài viết này, bạ sẽ học làm thế nào để sử dụng giám sát khu vục - region monitoring trong iOS (sử dụng Region Monitoring API trong Core Location).

Trong trường hợp này, bạn sẽ tạo 1 ứng dụng nhắc nhở được gọi là Geotify - nó cho phép user tạo ra những lời nhắc nhở và kết hợp nó với địa điểm thực tế.

Getting Started

Đầu tiên bạn download source code để bắt đầu starter project . Project cung cấp giao diện người dùng đơn giản để thực hiện thêm/xoá annotation tới/đi của map. Mỗi annotation là đại diện của 1 reminder với vị trí đó - 1 geotification.

Thực hiện build và run project, và bạn sẽ nhìn thấy 1 map như sau :

Ấn vào button + ở trên thanh navigation để thêm mới 1 geotification. Như vậy chúng ta sẽ thêm 1 màn hình riêng để thiết lập các thông số cho geotification.

Trong bài viết này, bạn sẽ thêm 1 notiication với các tham số :

  • Radius : 100 - đây là giá trị tính bằng đơn vị mét, nó thể hiện khoảng cách từ vị trí được chọn tới nơi mà iOS sẽ bắn ra thông báo. Cụ thể, khi thiết bị đi vào vùng (tâm là vị trí được chọn, bán kính là giá trị của radius ) thì chúng ta có thể hiển thị những thông báo vào/ra khỏi khu vực đó.
  • Note : nội dung thông báo khi vào/ra khỏi khu vực.

Click Add khi bạn đã chắc chắn với những giá trị đã thêm vào. Bạn sẽ nhìn thấy geotification xuất hiện annotation mới trên map, với vòng tròn bao quanh.

Tap vào pin và bạn sẽ thấy thông tin chi tiết của geotification. Nếu bạn muốn xoá bỏ geotification này thì hãy tap vào dấu x để xoá! Bên cạnh đó project sẽ sử dụng NSUserDefaults để lưu danh sách geotifications.

Setting Up a Location Manager and Permissions

Tại thời điểm này, tất cả geotifications bạn đã thêm vào mới chỉ được hiển thị trên map. Mỗi geotification chưa có mối liên hệ nào với geofence của Core Location để theo dõi. Nhưng trước khi geofence hoạt động, bạn cần thiết lập Location Manager và request những persmissions cần thiết. Mở file GeotificationsViewController.swift và khai báo 1 biến constant của CLLocationManager :

class GeotificationsViewController: UIViewController {
 
  @IBOutlet weak var mapView: MKMapView!
 
  var geotifications = [Geotification]()
  let locationManager = CLLocationManager() // Add this statement
 
  ...
}

Tiếp theo ở viewDidLoad():

override func viewDidLoad() {
  super.viewDidLoad()
  // 1
  locationManager.delegate = self
  // 2
  locationManager.requestAlwaysAuthorization()
  // 3
  loadAllGeotifications()
}

Thêm key vào file Info.plist : NSLocationAlwaysUsageDescription. Thực hiện build and run project, và bạn sẽ thấy thông báo:

Bạn đã thiết lập yêu cầu quyền sử dụng location service. Click vào Allow để chắc chắn rằng location manager sẽ nhận được callbacks. Trước khi bạn thực hiện gèoencing, có 1 vấn đề nhỏ mà bạn cần xử lý : vị trí hiện tại sẽ không hiển thị trên map - bởi vì tính năng này bị ẩn. Trong file GeotificationsViewController.swift, thêm vào delegte của CLLocationManagerDelegate.

extension GeotificationsViewController: CLLocationManagerDelegate {
  func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    mapView.showsUserLocation = (status == .authorizedAlways)
  }
}

Thực hiện build và run project, bạn sẽ thấy :

Registering Your Geofences

Sau khi đã thiết lập xong Location manager, nghiệp vụ tiếp theo là cho phép ứng dụng đăng ký geofences để theo dõi.

Trong project, thông tin khu vực của user được lưu trong Geotification model. Tuy nhiên, Core Location yêu cầu mỗi geofence phải thể hiện như là đối tượng của CLCircularRegion trướckhi nó có thể đăng ký để theo dõi. Để xử lý yêu cầu này, bạn sẽ tạo method để trả ra CLCircularRegion từ đối tượng Geotification.

Open GeotificationsViewController.swift và thêm vào phương thức:

func region(withGeotification geotification: Geotification) -> CLCircularRegion {
  // 1
  let region = CLCircularRegion(center: geotification.coordinate, radius: geotification.radius, identifier: geotification.identifier)
  // 2
  region.notifyOnEntry = (geotification.eventType == .onEntry)
  region.notifyOnExit = !region.notifyOnEntry
  return region
}

Tiếp theo, bạn cần thêm 1 phương thức để khởi động việc thoi dõi một geoticication. Thêm những phương thức dưới đây vào GeotificationViewController :

func startMonitoring(geotification: Geotification) {
  // 1
  if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
    showAlert(withTitle:"Error", message: "Geofencing is not supported on this device!")
    return
  }
  // 2
  if CLLocationManager.authorizationStatus() != .authorizedAlways {
    showAlert(withTitle:"Warning", message: "Your geotification is saved but will only be activated once you grant Geotify permission to access the device location.")
  }
  // 3
  let region = self.region(withGeotification: geotification)
  // 4
  locationManager.startMonitoring(for: region)
}
  1. isMonitoringAvailableForClass : xác định nếu thiết bị phải yêu cầu phần cứng để hỗ trợ theo dõi của geofences.

  2. Tiếp theo, bạn sẽ kiểm tra trạng thái xác thực để chắ chắcn rằng ứng dụng cũng có quyền sử dụng Location service. Nếu ứng dụng không được xác thực, nó sẽ không nhận được bất kỳ thông báo nào liên quan tới geofence.

  3. Tạo thể hiện của CLCircularRegion từ geotification nhận được.

  4. Cuối cùngm bạn đăng ký CLCircularRegion với Core Location để theo dõi.

    Sau khi hoàn thành, bạn cũng cần phương thức để dùng theo dõi 1 geotification khi người dùng xoá nó khỏi ứng dụng. Trong GeotificationsViewController.swift, thêo những phương thức dưới đây vào dưới startMonitoringGeotification :

func stopMonitoring(geotification: Geotification) {
  for region in locationManager.monitoredRegions {
    guard let circularRegion = region as? CLCircularRegion, circularRegion.identifier == geotification.identifier else { continue }
    locationManager.stopMonitoring(for: circularRegion)
  }
}

Bây giờ bạn đã có cả 2 phương thức start và stop, bạn sẽ sử dụng chúng bất khi cứ khi nào thêm hoặc xoá 1 geotification. Nhìn vào phương thức addGeotificationViewController (: didAddCoordinate) trong GeotificationsViewController.swift. Đây là 1 delegate được gọi bởi AddGeotificationViewController khi tạo 1 geotification, nó trả ra 1 Geotitication mới tương ứng.

func addGeotificationViewController(controller: AddGeotificationViewController, didAddCoordinate coordinate: CLLocationCoordinate2D, radius: Double, identifier: String, note: String, eventType: EventType) {
  controller.dismiss(animated: true, completion: nil)
  // 1
  let clampedRadius = min(radius, locationManager.maximumRegionMonitoringDistance)
  let geotification = Geotification(coordinate: coordinate, radius: clampedRadius, identifier: identifier, note: note, eventType: eventType)
  add(geotification: geotification)
  // 2
  startMonitoring(geotification: geotification)
  saveAllGeotifications()
}

Bạn vừa tạo ra 2 thay đổi trong code :

  1. Bạn chắc chắn rằng giá trị radius không vượt quá maximumRegionMonitoringDistance của locationManager. Điều này là rất quan trọng, bởi vì bất cứ giá trị nào vượt quá maximum sẽ không thể theo dõi.

  2. Bạn thêm việc gọi phương thức startMonitoringGeotification để chắc chắn rằng geofence tương ứng với geotification mới được thêm đã được đăn gkys với Core Location để theo dõi.

    Tại thời điểm này, ứng dụng đã có đầy đủ tính năng đăng ký mới 1 geofences để theo dõi. Tuy nhiên, có 1 hạn chế là geofences là 1 tài nguyên hệ thống dùng chung, vì thế Core Location chỉ cho phép số lượng geofences được đăng ký không vượt quá 20 trên 1 ứng dụng. Vì thế chúng ta sẽ giới hạn số lượng geotification có thể thêm :

func updateGeotificationsCount() {
  title = "Geotifications ((geotifications.count))"
  navigationItem.rightBarButtonItem?.isEnabled = (geotifications.count < 20)  // Add this line
}

Cuối cùng, liên quan tới việc user xoá geotifications. Chức năng này sẽ được xử lý trong phương thức mapView (annotaitonView:calloutAccessoryControlTapped:), cái mà sẽ được gọi bất cứ khi nào user tap vào button "delete".

func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
  // Delete geotification
  let geotification = view.annotation as! Geotification
  stopMonitoring(geotification: geotification)   // Add this statement
  removeGeotification(geotification)
  saveAllGeotifications()
}

Thêm nữa trước khi gọi phương thức stop theo dõi của geofence liên quan tới geotification, trước khi xoá geotificaiton và lưu lại NSUserDefaults. Tại thời điểm này thì ứng dụng đã có đầy đủ tính năng để theo dõi hoặc bỏ theo dõi khu vực mà user đã thiết lập trước.

0