-
Notifications
You must be signed in to change notification settings - Fork 5
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
Ghibli Movie APp #4
base: main
Are you sure you want to change the base?
Conversation
e0a4404
to
3da7100
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Azri.
guard let url = URL(string: "https://ghibliapi.herokuapp.com/films") else { | ||
DispatchQueue.main.async { | ||
completion(.failure("Bad URL")) | ||
} | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jangan pake guard let buat unwrap URL disini, kenapa? karena klo salah disini itu developer mistake bukan codenya, jadi untuk mencegah hal tersebut lebih baik ini di force unwrap.
guard let url = URL(string: "https://ghibliapi.herokuapp.com/films") else { | |
DispatchQueue.main.async { | |
completion(.failure("Bad URL")) | |
} | |
return | |
} | |
let url = URL(string: "https://ghibliapi.herokuapp.com/films")! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mau nanya Bang. Semisalnya invalid URL, ini ga ngecrash Bang kalo di force unwrap?
guard let url = URL(string: "https://ghibliapi.herokuapp.com/films/\(movieId)") else { | ||
DispatchQueue.main.async { | ||
completion(.failure("Bad URL")) | ||
} | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sama ini juga kyk yg diatas
guard let url = URL(string: "https://ghibliapi.herokuapp.com/films/\(movieId)") else { | |
DispatchQueue.main.async { | |
completion(.failure("Bad URL")) | |
} | |
return | |
} | |
let url = URL(string: "https://ghibliapi.herokuapp.com/films/\(movieId)")! |
DispatchQueue.global(qos: .background).async { | ||
guard let data = data else { | ||
DispatchQueue.main.async { | ||
completion(.success(nil)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throw error disini jangan success kalau emang datanya gaada, completion aja dengan completion(.failure("Data not found"))
URLSession.shared.dataTask(with: url) { (data, response, error) in | ||
DispatchQueue.global(qos: .background).async { | ||
guard let data = data else { | ||
DispatchQueue.main.async { | ||
completion(.success(nil)) | ||
} | ||
return | ||
} | ||
|
||
do { | ||
let result = try JSONDecoder().decode(MovieResponse.self, from: data) | ||
let response = ObjectMapper().mapMovieResponseToMovieDomain(movieResponse: result) | ||
DispatchQueue.main.async { | ||
completion(.success(response)) | ||
} | ||
} catch { | ||
DispatchQueue.main.async { | ||
completion(.failure("Failed to convert")) | ||
} | ||
} | ||
} | ||
}.resume() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dari pada manggil DispatchQueue.main.async banyak'' bisa lu ganti aja tuh yg di line 67 DispatchQueue.global(qos: .background).async
jadi langsung pake yg DispatchQueue.main.async
. Karena URLSession defaultnya udah di background thread
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mapping object di Main thread gamasalah Bang?
guard let movieId = movieId else { | ||
self.navigationController?.popViewController(animated: true) | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ini bakal jadi weird behavior klo lu movieId missing ngepaksa user balik ke controller sebelumnya, ini bakal dikira ada bug. ada baiknya kalau emang missing yang lu bikin layout errornya jangan di force kyk gini.
isDownloadingImage(true, movieImageContainerView) | ||
isDownloadingImage(true, movieBannerContainerView) | ||
|
||
movieImageView.kf.setImage( | ||
with: url, | ||
placeholder: nil, | ||
options: nil, | ||
progressBlock: { [weak self] _, _ in | ||
self?.isDownloadingImage(true, self?.movieImageContainerView) | ||
}, | ||
completionHandler: { [weak self] result in | ||
switch result { | ||
case .success(_): | ||
self?.isDownloadingImage(false, self?.movieImageContainerView) | ||
case .failure(let error): | ||
self?.isDownloadingImage(false, self?.movieImageContainerView) | ||
print(error) | ||
} | ||
} | ||
) | ||
|
||
movieBannerImageView.kf.setImage( | ||
with: urlPoster, | ||
placeholder: nil, | ||
options: nil, | ||
progressBlock: { [weak self] _, _ in | ||
self?.isDownloadingImage(true, self?.movieBannerContainerView) | ||
}, | ||
completionHandler: { [weak self] result in | ||
switch result { | ||
case .success(_): | ||
self?.isDownloadingImage(false, self?.movieBannerContainerView) | ||
case .failure(let error): | ||
self?.isDownloadingImage(false, self?.movieBannerContainerView) | ||
print(error) | ||
} | ||
} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ini sebenernya bisa lu rubah jadi satu fungsi tinggal dirubah parameter imageViewnya aja.
isDownloadingImage(true, movieImageContainerView) | |
isDownloadingImage(true, movieBannerContainerView) | |
movieImageView.kf.setImage( | |
with: url, | |
placeholder: nil, | |
options: nil, | |
progressBlock: { [weak self] _, _ in | |
self?.isDownloadingImage(true, self?.movieImageContainerView) | |
}, | |
completionHandler: { [weak self] result in | |
switch result { | |
case .success(_): | |
self?.isDownloadingImage(false, self?.movieImageContainerView) | |
case .failure(let error): | |
self?.isDownloadingImage(false, self?.movieImageContainerView) | |
print(error) | |
} | |
} | |
) | |
movieBannerImageView.kf.setImage( | |
with: urlPoster, | |
placeholder: nil, | |
options: nil, | |
progressBlock: { [weak self] _, _ in | |
self?.isDownloadingImage(true, self?.movieBannerContainerView) | |
}, | |
completionHandler: { [weak self] result in | |
switch result { | |
case .success(_): | |
self?.isDownloadingImage(false, self?.movieBannerContainerView) | |
case .failure(let error): | |
self?.isDownloadingImage(false, self?.movieBannerContainerView) | |
print(error) | |
} | |
} | |
) | |
private func loadImageUsingKingfisher(url: URL, urlPoster: URL) { | |
downloadImage(for : movieImageContainerView, with : url) | |
downloadImage(for : movieBannerImageView, with : urlPoster) | |
} | |
private func downloadImage(for imageView: UIImageView, with url: URL) { | |
isDownloadingImage(true, imageView) | |
imageView.kf.setImage( | |
with: url, | |
placeholder: nil, | |
options: nil, | |
progressBlock: { [weak self] _, _ in | |
self?.isDownloadingImage(true, imageView) | |
}, | |
completionHandler: { [weak self] result in | |
switch result { | |
case .success(_): | |
self?.isDownloadingImage(false, imageView) | |
case .failure(let error): | |
self?.isDownloadingImage(false, imageView) | |
print(error) | |
} | |
} | |
) | |
} |
movieImageView.kf.setImage( | ||
with: url, | ||
placeholder: nil, | ||
options: nil, | ||
progressBlock: nil, | ||
completionHandler: { [weak self] result in | ||
switch result { | ||
case .success(_): | ||
self?.isDownloadingImage(false) | ||
case .failure(let error): | ||
self?.isDownloadingImage(false) | ||
print(error) | ||
} | ||
} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ini juga sama, klo lu ngerasa berulang bgt, bisa dibikin ImageLoader nya sendiri jadi tinggal manggil class itu
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { action in | ||
switch action.style { | ||
case .default: | ||
print("default") | ||
|
||
case .cancel: | ||
print("cancel") | ||
|
||
case .destructive: | ||
print("destructive") | ||
|
||
default: | ||
print("default") | ||
} | ||
})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Klo gaada action yg dihandle ga perlu diimplement switch case nya
class DetailMovieViewModel { | ||
private let movieNetworkModel : MovieNetworkModel | ||
|
||
init(movieNetworkModel: MovieNetworkModel) { | ||
self.movieNetworkModel = movieNetworkModel | ||
} | ||
|
||
func retrieveMovie(movieId: String, completion : @escaping (RetrievingDetailMovieState) -> ()) { | ||
movieNetworkModel.getMovie(movieId: movieId) { result in | ||
switch result { | ||
case .success(let movie): | ||
completion(.success(movie)) | ||
case .failure(let message): | ||
completion(.failure(message)) | ||
} | ||
} | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Klo niat lu implement MVVM, lu gabisa bilang ini ViewModel, karena viewModel di MVVM itu punya binder/observer yang menjadi pembeda itu sama MVC. lu harus bikin binder/observernya terus di observer di view, instead of lu handle from escaping when calling retrieveMovie(..). jadi Klo MVVM gamake result dari Completion ini jadi ga perlu ada completion di function retrieveMovie(..).
Ini berlaku untuk semua ViewModel yang lu implementasikan Project ini
class DetailMovieViewModel { | |
private let movieNetworkModel : MovieNetworkModel | |
init(movieNetworkModel: MovieNetworkModel) { | |
self.movieNetworkModel = movieNetworkModel | |
} | |
func retrieveMovie(movieId: String, completion : @escaping (RetrievingDetailMovieState) -> ()) { | |
movieNetworkModel.getMovie(movieId: movieId) { result in | |
switch result { | |
case .success(let movie): | |
completion(.success(movie)) | |
case .failure(let message): | |
completion(.failure(message)) | |
} | |
} | |
} | |
} | |
class DetailMovieViewModel { | |
private let movieNetworkModel : MovieNetworkModel | |
private var resultsObserver : ((Movie) -> ()?) // nanti ini yg lu observer di Controllernya | |
private var errorObserver : ((String) -> ()?) // nanti ini yg lu observer di Controllernya | |
init(movieNetworkModel: MovieNetworkModel) { | |
self.movieNetworkModel = movieNetworkModel | |
} | |
func retrieveMovie(movieId: String) { | |
movieNetworkModel.getMovie(movieId: movieId) { result in | |
switch result { | |
case .success(let movie): | |
resultsObserver?(movie) | |
case .failure(let message): | |
errorObserver?(message) | |
} | |
} | |
} | |
} |
No description provided.