De Cordova a Capacitor: Guía Completa de Migración para Aplicaciones Móviles
La Gran Migración: De Cordova a Capacitor
Apache Cordova ha sido durante años el estándar de facto para el desarrollo de aplicaciones móviles híbridas. Sin embargo, la evolución del ecosistema móvil y las limitaciones inherentes de Cordova han llevado a muchos equipos a considerar Capacitor como su sucesor natural. En esta guía completa, exploramos el proceso de migración, las diferencias clave y las mejores prácticas para una transición exitosa.
¿Por qué Migrar de Cordova a Capacitor?
Limitaciones de Cordova
- Arquitectura monolítica: Dificulta la modularización
- Dependencia de plugins: Ecosistema fragmentado
- Configuración compleja: XML verboso y propenso a errores
- Debugging limitado: Herramientas de desarrollo básicas
- Performance: Overhead del bridge JavaScript-nativo
Ventajas de Capacitor
- Arquitectura moderna: Basada en web APIs estándar
- Desarrollo nativo: Acceso directo a proyectos iOS/Android
- TypeScript first: Mejor experiencia de desarrollo
- Hot reload: Desarrollo más ágil
- Plugins modernos: APIs consistentes y bien documentadas
Análisis Pre-Migración
Auditoría del Proyecto Cordova
# Script de análisis de proyecto Cordova
#!/bin/bash
echo "=== ANÁLISIS DE PROYECTO CORDOVA ==="
echo
# Información básica
echo "📱 Plataformas instaladas:"
cordova platform list
echo
# Plugins instalados
echo "🔌 Plugins instalados:"
cordova plugin list
echo
# Configuración
echo "⚙️ Configuración (config.xml):"
if [ -f "config.xml" ]; then
echo "✅ config.xml encontrado"
grep -E "<(widget|name|description|platform|plugin)" config.xml
else
echo "❌ config.xml no encontrado"
fi
echo
# Estructura de archivos
echo "📁 Estructura del proyecto:"
tree -L 2 -I 'node_modules|platforms|plugins'
echo
# Dependencias
echo "📦 Dependencias (package.json):"
if [ -f "package.json" ]; then
echo "✅ package.json encontrado"
jq '.dependencies, .devDependencies' package.json 2>/dev/null || echo "Error leyendo package.json"
else
echo "❌ package.json no encontrado"
fiMapeo de Plugins Cordova → Capacitor
// plugin-migration-map.ts
export const PLUGIN_MIGRATION_MAP = {
// Plugins oficiales con equivalentes directos
'cordova-plugin-camera': '@capacitor/camera',
'cordova-plugin-geolocation': '@capacitor/geolocation',
'cordova-plugin-device': '@capacitor/device',
'cordova-plugin-network-information': '@capacitor/network',
'cordova-plugin-statusbar': '@capacitor/status-bar',
'cordova-plugin-splashscreen': '@capacitor/splash-screen',
'cordova-plugin-keyboard': '@capacitor/keyboard',
'cordova-plugin-file': '@capacitor/filesystem',
'cordova-plugin-media': '@capacitor/media',
'cordova-plugin-dialogs': '@capacitor/dialog',
'cordova-plugin-vibration': '@capacitor/haptics',
'cordova-plugin-battery-status': '@capacitor/battery',
'cordova-plugin-contacts': '@capacitor/contacts',
// Plugins que requieren alternativas
'cordova-plugin-inappbrowser': '@capacitor/browser',
'cordova-plugin-local-notification': '@capacitor/local-notifications',
'phonegap-plugin-push': '@capacitor/push-notifications',
'cordova-plugin-file-transfer': 'Custom HTTP implementation',
'cordova-plugin-whitelist': 'Capacitor security model',
// Plugins sin equivalente directo
'cordova-plugin-crosswalk-webview': 'Not needed (modern WebView)',
'cordova-plugin-wkwebview-engine': 'Default in Capacitor iOS',
'cordova-plugin-ionic-webview': 'Built into Capacitor',
// Plugins de terceros populares
'cordova-plugin-googlemaps': '@capacitor-community/googlemaps',
'cordova-plugin-firebase': '@capacitor-firebase/*',
'cordova-plugin-facebook4': '@capacitor-community/facebook-login',
'cordova-plugin-googleplus': '@codetrix-studio/capacitor-google-auth',
'cordova-sqlite-storage': '@capacitor-community/sqlite'
};
// Función para analizar plugins
export function analyzePlugins(cordovaPlugins: string[]): PluginAnalysis {
const analysis: PluginAnalysis = {
directMigration: [],
requiresAlternative: [],
noEquivalent: [],
customImplementation: []
};
cordovaPlugins.forEach(plugin => {
const capacitorEquivalent = PLUGIN_MIGRATION_MAP[plugin];
if (!capacitorEquivalent) {
analysis.noEquivalent.push(plugin);
} else if (capacitorEquivalent.startsWith('@capacitor/')) {
analysis.directMigration.push({
cordova: plugin,
capacitor: capacitorEquivalent
});
} else if (capacitorEquivalent.includes('Custom')) {
analysis.customImplementation.push(plugin);
} else {
analysis.requiresAlternative.push({
cordova: plugin,
capacitor: capacitorEquivalent
});
}
});
return analysis;
}
interface PluginAnalysis {
directMigration: Array<{cordova: string, capacitor: string}>;
requiresAlternative: Array<{cordova: string, capacitor: string}>;
noEquivalent: string[];
customImplementation: string[];
}Proceso de Migración Paso a Paso
Fase 1: Preparación del Entorno
# 1. Backup del proyecto actual
cp -r my-cordova-app my-cordova-app-backup
# 2. Instalar Capacitor CLI
npm install -g @capacitor/cli
# 3. Verificar versiones
node --version # >= 16
npm --version # >= 8
ionic --version # Si usas Ionic
# 4. Crear rama de migración
git checkout -b migration/cordova-to-capacitor
git add .
git commit -m "Backup before Capacitor migration"Fase 2: Inicialización de Capacitor
# En el directorio del proyecto Cordova
npm install @capacitor/core @capacitor/cli
# Inicializar Capacitor
npx cap init "My App" "com.company.myapp" --web-dir="www"
# Instalar plataformas
npm install @capacitor/ios @capacitor/android
npx cap add ios
npx cap add androidFase 3: Configuración de Capacitor
// capacitor.config.ts
import { CapacitorConfig } from '@capacitor/cli';
// Migrar configuración desde config.xml
const config: CapacitorConfig = {
appId: 'com.company.myapp', // De config.xml widget id
appName: 'My App', // De config.xml name
webDir: 'www', // Directorio de build
server: {
androidScheme: 'https'
},
plugins: {
// Migrar preferencias de Cordova
SplashScreen: {
launchShowDuration: 2000,
backgroundColor: '#ffffff',
showSpinner: false
},
StatusBar: {
style: 'default',
backgroundColor: '#000000'
},
Keyboard: {
resize: 'body',
style: 'dark'
}
},
// Configuración específica por plataforma
ios: {
scheme: 'My App',
contentInset: 'automatic'
},
android: {
allowMixedContent: true,
captureInput: true,
webContentsDebuggingEnabled: true
}
};
export default config;Fase 4: Migración de Plugins
# Instalar plugins de Capacitor equivalentes
npm install @capacitor/camera @capacitor/geolocation @capacitor/device
npm install @capacitor/network @capacitor/status-bar @capacitor/splash-screen
npm install @capacitor/keyboard @capacitor/filesystem @capacitor/dialog
# Plugins de la comunidad si es necesario
npm install @capacitor-community/sqlite
npm install @capacitor-community/googlemapsMigración de Código
Ejemplo: Migración del Plugin de Cámara
// ANTES (Cordova)
function takePicture() {
const options = {
quality: 75,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.CAMERA,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 300,
targetHeight: 300
};
navigator.camera.getPicture(
function(imageURI) {
console.log('Image URI: ' + imageURI);
displayImage(imageURI);
},
function(error) {
console.error('Camera error: ' + error);
},
options
);
}
// DESPUÉS (Capacitor)
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
async function takePicture() {
try {
const image = await Camera.getPhoto({
quality: 75,
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
width: 300,
height: 300
});
console.log('Image URI:', image.webPath);
displayImage(image.webPath!);
} catch (error) {
console.error('Camera error:', error);
}
}Ejemplo: Migración de Geolocalización
// ANTES (Cordova)
function getCurrentLocation() {
const options = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 60000
};
navigator.geolocation.getCurrentPosition(
function(position) {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
console.log('Location:', lat, lng);
},
function(error) {
console.error('Location error:', error.message);
},
options
);
}
// DESPUÉS (Capacitor)
import { Geolocation } from '@capacitor/geolocation';
async function getCurrentLocation() {
try {
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 10000
});
const lat = position.coords.latitude;
const lng = position.coords.longitude;
console.log('Location:', lat, lng);
} catch (error) {
console.error('Location error:', error.message);
}
}Migración de Almacenamiento Local
// ANTES (Cordova con cordova-plugin-file)
function writeFile(fileName, data) {
window.resolveLocalFileSystemURL(
cordova.file.dataDirectory,
function(dirEntry) {
dirEntry.getFile(fileName, { create: true }, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.write(new Blob([data], { type: 'text/plain' }));
});
});
}
);
}
// DESPUÉS (Capacitor)
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
async function writeFile(fileName: string, data: string) {
try {
await Filesystem.writeFile({
path: fileName,
data: data,
directory: Directory.Data,
encoding: Encoding.UTF8
});
console.log('File written successfully');
} catch (error) {
console.error('Error writing file:', error);
}
}
async function readFile(fileName: string): Promise {
try {
const result = await Filesystem.readFile({
path: fileName,
directory: Directory.Data,
encoding: Encoding.UTF8
});
return result.data as string;
} catch (error) {
console.error('Error reading file:', error);
throw error;
}
} Migración de Configuración Nativa
iOS: Info.plist
<!-- ANTES (Cordova) -->
<!-- En config.xml -->
<platform name="ios">
<edit-config target="*-Info.plist" file="*-Info.plist" mode="merge">
<dict>
<key>NSCameraUsageDescription</key>
<string>This app needs access to camera to take photos</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access</string>
</dict>
</edit-config>
</platform>
<!-- DESPUÉS (Capacitor) -->
<!-- En ios/App/App/Info.plist -->
<key>NSCameraUsageDescription</key>
<string>This app needs access to camera to take photos</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photo library</string>Android: Permisos y Configuración
<!-- ANTES (Cordova) -->
<!-- En config.xml -->
<platform name="android">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</platform>
<!-- DESPUÉS (Capacitor) -->
<!-- En android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Configuración adicional en android/app/build.gradle -->
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 22
targetSdkVersion 34
}
}Herramientas de Migración Automática
Script de Migración Personalizado
#!/usr/bin/env node
// migration-script.js
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
class CordovaToCapacitorMigrator {
constructor(projectPath) {
this.projectPath = projectPath;
this.configXmlPath = path.join(projectPath, 'config.xml');
this.packageJsonPath = path.join(projectPath, 'package.json');
}
async migrate() {
console.log('🚀 Iniciando migración de Cordova a Capacitor...');
try {
await this.analyzeProject();
await this.setupCapacitor();
await this.migratePlugins();
await this.migrateConfiguration();
await this.generateMigrationReport();
console.log('✅ Migración completada exitosamente!');
} catch (error) {
console.error('❌ Error durante la migración:', error.message);
process.exit(1);
}
}
async analyzeProject() {
console.log('📊 Analizando proyecto Cordova...');
// Leer config.xml
if (fs.existsSync(this.configXmlPath)) {
const configXml = fs.readFileSync(this.configXmlPath, 'utf8');
this.extractConfigData(configXml);
}
// Leer package.json
if (fs.existsSync(this.packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(this.packageJsonPath, 'utf8'));
this.extractDependencies(packageJson);
}
// Analizar plugins instalados
this.analyzeInstalledPlugins();
}
extractConfigData(configXml) {
// Extraer información básica
const widgetMatch = configXml.match(/]*id="([^"]*)"/);;
const nameMatch = configXml.match(/([^<]*)<\/name>/);
const descriptionMatch = configXml.match(/([^<]*)<\/description>/);
this.appConfig = {
appId: widgetMatch ? widgetMatch[1] : 'com.example.app',
appName: nameMatch ? nameMatch[1] : 'My App',
description: descriptionMatch ? descriptionMatch[1] : ''
};
console.log('📱 App ID:', this.appConfig.appId);
console.log('📱 App Name:', this.appConfig.appName);
}
extractDependencies(packageJson) {
this.cordovaPlugins = [];
// Buscar plugins de Cordova en dependencias
const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
Object.keys(allDeps).forEach(dep => {
if (dep.startsWith('cordova-plugin-') || dep.includes('phonegap')) {
this.cordovaPlugins.push(dep);
}
});
console.log('🔌 Plugins Cordova encontrados:', this.cordovaPlugins.length);
}
analyzeInstalledPlugins() {
try {
const pluginList = execSync('cordova plugin list', { encoding: 'utf8' });
console.log('📋 Plugins instalados:');
console.log(pluginList);
} catch (error) {
console.log('⚠️ No se pudo obtener la lista de plugins instalados');
}
}
async setupCapacitor() {
console.log('⚙️ Configurando Capacitor...');
// Instalar Capacitor
execSync('npm install @capacitor/core @capacitor/cli', { stdio: 'inherit' });
// Inicializar Capacitor
const initCommand = `npx cap init "${this.appConfig.appName}" "${this.appConfig.appId}" --web-dir="www"`;
execSync(initCommand, { stdio: 'inherit' });
// Agregar plataformas
execSync('npm install @capacitor/ios @capacitor/android', { stdio: 'inherit' });
execSync('npx cap add ios', { stdio: 'inherit' });
execSync('npx cap add android', { stdio: 'inherit' });
}
async migratePlugins() {
console.log('🔄 Migrando plugins...');
const pluginMigrations = this.getPluginMigrations();
const capacitorPlugins = [];
for (const cordovaPlugin of this.cordovaPlugins) {
const capacitorEquivalent = pluginMigrations[cordovaPlugin];
if (capacitorEquivalent && capacitorEquivalent.startsWith('@capacitor')) {
capacitorPlugins.push(capacitorEquivalent);
console.log(`✅ ${cordovaPlugin} → ${capacitorEquivalent}`);
} else {
console.log(`⚠️ ${cordovaPlugin} requiere migración manual`);
}
}
// Instalar plugins de Capacitor
if (capacitorPlugins.length > 0) {
const installCommand = `npm install ${capacitorPlugins.join(' ')}`;
execSync(installCommand, { stdio: 'inherit' });
}
}
getPluginMigrations() {
return {
'cordova-plugin-camera': '@capacitor/camera',
'cordova-plugin-geolocation': '@capacitor/geolocation',
'cordova-plugin-device': '@capacitor/device',
'cordova-plugin-network-information': '@capacitor/network',
'cordova-plugin-statusbar': '@capacitor/status-bar',
'cordova-plugin-splashscreen': '@capacitor/splash-screen',
'cordova-plugin-keyboard': '@capacitor/keyboard',
'cordova-plugin-file': '@capacitor/filesystem',
'cordova-plugin-dialogs': '@capacitor/dialog',
'cordova-plugin-vibration': '@capacitor/haptics'
};
}
async migrateConfiguration() {
console.log('📝 Generando configuración de Capacitor...');
const capacitorConfig = `import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: '${this.appConfig.appId}',
appName: '${this.appConfig.appName}',
webDir: 'www',
server: {
androidScheme: 'https'
},
plugins: {
SplashScreen: {
launchShowDuration: 2000,
backgroundColor: '#ffffff'
},
StatusBar: {
style: 'default'
}
}
};
export default config;`;
fs.writeFileSync('capacitor.config.ts', capacitorConfig);
console.log('✅ Configuración de Capacitor generada');
}
async generateMigrationReport() {
console.log('📊 Generando reporte de migración...');
const report = `# Reporte de Migración: Cordova → Capacitor
## Información del Proyecto
- **App ID:** ${this.appConfig.appId}
- **App Name:** ${this.appConfig.appName}
- **Fecha de migración:** ${new Date().toISOString()}
## Plugins Migrados
${this.cordovaPlugins.map(plugin => `- ${plugin}`).join('\n')}
## Próximos Pasos
1. Revisar y actualizar el código que usa plugins
2. Probar la aplicación en dispositivos
3. Actualizar scripts de build
4. Configurar CI/CD para Capacitor
## Recursos
- [Documentación de Capacitor](https://capacitorjs.com/docs)
- [Guía de migración](https://capacitorjs.com/docs/cordova/migrating-from-cordova-to-capacitor)
`;
fs.writeFileSync('MIGRATION_REPORT.md', report);
console.log('✅ Reporte guardado en MIGRATION_REPORT.md');
}
}
// Ejecutar migración
if (require.main === module) {
const projectPath = process.argv[2] || process.cwd();
const migrator = new CordovaToCapacitorMigrator(projectPath);
migrator.migrate();
}
module.exports = CordovaToCapacitorMigrator; Testing y Validación
Checklist de Migración
// migration-checklist.ts
export interface MigrationChecklist {
setup: ChecklistItem[];
plugins: ChecklistItem[];
configuration: ChecklistItem[];
testing: ChecklistItem[];
deployment: ChecklistItem[];
}
interface ChecklistItem {
task: string;
completed: boolean;
notes?: string;
}
export const MIGRATION_CHECKLIST: MigrationChecklist = {
setup: [
{ task: 'Backup del proyecto Cordova', completed: false },
{ task: 'Instalar Capacitor CLI', completed: false },
{ task: 'Inicializar proyecto Capacitor', completed: false },
{ task: 'Agregar plataformas iOS/Android', completed: false }
],
plugins: [
{ task: 'Mapear plugins Cordova → Capacitor', completed: false },
{ task: 'Instalar plugins de Capacitor', completed: false },
{ task: 'Migrar código de plugins', completed: false },
{ task: 'Probar funcionalidad de plugins', completed: false }
],
configuration: [
{ task: 'Migrar config.xml → capacitor.config.ts', completed: false },
{ task: 'Configurar permisos iOS (Info.plist)', completed: false },
{ task: 'Configurar permisos Android (AndroidManifest.xml)', completed: false },
{ task: 'Actualizar scripts de build', completed: false }
],
testing: [
{ task: 'Probar en simulador iOS', completed: false },
{ task: 'Probar en emulador Android', completed: false },
{ task: 'Probar en dispositivos físicos', completed: false },
{ task: 'Validar funcionalidad crítica', completed: false },
{ task: 'Probar performance', completed: false }
],
deployment: [
{ task: 'Configurar signing iOS', completed: false },
{ task: 'Configurar signing Android', completed: false },
{ task: 'Actualizar CI/CD pipeline', completed: false },
{ task: 'Generar builds de producción', completed: false },
{ task: 'Subir a App Store/Play Store', completed: false }
]
};Tests Automatizados
// migration.test.ts
import { describe, it, expect, beforeAll } from '@jest/jest';
import { Camera } from '@capacitor/camera';
import { Geolocation } from '@capacitor/geolocation';
import { Device } from '@capacitor/device';
describe('Capacitor Plugin Migration Tests', () => {
describe('Camera Plugin', () => {
it('should be available', () => {
expect(Camera).toBeDefined();
expect(typeof Camera.getPhoto).toBe('function');
});
it('should have correct permissions', async () => {
const permissions = await Camera.checkPermissions();
expect(permissions).toHaveProperty('camera');
expect(permissions).toHaveProperty('photos');
});
});
describe('Geolocation Plugin', () => {
it('should be available', () => {
expect(Geolocation).toBeDefined();
expect(typeof Geolocation.getCurrentPosition).toBe('function');
});
it('should check permissions', async () => {
const permissions = await Geolocation.checkPermissions();
expect(permissions).toHaveProperty('location');
});
});
describe('Device Plugin', () => {
it('should get device info', async () => {
const info = await Device.getInfo();
expect(info).toHaveProperty('platform');
expect(info).toHaveProperty('model');
expect(info).toHaveProperty('operatingSystem');
});
});
});Optimización Post-Migración
Performance Tuning
// capacitor.config.ts - Configuración optimizada
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.company.myapp',
appName: 'My App',
webDir: 'www',
server: {
androidScheme: 'https',
// Optimización para desarrollo
hostname: 'localhost',
iosScheme: 'capacitor'
},
plugins: {
// Optimizaciones de splash screen
SplashScreen: {
launchShowDuration: 1500,
backgroundColor: '#ffffff',
showSpinner: false,
androidSpinnerStyle: 'large',
iosSpinnerStyle: 'small',
spinnerColor: '#999999',
splashFullScreen: true,
splashImmersive: true
},
// Optimizaciones de teclado
Keyboard: {
resize: 'body',
style: 'dark',
resizeOnFullScreen: true
},
// Configuración de status bar
StatusBar: {
style: 'default',
backgroundColor: '#000000',
overlaysWebView: false
}
},
// Configuración específica iOS
ios: {
scheme: 'My App',
contentInset: 'automatic',
scrollEnabled: true,
backgroundColor: '#ffffff'
},
// Configuración específica Android
android: {
allowMixedContent: true,
captureInput: true,
webContentsDebuggingEnabled: false, // Solo en desarrollo
backgroundColor: '#ffffff',
loggingBehavior: 'none' // Producción
}
};
export default config;Scripts de Build Optimizados
// package.json - Scripts actualizados
{
"scripts": {
// Scripts de desarrollo
"dev": "ionic serve",
"dev:ios": "ionic capacitor run ios --livereload --external",
"dev:android": "ionic capacitor run android --livereload --external",
// Scripts de build
"build": "ionic build --prod",
"build:ios": "ionic build --prod && npx cap sync ios && npx cap open ios",
"build:android": "ionic build --prod && npx cap sync android && npx cap open android",
// Scripts de sincronización
"sync": "npx cap sync",
"sync:ios": "npx cap sync ios",
"sync:android": "npx cap sync android",
// Scripts de limpieza
"clean": "rm -rf www && rm -rf node_modules && npm install",
"clean:ios": "npx cap clean ios",
"clean:android": "npx cap clean android",
// Scripts de testing
"test": "jest",
"test:e2e": "cypress run",
"test:migration": "jest --testPathPattern=migration"
}
}Troubleshooting Común
Problemas Frecuentes y Soluciones
// troubleshooting-guide.ts
export const COMMON_ISSUES = {
// Error: Plugin no encontrado
'Plugin not found': {
problem: 'Plugin de Capacitor no está registrado',
solution: `
1. Verificar instalación: npm list @capacitor/plugin-name
2. Sincronizar: npx cap sync
3. Limpiar y rebuild: npx cap clean && npx cap sync
`
},
// Error: Permisos
'Permission denied': {
problem: 'Permisos no configurados correctamente',
solution: `
iOS: Agregar keys en Info.plist
Android: Agregar permissions en AndroidManifest.xml
Verificar: await Plugin.checkPermissions()
`
},
// Error: Build iOS
'iOS build failed': {
problem: 'Problemas de configuración iOS',
solution: `
1. Abrir Xcode: npx cap open ios
2. Verificar signing & capabilities
3. Limpiar build folder en Xcode
4. Verificar deployment target
`
},
// Error: Build Android
'Android build failed': {
problem: 'Problemas de configuración Android',
solution: `
1. Verificar Android SDK
2. Limpiar: ./gradlew clean
3. Verificar build.gradle
4. Sincronizar: npx cap sync android
`
},
// Error: Live reload
'Live reload not working': {
problem: 'Hot reload no funciona en dispositivo',
solution: `
1. Verificar que dispositivo y PC estén en misma red
2. Usar --external flag
3. Verificar firewall
4. Usar IP específica en capacitor.config.ts
`
}
};Mejores Prácticas Post-Migración
1. Estructura de Proyecto
- Separar lógica nativa: Mantén código nativo en carpetas específicas
- Versionado: Incluye carpetas ios/ y android/ en git
- Configuración: Usa variables de entorno para diferentes builds
2. Desarrollo
- Hot reload: Usa live reload para desarrollo ágil
- Debugging: Aprovecha las herramientas nativas
- Testing: Prueba en dispositivos reales regularmente
3. Deployment
- CI/CD: Automatiza builds y deployments
- Code signing: Gestiona certificados correctamente
- Versionado: Sincroniza versiones entre web y nativo
Conclusión
La migración de Cordova a Capacitor representa una evolución natural hacia un desarrollo móvil más moderno y eficiente. Aunque el proceso requiere planificación cuidadosa y testing exhaustivo, los beneficios en términos de performance, experiencia de desarrollo y mantenibilidad justifican ampliamente el esfuerzo.
Capacitor no solo resuelve las limitaciones técnicas de Cordova, sino que también abre nuevas posibilidades para el desarrollo híbrido, acercándolo más a la experiencia nativa sin sacrificar la productividad del desarrollo web.
La clave del éxito está en una migración gradual, testing continuo y aprovechamiento de las nuevas capacidades que ofrece Capacitor para crear aplicaciones móviles de calidad superior.
¿Has migrado ya de Cordova a Capacitor? ¿Qué desafíos encontraste en el proceso? Comparte tu experiencia en los comentarios.