Push et Géolocalisation

Bonjour,


 


Je suis en pleine reflexion sur l'envoi de push avec géolocalisation, je m'explique :


 


J'aimerai envoyer un push à  tout ceux qui sont dans un rayon de X km de ma position.


 


Quelle est la meilleure manière de faire ?


 


1/ Je sauvegarde la position GPS à  interval régulier dans ma bdd ?


Inconvénient :


- si l'utilisateur ferme l'appli, la position n'est plus mise à  jour.


- la comparaison des positions se fera sur le serveur, ce qui peut prendre du temps si j'ai beaucoup de ligne.


 


2/ J'envoie un push silencieux avec MA position, je récupère la position de mon utilisateur à  ce moment là  et je compare si il est dans le rayon du push ?


- Est-ce-que c'est possible techniquement de faire ça ? Si oui, avez-vous des pistes (surtout pour l'envoi de push silencieux, parce que chaque push que j'envoie affiche la notification et c'est pas le but :s) ?


 


Si quelqu'un à  une autre idée, je suis preneur aussi ^^


Réponses

  • Bonjour,


     



    J'aimerai envoyer un push à  tout ceux qui sont dans un rayon de X km de ma position.



     


    De ta position à  toi tout seul ou de celui qui utilisera ton app ?


     




    1/ Je sauvegarde la position GPS à  interval régulier dans ma bdd ?




     


    Tu as une idée du temps des intervals ? C'est pour évaluer le nombre de ligne...


     



    - si l'utilisateur ferme l'appli, la position n'est plus mise à  jour.


     


    Non puisque tu as la possibilité de demander à  l'utilisateur que ton app accède à  la localisation même si cette dernière n'est pas active.


     



    - la comparaison des positions se fera sur le serveur, ce qui peut prendre du temps si j'ai beaucoup de ligne.


     


    Oui mais tu as la possibilité de garder en ligne uniquement la dernière position (pas d'historique en somme), ce qui limite considérablement le nombre de ligne.


     



    2/ J'envoie un push silencieux avec MA position, je récupère la position de mon utilisateur à  ce moment là  et je compare si il est dans le rayon du push ?

    - Est-ce-que c'est possible techniquement de faire ça ? Si oui, avez-vous des pistes (surtout pour l'envoi de push silencieux, parce que chaque push que j'envoie affiche la notification et c'est pas le but :s) ?




     


    C'est quoi un push silencieux ? Tu demandes la position de l'iPhone de ton pote, une requête part sur son smartphone et te retourne sa position ? ::)


     



    Si quelqu'un à  une autre idée, je suis preneur aussi ^^



     


    Dans l'hypothèse où tu es l'unique référence dont on doit comparer la distance, j'aurais fait l'inverse. Chaque utilisateur envoie sa position toutes X minutes via une requête (post). Le serveur compare sa position par rapport à  la tienne, si tu es pas loin, tu retournes dans la réponse de ta requête (via du json) un boolean à  true pour dire de déclencher une notif (et pourquoi pas rajouté dans la réponse une variable pour la distance). De ce fait, tu sauvegarderas uniquement ta position à  toi.


  • InsouInsou Membre
    février 2017 modifié #3

    De ta position à  toi tout seul ou de celui qui utilisera ton app ?



     


    D'une position à  moi (via l'appli) ou d'une position choisie (via une interface admin).


     



    Tu as une idée du temps des intervals ? C'est pour évaluer le nombre de ligne...



     


    Toute les 10 minutes environ.


    Après en effet, je peux réduire considérablement le nombre de ligne de position en ne gardant que la dernière mais si j'ai 100000 utilisateurs, ça me fait quand même 100000 positions à  comparé sur le serveur.. donc un temps de latence plus important et une possibilité que ça échoue..


     



    C'est quoi un push silencieux ? Tu demandes la position de l'iPhone de ton pote, une requête part sur son smartphone et te retourne sa position ?



     


    Dans ma tête, ça serait un push que le téléphone reçoit et en fonction de ce qu'il y a dedans, si il est déclaré comme "silencieux" alors il ne s'affiche pas et fait une action (comme récupérer la position du téléphone, la comparer, etc etc)


     


    En fait j'ai cru lire des trucs sur des "push silencieux" et j'ai imaginé ce principe mais si ça se trouve, ce n'est pas possible du tout.. d'où ma question ^^


  • LarmeLarme Membre
    février 2017 modifié #4

    Une silent notification est bien une application qui n'apparaà®t pas à  l'écran.


    Cela permet d'avertir l'application de certaines modifications.


    Je pense que c'est notamment le mécanisme utilisé quand tes notifications disparaissent car tu les as lu sur un autre device (ex. d'un message Messenger ou autre).


    Cela devrait permettre de n'afficher une notification que si elle a du sens aussi. Comme dans ton cas, tu vérifies les positions GPS, et si c'est bon, tu affiches une local notification et sinon, tu ne fais rien. L'utilisateur ne voit pas la différence entre une locale et une push.


     


    Il faut bien configurer  payload: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW1


  • Moi je l'aurais fait dans l'autre sens. Chaque user envoie une requête à  interval régulier avec leur position courante. Dans la réponse il aurait la distance et un ordre pour déclencher une notification. De ce fait, si tu as 10000 users, tu n'auras pas à  tous les gérer en même temps, ils arriverons par vagues à  chaque instant.




  • Une silent notification est bien une application qui n'apparaà®t pas à  l'écran.




     


    Quelle est la différence fondamentale entre ça et envoyer une requête http à  un serveur ? Qu'une part du smarphone et va au serveur et que l'autre part du serveur et va au smartphone (du coup t'affranchis de faire des requêtes à  intervalle régulier) ?

  • Merci Larme de confirmer ce que j'pensais.. j'ai eu peur de m'être inventé un truc qui n'existe pas ^^


    Je vais continuer mes recherches et mes tests de ce côté là  et je reviendrai poster une solution qui fonctionne si ça intéresse d'autres personnes :)


     




    Moi je l'aurais fait dans l'autre sens. Chaque user envoie une requête à  interval régulier avec leur position courante. Dans la réponse il aurait la distance et un ordre pour déclencher une notification. De ce fait, si tu as 10000 users, tu n'auras pas à  tous les gérer en même temps, ils arriverons par vagues à  chaque instant.




     


     


    J'ai vu avec mon n+1, il vaudrait mieux éviter de surcharger notre serveur avec des requêtes qui tape dessus toute les X minutes.. puis en plus, ça consommera moins de batterie ^^


    Envoyer un push silencieux et faire le travail sur le téléphone est moins gourmand pour nous et les serveurs de push d'Apple sont plus stable que le notre ^^


  • J'ai vu avec mon n+1, il vaudrait mieux éviter de surcharger notre serveur avec des requêtes qui tape dessus toute les X minutes.. puis en plus, ça consommera moins de batterie ^^

    Envoyer un push silencieux et faire le travail sur le téléphone est moins gourmand pour nous et les serveurs de push d'Apple sont plus stable que le notre ^^




     


    Oui, j'ai lu la doc et la solution de Larme est bien mieux. Tu envoies la position de référence, l'iPhone fait le calcul et déclenche une notification (ou pas) en fonction de la distance que tu auras fixé.

  • @Jérémy :


    Tu évites d'une part le polling qui est souvent déconseillé.


    À moins de centraliser ton polling en un seul endroit, tu devrais en théorie faire ton polling sur chaque API qui t'intéresse (ce qui peut faire 3/4 appels du coup), alors que la notification pourrait t'indiquer le type de nouveau contenu, et tu sais donc où appeler.


    Les applications iOS ne peuvent en général pas vivre ad-vitam eternam. Loin de là . Et poller en continu en background, non.


  • CéroceCéroce Membre, Modérateur
    février 2017 modifié #10
    Regarde si le Geofencing ne serait pas plus adapté. C'est bien plus simple à  implémenter et économe en batterie.
  • J'viens de tester un exemple avec le geofencing et c'est en effet simple à  implémenter (en tout cas, l'exemple que j'ai trouvé fonctionne à  merveille : https://www.raywenderlich.com/136165/core-location-geofencing-tutorial )


     


    Je vais partir sur ça ^^


     


    Merci :)




  • @Jérémy :


    Tu évites d'une part le polling qui est souvent déconseillé.


    À moins de centraliser ton polling en un seul endroit, tu devrais en théorie faire ton polling sur chaque API qui t'intéresse (ce qui peut faire 3/4 appels du coup), alors que la notification pourrait t'indiquer le type de nouveau contenu, et tu sais donc où appeler.


    Les applications iOS ne peuvent en général pas vivre ad-vitam eternam. Loin de là . Et poller en continu en background, non.




     


    J'avoue que c'est quelque chose que je ne connaissais pas. L'utilisateur il peut désactiver les silent notifications ? SI c'est le cas ça pourrait être problématique... ::)



  • J'avoue que c'est quelque chose que je ne connaissais pas. L'utilisateur il peut désactiver les silent notifications ? SI c'est le cas ça pourrait être problématique... ::)




    Normalement oui.


    Une notification silencieuse n'est qu'une Push Notification spécifique. Or on demande toujours à  l'utilisateur de les autoriser. Libre à  lui de les autoriser/refuser, de les afficher comme bon lui semble.


    Il y a aussi de la démagogie à  faire : Expliquer à  l'utilisateur à  quoi elles servent. Il ne faut pas compter uniquement sur des notifications pour être à  jour quand on ouvre l'application, mais comme un outil supplémentaire.



  • Normalement oui.


    Une notification silencieuse n'est qu'une Push Notification spécifique. Or on demande toujours à  l'utilisateur de les autoriser. Libre à  lui de les autoriser/refuser, de les afficher comme bon lui semble.


    Il y a aussi de la démagogie à  faire : Expliquer à  l'utilisateur à  quoi elles servent. Il ne faut pas compter uniquement sur des notifications pour être à  jour quand on ouvre l'application, mais comme un outil supplémentaire.




     


    D'accord, il n'existe pas de solution de contournement ? Utiliser la même approche sans que l'utilisateur puisse désactiver le système ?



  • D'accord, il n'existe pas de solution de contournement ? Utiliser la même approche sans que l'utilisateur puisse désactiver le système ?




    Non, normalement non, et j'espère que non (en tant qu'utilisateur).



  • Non, normalement non, et j'espère que non (en tant qu'utilisateur).




     


    Oui mais c'est sur certains aspects dommage. On pourrait mettre à  jour l'App (datas) sans que le système soit trop énergivore...



  • Oui mais c'est sur certains aspects dommage. On pourrait mettre à  jour l'App (datas) sans que le système soit trop énergivore...




     


    ça laisse surtout la porte ouverte à  n'importe quoi ^^


     


    Pour info, sur ce projet, je suis parti sur l'idée de Céroce pour le geofencing et je couplerai ça aux notifications silencieuses pour mettre à  jour les zones..


    Pour le moment ça se déroule à  peu près bien mais je sens que ça va merder dans pas longtemps donc j'reviendrai poster quand j'serai coincé :p



  • ça laisse surtout la porte ouverte à  n'importe quoi ^^




     


    Pas faux... ::)


  • InsouInsou Membre
    février 2017 modifié #19

    Je continue sur mon projet de géotification et ça fonctionne plutôt bien..


    Par contre, je me heurte à  un petit soucis de "delegate" :/


     


    En gros, j'ai une page avec une carte qui affiche des zones (geotification) et j'ai une page d'ajout de zone..

    Lorsque j'ajoute une zone, j'aimerai qu'elle s'affiche directement sur la carte de la première page, dans l'exemple que j'ai, ça fonctionne bien et quand j'essaie de reproduire dans mon code, j'ai l'impression que je ne passe pas dedans donc l'ajout ne se fait pas.


     


    Dans mon code de ma première vue (la carte avec les zones):



    extension IncidentsViewController: AjouterUnIncidentsViewControllerDelegate {


    func addGeotificationViewController(controller: AjouterUnIncidentViewController, didAddCoordinate coordinate: CLLocationCoordinate2D, radius: Double, identifier: String, note: String, id : Int) {
    print("delegate addgeotification")
    let clampedRadius = min(radius, locationManager.maximumRegionMonitoringDistance)
    let nouvelleGeotification = Geotification(coordinate: coordinate, radius: clampedRadius, identifier: identifier, note: note, id : id)
    add(geotification: nouvelleGeotification, withMap : true)
    startMonitoring(geotification: nouvelleGeotification)
    }

    }

    Dans mon code dans la 2eme vue (celle pour ajouter une zone) :



    protocol AjouterUnIncidentsViewControllerDelegate {
    func addGeotificationViewController(controller: AjouterUnIncidentViewController, didAddCoordinate coordinate: CLLocationCoordinate2D,radius: Double, identifier: String, note: String, id: Int)
    }

    ...

    class AjouterUnIncidentViewController: FormViewController{

    var delegate: AjouterUnIncidentsViewControllerDelegate?

    @IBAction func actionAjouterIncident(_ sender: Any) {

    ...

    // récupération des données
    let Zone = recupJSON["Zone"]
    let TabCoordonnées = Zone["Adresse"].stringValue.components(separatedBy: ",")
    let coordinate:CLLocationCoordinate2D = CLLocationCoordinate2DMake(Double(TabCoordonnées[0])!, Double(TabCoordonnées[1])!);
    let radius = Double(Zone["Rayon"].intValue)
    let identifier = NSUUID().uuidString
    let note = Zone["Note"].stringValue
    let id = Zone["Id"].intValue

    delegate?.addGeotificationViewController(controller: self, didAddCoordinate: coordinate, radius: radius, identifier: identifier, note: note, id : id)
    }
    }

    D'après ce que j'ai compris, lorsque dans ma 2eme vue, j'ajoute une zone, je suis censé passer dans le code de ma première vue et déclencher mon "print("delegate addgeotification")" .. sauf que je ne passe jamais dedans et j'ai l'impression d'avoir louper un truc :s


     


    Une piste ? ^^


  • Peux-tu vérifier self.delegate dans actionAjouterIncident(_ sender:)?


  • J'ai fais un print(self.delegate) et il est à  nil :s


     


    Où-est-ce que j'ai oublié un truc ? :/


  • CéroceCéroce Membre, Modérateur
    Ce n'est pas clair pour moi. Tu parles de deux vues... Deux UIViewControllers, je comprends. Le CLLocationManager ne peut évidemment avoir qu'un délégué à  la fois, et ce serait mieux que ce ne soit pas un View Controller.
  • Bah là , il n'y a qu'un seul délégué non ?


    Quel est le problème avec un ViewController ?


     


    En gros, je veux qu'en ajoutant une zone (depuis la vue n°2), elle apparaisse sur la carte (vue n°1).


     


    J'ai un peu du mal encore à  comprendre comment bien utiliser le delegate..


  • CéroceCéroce Membre, Modérateur

    Quel est le problème avec un ViewController ?

    Si c'est lui qui retient le CLLocationManager, la géolocalisation n'est plus effectuée s'il n'est pas lui-même en mémoire (≈ affiché).
  • J't'avouerai que là , ça me dépasse un peu..


    Il n'y a pas un truc qui peut détecter quand on revient sur une page (je ferme ma page d'ajout de zone, là  je reviens sur ma page avec la carte, je detecte ce moment là  et je relance la fonction de chargement des zones sur la carte)..?
  • J'avoue que c'est pas très clair cette histoire :


    Où est défini le CLLocationManager ? Est-ce une sharedInstance ? Une propriété d'une classe ? Qui est son delegate ?


     


    Si j'ai bien suivi :


    VCA présente VCB


    VCB "trouve" un nouveau point à  afficher.

    VCA devrait le connaà®tre.


     


    Donc tu utilises le pattern delegate.


    VCB possède donc une propriété pour ce delegate.


    Mais l'as-tu configuré ? Où fais-tu vcb.delegate = vca ?

  • CéroceCéroce Membre, Modérateur
    Si, les méthodes viewWill/DidAppear() de UIViewControllers sont appelées, mais ce n'est pas la question.

    La géolocalisation et l'affichage sont deux choses différentes, d'ailleurs dans des frameworks différentes (CoreLocation et MapKit). En principe, la géoloc se fait en continu, donc on va par exemple créer une sous-classe de NSObject qui va instancier un CLLocationManager et qui se conformera à  CLLocationDelegate. Appelons-là  Locator.

    Ensuite, il faut qu'un autre objet instancie un Locator, et le retienne. Comme le Locator appartient à  toute l'appli, ça a du sens que ce soit l'AppDelegate qui le fasse. Il va donc l'instancier et le garder dans une propriété strong.

    Maintenant le plus difficile est de passer l'instance de Locator aux view controllers, surtout si on utilise les Storyboards. Les view controllers doivent se le passer de proche en proche. Je ne sais pas si ce que je j'écris est clair pour toi, mais c'est un problème habituel dans toute appli iOS: le Modèle est partagé par plusieurs View Controllers, il faut donc leur passer.
  • Bon, je pense que je reviendrai sur ce soucis là  plus tard parce que pour l'instant, ça me parait un poil complexe au vue de mes compétences :s


     


    J'ai bidouillé une rustine qui fonctionne mais c'est pas propre du tout, cependant pour le moment, ça ira bien !


     


    J'ai honte d'expliquer ma rustine ici parce que j'sens que j'vais me faire tirer dessus par les codeurs plus expérimentés :'(


     


    Enfin bref :


     


    - Dans ma vue 1 (celle avec la carte), j'ai mis un textfield (caché sous la carte) avec la valeur "0"


     


    - Dans mon prepare(for segue), lorsque je passe sur ma vue 2 (celle d'ajout d'une zone), je lui transmet le textfield



    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    //print(segue.identifier)
    switch segue.identifier! {

    case "versAjouterIncidents":
    // envoi les données vers l'autre vue
    var TabInfosTextfield : [String:UITextField] = [:]
    TabInfosTextfield = [
    "Textfield" : hiddenRefreshTextfield
    ]
    let svc = segue.destination as! UINavigationController
    let addEventViewController = svc.topViewController as! AjouterUnIncidentViewController
    addEventViewController.TabInfosTextfield = TabInfosTextfield

    default:
    break
    }
    }

    - Dans viewDidAppear, je vérifie la valeur du textfield :



    override func viewDidAppear(_ animated: Bool) {

    if let valeur = hiddenRefreshTextfield.text{
    if(valeur == "1"){
    self.removeAllRadiusOverlay() // supprime tout les rayons
    self.removeAllAnnotations() // Supprime tout les points
    self.loadAllGeotifications() // recharge toute les geotifications
    self.hiddenRefreshTextfield.text = "0" // remet à  zéro
    }
    }
    }

    - Dans ma vue 2 (celle de l'ajout d'une zone),je récupère le textfield 



    var TabInfosTextfield : [String:UITextField] = [:]

    Que je peux utiliser dans ma fonction d'ajout de zone, une fois la zone ajoutée, je change la valeur du textfield à  "1"



    self.TabInfosTextfield["Textfield"]!.text = "1"

    - Lorsque je retourne sur la vue 1, viewDidAppear est appelé, il check la valeur du textfield, si il est à  1, il supprime tout ce qu'il y a sur la carte et refait un chargement des zones (avec celle ajoutée récemment) 


     


    Finalité : c'est dégueulasse comme méthode de faire mais ça fonctionne et ça ira bien pour l'instant ^^


  • CéroceCéroce Membre, Modérateur
    février 2017 modifié #29
    Oui, c'est clairement dégueulasse, mais effectivement ça dépend de ton niveau. D'expérience (personnelle, et aussi à  donner des formations), il faut d'abord se frotter à  un problème afin de cerner la difficulté et comprendre pourquoi une solution convient.

    L'étape d'après est de comprendre comment impléménter la délégation toi-même, et tu pourras passer une String plutôt qu'un UITextField.

    Désolé si je t'ai un peu embrouillé; sur un forum on manque toujours de temps ou de courage pour expliquer où on veut en venir. Mais si tu continues, tu vas forcément te frotter au problème que j'ai évoqué.
  • Je viens de me rendre compte d'un petit soucis au niveau d'une notification locale..


     


    Voici comment je m'y prend : 



    if let message = note(fromRegionIdentifier: region.identifier){
    let content = UNMutableNotificationContent()
    content.title = "ATTENTION"
    content.body = message
    content.sound = .default()

    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
    let request = UNNotificationRequest(identifier: "NotifLocale", content: content, trigger: trigger)
    UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
    }

    Par contre, quand je reçois la notification, le téléphone ne sonne et ne vibre pas..


    Pourtant mon content.sound est à  .default(), ce qui devrait fonctionner nan ?


  • Personne n'a jamais eu ce soucis là  ? :/


Connectez-vous ou Inscrivez-vous pour répondre.