We have a function that when it finishes another function should be called in it's completion block but whatever is inside the completion block is never called. Here is the function:
func appendAllData (completion: () -> Void) {
guard let movieDetails = self.movieDetailsData else {
// handle nil case
return;
}
if let posterImage = self.movieDetailsData?.poster {
self.posterArray.append(posterImage)
}
if let overview = self.movieDetailsData?.overview {
self.overviewArray.append(overview)
}
if let releaseDate = self.movieDetailsData?.releaseData {
self.releaseInfoArray.append(releaseDate)
}
if let runtime = self.movieDetailsData?.runtime {
self.releaseInfoArray.append(String(describing: runtime))
}
if let genre = self.movieDetailsData?.genre {
if !genre.isEmpty {
self.releaseInfoArray.append(genre[0].name)
}
}
if let budget = self.movieDetailsData?.budget {
self.boxOfficeArray.append(budget)
}
if let revenue = self.movieDetailsData?.revenue {
self.boxOfficeArray.append(revenue)
}
if let homepage = self.movieDetailsData?.homepage {
self.homePageArray.append(homepage)
}
if let images = self.movieDetailsData?.images {
self.imageArray += images.backdropImages.map{ $0.filePath }
}
}
Here is how it's used:
self.appendAllData(completion: { _ in
//Nothing inside here gets called
DispatchQueue.main.async {
print(self.movieDetailsData?.poster )
if let bgImage = self.movieDetailsData?.poster {
self.backgroundImage.sd_setImage(with: URL(string:"\(baseImageURL)\(bgImage)"))
print("background pic loaded")
self.backgroundImage.addBlurEffect()
}
self.detailTableView.reloadData()
}
})
Nothing inside the completion block is called, any idea how to fix this?
I believe you need to call the completion() at the end for it execute your completion code.
func appendAllData (completion: () -> Void) {
guard let movieDetails = self.movieDetailsData else {
// handle nil case
return;
}
if let posterImage = self.movieDetailsData?.poster {
self.posterArray.append(posterImage)
}
if let overview = self.movieDetailsData?.overview {
self.overviewArray.append(overview)
}
if let releaseDate = self.movieDetailsData?.releaseData {
self.releaseInfoArray.append(releaseDate)
}
if let runtime = self.movieDetailsData?.runtime {
self.releaseInfoArray.append(String(describing: runtime))
}
if let genre = self.movieDetailsData?.genre {
if !genre.isEmpty {
self.releaseInfoArray.append(genre[0].name)
}
}
if let budget = self.movieDetailsData?.budget {
self.boxOfficeArray.append(budget)
}
if let revenue = self.movieDetailsData?.revenue {
self.boxOfficeArray.append(revenue)
}
if let homepage = self.movieDetailsData?.homepage {
self.homePageArray.append(homepage)
}
if let images = self.movieDetailsData?.images {
self.imageArray += images.backdropImages.map{ $0.filePath }
}
completion()
}
Related
I have the following code, I would like to use async-await for the two calls.
At the moment inside the function I used a kind of check variable, which when both are set the code is executed.
How can I do?
....
.onAppear {
DispatchQueue.global(qos: .background).async {
getRemoteHead(url: repoUrlStr)
getRemoteBranch(url: repoUrlStr)
}
}
func getRemoteHead(url: String) {
do {
let branch = try Remote().getRemoteHEAD(url: url)
if branch[0].contains("fatal:") {
Log.warning("Error: getRemoteHead")
activeSheet = .error("Error: getRemoteHead")
} else {
self.mainBranch = branch[0]
self.selectedBranch = branch[0]
self.check += 1
if check == 2 {
check = 0
activeSheet = .select
}
}
} catch {
Log.error("Failed to find main branch name.")
}
}
func getRemoteBranch(url: String) {
do {
let branches = try Remote().getRemoteBranch(url: url)
if branches[0].contains("fatal:") {
Log.warning("Error: getRemoteBranch")
activeSheet = .error("Error: getRemoteBranch")
} else {
self.arrayBranch = branches
self.check += 1
if check == 2 {
check = 0
activeSheet = .select
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// Force a UI Update.
allBranches.toggle()
}
}
}
} catch {
Log.error("Failed to find branches.")
}
}
This is my enum, which is declared globally in my home view controller (Before class HomeViewController: UIViewController,)
enum HomeVCSectionTypes: String, CaseIterable {
case mostPopularBoolValue = "mostPopularBoolValue"
case discountedBoolValue = "discountedBoolValue"
case newlyAddedBoolValue = "newlyAddedBoolValue"
init?(id : Int) {
switch id {
case 1: self = .mostPopularBoolValue
case 2: self = .discountedBoolValue
case 3: self = .newlyAddedBoolValue
default: return nil
}
}
}
And I am iterating through it in my view will appear like this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
for sectionType in HomeVCSectionTypes.allCases {
fetchData(homeVCSectionTypes: sectionType)
}
}
But it is not showing the values according to their serial indexes. What am I missing here?
func fetchData(homeVCSectionTypes: HomeVCSectionTypes) {
self.activityIndicator.startAnimating()
objectArray.removeAll()
let semaphore = DispatchSemaphore(value: 0)
let dispatchQueue = DispatchQueue.global(qos: .background)
dispatchQueue.async {
let docRef = Firestore.firestore().collection("album").order(by: "timestamp", descending: true).whereField(homeVCSectionTypes.rawValue, isEqualTo: true).limit(to: 10)
docRef.getDocuments { (snapshot, error) in
guard let snapshot = snapshot else { return }
var items = [ProductCategoryAlbum]()
if snapshot.documents.count > 0 {
do {
for document in snapshot.documents {
let object = try document.decode(as: ProductCategoryAlbum.self)
items.append(object)
}
self.objectArray.append(HomeObject(sectionName: homeVCSectionTypes.rawValue, sectionObjects: items))
} catch {
print(error)
}
DispatchQueue.main.async {
self.homeTableView.reloadData()
self.activityIndicator.stopAnimating()
}
} else {
DispatchQueue.main.async {
self.homeTableView.isHidden = true
self.noDataLabel.isHidden = false
self.activityIndicator.stopAnimating()
}
}
semaphore.signal()
}
semaphore.wait()
}
}
I'm trying to create a completion block that can execute another function after its completed, in this case it's a tableview reload. I get the error :
'async' produces '()', not the expected contextual result type 'Bool'
This is the function:
func appendAllData (completion: () -> Bool) {
if self.movieDetailsData?.poster != nil {
if let posterImage = self.movieDetailsData?.poster {
self.posterArray.append(posterImage)
}
}
if self.movieDetailsData?.overview != nil {
if let overview = self.movieDetailsData?.overview {
self.overviewArray.append(overview)
}
}
if self.movieDetailsData?.releaseData != nil {
if let releaseDate = self.movieDetailsData?.releaseData {
self.releaseInfoArray.append(releaseDate)
}
}
if self.movieDetailsData?.runtime != nil {
if let runtime = self.movieDetailsData?.runtime {
self.releaseInfoArray.append(String(describing: runtime))
}
}
if self.movieDetailsData?.genre != nil {
if let genre = self.movieDetailsData?.genre {
if genre.isEmpty {
} else {
self.releaseInfoArray.append(genre[0].name)
}
}
}
if self.movieDetailsData?.budget != nil {
if let budget = self.movieDetailsData?.budget {
self.boxOfficeArray.append(budget)
}
}
if self.movieDetailsData?.revenue != nil {
if let revenue = self.movieDetailsData?.revenue {
self.boxOfficeArray.append(revenue)
}
}
if self.movieDetailsData?.homepage != nil {
if let homepage = self.movieDetailsData?.homepage {
self.homePageArray.append(homepage)
}
}
if self.movieDetailsData?.images != nil {
if let images = self.movieDetailsData?.images {
let posters = images.backdropImages
for poster in posters {
self.imageArray.append(poster.filePath)
}
}
}
}
This is how it's used:
self.appendAllData(completion: { _ in
DispatchQueue.main.async { //error here: 'async' produces '()', not the expected contextual result type 'Bool'
self.detailTableView.reloadData()
}
})
Your completion closure signature is completion: () -> Bool but you used () -> (). Just change function parameter from completion: () -> Bool to completion: () -> Void or completion: () -> ().
And you should follow njzk2's comment.
I need to run a function when the contacts have changed. If the application is active, you can do this with NotificationCenter as narrated in this post (sometimes It works when I add a new number to an existing contact). How do I know that the contact (or contacts) have been changed after the launch of the application?
I made the following functions for my task
#objc private func matchingContacts() {
if isSuccessContactUploading {
contactManager.matchingContacts(notMatch: { [weak self] in
guard let _self = self else { return }
debugPrint("matchingContacts != equals")
_self.isSuccessContactUploading = false
_self.syncContacts()
})
}
}
These functions are in ContactManager
func matchingContacts(notMatch: (() -> Void)?) {
getContacts { (contacts, error) in
if error == nil {
debugPrint("contacts count", contacts.count)
self.getContactsDictionaryFromCache(contacts, notMatch: {
notMatch?()
})
}
}
}
private func getContactsDictionaryFromCache(_ contacts: [CNContact], notMatch: (() -> Void)?) {
var isMatching = true
for contact in contacts {
let key = contact.identifier
do {
let cache = try Cache<NSDictionary>(name: "Contacts")
if let contactDictionary = cache[key] {
if !contactDictionary.isEqual(to: contact.dictionary) {
debugPrint("contactDictionary not matching")
isMatching = false
}
} else {
debugPrint("contactDictionary isn't here")
isMatching = false
}
} catch {
debugPrint(error.localizedDescription)
isMatching = false
}
}
if !isMatching {
notMatch?()
}
cacheContacts(contacts)
}
private func cacheContacts(_ contacts: [CNContact]) {
for contact in contacts {
let contactDictionary = contact.dictionary as NSDictionary
let key = contact.identifier
do {
let cache = try Cache<NSDictionary>(name: "Contacts")
cache[key] = contactDictionary
} catch {
debugPrint(error.localizedDescription)
}
}
}
I added a "favorite icon" (a heart) in the top Navigation Bar:
var faveMeItem = UIBarButtonItem (title: dua.isFavorite() ? "❤️" : "💔", style: .Plain, target: self, action: "toggleFav")
Is there a way to ensure it is changed (to a broken heart) as soon as it is tapped?
I have to go back to the tableView and come back to the detail, and then I see the updated icon. Tapping on it does the logic, but the heart is not updated. These are the functions in my Dua Class.
func removeFromFavorites() {
//retrieve all favorites
let favoriteDuaIds = NSUserDefaults.standardUserDefaults().objectForKey(Dua.favoriteDuasKey) as! [Int]?
if let favoriteDuaIds = favoriteDuaIds {
//iterate through all Duas and comapre their IDs
let newFavoriteDuaIds = favoriteDuaIds.filter { favoriteDuaId in
return favoriteDuaId != duaId
}
NSUserDefaults.standardUserDefaults().setObject(newFavoriteDuaIds, forKey: Dua.favoriteDuasKey)
}
}
func isFavorite() -> Bool {
//retrieve all favorites
let favoriteDuaIds = NSUserDefaults.standardUserDefaults().objectForKey(Dua.favoriteDuasKey) as! [Int]?
if let favoriteDuaIds = favoriteDuaIds {
//iterate through all Duas and comapre their IDs
for favoriteDuaId in favoriteDuaIds {
print (favoriteDuaId)
if favoriteDuaId == duaId {
return true
}
}
}
return false
}
func toggleFavorite() {
if isFavorite() {
removeFromFavorites()
} else {
addToFavorites()
}
}
class func favorites() -> [Dua] {
let favoriteDuaIds = NSUserDefaults.standardUserDefaults().objectForKey(favoriteDuasKey) as! [Int]?
if let favoriteDuaIds = favoriteDuaIds {
return DuasDataSource.duas.filter { dua in
return favoriteDuaIds.contains(dua.duaId)
}
} else {
return []
}
}
}
func toggleFavorite() {
if isFavorite() {
removeFromFavorites()
} else {
addToFavorites()
}
faveMeItem.title = dua.isFavorite() ? "❤️" : "💔" //Add this
}