Wednesday, May 15, 2013

Creando un UI como la de Netflix para iOS con C#, Xamarin iOS y NucliOS


Este articulo es una adaptación del articulo en inglés "Creating a Netflix style iOS Grid with C#, Xamarin.iOS and NucliOS".
Repositorio en Github.
Mientras me relajaba un poco, leyendo un poco de mi lista de lectura (via Flipboard), encontré este interesante articulo de como crear una Interfaz de Usuario utilizando la libreria INucliOS de Infragistics. Casi instantaneamente se me ocurrió que sería una buena idea para un articulo a mi blog. De pronto, casi como segunda naturaleza ya mis manos se desplazaban hacia el teclado, abrieron una ventana de IE 10 y buscaron -vía Bing - por los terminos de busqueda "NucliOS MonoTouch Bindings". Para mi sorpresa, el producto ya viene con los "bindings" para Xamarin.iOS en la descarga oficial. 
Es aquí cuando comienza la travesía. Como parte del trabajo de este articulo está basado en un articulo publicado por Stephen Zaharuk, bajo el titulo "Creating a Netflix style iOS Grid", no les aburriré con los detalles; en vez, les monstraté como tomar el original y crear un UI similar en Xamarin.iOS sin mucho esfuerzo. 
Aquí están las cosas que deben de tener en cuenta cuando "traducen"  código en Obj-C y XCode al glorioso C# y Xamarin.iOS. 

// Obj-C Implementation of the model 

@interface NetflixData : NSObject
       @property (nonatomic, retain)NSString* category;
       @property (nonatomic, retain)NSMutableArray* media;
       @property (nonatomic, assign)CGFloat scrollPosition;
@end 

1- Recuerda decorar con el atributo[Export()] a las propiedades de tus objectos que derivande NSObject. Esto le permitirá a la implementación nativa usar las propiedades de tu modelo de datos.

public class NetflixMedia: NSObject
    {
        [Export("ImageUrl")]
        public string ImageUrl {
            get;
            set;
        }

        [Export("Title")]
        public string Title {
            get;
            set;
        }

        [Export("Genre")]
        public string Genre {
            get;
            set;
        }
    }


2 - Usa la propiedad WeakDelagate que se llama WeakDataSource
 //NucliosViewController.cs  - Obj-C
 _gridView.WeakDataSource = _ds;
...
Instead of..
//NucliosViewController.m  - Obj-C
_gridView.dataSource = _ds;
...
3- Cuando crees "constructores sobrecargados", recuerda extraer el código de inicialización a un metodo aparte (eso es puro sentido común), y por último no trates de escribir código con el estomago vacío, que el "Malcomío no piensa".
public class MediaCell:IGGridViewCell
{
    public MediaCell (string identifier): base(identifier)
    {
        Init ();
    }
4- Y por último, si encuentras algo escrito de forma que no hace sentido reproducirlo tal cual, luce lo que sabes y re-escribelo, y utiliza lo que está de punta (Async) y echa vainas a tus colegas.
//Obj-C
+(NSMutableArray*)generateData
{
NSMutableDictionary* mediaLookup = [[NSMutableDictionary alloc]init];
NSMutableArray* netFlixData = [[NSMutableArray alloc]init];

NSString* rootUrl = @"https://itunes.apple.com/search?term=%@&media=movie&entity=movie&limit=600&attribute=releaseYearTerm";

NSArray* keys = @[@"2012", @"2013"];

for(NSString* key in keys)
{
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:rootUrl, key]];

    NSData* data = [NSData dataWithContentsOfURL:url];
    NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
    NSArray* results = [json valueForKey:@"results"];

    if(results != nil)
    {
        for(NSDictionary* mediaInfo in results)
        {
            NSString* genre = [NSString stringWithFormat:@"%@", [mediaInfo valueForKey:@"primaryGenreName"]];

            NSString* url = [mediaInfo valueForKey:@"artworkUrl100"];

            MediaItem* media = [[MediaItem alloc]init];
            media.title = [mediaInfo valueForKey:@"trackName"];
            media.imgUrl = [NSURL URLWithString:url];

            MediaData* nfd = [mediaLookup valueForKey:genre];
            if(nfd == nil)
            {
                nfd = [[MediaData alloc]init];
                nfd.genre = genre;
                nfd.media = [[NSMutableArray alloc]init];
                [mediaLookup setValue:nfd forKey:genre];
                [netFlixData addObject:nfd];
            }

            [nfd.media addObject:media];
        }

    }
}


return netFlixData;

//C#
async Task<IList<NetflixMedia>> GetData ()
    {
        var movies = new List<NetflixMedia> ();
        var url = "https://itunes.apple.com/search?term={0}&media=movie&entity=movie&limit=600&attribute=releaseYearTerm";

        var client = new HttpClient ();

        var response = await client.GetAsync (string.Format (url, "2013"));
        var stringData = await response.Content.ReadAsStringAsync ();
        var json = JsonObject.Parse (stringData);

        var results = json["results"];

        foreach(JsonValue movie in results)
        {
            movies.Add (new NetflixMedia(){
                Title = movie["trackName"],
                ImageUrl = movie["artworkUrl100"],
                Genre = movie["primaryGenreName"],
            });

        }
        return movies;
    }

El resultado final