Apologies
You must be signed in to watch this lesson.
Retain Cycle + Async Loading + Search Delay Fixes
Podcasts
Quick fixes to a few issues that are currently present in our application. First I want to address a retain cycle that was introduced in our AVPlayer observation code. Secondly, there's a synchronous load call using FeedKit's FeedParser constructor. Lastly, I want to introduce a small delay for searching of podcasts in our initial screen. Very interesting things to look at when developing a user friendly application.

Comments (14)
guleadrian
6 years ago
These kinds of videos is why I love this channel/website so much. I tried to implement this delay when we first created the search functionality, and ended up with the code down under, which is a bit different from yours in this video. I am very curious of what you think of this code, is it OK? var textCount = Int() func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { textCount = searchText.count DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) { let currentTextCount = searchText.count if self.textCount == currentTextCount { self.searchiTunes(searchText: searchText) } } }
Brian Voong
6 years ago
Tube
6 years ago
Very useful video: Changes are very noticeable. I never liked the way search worked; so, I changed over to searchBarTextDidEndEditing instead. And, I made activityIndicator a computed property, because I was afraid it was still running invisibly. So, now I can stop it.
fiftysoft
6 years ago
we waiting next episode :) , good job
Farukh
6 years ago
challange + attributedText: override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) activityIndicatorView.color = .darkGray activityIndicatorView.startAnimating() activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(activityIndicatorView) NSLayoutConstraint.activate([ activityIndicatorView.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 20), activityIndicatorView.centerXAnchor.constraint(equalTo: tableView.centerXAnchor) ]) let label = UILabel() let attributedText = NSMutableAttributedString(string: "Loading ", attributes: [NSAttributedStringKey.foregroundColor: UIColor.darkGray, NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)]) attributedText.append(NSAttributedString(string: "Podcast episode", attributes: [NSAttributedStringKey.foregroundColor: UIColor.purple, NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 14)])) label.attributedText = attributedText label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false activityIndicatorView.addSubview(label) NSLayoutConstraint.activate([ label.topAnchor.constraint(equalTo: activityIndicatorView.bottomAnchor, constant: 6), label.heightAnchor.constraint(equalToConstant: 30), label.widthAnchor.constraint(equalToConstant: 200), label.centerXAnchor.constraint(equalTo: activityIndicatorView.centerXAnchor) ]) return activityIndicatorView }
이동건
6 years ago
Hello brian i got some error randomly when i clicked the episode In "toDisplayString()" of CMTime Extension when we try to access Int(CMTimeGetSeconds(self)) this code error said Fatal error: Double value cannot be converted to Int because it is either infinite or NaN then in console self = (CMTime) indefinite and also said query={ class = inet; "m_Limit" = "m_LimitAll"; "r_Attributes" = 1; sync = syna; } what is the error meaning? some times it goes well but some times it occurs theses errors
이동건
6 years ago
not in simulator only in real device
이동건
6 years ago
i solve this error's like below is it correct? if let durationTime = self?.player.currentItem?.duration, self?.player.currentItem?.status == .readyToPlay { self?.durationLabel.text = durationTime.toDisplayString() self?.currentTimeSlider.value = Float(CMTimeGetSeconds(time)) / Float(CMTimeGetSeconds(durationTime)) }
Huo En Ci
6 years ago
Very useful video Brian. May I clarify about weak self? The reason we use weak is to make the self optional. Does it then mean that if the self is deinit, the functions that are tied to the self will not be triggered? for instance: self?.enlargeEpisodeImageView() I am interpreting that when self is not present, the enlargeEpisodeImageView method will not be called?
Brian Voong
6 years ago
Huo En Ci
6 years ago
Thanks Brian for your prompt reply. I have been reading up on Retain Cycle lately on StackOverflow which can be confusing. May I further ask when will [unowned self] be useful?
Brian Voong
6 years ago
weare99
5 years ago
At the Timer scheduled function on the completion block does I need [weak self] (timer) in ....?
Brian Voong
5 years ago
Backerich
5 years ago
When I am trying to run the app on a real device a CredStore error occurs while trying to load the audio. Any suggestions?
Brian Voong
5 years ago
meher
5 years ago
Hi Brian , you said in the video that you are going to provide some links on retain cycles.Where are the links? i did not understand this retain cycle thing...
awjenson
5 years ago
Hi Brian, I would like to read your recommended retain cycle articles. thanks
Brian Voong
5 years ago
meher
5 years ago
Brian, How did you know that the player object had retain cycle problem when we had other objects ? Is it because of the sound was playing after we dismiss? or did you use the leaks tool of Xcode? by the way i used the leak tool and we have so many other leaks as well. Please make a video on this retain cycle thing, it is very difficult to understand and solve these leaks
Dew Douglass
5 years ago
Excellent homework exercise
Dew Douglass
5 years ago
Just noticed a glitch. I posted the above comment 30 seconds ago on 7-10-18. But it says I posted it 3 months ago.
Dew Douglass
5 years ago
Now that I reloaded the page the correct time is shown. Nevermind.
petar7
5 years ago
Hi Brian could you please look at the code , this challenge I tried it worked for me but when I type fast the indicator keeps spinnig var activity = UIActivityIndicatorView() var label = UILabel() func handleActivityIndicator() { label = UILabel() label.textAlignment = .center label.text = "Currently Searching" label.font = UIFont.boldSystemFont(ofSize: 16) activity = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) activity.color = .darkGray activity.translatesAutoresizingMaskIntoConstraints = false label.translatesAutoresizingMaskIntoConstraints = false view.addSubview(label) label.addSubview(activity) label.topAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: 100).isActive = true label.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true label.addSubview(activity) activity.centerXAnchor.constraint(equalTo: (label.centerXAnchor)).isActive = true activity.bottomAnchor.constraint(equalTo: (label.topAnchor), constant: 0).isActive = true activity.heightAnchor.constraint(equalToConstant: 50).isActive = true activity.widthAnchor.constraint(equalToConstant: 50).isActive = true activity.startAnimating() } and then in fetchPodcasts function I added this code if podcast.isEmpty { self.handleActivityIndicator() } else { DispatchQueue.main.async { self.activity.stopAnimating() self.activity.hidesWhenStopped = true self.label.text = "" }
PreachOnBerto
5 years ago
For the Search Delay Fixes, I found an alternative solution using an async approach with DispatchWorkItem. private var pendingRequestWorkItem: DispatchWorkItem? func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { /* Cancel the currently pending item */ pendingRequestWorkItem?.cancel() /* Wrap the request into a work item */ let requestWorkItem = DispatchWorkItem { [weak self] in APIService.shared.fetchPodcasts(with: searchText) { (podcasts) in self?.podcasts = podcasts self?.tableView.reloadData() } } /* Save the new work item and execute it after 500 ms */ pendingRequestWorkItem = requestWorkItem DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500), execute: requestWorkItem) } I was able to use this approach for the Firebase Chat App as well when loading the Messages Controller. Found this approach under: https://www.swiftbysundell.com/posts/a-deep-dive-into-grand-central-dispatch-in-swift
james91
4 years ago
DispatchQueue 'qos' stands for 'quality of service'
HELP & SUPPORT