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 ) ?
Si quelqu'un à une autre idée, je suis preneur aussi ^^
Réponses
Bonjour,
De ta position à toi tout seul ou de celui qui utilisera ton app ?
Tu as une idée du temps des intervals ? C'est pour évaluer le nombre de ligne...
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.
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.
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 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.
D'une position à moi (via l'appli) ou d'une position choisie (via une interface admin).
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..
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 ^^
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.
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
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.
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'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.
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).
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é
Pas faux... ::)
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):
Dans mon code dans la 2eme vue (celle pour ajouter une zone) :
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
Une piste ? ^^
Peux-tu vérifier self.delegate dans actionAjouterIncident(_ sender:)?
J'ai fais un print(self.delegate) et il est à nil
Où-est-ce que j'ai oublié un truc ?
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..
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 ?
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
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
- Dans viewDidAppear, je vérifie la valeur du textfield :
- Dans ma vue 2 (celle de l'ajout d'une zone),je récupère le textfield
Que je peux utiliser dans ma fonction d'ajout de zone, une fois la zone ajoutée, je change la valeur du textfield à "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 ^^
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 :
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à ?