Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature [RM79] Character List View Model #82

Merged
merged 2 commits into from
Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Rick-and-Morty/CharacterRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class CharacterRepository: CharacterRepositoryProtocol {

func getCharacters(completion: @escaping (([Character]) -> Void)) {
if let url = characterPageURL {
rickAndMortyService.fetchData(url: url) { (charactersResponse: CharactersResponse) in
rickAndMortyService.fetchData(url: url) { (charactersResponse: CharacterResponse) in
if let nextURLString = charactersResponse.info.next {
if let nextURL = URL(string: nextURLString) {
self.characterPageURL = nextURL
Expand Down
24 changes: 24 additions & 0 deletions Rick-and-Morty/Rick And Morty.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
174B064B26C6611D0080ADD0 /* RickAndMortyService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174B064A26C6611D0080ADD0 /* RickAndMortyService.swift */; };
174B064D26C661470080ADD0 /* RickAndMortyServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174B064C26C661470080ADD0 /* RickAndMortyServiceProtocol.swift */; };
174B065126C671580080ADD0 /* CharacterRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174B065026C671580080ADD0 /* CharacterRepository.swift */; };
174B065426C6740E0080ADD0 /* CharacterListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174B065326C6740E0080ADD0 /* CharacterListViewModel.swift */; };
174B065726C6751F0080ADD0 /* CharacterCardStateFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174B065626C6751F0080ADD0 /* CharacterCardStateFactory.swift */; };
17588BAC26C1750B008ECC31 /* Character.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17588BAB26C1750B008ECC31 /* Character.swift */; };
17588BAF26C273BB008ECC31 /* CharacterCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17588BAE26C273BB008ECC31 /* CharacterCard.swift */; };
B811686D1CFF1C9900301A0A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B811686C1CFF1C9900301A0A /* AppDelegate.swift */; };
Expand All @@ -34,6 +36,8 @@
174B064A26C6611D0080ADD0 /* RickAndMortyService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RickAndMortyService.swift; sourceTree = "<group>"; };
174B064C26C661470080ADD0 /* RickAndMortyServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RickAndMortyServiceProtocol.swift; sourceTree = "<group>"; };
174B065026C671580080ADD0 /* CharacterRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterRepository.swift; sourceTree = "<group>"; };
174B065326C6740E0080ADD0 /* CharacterListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterListViewModel.swift; sourceTree = "<group>"; };
174B065626C6751F0080ADD0 /* CharacterCardStateFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCardStateFactory.swift; sourceTree = "<group>"; };
17588BAB26C1750B008ECC31 /* Character.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Character.swift; sourceTree = "<group>"; };
17588BAE26C273BB008ECC31 /* CharacterCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCard.swift; sourceTree = "<group>"; };
B81168691CFF1C9900301A0A /* Rick And Morty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Rick And Morty.app"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -81,6 +85,22 @@
name = Repositories;
sourceTree = "<group>";
};
174B065226C673FB0080ADD0 /* ViewModels */ = {
isa = PBXGroup;
children = (
174B065326C6740E0080ADD0 /* CharacterListViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
};
174B065526C674FD0080ADD0 /* Factories */ = {
isa = PBXGroup;
children = (
174B065626C6751F0080ADD0 /* CharacterCardStateFactory.swift */,
);
path = Factories;
sourceTree = "<group>";
};
17588BAA26C174FB008ECC31 /* Model */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -120,6 +140,8 @@
B811686B1CFF1C9900301A0A /* Rick And Morty */ = {
isa = PBXGroup;
children = (
174B065526C674FD0080ADD0 /* Factories */,
174B065226C673FB0080ADD0 /* ViewModels */,
174B064926C660FF0080ADD0 /* Services */,
17588BAD26C273A2008ECC31 /* Views */,
17588BAA26C174FB008ECC31 /* Model */,
Expand Down Expand Up @@ -247,7 +269,9 @@
174B064D26C661470080ADD0 /* RickAndMortyServiceProtocol.swift in Sources */,
174B065126C671580080ADD0 /* CharacterRepository.swift in Sources */,
174B064B26C6611D0080ADD0 /* RickAndMortyService.swift in Sources */,
174B065726C6751F0080ADD0 /* CharacterCardStateFactory.swift in Sources */,
17588BAF26C273BB008ECC31 /* CharacterCard.swift in Sources */,
174B065426C6740E0080ADD0 /* CharacterListViewModel.swift in Sources */,
17588BAC26C1750B008ECC31 /* Character.swift in Sources */,
1711B39E26B1898100BE935B /* CharacterListView.swift in Sources */,
B811686D1CFF1C9900301A0A /* AppDelegate.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// CharacterCardStateFactory.swift
// Rick And Morty
//
// Created by Scottie Gray on 2021-08-13.
// Copyright © 2021 Novoda. All rights reserved.
//

import Foundation

final class CharacterCardStateFactory {
private func getIsAlive(character: Character) -> Bool {
if character.status == "Alive" {
return true
}

return false
}
Comment on lines +12 to +18

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly there is also state unknown. is the bool correct choice here?


private func getFirstEpisode(character: Character) -> String? {
if let firstEpisode = character.episodeURLs.first {
return firstEpisode
}

return nil
}
Comment on lines +20 to +26

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're returning nil this code doesn't do anything. you could just call character.episodeURLs.first


func createCharacterCardState(from character: Character) -> CharacterCardState {
let isAlive = getIsAlive(character: character)
let firstEpisode = getFirstEpisode(character: character) ?? "Unknown"
let characterCardState = CharacterCardState(id: character.id, name: character.name, imageURL: character.imageURL, isAlive: isAlive, species: character.species, lastLocation: character.lastLocation.name, firstEpisode: firstEpisode)

return characterCardState
}
}
2 changes: 1 addition & 1 deletion Rick-and-Morty/Rick And Morty/Model/Character.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

struct CharactersResponse: Codable {
struct CharacterResponse: Codable {
enum CodingKeys: String, CodingKey {
case info = "info"
case characters = "results"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// CharacterListViewModel.swift
// Rick And Morty
//
// Created by Scottie Gray on 2021-08-13.
// Copyright © 2021 Novoda. All rights reserved.
//

import Foundation

final class CharacterListViewModel: ObservableObject {
@Published var characterListViewState: CharacterListViewState = CharacterListViewState(characterCardStates: [])

private let characterRepository: CharacterRepositoryProtocol = CharacterRepository()
private let characterCardStateFactory = CharacterCardStateFactory()

init() {
loadCardStates()
}

func loadCardStates() {
characterRepository.getCharacters { characters in
for character in characters {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using .map here

let cardState = self.characterCardStateFactory.createCharacterCardState(from: character)
self.characterListViewState.characterCardStates.append(cardState)
}
}
}
}
12 changes: 6 additions & 6 deletions Rick-and-Morty/Rick And Morty/Views/CharacterCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Foundation
struct CharacterCardState {
var id: Int
var name: String
var image: UIImage
var imageURL: String
var isAlive: Bool
var species: String
var lastLocation: String
Expand All @@ -35,9 +35,9 @@ struct CharacterCard: View {

var body: some View {
HStack(alignment: .top) {
Image(uiImage: characterCardState.image)
.resizable()
.aspectRatio(contentMode: .fit)
//Image(uiImage: characterCardState.image)
//.resizable()
//.aspectRatio(contentMode: .fit)
Comment on lines +38 to +40

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They not to commit commented out code.


VStack(alignment: .leading, spacing: constants.VSpacing) {
VStack(alignment: .leading, spacing: constants.noSpacing) {
Expand Down Expand Up @@ -81,11 +81,11 @@ struct CharacterTileView_Previews: PreviewProvider {
static var previews: some View {
Group {
VStack {
CharacterCard(characterCardState: CharacterCardState(id: 2, name: "Morty", image: UIImage(named: "morty-image")!, isAlive: true, species: "Human", lastLocation: "Earth", firstEpisode: "Episode 1"))
//CharacterCard(characterCardState: CharacterCardState(id: 2, name: "Morty", image: UIImage(named: "morty-image")!, isAlive: true, species: "Human", lastLocation: "Earth", firstEpisode: "Episode 1"))
}
.preferredColorScheme(.light)
VStack {
CharacterCard(characterCardState: CharacterCardState(id: 2, name: "Morty", image: UIImage(named: "morty-image")!, isAlive: true, species: "Human", lastLocation: "Earth", firstEpisode: "Episode 1"))
//CharacterCard(characterCardState: CharacterCardState(id: 2, name: "Morty", image: UIImage(named: "morty-image")!, isAlive: true, species: "Human", lastLocation: "Earth", firstEpisode: "Episode 1"))
}
.preferredColorScheme(.dark)

Expand Down
5 changes: 5 additions & 0 deletions Rick-and-Morty/Rick And Morty/Views/CharacterListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

import SwiftUI

struct CharacterListViewState {
let title: String = "Characters"
var characterCardStates: [CharacterCardState]
}

struct CharacterListView: View {

var body: some View {
Expand Down