sveltesveltekitdevicemotion

Why devicemotion not working on Safari/Chrome iOS


I read on the docs, that devicemotion events are fully supported on Safari on iOS

Full support

But from what I tested, doesn't seem to work both on Safari iOS and Chrome iOS. I use svelte, but because the available REPL (sveltelab) doesn't support devicemotion, I put a built link, with the code under. This link works on chrome android.

<script context="module">
    // @ts-nocheck
  import Device from 'svelte-device-info'
</script>

<script>
    import { onMount } from "svelte";
    import { browser } from '$app/environment';

    let tempX = 0, tempY = 0, tempZ = 0
    let distX = 0, distY = 0, distZ = 0
    let avgXYZ = 0, total = 0
    let startShake = false
    let mobile = false
    let ready = false
    let isSafari = false

    function handleAcl(event) {
        distX = event.accelerationIncludingGravity.x - tempX
        distY = event.accelerationIncludingGravity.y - tempY
        distZ = event.accelerationIncludingGravity.z - tempZ
        
        avgXYZ = (Math.abs(distX)+Math.abs(distY)+Math.abs(distZ))/3
        
        if (avgXYZ > 1) {
            total = total + avgXYZ
        }
        
        tempX = event.accelerationIncludingGravity.x
        tempY = event.accelerationIncludingGravity.y
        tempZ = event.accelerationIncludingGravity.z
    }

    onMount(() => {
        mobile = Device.isMobile
        ready = true
        startShake = true
        if (browser) {
            let ua = window.navigator.userAgent.toLowerCase(); 

            if (ua.indexOf('safari') != -1) { 
                if (ua.indexOf('chrome') > -1) {
                    isSafari = false
                } else {
                    isSafari = true
                }
            }
        }
    })

</script>

<svelte:window on:devicemotion={startShake ? handleAcl : null}></svelte:window>
<svelte:head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <meta name="HandheldFriendly" content="true" />
</svelte:head>

<section>
{#if ready}
    {#if mobile}
        <h1>Safari Debugger</h1>
        <h3>Your browser is {isSafari ? 'Safari' : 'not Safari'}</h3>
        <br>
        <p>X: <strong>{distX.toFixed(2)}</strong></p>
        <p>Y: <strong>{distY.toFixed(2)}</strong></p>
        <p>Z: <strong>{distZ.toFixed(2)}</strong></p>
        <br>
        <p>Average: {avgXYZ.toFixed(2)}</p>
        <div 
            style:width="{avgXYZ*10}px"
            class="bar"></div>
    {:else}
        <h1>Play this game on your mobile phone</h1>
    {/if}
{/if}
</section>


<style>
    :global(body) {
        margin: 0;
        padding: 0;
        overflow: hidden;
        background-color: rgb(22, 18, 48);
    }
    h1 {
        font-size:2rem;
        margin: 0;
        font-family: monospace;
        text-align: center;
        color:whitesmoke;
    }
    h3 {
        font-size:1.5rem;
        margin: 0;
        font-family: monospace;
        text-align: center;
        color:whitesmoke;
    }
    p {
        margin: 0;
        font-size:1.2rem;
        color:whitesmoke;
        font-family: sans-serif;
    }
    .bar {
        margin-top: 2rem;
        background-color: orange;
        height:50px;
    }
    section {
        width:100%;
        height:100vh;
        display:flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
    }
</style>

Solution

  • So, apple decided to add a permission request to access the devicemotion & deviceorientation events. It will granted by adding gesture (click/tap, swipe, etc). This is similar to permission request to access camera.

    So I have to add a button (or any gesture event), and it will be prompted with permission request.

    I add this:

    function deviceMotionPermissionRequest() {
        // feature detect
        if (typeof DeviceMotionEvent.requestPermission === 'function') {
          DeviceMotionEvent.requestPermission()
            .then(permissionState => {
              if (permissionState === 'granted') {
                window.addEventListener('devicemotion', () => {});
              }
            })
            .catch(console.error);
        } else {
          // handle regular non iOS 13+ devices
        }
      }
    

    This function already updated on my original link, so it should be working.