Skip to content

Commit

Permalink
Merge pull request #91 from novoda/feature-rm-88-handle-server-error
Browse files Browse the repository at this point in the history
Feature [RM88] Handle Server Error
  • Loading branch information
swg99 authored Aug 27, 2021
2 parents 379a39e + b99cc75 commit 2564e7d
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 19 deletions.
9 changes: 4 additions & 5 deletions Rick-and-Morty/CharacterRepository.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Foundation

protocol CharacterRepositoryProtocol {
func getCharacters(completion: @escaping (([Character]) -> Void))
func getCharacters(completion: @escaping (([Character]) -> Void), fail: @escaping ((NSError?) -> Void))
}

final class CharacterRepository: CharacterRepositoryProtocol {
private var characterPageURL: URL? = RickAndMortyService.baseURL.appendingPathComponent("character")

private let rickAndMortyService: RickAndMortyServiceProtocol = RickAndMortyService()

func getCharacters(completion: @escaping (([Character]) -> Void)) {
func getCharacters(completion: @escaping (([Character]) -> Void), fail: @escaping ((NSError?) -> Void)) {
if let url = characterPageURL {
rickAndMortyService.fetchData(url: url) { (charactersResponse: CharacterResponse) in
if let nextURLString = charactersResponse.info.next {
Expand All @@ -21,9 +21,8 @@ final class CharacterRepository: CharacterRepositoryProtocol {
}

completion(charactersResponse.characters)
} error: { error in
print(error.debugDescription)
return
} fail: { (error: NSError?) in
fail(error)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Rick-and-Morty/EpisodeRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class EpisodeRepository: EpisodeRepositoryProtocol {
rickAndMortyService.fetchData(url: url) { (episode: Episode) in
EpisodeRepository.cachedEpisodeNames[urlString] = episode
completion(episode)
} error: { error in
} fail: { error in
print(error.debugDescription)
return
}
Expand Down
4 changes: 4 additions & 0 deletions Rick-and-Morty/Rick And Morty.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
1711B39E26B1898100BE935B /* CharacterListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1711B39D26B1898100BE935B /* CharacterListView.swift */; };
174655E826CBB74100A52819 /* EpisodeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174655E726CBB74100A52819 /* EpisodeDetailView.swift */; };
174655EA26CBBD2300A52819 /* EpisodeDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174655E926CBBD2300A52819 /* EpisodeDetailViewModel.swift */; };
174878F626D4E3CA004293AB /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174878F526D4E3CA004293AB /* ErrorView.swift */; };
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 */; };
Expand Down Expand Up @@ -41,6 +42,7 @@
1711B39D26B1898100BE935B /* CharacterListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterListView.swift; sourceTree = "<group>"; };
174655E726CBB74100A52819 /* EpisodeDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeDetailView.swift; sourceTree = "<group>"; };
174655E926CBBD2300A52819 /* EpisodeDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeDetailViewModel.swift; sourceTree = "<group>"; };
174878F526D4E3CA004293AB /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
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>"; };
Expand Down Expand Up @@ -140,6 +142,7 @@
1711B39D26B1898100BE935B /* CharacterListView.swift */,
17588BAE26C273BB008ECC31 /* CharacterCard.swift */,
174655E726CBB74100A52819 /* EpisodeDetailView.swift */,
174878F526D4E3CA004293AB /* ErrorView.swift */,
174941D126CD1216001AFD9A /* Utilities */,
);
path = Views;
Expand Down Expand Up @@ -308,6 +311,7 @@
B811686D1CFF1C9900301A0A /* AppDelegate.swift in Sources */,
174B065926C680850080ADD0 /* EpisodeRepository.swift in Sources */,
174655EA26CBBD2300A52819 /* EpisodeDetailViewModel.swift in Sources */,
174878F626D4E3CA004293AB /* ErrorView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
17 changes: 12 additions & 5 deletions Rick-and-Morty/Rick And Morty/Services/RickAndMortyService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import Foundation
final class RickAndMortyService: RickAndMortyServiceProtocol {
static let baseURL = URL(string: "https://rickandmortyapi.com/api")!

func fetchData<T:Decodable>(url: URL, success: @escaping (T) -> (), error: @escaping (Error?) -> ()) {
func fetchData<T:Decodable>(url: URL, success: @escaping (T) -> (), fail: @escaping (NSError?) -> ()) {

let request = URLRequest(url: url)

URLSession.shared.dataTask(with: request) { data, response, requestError in
Expand All @@ -16,15 +17,21 @@ final class RickAndMortyService: RickAndMortyServiceProtocol {
success(decodedData)
}
} catch let decodingError {
DispatchQueue.main.async {
error(decodingError)
if let error = decodingError as NSError? {
DispatchQueue.main.async {
fail(error)
}
} else {
print("Unknown Error: \(decodingError.localizedDescription)")
}
}
} else {
if requestError != nil {
if let error = requestError as NSError? {
DispatchQueue.main.async {
error(requestError)
fail(error)
}
} else {
print("Unknown Error: \(String(describing: requestError?.localizedDescription))")
}
}
}.resume()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation

protocol RickAndMortyServiceProtocol {
func fetchData<T:Decodable>(url: URL, success: @escaping (T) -> (), error: @escaping (Error?) -> ())
func fetchData<T:Decodable>(url: URL, success: @escaping (T) -> (), fail: @escaping (NSError?) -> ())
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ final class CharacterListViewModel: ObservableObject {

struct CharacterListViewState {
let title: String = "Characters"
var errorMessage: String? = nil
var characters: [Character]
var state: State = .doneLoading

enum State {
case loading
case doneLoading
case error
}
}

private let characterRepository: CharacterRepositoryProtocol = CharacterRepository()
Expand All @@ -16,11 +24,28 @@ final class CharacterListViewModel: ObservableObject {
}

func loadCharacters() {
characterRepository.getCharacters {
characters in
characterListViewState.state = .loading

characterRepository.getCharacters { characters in
self.characterListViewState.state = .doneLoading
self.characterListViewState.errorMessage = nil

for character in characters {
self.characterListViewState.characters.append(character)
}
} fail: { error in

self.characterListViewState.state = .error

if let error = error {
if error.code == 4864 {
self.characterListViewState.errorMessage = "Wubba Lubba Dub Dub! There was a problem loading the characters."
} else {
self.characterListViewState.errorMessage = error.localizedDescription
}

self.characterListViewState.errorMessage?.append(" Tap the refresh button to try again..")
}
}
}

Expand Down
19 changes: 14 additions & 5 deletions Rick-and-Morty/Rick And Morty/Views/CharacterListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ struct CharacterListView: View {

var body: some View {
NavigationView {
List(viewModel.characterListViewState.characters, id: \.id) { character in
CharacterCard(viewModel: CharacterCardViewModel(character: character))
.onAppear(perform: {
viewModel.loadIfNeeded(characterID: character.id)
})
ZStack {
List(viewModel.characterListViewState.characters, id: \.id) { character in
CharacterCard(viewModel: CharacterCardViewModel(character: character))
.onAppear(perform: {
viewModel.loadIfNeeded(characterID: character.id)
})
}

if viewModel.characterListViewState.state == .error {
ErrorView(errorMessage: viewModel.characterListViewState.errorMessage, refreshAction: viewModel.loadCharacters)
} else if viewModel.characterListViewState.state == .loading {
ProgressView()
}
}
.listStyle(PlainListStyle())
.navigationTitle(viewModel.characterListViewState.title)
}
}
Expand Down
37 changes: 37 additions & 0 deletions Rick-and-Morty/Rick And Morty/Views/ErrorView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// ErrorView.swift
// Rick And Morty
//
// Created by Scottie Gray on 2021-08-24.
// Copyright © 2021 Novoda. All rights reserved.
//

import SwiftUI

struct ErrorView: View {
let errorMessage: String?
let refreshAction: () -> ()

var body: some View {
if let error = errorMessage {
VStack {
Text(error)
.foregroundColor(.gray)
.multilineTextAlignment(.center)
.padding()
Button(action: refreshAction, label: {
Image(systemName: "arrow.clockwise")
.resizable()
.frame(width: 30, height: 30, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
})
}
}
}
}

struct ErrorView_Previews: PreviewProvider {
static var previews: some View {
ErrorView(errorMessage: "Wubba Lubba Dub Dub! There was a problem loading. Tap the button to try again.", refreshAction: {})

}
}

0 comments on commit 2564e7d

Please sign in to comment.