Contenido del curso

Acceso a sensores y cámara

Cómo leer la batería de iOS con Swift

Resumen

Configurar la comunicación nativa entre Flutter y iOS exige tocar el AppDelegate.swift y conectar un Flutter Method Channel con el sistema operativo. Aquí aprendes a leer el nivel de batería del dispositivo desde Swift y devolver ese dato a Flutter, replicando la lógica que ya construiste en Android pero adaptada al ecosistema de Apple.

Cómo se estructura el AppDelegate.swift para usar Method Channel

El punto de partida vive dentro de la carpeta iOS, en el apartado Runner, donde encontrarás el archivo AppDelegate.swift con sus imports de Flutter y UIKit listos para extender.

La idea es agregar dos piezas: una función privada que consulte la batería del sistema y la lógica dentro de application que cree el canal de comunicación. Ambas se conectan mediante un handler que escucha las llamadas que llegan desde Dart.

¿Qué es un Method Channel en Flutter? Es el puente que permite a tu código Dart invocar funciones nativas en Android o iOS y recibir una respuesta. Funciona como un canal nombrado donde ambos lados acuerdan qué métodos existen.

Cómo crear la función privada getBatteryLevel en Swift

Dentro del AppDelegate declaras una función private func getBatteryLevel que recibe un parámetro result marcado con @escaping FlutterResult. Ese atributo escaping le dice a Swift que el closure puede ejecutarse después de que la función termine, algo común cuando hablas con APIs asíncronas.

El flujo dentro de la función es directo:

  • Habilitar el monitoreo con UIDevice.current.isBatteryMonitoringEnabled = true.
  • Capturar el valor con let batteryLevel = UIDevice.current.batteryLevel.
  • Validar con un if que batteryLevel >= 0 antes de devolverlo.
  • Multiplicar por 100, convertir a Int y concatenar el símbolo de porcentaje.

Si el valor es negativo, significa que iOS no pudo leer la batería. En ese caso devuelves un FlutterError con código "UNAVAILABLE", un mensaje descriptivo y details en nil. Esa estructura le permite a Flutter capturar el error como excepción y reaccionar en la UI.

Cómo conectar el FlutterViewController con el canal nativo

La segunda mitad del trabajo ocurre dentro del método application del AppDelegate. Ahí inicializas el controlador y registras el canal que escuchará las invocaciones desde Dart.

Primero defines el controlador con let controller = window?.rootViewController as! FlutterViewController. Ese cast con as! es lo que te permite acceder al binaryMessenger, una propiedad exclusiva del controlador de Flutter en iOS.

Luego creas el canal con FlutterMethodChannel(name: "native.communicator", binaryMessenger: controller.binaryMessenger). El name debe ser idéntico al que usaste en Android y en Dart, porque es el identificador que une los tres lados.

¿Por qué el name del canal debe coincidir en Android, iOS y Dart? Porque Flutter enruta las llamadas usando ese string como dirección. Si hay una diferencia de un carácter, el método nunca llega al lado nativo.

Cómo manejar las llamadas con setMethodCallHandler

Con el canal creado, llamas a batteryChannel.setMethodCallHandler y le pasas un closure que recibe dos parámetros: call: FlutterMethodCall y result: @escaping FlutterResult.

Dentro del closure va el ruteo de métodos:

  • Un if call.method == "getBatteryLevel" que invoca self?.getBatteryLevel(result: result).
  • Un else que responde con FlutterMethodNotImplemented cuando el nombre no coincide con ningún método registrado.

El uso de self aquí equivale a un this en otros lenguajes y permite acceder a la función privada que definiste arriba. Recuerda envolver todo el bloque del handler dentro de llaves y paréntesis para que Swift lo interprete como un closure válido.

Qué errores devolver cuando la batería no está disponible

La elegancia de este patrón está en separar éxito y fallo desde el nativo. Cuando todo funciona, mandas un string con el porcentaje. Cuando falla, mandas un FlutterError estructurado con tres campos:

  • code: un identificador corto como "UNAVAILABLE".
  • message: una descripción legible para debugging.
  • details: información extra opcional, en este caso nil.

Ese contrato hace que el código Dart pueda usar try/catch limpio y mostrar mensajes apropiados en la UI sin acoplarse a la implementación de cada plataforma.

Con el AppDelegate listo, ya tienes la comunicación bidireccional configurada en Android y en iOS apuntando al mismo canal. Cuéntame en los comentarios si vas a usar este patrón para leer otra propiedad del sistema operativo en tu próximo proyecto.