Repositorios

Anime Catalogue

Requerimientos

  • Crear un listado que muestre cada ítem con el póster e información
  • Incluir una barra de búsqueda para encontrar cualquier anime
  • Incluir filtro por tipo de Anime
  • Incluir una manera de ordenación (título, año, rate…)
  • Posibilidad de marcar el anime como visto, que deberá ser persistente
  • Incluir navegación maestro/detalle

Características Adicionales

  • Animes vistos presentados en un Grid
  • Incluir animes recomendados o relacionados
  • Cargar la preview con datos de prueba
  • Añadir un splash a manera de LaunchScreen
  • Posibilidad de compartir un anime
  • Soporte para iPad
  • Test Unitarios

Vista Previa

Reutilización de código

La creación de un neumorphic design.

struct NeumorphicStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .shadow(color: .gray, radius: 15, x: 10, y: 10)
            .shadow(color: .white, radius: 15, x: -10, y: -10)
    }
}

extension View {
    func styled() -> some View {
        modifier(NeumorphicStyle())
    }
}

El extender la vista con un init, permite cierta abstracción de la vista y el modelo.

extension AnimeListCell {
    init(anime:Anime) {
        title = anime.title
        type = anime.type
        rate = anime.rateDouble
        statusIcon = anime.statusIcon
        typeIcon = anime.typeIcon
        imageURL = anime.image
    }
}

Utilizar estados para las transacciones entre el splash, la pantalla de bienvenida y la vista principal

struct InitialView: View {
    @StateObject var viewModel = AnimesViewModel()
    @Namespace var namespace
    @Binding var navigationState: NavigationState
    
    var body: some View {
        Group {
            switch navigationState {
                
            case .splash:
                LaunchScreenView(navigationState: $navigationState, namespace: namespace)
                    .transition(.opacity)
                
            case .welcome: WelcomeView(navigationState: $navigationState, namespace: namespace)
                    .transition(.opacity)
                
            case .home:
                Group {
                    if UIDevice.current.userInterfaceIdiom == .pad {
                        iPadAnimeTabView(navigationState: $navigationState, namespace: namespace)
                            .transition(.push(from: .top))
                        
                    } else {
                        AnimeTabView(navigationState: $navigationState, namespace: namespace)
                            .transition(.push(from: .top))
                    }
                }
            }
        }
        .animation(.easeIn, value:navigationState)
    }
}