Archivo de la etiqueta: .NET core

Problemas con el Provisioning en las aplicaciones para iOS con Xamarin.Forms creadas con Visual Studio para Windows

Pues eso… Si estás haciendo una aplicación con Xamarin.Forms para iOS (de Apple) y quieres probar el código en un emulador o un dispositivo conectado a tu equipo, debes tener en cuenta que si estás desarrollando con Visual Studio para Mac todo irá bien, pero si estás usando Visual Studio para Windows, simplemente no te funcionará… salvo que antes te funcionase… ahora te lo explico 😉

Aclaro esto último:
Según me cuentan hay un problema con el provisioning (el que se indica en iOS Bundle Signing), es decir, autenticar tu cuenta de Apple Developer (*) con la aplicación, debido a que recientemente Apple ha realizado un cambio que provoca que se bloquee temporalmente las peticiones y se obtenga un error.

También según me cuentan: Es una issue con prioridad alta y están revisándolo.

Así que… esperemos que pronto esté solucionado.

Lo que comenté de salvo que antes te funcionase es porque (también según me cuentan): Si en el PC, previamente se ha utilizado Hot Restart con la cuenta, se tiene en caché lo necesario para evitar alguna petición y por ese motivo, te puede funcionar sin problemas.

 

Capturas de iOS Bundle Signing

Aquí te dejo un par de (o tres) capturas de la opción iOS Bundle Signing en las opciones del proyecto para iOS, tanto en Windows (que es donde da el error) como en un Mac (que no da error y funciona sin problemas).

Figura 1. La firma (autenticación) en Visual Studio para Mac funciona correctamente.

 

Figira 2. Fallo al autenticar con Visual Studio para Windows usando un dispositivo.

 

Figura 3. Fallo al autenticar con Visual Studio para Windows usando el emulador.

 

Y esto es todo, espero que te sea de utilidad.

Cuando esté solucionado intentaré poner una nota en este post de que así es… ¡espero no olvidarme! 😉

Nos vemos.
Guillermo

(*)
Para desarrollar (creo que incluso para probar) aplicaciones de Xamarin.Forms para iOS (Apple) debes tener creada una cuenta de Apple Developer, que a día de hoy cuesta 99€ al año. Esa cuenta se debe usar tanto en Windows como en Mac.

P.S.
Título en inglés:
Provisioning issues in iOS apps with Xamarin.Forms built with Visual Studio for Windows.

Charlas (podcasts) con Luis del Valle Hernández de ProgramarFacil

Pues eso… esto es de hace unos años, del 4 de junio de 2015 concretamente (según me ha dicho Luis, porque yo ya no lo recordaba), y aunque la entrevista/charla se hizo de una vez, él las publicó (en modo podcast, audio) en dos partes.

Pero como las he buscado y no las he encontrado ni en mi sitio (elguille.info) ni en este blog, lo publico ahora aunque sea después de tanto tiempo ya que considero que deben estar «localizables» porque fueron las primeras :-).
Así podrás saber más cosas sobre mí… aunque a fecha de hoy ya hay 4 charlas en YouTube, es conveniente que estos dos podcasts también estén visibles al buscar las charlas o entrevistas que me han hecho.

Así que… aquí tienes los enlaces a estos dos podcasts con la entrevista de Luis del Valle Hernández que me hizo el 4 de junio de 2015:

Charla 1: Primera parte de la charla con El Guille programación .NET

Charla 2: Segunda parte de la charla con El Guille programación .NET

 

Y ya que estamos con los enlaces, te pongo también los 4 de YouTube que me han hecho entre junio de 2020 y abril de 2021 (en plena pandemia del COVID-19):

1- Entrevista a Guillermo Som (elGuille) por Sergio Alejandro Campos para el canal EXCELeINFO (16-jun-2020)

2- Pablo Tilotta entrevista a ElGuille (21-jul-2020)

3- La evolución de .Net, una plática de Héctor de León con «El Guille» (17-nov-2020)

4- SevillaDotNet: Café Virtual con ‘ElGuille’ (07-abr-2021)

 

Nos vemos.
Guillermo

Acompáñame en el café virtual con Marcelo este miércoles 7 de abril

Pues eso… Marcelo de SevillaDotNet me ha invitado a un café virtual el próximo miércoles día 7 de abril a las 18:30 hora de la España peninsular (una hora menos en Canarias), así que… si nos quieres acompañar regístrate y asiste siguiendo este enlace: Café Virtual con Guillermo Som.

Nota del martes 6 de abril
Si quieres, también puedes verlo en directo por YouTube o bien usar este mismo enlace para después de mañana 7 de abril de 2021:

SevillaDotNet: Café Virtual con ‘ElGuille’

 

 

Si quieres ver qué hora es ahora en España (Madrid) sigue el enlace y así sabrás qué hora será en tu país 😉
Me comentan que al pulsar el enlace te muestra la hora en tu ciudad.

Este es el resumen de la charla cafetera 🙂

En este café virtual vamos a charlar con Guillermo Som. Tras tantos años participando y ayudándonos a todos con su sitio elguille.info, probablemente te haya ayudado en algún momento.
Vamos a charlar sobre sus inicios en la informática, sobre el camino que él ha tenido cómo programador, de .NET y su evolución y mucho más.

¿Te unes a nosotros?

¡Te espero!

Nos vemos.
Guillermo

Solucionando problemas varios con Xamarin.Forms para UWP

Pues eso… a raíz de lo que te puse hace un rato (sobre GetExecutingAssembly) que al querer hacer la captura en el proyecto de prueba (Prueba Mobile) para UWP (Universal Windows Platform o Plataforma Universal de Windows), resulta que no me funcionaba… pero como la otra aplicación que estoy haciendo (Reservas Kayak Mobile) para kayakmaro.com si que iba la versión para UWP, pues hice ahí la captura.

La cuestión es que tenía 4 errores, uno de ellos decía algo así:
type universe cannot resolve assembly, concretamente señalaba a una de las DLL que utilizo en el proyecto que usan las versiones de Android, UWP e iOS (bueno, esta última no, ya que he desistido en crear proyectos para iOS porque ni usando un Mac de forma remota lo compila).

El error concreto es (sin indicar el nombre de la DLL ni resto de información):
Cannot resolve Assembly or Windows Metadata file ‘Type universe cannot resolve assembly:

El proyecto ese que te comento que vale para las distintas versiones de la aplciación es una DLL (biblioteca de clases de .NET Standard 2.0) de forma que se pueda usar en distintas plataformas.
Esa DLL la tienes que tener referenciada en cada proyecto (de cada plataforma) y así es como funciona esto con Xamarin.Forms.
En mi caso, la DLL de .NET Standard se llama Prueba Mobile y la aplicación para Android es: Prueba Mobile.Android, y sin necesidad de poner mucha imaginación sabrás que la de UWP se llama Prueba Mobile.UWP.

Ese proyecto (el de la DLL) tiene referencias a otros dos proyectos, también creados para .NET Standard 2.0, en estos dos casos, los he hecho con Visual Basic para utilizar (con algunos cambios en cierto código) el código que ya tenía hecho y que no era plan de volver a hacerlo de nuevo y en C#, que es el lenguaje que usan las aplicaciones de Xamarin.Forms.

Pues bien, el error ese de error type universe cannot resolve assembly me apuntaba a una de esas dos DLL que hace referencia la DLL principal (el nombre da lo mismo), y según parece, al no encontrar esa DLL generaba otros errores, concretamente que no podía copiar ciertos ficheros de la carpeta obj\x86\Debug porque no existían.
Y ahí se quedaba.

Los erores de que no podía copiar ficheros de la carpeta obj\x86\Debug eran:
Could not copy the file «obj\x86\Debug\MainPage.xbf» because it was not found.
Could not copy the file «obj\x86\Debug\App.xbf» because it was not found.
Could not copy the file «obj\x86\Debug\Prueba Mobile.UWP.xr.xml» because it was not found.

Así que… me pongo a buscar en internet y me encuentro con varios consejos (algunos copiados de otros previamente hallados, por lo que supongo que el que lo decía, ni lo había comprobado), pero esos consejos (o casi todos) hacían referencia a .NET Framework y «el target framework» o a hacer referencias a paquetes NuGet, pero no eran esos los casos… ya que dicha DLL ni está creada con .NET Framework ni está en un paquete de NuGet, aún así… lo intenté… pero sin resultados, al menos con lo de los paquetes NuGet, ya que lo del target framework, como que no… en fin…

Por probar, copié el proyecto para UWP del otro proyecto al de prueba (cambiando los espacios de nombres, etc.), pero tampoco.

Que si eliminar el directorio .vs, que si eliminar las carpetas obj y bin, pero nada de nada…

La solución

Al final decidí añadir una referencia en el proyecto para UWP (que en realidad no debería hacer falta) a la DLL que generaba ese error, y… ¡SOLUCIONADO!
¡MANDA COHONES!

Repasando los errores

Y para ser más concreto, he vuelto a poner todo como estaba antes de solucionarlo, con idea de poder hacer capturas y ponerte la descripción exacta de los errores.

En realidad lo que he tenido que hacer es quitar la referencia a la DLL y eliminar las carpetas obj y bin del proyecto para UWP.

Esta es la captura de los errores de Visual Studio (pincha en la imagen para agrandar):

Figura 1. Los errores en Visual Studio.

Y esta captura (figura 2) tiene el proyecto para UWP con la referencia (realmente no necesaria) a la DLL que no puede resolver, hecho esto… todo funciona bien.

Figura 2. Las propiedades del proyecto con la referencia a la DLL «fatídica».

Te he dicho antes que realmente no necesaria (añadir la referencia al proyecto UWP) porque en realidad ese proyecto no utiliza esa DLL, y si la utiliza es a través de la DLL Prueba Mobile, que como puedes ver en la figura 3 tiene su propia referencia a MKNDatos, ya que desde el proyecto Prueba Mobile sí que se utilizan las clases de MKNDatos.

Figura 3. Los proyectos referenciados en Prueba Mobile.

Lo que he buscado en la red (en Google)

Aquí tienes el enlace (unod e ellos) que he usado para hacer la búsqueda en Google:

https://www.google.es/search?q=type+universe+cannot+resolve+assembly

Aunque en la figura 1 no se ve, originalmente me dio el XamlCompiler error WMC106 e hice esta búsqueda:

XamlCompiler error WMC1006

Y esto es todo amigo. Espero que, como de costumbre, te sea de utilidad 😉

Nos vemos.
Guillermo

Mejorar el rendimiento al usar GetExecutingAssembly

Pues eso… ya te lo comenté hace unos meses, aunque estaba escondido en las novedades de gsNotasNET.Android v2.0.0.33, y era porque me daba error (sin avisar, para más señas) a la hora de usar la forma recomendada de usar Assembly.GetExecutingAssembly.

En esa ocasión me daba error la aplicación de Xamarin.Forms y sin saber porqué… Pero la he vuelto a probar y por ahora, al menos en el IDE de Visual Studio 2019 va bien.
De todas formas, ya sabes si usas lo que te voy a explicar aquí, y ves que la aplicación casca, ya sabes porqué es.

La documentación (en inglés) dice esto:

For performance reasons, you should call this method only when you do not know at design time what assembly is currently executing. The recommended way to retrieve an Assembly object that represents the current assembly is to use the Type.Assembly property of a type found in the assembly.

Que en el idioma actualizado de Cervantes viene a decir esto (según Google Translator):

Por motivos de rendimiento, debe llamar a este método solo cuando no sepa en tiempo de diseño qué ensamblado se está ejecutando actualmente. La forma recomendada de recuperar un objeto Ensamblado que representa el ensamblado actual es usar la propiedad Type.Assembly de un tipo que se encuentra en el ensamblado.

Aquí te voy a poner el código (tanto de Visual Basic como de C#) para usar esta forma recomendada de asignar un ensamblado (de la clase System.Reflection.Assembly).

Este código está en bibliotecas (proyectos del tipo Class Library) para usar con .NET Standard 2.0 (de esta forma los ensamblados se podrán usar tanto en .NET Core como en .NET Framework).

Ejemplo para C#

public static string VersionDLL()
{
    var ensamblado = typeof(AboutViewModel).Assembly;
    var fvi = FileVersionInfo.GetVersionInfo(ensamblado.Location);
    // FileDescription en realidad muestra (o eso parece) lo mismo de ProductName
    var s = $"{fvi.ProductName} v{fvi.ProductVersion} ({fvi.FileVersion})" + 
        $"\r\n{fvi.Comments}";

    return s;
}

Ejemplo para Visual Basic

Public Function VersionDLL() As String
    Dim ensamblado = GetType(DatosMostrar).Assembly
    Dim fvi = FileVersionInfo.GetVersionInfo(ensamblado.Location)
    ' FileDescription en realidad muestra (o eso parece) lo mismo de ProductName
    Dim s = $"{fvi.ProductName} v{fvi.ProductVersion} ({fvi.FileVersion})" &
        $"{vbCrLf}{fvi.Comments}"

    Return s
End Function

Ese código mostrará la versión del ensamblado, la versión del fichero y la descripción, aunque en realidad a la propiedad a la que tienes que acceder es a Comments, ya que FileDesciption muestra lo mismo que ProductName.

Y aquí tienes una captura para la aplicación de Android con los comentarios de las 3 bibliotecas que estoy usando, 2 de ellas escritas con Visual Basic y la tercera (la que le da funcionalidad visual a la aplicación está escrita en C#).

Figura 1. Captura en el emulador de Android.
Figura 1. Captura en el emulador de Android.

Y esta otra captura es de la aplicación en el emulador (local) de UWP (Universal Windows Platform) pero con la aplicación real. Aunque en modo depuración.

Figura 2. Captura en el (emulador) de UWP.
Figura 2. Captura en el (emulador) de UWP.

Espero que te sea de utilidad… Esa es siempre la idea…

Nos vemos.
Guillermo

P.S.
El puñetero Jetpack agrega código (de más) a las imágenes y cuando estas se muestran desde www.elguille.info, simplemente no se ven… pero… puedes pulsar en el sitio (en blanco) en el que está la imagen y te la mostrará…
Lo mismo quito el Jetpack, que, aparte del «bloque» Markdown y el poder publicar en twitter y mi sitio de facebook, no me sirve de mucho…

Nuevas ‘versiones’ de Visual Studio 2019

Pues eso… con fecha de anteayer 2 de marzo, hay nuevas versiones de Visual Studio 2019, tanto para la versión final: v16.9.0 como para la preview: v16.10 Preview 1.0.

Este es un extracto de las novedades de .NET Productivity de la versión 16.9.0:
He resaltado las que, a mí me, parecen más interesantes.

.NET Productivity (versión final v16.9.0)

  • There is now IntelliSense completion for preprocessor symbols.
  • Solution Explorer now displays the new .NET 5.0 Source Generators.
  • Go To All won’t display duplicate results across netcoreapp3.1 and netcoreapp2.0.
  • Quick Info now displays compiler warning IDs or numbers for suppressions.
  • Using directives will now automatically get added when copying and pasting types to a new file.
  • IntelliSense completion will now automatically insert a semicolon as a commit character for object creation and method completion.
  • Semantic colorization for C# 9.0 records.
  • Refactoring that removes unnecessary discards.
  • Refactoring that converts a verbatim and regular string to an interpolated string preserving curly braces that were intended to go in the output.
  • Code fix in Visual Basic that removes the shared keyword when you convert methods that are shared to a module.
  • A refactoring that suggests using new(…) in non-contentious scenarios
  • A code fix that removes redundant equality expressions for both C# and Visual Basic
  • The .NET Code Style (IDE) analyzers can now be enforced on build
  • The Syntax Visualizer shows the current foreground color for enhanced colors
  • A new tooltip when hovering over the diagnostic ID for pragma warnings
  • When you type the return key from within a comment the new line is now automatically commented out
  • Inline parameter name hints enhancements
  • .NET Core Debugging with WSL 2

Para la versión preview… pues… mejor lo miras en la página de información publicada por la gente de Microsoft.

Nos vemos.
Guillermo

Quien dice 32, dice 33: gsNotasNET.Android v2.0.0.33

gsNotasNET.Android v2.0.0.33

Utilidad para dispositivos móviles Android para tomar notas y guardarlas localmente o en una base de datos externa.

En esta versión (v2.0.0.33) la utilidad/aplicación hace lo siguiente:

Permite crear notas y marcarlas con estos atributos/propiedades:

  • Indicar a qué grupo (tag/etiqueta) pertenece.

  • Se pueden crear tantos grupos como se deseen.

  • El programa recordará el último nombre de grupo utilizado al guardar una nota.

  • Se pueden ver los grupos que hay creados, mostrando la información de las notas que contiene.

  • Al mostrar los grupos se muestra la información de las notas que contiene.

  • (v2.0.0.32) Al seleccionar un grupo (clic o tap) te muestra la información de tallada de las notas que contiene, así como la lista de esas notas pertenecientes a ese grupo. Desde esa lista puedes editar las notas.

Atributos /Propiedades de cada nota:

  • Favorita, al mostrar las notas (de la base de datos externa) las notas favoritas se muestran al principio.

  • Notificar, las notas marcadas para notificar se pueden usar para que al hacer login se muestren automáticamente, ahí puedes anotar los recordatorios que necesites ver cada vez que inicias sesión.

  • Archivada, esto hará que la nota no se muestre al mostrar las notas activas.

  • Eliminada, es otra forma de ocultar las notas, ya que nunca se eliminan físicamente de la base de datos (tanto local como externa).

  • Sincronizada, esta propiedad indica si la nota está sincronizada entre las bases local y externa. Las notas siempre se sincronizan entre las dos bases de datos.

Para mostrar las notas hay varios apartados en el menú o pantalla principal, pudiendo mostrar las notas según el atributo o propiedad seleccionado.

  • En la lista de notas se muestra la siguiente información:

  • El título de la nota que consiste en los primeros 50 caracteres o si hay cambios de líneas en la nota, la primera línea.

  • El nombre del grupo, la fecha de modificación y una letra cn el nombre del atributo y si está marcado (True) o no lo está (False).

  • Las abreviaturas de los atributos son:

    Favorita, Notificar, Archivada y Eliminada.

  • En una nueva actualización añadiré Sincronizada, aunque se supone que siempre deben estar sincronizadas.

  • Seguramente también añadiré una página (opción) para mostrar las notas sincronizadas y poder asegurarte que si no está sincronizada, lo haga.

En la configuración puedes indicar:

  • Recordar el usuario con el que se ha hecho Login.
  • Recordar el password usado.
  • Iniciar la aplicación con el último usuario.
  • Mantener las notas sincronizadas (siempre está activada esta opción).
  • Mostrar las notas a Notificar al iniciar el programa (o hacer Login).
  • Usar las notas locales (cuando se activa) o usar las notas de la base externa.

En la información del Perfil te muestra los datos de la cuenta con la que has hecho Login.

  • Ahí puedes cambiar la cuenta de correo y el password.

  • Si cambias la cuenta de correo, se enviarán 2 mensajes, uno a cada cuenta pidiendo confirmación.

  • Te muestra la inforamción de la fecha de alta, último acceso, cuántas notas de la base externa puedes escribir:

  • Para los primeros 90 usuarios que se registren tienen una cuota de 1.000 notas, a partir del usuario 100 (los otros 10 los tengo reservados para mí) tendrán 100 notas como máximo de forma totalmente gratuita.

  • En esa cantidad, se cuentan todas las notas, estén o no eliminadas (ya que nunca se eliminan las notas).

  • El importe por cada 1.000 notas será de una donación anual de 12$ USD (unos 10€).

  • En esa cuota no se cuentan las notas ofrecidas gratuitamente.

  • El importe indicado en Pagos será el importe que hayas ido pagando.

  • Ya te digo que solamente debes pagar si quieres más cantidad de notas. La aplicación es totalmente gratuita. Salvo que prefieras hacer un donativo voluntario.

  • El donativo lo puedes hacer mediante este enlace: Donativo para elGuille.

  • Todos los donativos serán siempre bienvenidos 🙂

Puedes realizar búsquedas en las notas (tanto en la base local como en la externa).

  • La búsqueda se realiza sin tener en cuenta el case (no diferencia entre mayúsculas y minúsculas).

  • Por ahora solo hace la búsqueda en el Texto de las notas.

  • En una nueva actualización incluiré que se pueda hacer tanbién (o solo) en los nombres de los grupos).

  • En el resultado de la búsqueda puedes pulsar (hacer clic o tap) en la nota para editarla.

  • Aunque estés usando las notas de la base de datos remota (de SQL Server) puedes mostrar las notas de la base de datos loca, en este caso las notas no son editables, solo se muestran en la lista y haciendo tap o clic en ellas no se muestran. ⋅⋅⋅ Si quieres editar una nota local, debes cambiarlo en la configuración seleccionando la opción Usar las notas locales.

En la pantalla principal además te encontrarás con estas opciones:

  • Validar Email. Esto tendrás que usarlo cuando te registres con un nuevo usuario. O al crear un nuevo usuario.

  • Sincronizar. Esta opción te sirve para comprobar si las notas están sincronizadas (deberían estarlo), pero en caso de que no lo estuvieran, puedes sincronizar las locales al romoto o al revés.

  • Comentarios. Por si quieres enviarme alguna sugerencia, bug o cualquier cosa que me quieras decir. Esta opción te permite indicar un comentario y al pulsar en el botón de ENVIAR… abrirá una cuenta de email que tengas configurada en tu dispositivo para poder enviar el mensaje. El texto que hayas escritor aparecerá en el cuerpo del mensaje.

  • Acerca de Abre una ventana con un poco de ayuda sobre la aplicación. Seguramente añadiré un enlace a esta página descriptiva, ya que tanto texto no se puede poner en ese tipo de páginas… o al menos no se vería bien.

  • Cambiar de usuario. Desde ahí puedes hacer login, si no lo has hecho ya, o bien poder registrar un nuevo usuario.

  • Con esta última actualización, tanto en la ventana principal (Menú) como en la de Acerca de se comprueba si hay una nueva versión y de ser así, se indica de ese hecho, De esta forma puedes estar avisado y te puedes descargar el paquete de instalación.

NOTA: Esta release (recuerda que las releases o paquetes de instalación) siempre son versiones Release no versiones Debug. Y en esta he tenido que hacer 9 comprobaciones en el dispositivo físico ya que me daba error al comprobar si había una nueva versión. En modo debug funcionaba bien, pero en la release daba error. Y todo era por hacer caso a los tips de la documentación de .NET, y es que para esa comprobación uso el ensamblado que se está ejecutando y yo siempre usaba (y seguiré usando, al menos en las versiones para móviles) este código para obtener el ensamblado:

var ensamblado = System.Reflection.Assembly.GetExecutingAssembly();

La documentación dice esto:

Remarks For performance reasons, you should call this method only when you do not know at design time what assembly is currently executing. The recommended way to retrieve an Assembly object that represents the current assembly is to use the Type.Assembly property of a type found in the assembly, as the following example illustrates.

using System;
using System.Reflection;

public class Example
{
   public static void Main()
   {
      Assembly assem = typeof(Example).Assembly;
      Console.WriteLine("Assembly name: {0}", assem.FullName);
   }
}
// The example displays output like the following:
//    Assembly name: Assembly1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

Pero al usar un código parecido, tal como este:

System.Reflection.Assembly ensamblado = typeof(AcercaDegsNotasNET).Assembly;

Da error y, sin nada de descripción, solo el nombre del ensamblado desde donde se ha llamado. Y lo curioso es eso, que no pod´çia saber porqué fallaba la aplicación, salvo después de ¡10 intentos! (y porque me imaginaba por dónde iban los tiros), en fin…

En todas (o casi todas) existe un botón POLÍTICA DE PRIVACIDAD que al pulsarlo te muestra (en el navegador) la política de privacidad, es decir, qué datos personales se recogen.

Ahora muestra el contenido de elguillemola.com: Política de privacidad en elguillemola.com pero seguramente lo cambiaré a una que sea específica de esta aplicación.

De todas formas, comentarte que con los datos que facilitas o las notas que escribe yo no hago nada, ni cambairé nada, salvo que tú me lo pidas expresamente, bien porque no tengas acceso o cualquier otra circunstancia que te impida acceder a las notas o tu cuenta de usuario.

Bueno y creo que esto es todo…

He escrito tanto con idea de crear una página en elguillemola.com y así poder usarla como ayuda de la aplicación.

También crearé un video explicativo con las cosas que debes saber sobre la aplicación.

Si has llegado leyendo hasta aquí… ¡muchas gracias! 🙂

Y como estamos en las fechas que estamos (30 de diciembre de 2020) te deseo ¡FELICES FIESTAS!

Guillermo

 

Galería de fotos / capturas de la versión 2.0.0.33

De la versión 2..33 solo hay dos que muestra la «nota» de comprobación de si hay una nueva versión, el resto son de las versiones v2.0.0.31 y v2.0.0.32

 

Nota:

Pulsa en la foto que quieras ver con más detalle para que se muestre a tamaño real.

Puedes descargar el "paquete" de instalación desde GitHub:

Este enlace es para la versión v2.0.0.33.

Este otro es para la versión v2.0.0.32 (si no te funciona el enlace es que lo has pulsado antes de que yo lo haya publicado 😉 )

 

Nos vemos.
Guillermo

gsNotasNET para Android con Xamarin.Forms

Pues eso… que ayer (o anteayer) me puse a trastear con el Xamarin para crear aplicaciones para móviles (para Android, ya que no tengo conexión a un Mac y por tanto no puedo desarrollar para iPhone) y he creado una versión de gsNotasNET pero para dispositivos móviles de Android. Y ya lo tengo en funcionamiento en mi móvil 😉

El código está adaptado de un tutorial paso a paso que hay en la documentación de Microsoft, este es el enlace del primer ejemplo:

Create a single page Xamarin.Forms application – Xamarin | Microsoft Docs

He puesto el código fuente en GitHub: gsNotasNET.Android.

 

Un video de la aplicación funcionandoi en el emulador de Android

Nunca me acuerdo de apagar las bocinas (altavoces/speakers) cuando lo grabo y parece que estoy hablando desde el fondo de un cubo… en fin…

 

 

Cuando haga algunas capturas las pondré por aquí para que veas cómo queda.

Bueno, intentaré hacerle una para que no se quede muy soso este post 😉

 

Al final he hecho tres capturas.

Figura 1. La pantalla prinicpal

 

Figura 2. Editar una nota.

 

Figura 3. Escribir una nota nueva.

 

Espero que te sea de utilidad.

 

Nos vemos.
Guillermo

Tutorial Google APIs – Código para acceder a los contactos con People API

Ya estás en la segunda entrega de este Tutorial sobre el uso de las APIs de Google.
Aquí te mostraré el código de Visual Basic y C# para acceder a los contactos de Google usando People API. Como te comenté en la priumera entrega sobre cómo crear un proyecto en Google Cloud, el API que usaremos es People API, no te confundas con Contacts API (como a mí me ocurrió) ya que parece que será la más adecuada, pero además de que Google no recomienda que se use Contacts API, la he probado y no he conseguido sacar prácticamente nada de información, y al que he sacado es de unos pocos contactos (que ni recordaba que los tenía creados).

Nota:
Está uno acostumbrado a que te marquen los errores horrográficos que esto de que no te los marque es un Orror! 😛

Bueno vamos empezar con el código de C# que es lo que la gente suele ver en internet (de VB no he visto ni un ejemplo, así que… ya mismo lo verás en primicia).

Después de escribir el párrafo anterior he cambiado de opinión y te voy a enseñar conjuntamente el código de los dos lenguajes, para que vayas comparando.

Código de C# y VB para mostrar los contactos de una cuenta de Google usando People API

Nota:
Debo decir que parte de este código lo he visto en algunos ejemplosencontrados tanto en la documentación de Google como en foros de C#. Pero principalmente de la documentación (escasa) de Google y en agunos casos, los ejemplos eran de java, ni siquiera de C#.

Como te comenté en el preámbulo del tutorial, debes añadir las referencias a los paquetes de NuGet que instalarán las APIs en tu proyecto. Yo he añadido todas las que te indiqué en esa primera página del tutorial. Por supuesto si quieres acceder, por ejemplo al calendario o a GMail, tendrás que añadir referencias a esas APIs, además de añadirlas al proyecto creado en Google Cloud Platform.

Veamos el código inicial (con las importaciones de los espacios de nombres) que será básicamente el mismo en todas las apliocaciones que hagas para acceder a las APIs de Google.

// Genéricas
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;
// People API
using Google.Apis.People.v1;
using Google.Apis.People.v1.Data;
// Docs API
using Google.Apis.Docs.v1;
using Google.Apis.Docs.v1.Data;
// Drive API
using Google.Apis.Drive.v3;
using Google.Apis.Drive.v3.Data; // Para el tipo File

using System;
using System.Collections.Generic;
//using System.IO;
using System.Threading;
using System.Text;
using System.Linq;
'// Genéricas
Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Services
Imports Google.Apis.Util.Store
'// People API
Imports Google.Apis.People.v1
Imports Google.Apis.People.v1.Data
'// Docs API
Imports Google.Apis.Docs.v1
Imports Google.Apis.Docs.v1.Data
'// Drive API
Imports Google.Apis.Drive.v3
Imports Google.Apis.Drive.v3.Data '// Para el tipo File

Imports System
Imports System.Collections.Generic
Imports System.Threading
Imports System.Text
Imports System.Linq

Ahora vamos a crear una serie de campos estáticos/compartidos (static en C#, shared en VB) que usaremos también de forma genérica en todos los proyectos de acceso a Google APIs.

class Program
{
    // If modifying these scopes, delete your previously saved credentials
    // at ~/.credentials/docs.googleapis.com-dotnet-quickstart.json
    static string[] Scopes = { DocsService.Scope.Documents, 
                               DocsService.Scope.DriveFile, 
                               PeopleService.Scope.ContactsReadonly };

    static string ApplicationName = "Tutorial Google APIs VB";

    // Los datos del proyecto creado para VB
    static ClientSecrets secrets = new ClientSecrets()
    {
        ClientId = "430211665266-t68vl99t2q40v3lbctgbph23j2644bpj.apps.googleusercontent.com",
        ClientSecret = "Xqexl0FMPedNc1KYs0iJt22A"
    };

    static DocsService docService;
    static DriveService driveService;
    static PeopleService peopleService;

    static UserCredential Credential;
Class Program

    Shared Scopes As String() =
        {
            DocsService.Scope.Documents,
            DocsService.Scope.DriveFile,
            PeopleService.Scope.ContactsReadonly
        }

    Shared ApplicationName As String = "Tutorial Google APIs VB"

    Shared secrets As ClientSecrets = New ClientSecrets() With
        {
            .ClientId = "430211665266-t68vl99t2q40v3lbctgbph23j2644bpj.apps.googleusercontent.com",
            .ClientSecret = "Xqexl0FMPedNc1KYs0iJt22A"
        }

    Shared docService As DocsService
    Shared driveService As DriveService
    Shared peopleService As PeopleService

    Shared Credential As UserCredential

Lo que ese primer comentario indica (es de un quickstart de Google) es que si cambias los valores del array Scopes debes eliminar el directorio creado en Documentos\.credentials (ahora verás porqué).

El array Scopes contiene los ámbitos de la aplicación, es a qué estamos pidiendo permiso para acceder. Ahí he añadido los tres ámbitos que usaré en este tutorial, pero si solo quieres acceder a los contactos con People API, añadiendo PeopleService.Scope.ContactsReadonly sería suficiente. Si te fijas, estás pidiendo autorización (a OAuth) para acceder de forma de solo lectura a los contactos.
En realidad estás pidiendo permisos para acceder a los Documentos y al Drive sin restricciones, pero a los contactos solo para leerlos. Estos dos últimos necesitan más permisos porque queremos crear ficheros, eliminarlos, lo mismo con los documentos.

Lo importante (también) es la asignación de ClientSecrets, ahí tendás que poner los datos que te haya generado al configurar el OAuth de la aplicación. Eso que te muestro son los valores que en mi proyecto para este tutorial he pedido, el que se indique que la aplicación se llama Tutorial Google APis VB no significa que no se pueda usar para C#, ese título o nombre de aplicación no es restrictivo para nada.

Nota:
Normalmente esos valores se recomienda que no se hagan públicos… ¡vaya el caso que hago! jejejeje… pero para este caso, me fio de ti, y sé que no se lo dirás a nadie y así lo mantenemos en secreto… 😉

Las tres declaraciones después de asignar el valor a secrets son para acceder a los servicios de las tres APIs que usaré en estos tutoriales, las defino a nivel de clase para que se puedan usar en todos los métodos que sean necesarios y no tener que pasarlas como argumento de llamada al método que lo necesite.

La última declaración (Credential) es para crear las credenciales del usuario. La asignación la haré en el método Main, y como es un valor compartido, también se podrá usar en cualquier parte de la clase.

El siguiente código es el método Main, en el que llamaremos al método que accede a los contactos y los muestra.

Y como verás en las capturas el correo que he usado no tenía ningún contacto y he tenido que crear uno para comprobar que funciona… las cosas del Guille…

static void Main(string[] args)
{
    Console.WriteLine("Tutorial Google APIs con C#");

    string credPath = System.Environment.GetFolderPath(
        Environment.SpecialFolder.Personal);
    
    // Directorio donde se guardarán las credenciales
    credPath = System.IO.Path.Combine(credPath, ".credentials/Tutorial-Google-APIs-VB");

    Credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
        secrets,
        Scopes,
        "user",
        CancellationToken.None,
        new FileDataStore(credPath, true)).Result;
    //Console.WriteLine("Credential file saved to: " + credPath);

    // Mostrar los contactos
    MostrarContactos();

    Console.WriteLine();
    Console.WriteLine("Pulsa una tecla.");
    Console.Read();
}
Public Shared Sub Main(ByVal args As String())

    Console.WriteLine("Tutorial Google APIs con Visual Basic")

    Dim credPath As String = System.Environment.GetFolderPath(
        Environment.SpecialFolder.Personal)

    '// Directorio donde se guardarán las credenciales
    credPath = System.IO.Path.Combine(credPath, ".credentials/Tutorial-Google-APIs-VB")

    Credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
        secrets,
        Scopes,
        "user",
        CancellationToken.None,
        New FileDataStore(credPath, True)).Result

    '// Mostrar los contactos
    MostrarContactos()

    Console.WriteLine()
    Console.WriteLine("Pulsa una tecla.")
    Console.Read()
End Sub

El valor de credPath es el direcorio donde se crearán las credenciales que vamos a usar en este programa. Se guardará en los documentos del usuario en una carpeta llamada Tutorial-Google-APIs-VB que estará incluida en .credentials.

Si haces cambios en los permisos o te han faltado permisos en la configuración de OAuth y los modificas, debes eliminar la carpeta del tutorial, a de .credentials la puedes dejar, por si la usas para almacenar las credenciales de otros programas que use el usuario.
También tendrás que borrarla si decides usar otra cuenta de Google (GMail) para acceder a esos contactos. Si solo tienes una cuenta de GMail, no hará falta que la borres salvo para los dos primeros casos.

Cuando ejecutemos la aplicación verás qué es lo que se guarda en esa carpeta.

A Credentials le asignamos las credenciales que queremos usar.

Aquí tengo que hacer una puntuialización.
El valor de secrets se asigna con los valores que hemos indicado (de foirma llana y clara) antes, pero también se puede hacer accediendo a un fichero que contiene esos valores, al menos así no se mostrarán directamente en el código los valores secretos. Aunque seguirán estando al alcance del usuario de la aplicación.

La forma de hacerlo lo pondré en otro ejemplo de este tutorial.

Ahora solo nos falta ver qué código contiene elmétodo MostrarContactos.

¡Vamos a ello!

private static void MostrarContactos()
{
    // Create Drive API service.
    peopleService = new PeopleService(new BaseClientService.Initializer()
    {
        HttpClientInitializer = Credential,
        ApplicationName = ApplicationName,
    });

    // Lista de los contactos (People)
    Console.WriteLine("Contactos:");

    // Este muestra todos los contactos
    GetPeople(peopleService, null);

    Console.WriteLine();
    Console.WriteLine($"Hay {total} contactos / People");
    Console.WriteLine();
}
Private Shared Sub MostrarContactos()
    peopleService = New PeopleService(New BaseClientService.Initializer() With
    {
        .HttpClientInitializer = Credential,
        .ApplicationName = ApplicationName
    })

    Console.WriteLine("Contactos:")

    GetPeople(peopleService, Nothing)

    Console.WriteLine()
    Console.WriteLine($"Hay {total} contactos / People")
    Console.WriteLine()
End Sub

Le asignamos el valor a peopleService para que pida esos permisos a Google, como ves se usa el protocolo HTTP.
Eso lo que hará es abrir una página en el navegador pidiendo permiso para acceder a los datos de una cuenta de GMail (ver la figura 1)

Figura 1. Seleccionar la cunta que queremos usar para acceder a sus contactos.

Esa primera pantalla nos pide que indiquemos a qué contactos queremos acceder, es decir, solo accederá a los contactos de la cuenta que indiques, y para indicar esa cuenta debes tener el password, si no… no hay nada que hacer.
Por tanto, esto que vamos a hacer es seguro, en el sentido de que yo no voy a acceder (ni puedo) a tus contactos… otra cosa es que mi aplicación se comunicara conmigo o guardara esa información en algún sitio… pero no te preocupes, no lo hace, además lo puedes comprobar por ti mismo viendo el código.

Una vez elegida la cuenta, te mostrará un aviso de que la aplicación no es segura o, en realidad, que no se ha verificado (ver la figura 2).
Tendrás que autorizarla para que pueda seguir.

Figura 2. DEbes mostrar la información oculta para permitir ir a la aplicación

Una vez hechoi esto, Google te pedirá confirmación para dejar que la aplicación haga lo que se ha indicado en Scopes. Así que… otras tres pantallitas más de autorización (ver figuras 3 a 5).

Figura 3. Permiso para ver los contactos.
Figura 4. Acceder al Drive.
Figura 5. Acceder a los documentos.

Y una vez permitido estas cosas, debes terminar de aceptarlo (ver la figura 6 en la está el resumen de los permisos que le darás a la aplicación).

Figura 6. Confirmación final de los permisos.

Después de pulsar en Permitir, el navegador te mostrará un mensaje de que puedes cerrar la página.

Estos permisos no te los volverá a pedir para usar esta aplicación. Y por tanto, siempre accederá a la cuenta de GMail que has indicado.

Si quieres usar otra cuenta diferente, borra la carpeta esa que te comenté antes (o el fichero que contiene) y eso hará que te vuelva a mostrar todas estas pantallas.

Antes de seguir, espera un poco que me estoy meando de la risa… ¡ay!

Es que te dije que definí la variable peopleService (y las otras dos) a nivel de clase para no tener que usarla como argumento a un método y voy y pongo como argumento el valor de esa variable compartida… en fin… la cabeza del Guille…

Bueno, ya.

Sigamos con el código que accede a los contactos.

private static int total = 0;

static void GetPeople(PeopleService service, string pageToken)
{
    // Define parameters of request.
    PeopleResource.ConnectionsResource.ListRequest peopleRequest =
            service.People.Connections.List("people/me");

    //
    // Lista de campos a usar en RequestMaskIncludeField:
    // https://developers.google.com/people/api/rest/v1/people/get
    //

    peopleRequest.RequestMaskIncludeField = new List<string>()
            {"person.names", "person.phoneNumbers", "person.emailAddresses",
              "person.birthdays", "person.Addresses"
            };


    if (pageToken != null)
    {
        peopleRequest.PageToken = pageToken;
    }

    ListConnectionsResponse people = peopleRequest.Execute();

    if (people != null && people.Connections != null && people.Connections.Count > 0)
    {
        total += people.Connections.Count;
        foreach (var person in people.Connections)
        {
            Console.Write(person.Names != null ? ($"{person.Names.FirstOrDefault().DisplayName} - ") : "");
            Console.Write(person.PhoneNumbers != null ? ($"{person.PhoneNumbers.FirstOrDefault().Value} - ") : "");
            Console.Write(person.EmailAddresses != null ? ($"{person.EmailAddresses.FirstOrDefault().Value} - ") : "");
            Console.Write(person.Addresses != null ? ($"{person.Addresses.FirstOrDefault()?.City} - ") : "");
            if (person.Birthdays != null)
            {
                var fecha = "";
                var b = person.Birthdays.FirstOrDefault()?.Date;
                if (b != null)
                    fecha = $"{b.Day}/{b.Month}/{b.Year}";
                Console.Write($"{fecha}");
            }
            Console.WriteLine();
        }

        if (people.NextPageToken != null)
        {
            Console.WriteLine();
            Console.WriteLine($"{total} contactos mostrados hasta ahora. Pulsa una tecla para seguir mostrando contactos.");
            Console.WriteLine();
            Console.ReadKey();

            GetPeople(service, people.NextPageToken);
        }
    }
    else
    {
        Console.WriteLine("No se han encontrado contactos.");
        return;
    }
}
Private Shared total As Integer = 0

Private Shared Sub GetPeople(service As PeopleService, pageToken As String)

    Dim peopleRequest As PeopleResource.ConnectionsResource.ListRequest =
        service.People.Connections.List("people/me")

    peopleRequest.RequestMaskIncludeField = New List(Of String)() From {
            "person.names",
            "person.phoneNumbers",
            "person.emailAddresses",
            "person.birthdays",
            "person.Addresses"
        }

    If pageToken IsNot Nothing Then
        peopleRequest.PageToken = pageToken
    End If

    Dim people As ListConnectionsResponse = peopleRequest.Execute()

    If people IsNot Nothing AndAlso
            people.Connections IsNot Nothing AndAlso
            people.Connections.Count > 0 Then
        total += people.Connections.Count

        For Each person In people.Connections
            Console.Write(If(person.Names IsNot Nothing,
                          ($"{person.Names.FirstOrDefault().DisplayName} - "), ""))
            Console.Write(If(person.PhoneNumbers IsNot Nothing,
                          ($"{person.PhoneNumbers.FirstOrDefault().Value} - "), ""))
            Console.Write(If(person.EmailAddresses IsNot Nothing,
                          ($"{person.EmailAddresses.FirstOrDefault().Value} - "), ""))
            Console.Write(If(person.Addresses IsNot Nothing,
                          ($"{person.Addresses.FirstOrDefault()?.City} - "), ""))

            If person.Birthdays IsNot Nothing Then
                Dim fecha = ""
                Dim b = person.Birthdays.FirstOrDefault()?.Date
                If b IsNot Nothing Then fecha = $"{b.Day}/{b.Month}/{b.Year}"
                Console.Write($"{fecha}")
            End If

            Console.WriteLine()
        Next

        If people.NextPageToken IsNot Nothing Then
            Console.WriteLine()
            Console.WriteLine($"{total} contactos mostrados hasta ahora. " &
                              "Pulsa una tecla para seguir mostrando contactos.")
            Console.WriteLine()
            Console.ReadKey()
            GetPeople(service, people.NextPageToken)
        End If
    Else
        Console.WriteLine("No se han encontrado contactos.")
        Return
    End If
End Sub

El valor de peopleRequest debe ser siempre people/me o bien si te sabes el ID de otro usuario lo podrías indicar en lugar de «me«. Ese me significa que accedes a tus contactos.
He buscado por todas partes, y no he averiguado como saber ese famoso ID, ni siquiera de una de mis cuentas para poder saber si se puede acceder a la cuenta de alguien que no seas tú.
Cuando lo averigüe te lo cuento.

Los valores asigndos a la lista RequestMaskIncludeField serán a los datos que queremos acceder y siempre deben empezar con person. seguido del campo al que queremos acceder. En este ejemplo estamos indicando que queremos acceder a: los nombres, teléfonos, emails, cumpleaños y domicilios.

Y como esos valores pueden ser nulos (y normalmente son una colección de valores), comprobamos si es nulo y en el caso de las colecciones accedemos al primero o al predeterminado, si hay algo lo mostramos y si no, se usa una cadena vacía.

Con el valor de Birtdays debemos hacer una comprobación más elaborada e incluso te recomendaría que usaras un try/catch si quieres acceder a los valores de la fecha, porque de las pruebas que he heco, en un caso me dio error al querer acceder al año… se ve que el contacto ese no quería dar su año de nacimiento y dio error al querer convertirlo a entero… en fin…

El NextPageToken nos indica que hay más págins de datos, así que… si las hay volvemos a llamar a este método y seguimos mostrando los contactos que falten por acceder.

Y esto es todo lo que hay que hacer para mostrar los contactos usando People API. Otra cosa es que quieras crear contactos o modificarlos, en esos casos tendrás que modificar el valor del Scope de los contatos.
Si hago pruebas de crear o modificar te los pondré por aquí.

Para terminar, la captura del programa en funcionamiento. Ver la figura 7.

Figura 7. La cuenta que he usado para acceder a los contactos, ¡no tiene contactos!

Pues la sorpresa que me llevé… pensé que lo mismo fallaba algo, así que… me fui a google, creé un contacto y volvía a probar… ¡Esta vez sí que sí! (ver la figira 8).

Figura 8. Un contacto tengo en la cuenta que he usado para esta prueba.

Y esto es todo por ahora.

Ahora a esperar a la siguiente entrega.

Este código está publicado en GitHub: Tutorial-APIs-Google.

Nos vemos.
Guillermo

P.S.
Versión en inglés / English version.

Tutorial Google APIs – Crear Proyecto en Google Cloud

Aquí tienes la primera entrega del tutorial sobre el uso de las APIs de Google para acceder a Drive, Contactos y Documentos.

Si quieres crer una cuenta y un proyecto para poder acceder a esos APIs de Google, lo primero es acceder a Google Cloud Platform y crear una cuenta, o mejor dicho, activarla con Google Cloud.

Una vez que tengas la cuenta, tendrás que crear un proyecto y configurarlo para que pueda acceder a esas APIs.

En este tutorial te explico paso a paso cómo hacerlo, y con un montón de capturas para que no te líes y yo no me olvide de contarte los paso, que todo hay que decirlo. 🙂

Nota:
Por ahora te voy dejar las capturas y en otra ocasión, si hace falta, te explico lo que cada captura significa.
Es que quiero pasar a mostrarte el código para acceder al API de People (contactos) que ya lo tengo hecho tanto con C# como con Visual Basic.

Figura 1. Crear un nuevo proyecto

 

Figura 2.

 

Figura 3.

 

Figura 4.

 

Figura 5.

 

Figura 6. En la ficha G Suite están las de Docs API y drive API

 

Figura 7. Tienes que elegir y habilitar una por una.

 

Figura 8.

 

Figura 9. Añadir más librerías (bibliotecas)

 

Figura 10. Elige People API. No elijas Contacts API, que aparte de estar obsoleta, no vale para nada…

 

Figura 11. Esta es la parte más importnte, sin la autorización OAuth no podrás acceder a nada.

 

Figura 12. Siempre debe ser External

 

Figura 13.

 

Figura 14.

 

Figura 15. Añadir las características que tu aplicación usará de cada API que has añadido.

 

Figura 16.

 

Figura 17.

Nota:
Los permisos debes ir probándolos a ver qué error te da, según lo que quieras hacer.
Pero básicamente necesitas acceso de lectura, salvo que quieras modificar un contacto, un documento o algún fichero del Drive.

Figura 18. No te olvides de darle a UPDATE ya que al estar tan abajo en la pantalla emergente ni se ve, y so no le das a Update, no se guardan los cambios.

 

Figura 19.

 

Figura 20.

 

Figura 21. Aquí tienes que añadir los usuarios autorizados para que usen la aplicación. El máximo es 100 y una vez que aades uno, ya no lo puedes eliminar.

 

Figura 22.

 

Figura 23. Los tipos de credenciales que crearemos

 

Figura 24.

 

Figura 25. Estas so las APIs que usaremos con este proyecto.

 

Figura 26. El tipo de aplicación que vamos a crear.

 

Figura 27.

 

Figura 28. Estas son las dos claves que necesitarás en tu código

 

Figura 29.

 

Figura 30.

 

Figura 31. Si no hs copiado las credenciales de acceso, descarga el fichero .json y ahí las tienes.

 

 

Pues esto es todo lo que debes saber para crear un proyecto.
En la siguiente entrega voy a mostrarte el código de una aplicación de consola para acceder a los contactos (People API). Sí, en C# y en VB.

 

 

Nos vemos.
Guillermo