27 juli 2020

Waarom iOS 13 volgens developers een stap terug is voor VoIP apps

Waar de Vialer app voor Android onlangs een mooie upgrade kreeg, hadden we aan de nieuwe iOS versie een lastige kluif. De oorzaak hiervan lag bij een ongelukkige verandering die Apple doorvoerde voor pushmeldingen voor VoIP apps. Een van onze app developers schreef een technisch blog over de uitdagingen en oplossingen voor onze app. Deze (ingekorte) blog is een vertaling van de originele publicatie op de site van Devhouse Spindle.

Pushmeldingen voor VoIP apps in iOS

Op WWDC 2019 (Apple’s World Wide Developer Conference) kondigde Apple een grote verandering aan in de manier waarop VoIP-pushmeldingen moeten worden afgehandeld in apps voor iOS 13. De wijziging werd ingevoerd omdat sommige app ontwikkelaars deze pushmeldingen misbruikten om te garanderen dat de app werd gewekt. Door dit misbruik had Apple minder controle over de levensduur van de iPhone batterij. Deze kleine aanpassing door Apple zou voor ons, als legitieme gebruiker van deze VoIP-meldingen, helaas leiden tot maanden van frustraties en ontevreden gebruikers.

Hoe inkomende oproepen in VoIP apps werken

Om te begrijpen waarom deze verandering problemen veroorzaakte, niet alleen voor onze VoIP app Vialer maar ook voor de meeste andere VoIP apps, is het handig om te weten hoe inkomende oproepen worden geïmplementeerd door app ontwikkelaars.

In tegenstelling tot een traditionele VoIP-telefoon die een open verbinding (via REGISTER-verzoeken) met een VoIP-platform in stand houdt, gebruiken mobiele apps voor inkomende gesprekken meestal pushmeldingen. Dit betekent dat Google’s FCM of Apple’s APNS tussen een VoIP-platform en een mobiele app zit.

Wanneer er geen actief gesprek is, kan de app volledig afgesloten zijn, maar wanneer er een gesprek binnenkomt, zal het VoIP-platform de Push Notificatiedienst inlichten, die dan de telefoon en de app wakker maakt en zich op dat moment kan gedragen als een normale VoIP-client. Het gaat SIP initialiseren, zich registreren bij het VoIP-platform, een inkomende oproep ontvangen en vervolgens de gebruiker waarschuwen door de telefoon over te laten gaan en een inkomende oproepscherm weer te geven.

Wat Apple nu forceert in VoIP apps

Voor apps die gericht zijn op iOS 13 besloot Apple dat de laatste stap van dit proces, het waarschuwen van de gebruiker, onmiddellijk en voor elke ontvangen melding moet plaatsvinden. Als hier niet aan wordt voldaan, worden de pushmeldingen voor deze apps uiteindelijk genegeerd. Telefoons zullen deze meldingen niet meer ontvangen, zonder dat er meer informatie wordt gegeven over hoe lang dit gebeurt, hoe je dit kunt terugdraaien, of wat voor toelichting dan ook.

Er bestaat een concept genaamd “Optimistic UI” waarin, wanneer een gebruiker een wijziging maakt die moet worden ingediend bij een server, de gebruikersinterface onmiddellijk zal worden bijgewerkt, optimistisch aangenomen dat er niets mis zal gaan wanneer het wordt ingediend.

Dit is in essentie wat Apple ons heeft geforceerd om in de app te implementeren. Maar, in plaats van het zien van een kleine UI glitch als er een probleem optreedt, heb je nu een telefoon die overgaat, een gebruiker die vervolgens zijn telefoon uit zijn broekzak haalt, om vervolgens te constateren dat er geen gesprek is aan de andere kant van de lijn.

De verschillende scenario’s van een pushbericht

Op het moment dat het pushbericht wordt ontvangen, weten we niet of er een echte oproep beschikbaar is, omdat de app net wakker wordt. Gezien het feit dat er een seconde vertraging is, zijn er ook andere scenario’s mogelijk:

  • De beller heeft de oproep geannuleerd
  • Een andere gebruiker nam het gesprek aan (bijvoorbeeld in een belgroep)
  • De gebruiker verloor zijn netwerkverbinding

In de 0 tot 2 seconden die nodig zijn om SIP te initialiseren en te registreren, komen we erachter of een van de bovenstaande zaken heeft plaatsgevonden. Echter, door de nieuwe eisen van Apple hebben we dan al de gebruiker moeten informeren en lastigvallen voor een oproep die niet bestaat tegen de tijd dat de gebruiker de telefoon in zijn hand heeft.

De applicatie kan deze tijd ook gebruiken om te beslissen of het gesprek überhaupt moet worden afgehandeld. Het kan bijvoorbeeld controleren of de huidige netwerkverbinding sterk genoeg is om een VoIP-gesprek te ondersteunen. Deze en andere soortgelijke controles kunnen nu niet meer worden uitgevoerd zonder de gebruiker te storen.

Een pushbericht is niet hetzelfde als een oproep

Het grote probleem is dat een pushbericht geen oproep is, maar slechts een melding met kleine hoeveelheden metadata over de oproep (bijvoorbeeld het nummer van de persoon die belt). Een gebruiker kan een pushbericht niet ‘beantwoorden’, we moeten een daadwerkelijke SIP-oproep hebben vastgesteld om dit mogelijk te maken.

Apple eist echter dat de gebruikersinterface (het inkomende oproepscherm) onmiddellijk wordt gepresenteerd. Dit is problematisch, omdat daarmee een oproep in het scherm wordt getoond die eigenlijk nog niet bestaat. Je kunt je voorstellen dat dit gevolgen heeft voor gebruikers, die denken een gesprek aan te nemen waarna er vervolgens niets gebeurt.

De implementatie van de veranderingen

Het aanpassen van onze app,  zodat deze aan de nieuwe eisen van Apple voldeed, bleek een hele lastige opgave. Verschillende factoren speelden hierbij een rol.

Gebrek aan updates in bestaande API’s

De belangrijkste reden om Apple de schuld te geven, is het feit dat ze geen enkele update aan hun bestaande API’s hebben gedaan om deze verandering te ondersteunen. Ze hadden functies in hun CallKit framework kunnen inbouwen, die de app ontwikkelaars zouden helpen om met de nieuwe situatie te werken. En, nog belangrijker, om ze in de juiste richting te sturen terwijl ze de wijzigingen toepasten. In plaats daarvan kwam hun advies alleen in de vorm van een forumpost door een lid van het Developer Technical Support team. Hoewel dit bericht zeer nuttig is geweest, gaat het om problemen die alle ontwikkelaars zullen tegenkomen bij de implementatie van VoIP. Daarom zou dit in het framework moeten zijn opgenomen in plaats van in enkele instructies aangeboden op een forum.

Hoe lang is te lang?

Een ander probleem dat we tegenkwamen, is wat er zou gebeuren als je je niet aan de nieuwe richtlijnen zou houden. Liet je bij het ontvangen van een pushbericht niet direct het inkomende oproepscherm zien, dan crashte de app. Was de tijd tussen het ontvangen van het pushbericht en het laten zien van de gebruikersinterface te lang, dan kreeg de app geen pushberichten meer. Het was echter totaal niet transparant hoeveel tijd er tussen het pushbericht en het tonen van het scherm mocht zitten. Daardoor waren we er nooit zeker van hoeveel tijd we daadwerkelijk hadden, en of er ooit inderdaad geen meldingen werden afgeleverd.

Het testen van inkomende gesprekken

Oproepen in het algemeen, maar met name inkomende oproepen zijn zeer lastig te testen. Dit maakt het doorvoeren van grote veranderingen tijdrovend, omdat het verzamelen van zoveel mogelijk feedback veel tijd in beslag neemt. Terwijl onze testers net zoveel uren spendeerden aan het testen van de app als de ontwikkelaars aan het implementeren van de wijzigingen, kon het voorkomen dat de app de testen met vlag en wimpel passeerden, om vervolgens in sommige situaties bij ‘echte’ gebruikers niet te werken.

Inkomende oproepen werken weer

Na maanden van werk en veel verschillende iteraties, waarbij we steeds verschillende problemen ondervonden, werd uiteindelijk duidelijk hoe we de verandering van Apple konden implementeren.

  • De eerste verandering moest aan de serverkant plaatsvinden. Om er zeker van te zijn dat de pushberichten de telefoon bereikten, stuurden we er eerder altijd meerdere en lieten we de telefoon alles negeren behalve het eerst aangekomen bericht. Dit zou betekenen dat als een pushbericht mislukt (wat wel eens gebeurt), het volgende bericht nog steeds de telefoon kan bereiken en het gesprek nog steeds succesvol zou zijn. Het niet reageren op de volgende berichten maakte echter dat de app crashte, dus dit was geen oplossing meer. We sturen nu slechts één pushbericht naar de telefoon en gaan ervan uit dat het bericht de telefoon met succes bereikt.
  • Wanneer dat ene pushbericht binnenkomt, beginnen we onmiddellijk met het waarschuwen van de gebruiker, zoals vereist. Tegelijkertijd beginnen we met het opstarten van SIP, het registreren en het melden aan het VoIP-platform dat we klaar zijn om de oproep te ontvangen. Als een van deze processen zou mislukken, zouden we de telefoon laten stoppen met rinkelen. Het belangrijkste om spookoproepen te voorkomen is een conditie die op ‘true’ komt te staan wanneer een oproep een ‘echte’ SIP-oproep wordt. Als dit niet binnen 2 seconden na het reageren op de server is gebeurd, beschouwen we het gesprek als niet (langer) bestaand en laten we het rinkelen stoppen.
  • De laatste wijziging die moest worden aangebracht, zoals voorgesteld door Apple, was het laten tonen van de accepteer/weiger-knoppen totdat we een ‘echte’ oproep hadden vastgesteld. Het tonen van knoppen die nog niets doen totdat er een echte oproep is, is zo’n beetje het tegenovergestelde van wat je probeert te bereiken met een goede gebruikersinterface. Dit hebben we helaas moeten accepteren omdat Apple het zo wil, er zijn geen betere opties.

Terwijl deze veranderingen niet zo ingewikkeld lijken, was de hoeveelheid kennis die moest worden opgedaan om de bestaande methode uit de app te verwijderen en dit te vervangen met de nieuwe methode, zonder dat er ergens in de app iets stuk ging, een enorm tijdrovende klus.

Van frustratie naar nieuwe versie

Het meest frustrerende aan de vele maanden die het heeft gekost om de veranderingen van Apple te ondersteunen, is dat we ondanks al het harde werken nu een minder goed product kunnen aanbieden dan voor iOS 13.

Ervan uitgaande dat onze implementatie nu is zoals Apple het voor ogen had, heeft de vereiste verandering gezorgd voor een minder soepele gebruikerservaring. Er zullen situaties voorkomen waarin een gebruiker naar de andere kant van de kamer rent om zijn telefoon te pakken, en dan ziet dat er geen gesprek is om op te nemen, of een gebruiker die meerdere keren op de antwoordknop tikt en zich afvraagt waarom dit niets doet. Deze situaties zijn misschien niet het einde van de wereld, maar het is niet de gebruikerservaring die we onze gebruikers willen, en vóór iOS 13 konden, bieden.

Een project dat zoveel tijd heeft gekost, met als enige tastbare resultaat dat het situaties kan opleveren die niet gebruiksvriendelijk zijn, is niet bepaald bevredigend. Het heeft voor ons nogmaals benadrukt dat je, wanneer je een app ontwikkelt voor zo’n gesloten platform als iOS, volledig bent overgeleverd aan de grillen van het bedrijf dat het product runt.

Uiteindelijk hebben we er alles aan gedaan om de nieuwe versie van de Vialer app goed te laten functioneren binnen de beperkingen van Apple. De nieuwe iOS versie is gratis te downloaden in de App Store.

{"id": "2cb0e436-7d14-4c2e-a522-c415071b5d1e", "request_id": "5f90a56c-8487-4e0d-b477-88f7f1c62180", "attempt": "5f90a56c-8487-4e0d-b477-88f7f1c62180", "status": "success"}