var monsterinsights_frontend = {"js_events_tracking":"true","download_extensions":"doc,pdf,ppt,zip,xls,docx,pptx,xlsx","inbound_paths":"[{\"path\":\"\\\/go\\\/\",\"label\":\"affiliate\"},{\"path\":\"\\\/recommend\\\/\",\"label\":\"affiliate\"}]","home_url":"https:\/\/accent-systems.com","hash_tracking":"false","v4_id":"G-069LN2YPE1"};
La demo de la App “iBKS Hello World” es un proyecto que contiene las funciones más importantes para comenzar a interactuar con un Beacon.
En este documento se explica cómo está estructurado este proyecto, implementado en swift y XCode 8.2.1, y cuáles son las funciones que se pueden encontrar en él.
PÚBLICO
Este documento está enfocado para desarrolladores de Apps que no tienen experiencia en la gestión de comunicación con beacons.
Después de descargar el proyecto “iBKS Hello World”, solo tienes que abrirlo en XCode, firmarlo con tu provisión de desarrollador y compilarlo. No hay bibliotecas adicionales ni pasos que debas incluir. El proyecto está estructurado para mostrar tres funcionalidades importantes, cada una en una clase diferente:
ScanViewController: escanea y lista los beacons que están anunciándose alrededor y permite descubrir servicios y características. Los servicios y características se imprimirán en la consola de salida de XCode. CoreBluetooth se utiliza para esta demo.
NotificationsViewController: Muestra un diálogo de notificación en la App activado por un paquete de beacon específico detectado. También incluye la vista de configuración que te permite configurar los parámetros en el beacon que el dispositivo está buscando. CoreLocation se utiliza para esta demo.
Monitoreo en segundo plano (desde AppDelegate): Inicia el monitoreo y el rango en segundo plano para beacons en una región y realiza algunas acciones cuando los detecta (enviar notificación, abrir la app, …) incluso cuando la app está detenida. El monitoreo en segundo plano comienza cuando la APP pasa a segundo plano y se detiene cuando la APP pasa a primer plano. CoreLocation se utiliza para esta demo.
3. Permisos de la App
Se necesitan varios permisos y solicitudes para este ejemplo. Bluetooth y Ubicación se utilizarán para escanear y medir beacons en primer plano, y Notificaciones Locales para el monitoreo en segundo plano con el fin de notificar al usuario.
3.1. Ubicación
Para permitir la ubicación tanto en modo de ejecución en primer plano como en segundo plano, es necesario agregar la clave “Privacy – Location Always Usage Description” en el archivo Info.plist. Debería verse así:
También solicita permisos de ubicación en el AppDelegate en el método “didFinishLaunchingWithOptions” de la siguiente manera:
El único paso que debes hacer para que Bluetooth funcione correctamente para este ejemplo es habilitar las “Actualizaciones de ubicación” y “Usa accesorios Bluetooth LE” en los Modos de Fondo para las Capacidades de la app. Esto es necesario solo para la función en segundo plano.
Ten en cuenta que incluso si CoreLocation y CoreBluetooth funcionan correctamente para el escaneo y rango en primer plano en este ejemplo, si planeas publicar tu app, se requerirá agregar algunas claves de privacidad y uso adicionales para Bluetooth en el archivo Info.plist. Por favor, consulta la guía de Desarrolladores de Apple para más información.
Las capacidades de la app deberían verse así (todo lo demás está deshabilitado):
3.3. Notificaciones Locales
La solicitud de Notificaciones Locales también se solicita en el AppDelegate dentro del método “didFinishLaunchingWithOptions” justo después del permiso de ubicación con el siguiente código:
let notificationSettings = UIUserNotificationSettings(types: [.sound, .alert], categories: nil)
UIApplication.shared.registerUserNotificationSettings(notificationSettings)
Al igual que para Bluetooth, puede ser necesaria la clave de descripción de Uso de Notificaciones Locales en el archivo Info.plist si la app debe publicarse en la App Store.
4. Escanear dispositivos Bluetooth
Todas las funciones necesarias utilizadas para escanear dispositivos Bluetooth están en ScanViewController. Los pasos para lograrlo son:
Implementar el CBCentralManagerDelegate en el ScanViewController
Definir e iniciar un CBCentralManager desde el método “viewDidLoad”. Asignar el delegado self al gestor central
Cuando se recibe el estado “.poweredOn” en el método “centralManagerDidUpdateState” (que es necesario para el CBCentralManagerDelegate), se puede iniciar el escaneo.
Los dispositivos escaneados se recibirán en el método “centralManager – didDiscover peripheral”. Trátalos adecuadamente según tus necesidades.
Opcionalmente, conecta al dispositivo. En el proyecto, la conexión se realiza cuando se hace clic en un dispositivo de la lista.
Para la conexión, se necesitan CBPeripheralDelegate y sus métodos. Una vez que comienza la conexión, el estado se recibirá en el método “centralManager – didConnect”. Luego se puede iniciar el descubrimiento de servicios y características. Para esta función, todos los pasos se registran en la consola de salida en XCode.
La app muestra una lista de todos los dispositivos Bluetooth detectados con su propio (y ordenado por) RSSI, nombre y UUID asignado.
5. Notificaciones
En algunos casos, es útil mostrar una notificación al usuario cuando se detecta un beacon específico en un rango de distancia particular. Este ejemplo se muestra en NotificationsViewController. CoreLocation se utiliza para este ejemplo.
iOS solo permite medir para un límite de 20 dispositivos. Establece los parámetros (UUID, Major, Minor, Rango y Mensaje) para el beacon que deseas detectar, define su región y comienza a medir con CoreLocartion. Estos son los pasos.:
Define la región del beacon, con el UUID, Major y Minor de tu beacon.
Comienza a medir para esa región y espera a que el beacon sea detectado.
Una vez detectado, verifica si está lo suficientemente cerca y la distancia coincide con el rango que definiste en la configuración.
Si el rango coincide para nuestra región, muestra un diálogo con el mensaje definido también en la configuración.
6. Escaneo en segundo plano
Otra función interesante es el escaneo en segundo plano. Esta permite monitorear los beacons en segundo plano incluso si la app está cerrada. El simple rango no está permitido directamente en segundo plano, pero hay una forma sencilla de hacerlo funcionar.
iOS es capaz de monitorear una región (beacon) incluso si la app ha sido cerrada. Informará en el método “didEnterRegion” cuando se detecte el beacon (puede tardar hasta 5 segundos) y solo ocurrirá cuando el estado cambie de fuera a dentro de la región.
También informará en el método “didExitRegion” cuando el beacon ya no se detecte durante algún tiempo. Esta detección puede tardar de 15 segundos a 5 minutos para evitar detecciones falsas. Este estado solo ocurrirá cuando se cambie de dentro a fuera de la región.
Además, hay un método “didDetermineState”, que es más útil ya que informa el estado actual y no solo cuando se cambian los estados. Es el método utilizado en este ejemplo. También es más seguro de usar en segundo plano según la documentación de Apple.
Todos los métodos de fondo utilizados para CoreLocation están definidos y delegados en el AppDelegat. Para este ejemplo, el monitoreo en segundo plano se inicia en el “applicationDidEnterBackground” (y solo si el interruptor en MainViewController está activado) y se detendrá en el método “applicationWillEnterForeground”
Cuando un beacon es detectado en segundo plano por el OS, esto despertará la APP por un período máximo de 10 segundos para procesar cualquier tarea necesaria. En este caso, la tarea será medir el beacon para verificar su proximidad y mostrar, si es necesario, una notificación local. Ten en cuenta que como la medición solo puede durar 10 segundos máximo, si el beacon no se mide correctamente o la proximidad no coincide con la establecida en la configuración, la notificación no se mostrará, pero el estado cambiará de todos modos a dentro de la región.
(function() {
var data = {"agents":[{"name":"Eli","phone":"+34653105809","time_start":"08:00","time_end":"17:00","days":[1,2,3,4,5]},{"name":"Albert","phone":"+34662925746","time_start":"08:00","time_end":"17:00","days":[1,2,3,4,5]}],"distribution":"random","message":"Hello! I would like to ask for more info."};
var button = document.getElementById('accent-whatsapp-button');
var agentsPanel = document.getElementById('accent-whatsapp-agents');
if (!button) return;
button.addEventListener('click', function(e) {
e.preventDefault();
if (data.distribution === 'all' && data.agents.length > 1 && agentsPanel) {
// Mostrar panel d'agents
agentsPanel.classList.toggle('active');
} else {
// Obrir WhatsApp directament amb un agent
var agent = data.distribution === 'random'
? data.agents[Math.floor(Math.random() * data.agents.length)]
: data.agents[0];
var phone = agent.phone.replace(/[^0-9]/g, '');
var message = encodeURIComponent(data.message);
var url = 'https://wa.me/' + phone + '?text=' + message;
window.open(url, '_blank');
}
});
// Tancar panel si es fa clic fora
document.addEventListener('click', function(e) {
if (agentsPanel && !e.target.closest('#accent-whatsapp')) {
agentsPanel.classList.remove('active');
}
});
})();
var yith_wcwl_l10n = {"ajax_url":"/wp-admin/admin-ajax.php","redirect_to_cart":"no","yith_wcwl_button_position":"after_add_to_cart","multi_wishlist":"","hide_add_button":"1","enable_ajax_loading":"","ajax_loader_url":"https://accent-systems.com/wp-content/plugins/yith-woocommerce-wishlist/assets/images/ajax-loader-alt.svg","remove_from_wishlist_after_add_to_cart":"1","is_wishlist_responsive":"1","time_to_close_prettyphoto":"3000","fragments_index_glue":".","reload_on_found_variation":"1","mobile_media_query":"768","labels":{"cookie_disabled":"Lo siento, pero esta caracter\u00edstica solo est\u00e1 disponible si las cookies de tu navegador est\u00e1n activadas.","added_to_cart_message":"\u003Cdiv class=\"woocommerce-notices-wrapper\"\u003E\u003Cdiv class=\"woocommerce-message\" role=\"alert\"\u003EProducto a\u00f1adido al carrito con \u00e9xito\u003C/div\u003E\u003C/div\u003E"},"actions":{"add_to_wishlist_action":"add_to_wishlist","remove_from_wishlist_action":"remove_from_wishlist","reload_wishlist_and_adding_elem_action":"reload_wishlist_and_adding_elem","load_mobile_action":"load_mobile","delete_item_action":"delete_item","save_title_action":"save_title","save_privacy_action":"save_privacy","load_fragments":"load_fragments"},"nonce":{"add_to_wishlist_nonce":"d8e55c6cf2","remove_from_wishlist_nonce":"a696414511","reload_wishlist_and_adding_elem_nonce":"208318d445","load_mobile_nonce":"ec4855d2b1","delete_item_nonce":"e5479778c5","save_title_nonce":"33b8baf47d","save_privacy_nonce":"3c635f96f2","load_fragments_nonce":"1e4815571b"},"redirect_after_ask_estimate":"","ask_estimate_redirect_url":"https://accent-systems.com","enable_notices":""};
//# sourceURL=jquery-yith-wcwl-js-extra
( function( domain, translations ) {
var localeData = translations.locale_data[ domain ] || translations.locale_data.messages;
localeData[""].domain = domain;
wp.i18n.setLocaleData( localeData, domain );
} )( "contact-form-7", {"translation-revision-date":"2025-12-01 15:45:40+0000","generator":"GlotPress\/4.0.3","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","plural-forms":"nplurals=2; plural=n != 1;","lang":"es"},"This contact form is placed in the wrong place.":["Este formulario de contacto est\u00e1 situado en el lugar incorrecto."],"Error:":["Error:"]}},"comment":{"reference":"includes\/js\/index.js"}} );
//# sourceURL=contact-form-7-js-translations
var elementorFrontendConfig = {"environmentMode":{"edit":false,"wpPreview":false,"isScriptDebug":false},"i18n":{"shareOnFacebook":"Compartir en Facebook","shareOnTwitter":"Compartir en Twitter","pinIt":"Pinear","download":"Descargar","downloadImage":"Descargar imagen","fullscreen":"Pantalla completa","zoom":"Zoom","share":"Compartir","playVideo":"Reproducir v\u00eddeo","previous":"Anterior","next":"Siguiente","close":"Cerrar","a11yCarouselPrevSlideMessage":"Diapositiva anterior","a11yCarouselNextSlideMessage":"Diapositiva siguiente","a11yCarouselFirstSlideMessage":"Esta es la primera diapositiva","a11yCarouselLastSlideMessage":"Esta es la \u00faltima diapositiva","a11yCarouselPaginationBulletMessage":"Ir a la diapositiva"},"is_rtl":false,"breakpoints":{"xs":0,"sm":480,"md":768,"lg":991,"xl":1440,"xxl":1600},"responsive":{"breakpoints":{"mobile":{"label":"M\u00f3vil vertical","value":767,"default_value":767,"direction":"max","is_enabled":true},"mobile_extra":{"label":"M\u00f3vil horizontal","value":880,"default_value":880,"direction":"max","is_enabled":false},"tablet":{"label":"Tableta vertical","value":991,"default_value":1024,"direction":"max","is_enabled":true},"tablet_extra":{"label":"Tableta horizontal","value":1200,"default_value":1200,"direction":"max","is_enabled":false},"laptop":{"label":"Port\u00e1til","value":1366,"default_value":1366,"direction":"max","is_enabled":false},"widescreen":{"label":"Pantalla grande","value":2400,"default_value":2400,"direction":"min","is_enabled":false}},"hasCustomBreakpoints":true},"version":"3.35.8","is_static":false,"experimentalFeatures":{"e_font_icon_svg":true,"additional_custom_breakpoints":true,"container":true,"e_optimized_markup":true,"e_pro_free_trial_popup":true,"nested-elements":true,"home_screen":true,"global_classes_should_enforce_capabilities":true,"e_variables":true,"cloud-library":true,"e_opt_in_v4_page":true,"e_components":true,"e_interactions":true,"e_editor_one":true,"import-export-customization":true},"urls":{"assets":"https:\/\/accent-systems.com\/wp-content\/plugins\/elementor\/assets\/","ajaxurl":"https:\/\/accent-systems.com\/wp-admin\/admin-ajax.php","uploadUrl":"https:\/\/accent-systems.com\/wp-content\/uploads"},"nonces":{"floatingButtonsClickTracking":"b069269947"},"swiperClass":"swiper","settings":{"page":[],"editorPreferences":[]},"kit":{"viewport_tablet":"991","active_breakpoints":["viewport_mobile","viewport_tablet"],"global_image_lightbox":"yes","lightbox_enable_counter":"yes","lightbox_enable_fullscreen":"yes","lightbox_enable_zoom":"yes","lightbox_enable_share":"yes","lightbox_title_src":"title","lightbox_description_src":"description"},"post":{"id":221800,"title":"iBKS%20Hello%20World%20para%20iOS%20-%20Accent%20Systems","excerpt":"","featuredImage":false}};
//# sourceURL=elementor-frontend-js-before
var js_porto_vars = {"rtl":"","theme_url":"https://accent-systems.com/wp-content/themes/porto-child","ajax_url":"https://accent-systems.com/wp-admin/admin-ajax.php?lang=es","cart_url":"https://accent-systems.com/cart/","change_logo":"","container_width":"1140","grid_gutter_width":"30","show_sticky_header":"1","show_sticky_header_tablet":"1","show_sticky_header_mobile":"","ajax_loader_url":"//accent-systems.com/wp-content/themes/porto/images/ajax-loader@2x.gif","category_ajax":"","compare_popup":"1","compare_popup_title":"","prdctfltr_ajax":"","slider_loop":"1","slider_autoplay":"","slider_autoheight":"","slider_speed":"5000","slider_nav":"","slider_nav_hover":"1","slider_margin":"","slider_dots":"1","slider_animatein":"","slider_animateout":"","product_thumbs_count":"4","product_zoom":"1","product_zoom_mobile":"1","product_image_popup":"1","zoom_type":"inner","zoom_scroll":"1","zoom_lens_size":"200","zoom_lens_shape":"square","zoom_contain_lens":"1","zoom_lens_border":"1","zoom_border_color":"#888888","zoom_border":"0","screen_xl":"1170","screen_xxl":"1420","mfp_counter":"%curr% of %total%","mfp_img_error":"\u003Ca href=\"%url%\"\u003EThe image\u003C/a\u003E could not be loaded.","mfp_ajax_error":"\u003Ca href=\"%url%\"\u003EThe content\u003C/a\u003E could not be loaded.","popup_close":"Close","popup_prev":"Previous","popup_next":"Next","request_error":"The requested content cannot be loaded.\u003Cbr/\u003EPlease try again later.","loader_text":"Loading...","submenu_back":"Back","porto_nonce":"640d676217","use_skeleton_screen":[],"user_edit_pages":"","quick_access":"Click to edit this element.","goto_type":"Go To the Type Builder.","legacy_mode":"","home_url":"https://accent-systems.com/es/inicio/","is_multisite":"","current_blog_id":"1","texts":{"search_history_title":"Search History","search_history_clear_all":"Clear"},"add_to_label":"A\u00f1adir al carrito","login_popup_waiting_msg":"Please wait...","wc_placeholder_img":"https://accent-systems.com/wp-content/uploads/woocommerce-placeholder-300x300.png","shop_filter_layout":null,"func_url":"https://accent-systems.com/wp-content/plugins/porto-functionality/","active_lang":"es"};
//# sourceURL=porto-theme-js-extra
(function() {
var data = {"lang":"es","siteUrl":"https:\/\/accent-systems.com","translations":{"Home":"Inicio","Devices":"Dispositivos","Shop":"Tienda","Products":"Productos","Categories":"Categor\u00edas"},"pageSlugMap":{"devices":"dispositivos","development-services":"servicios-desarrollo","about-us":"sobre-nosotros","contact-us":"contacto","projects":"proyectos","team":"equipo","blog":"blog-es"}};
function fixBreadcrumbs() {
// Buscar tots els breadcrumbs (NOMÉS breadcrumbs, no language switchers)
var breadcrumbs = document.querySelectorAll('.breadcrumbs-wrap, .breadcrumb, .woocommerce-breadcrumb, [class*="breadcrumb"]');
breadcrumbs.forEach(function(bc) {
// Saltar si és un language switcher
if (bc.closest('.asw-wrap') || bc.closest('.asw-lang') || bc.classList.contains('asw-wrap') || bc.classList.contains('asw-lang')) {
return;
}
var links = bc.querySelectorAll('a');
links.forEach(function(link) {
// Saltar links dins de language switchers
if (link.closest('.asw-wrap') || link.closest('.asw-lang')) {
return;
}
var text = link.textContent.trim();
var href = link.getAttribute('href');
// Traduir text
if (data.translations[text]) {
link.textContent = data.translations[text];
}
// Arreglar URLs
if (href) {
var newHref = href;
// Arreglar URL del home (va directament al site sense /es/)
if (href === data.siteUrl || href === data.siteUrl + '/') {
newHref = data.siteUrl + '/' + data.lang + '/';
}
// Arreglar pàgines amb slug EN
else {
for (var enSlug in data.pageSlugMap) {
var esSlug = data.pageSlugMap[enSlug];
var pattern = new RegExp('^' + data.siteUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '/' + enSlug + '/?$');
if (pattern.test(href)) {
newHref = data.siteUrl + '/' + data.lang + '/' + esSlug + '/';
break;
}
}
}
// Arreglar categories sense prefix d'idioma
if (href.indexOf('/product-category/') !== -1 && href.indexOf('/' + data.lang + '/') === -1) {
newHref = href.replace('/product-category/', '/' + data.lang + '/product-category/');
}
if (newHref !== href) {
link.setAttribute('href', newHref);
}
}
});
});
}
// Executar quan el DOM estigui llest
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fixBreadcrumbs);
} else {
fixBreadcrumbs();
}
// També executar després d'un petit delay per assegurar que tot s'ha carregat
setTimeout(fixBreadcrumbs, 100);
})();