Ios UITableView在API调用完成之前加载单元格
我正在开发这个应用程序,它可以帮助我在tweet上运行一些NLP,并使用Ios UITableView在API调用完成之前加载单元格,ios,json,swift,asynchronous,networking,Ios,Json,Swift,Asynchronous,Networking,我正在开发这个应用程序,它可以帮助我在tweet上运行一些NLP,并使用TableView在提要中显示结果 到今天为止,我的应用程序在设备上运行了所有NLP,并使用了一个定制模型,该模型是用苹果的CreateML自然语言构建的。当我打开应用程序时,tweet将被分析并在feed中显示结果 为了提高结果的准确性,我建立了自己的API&现在调用该API进行一些额外的分析。现在的问题是,当我打开应用程序时,有某种比赛条件。在我刷新之前,提要不会显示任何内容。在控制台中,我看到提要是在运行fetchAn
TableView
在提要中显示结果
到今天为止,我的应用程序在设备上运行了所有NLP,并使用了一个定制模型,该模型是用苹果的CreateML
自然语言构建的。当我打开应用程序时,tweet将被分析并在feed中显示结果
为了提高结果的准确性,我建立了自己的API&现在调用该API进行一些额外的分析。现在的问题是,当我打开应用程序时,有某种比赛条件。在我刷新之前,提要不会显示任何内容。在控制台中,我看到提要是在运行fetchAndAnalyze()
时完成的,该提要将在triplechecksential()
中的API调用未完成时获取结果
下面是关于架构的一些解释
网络API(仅相关代码):
// This function makes a call to the Twitter API & returns a JSON of a user's tweets.
static func getUserTimeline(screenName: String, completion: @escaping (JSON) -> Void) {
client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
if connectionError != nil {
print("Error: \(connectionError)")
}
do {
let json = try JSON(data: data!)
completion(json)
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
}
}
}
// This function makes a call to my API & checks the sentiment of a Tweet.
static func checkNegativeSentiment(tweet: Tweet, completion: @escaping (JSON) -> Void) {
let headers: HTTPHeaders = ["Content-Type": "application/json"]
AF.request(apiURL, method: .post, parameters: tweet, encoder: JSONParameterEncoder.default, headers: headers).response {
response in
do {
let json = try JSON(data: response.data!)
completion(json)
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
completion(JSON.init(parseJSON: "API OFFLINE."))
}
}
}
// This function is called from the app's feed to retrieve the most recent tweets.
func fetchTweets(completion: @escaping (Bool) -> Void) {
for friend in Common.listOfFriends {
NetworkingAPI.getUserTimeline(screenName: friend.handle, completion: {
success in
self.parseTweets() // This puts all the tweets returned in success in a list.
self.analyze() // Runs some NLP on the tweets.
completion(true)
})
}
}
func analyze() {
for tweet in listOfTweets {
// Does some on-device NLP using a model created with CreateML ...
if sentimentScore == "0" { // That is the tweet is negative.
doubleCheckSentiment(tweet: tweet)
}
}
}
func doubleCheckSentiment(tweet: Tweet) {
// Does some on-device NLP using Apple's generic framework NaturalLanguage.
if sentimentScore <= -0.8 { // Once again, the tweet is negative.
tripleCheckSentiment(tweet: tweet)
}
}
func tripleCheckSentiment(tweet: Tweet) {
NetworkingAPI.checkNegativeAzureSentiment(tweet: tweet, completion: {
json in
if json["value"]["sentiment"].int! == 2 { // We confirm the tweet is negative.
Common.negativeTweets.append(tweet)
}
}
}
// This function gets called when the view appears & at a bunch of different occasions.
func fetchAndAnalyze() {
var friendsAnalyzed = 0
tweetManager.fetchTweets(completion: {
success in
friendsAnalyzed += 1 // Every time completion hits, it means one friend was analyzed.
if friendsAnalyzed == Common.listOfFriends.count { // Done analyzing all friends.
self.tableView.reloadData() // Refresh & show the tweets in Common.negativeTweets in table.
}
}
TweetManager(仅相关代码):
// This function makes a call to the Twitter API & returns a JSON of a user's tweets.
static func getUserTimeline(screenName: String, completion: @escaping (JSON) -> Void) {
client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
if connectionError != nil {
print("Error: \(connectionError)")
}
do {
let json = try JSON(data: data!)
completion(json)
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
}
}
}
// This function makes a call to my API & checks the sentiment of a Tweet.
static func checkNegativeSentiment(tweet: Tweet, completion: @escaping (JSON) -> Void) {
let headers: HTTPHeaders = ["Content-Type": "application/json"]
AF.request(apiURL, method: .post, parameters: tweet, encoder: JSONParameterEncoder.default, headers: headers).response {
response in
do {
let json = try JSON(data: response.data!)
completion(json)
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
completion(JSON.init(parseJSON: "API OFFLINE."))
}
}
}
// This function is called from the app's feed to retrieve the most recent tweets.
func fetchTweets(completion: @escaping (Bool) -> Void) {
for friend in Common.listOfFriends {
NetworkingAPI.getUserTimeline(screenName: friend.handle, completion: {
success in
self.parseTweets() // This puts all the tweets returned in success in a list.
self.analyze() // Runs some NLP on the tweets.
completion(true)
})
}
}
func analyze() {
for tweet in listOfTweets {
// Does some on-device NLP using a model created with CreateML ...
if sentimentScore == "0" { // That is the tweet is negative.
doubleCheckSentiment(tweet: tweet)
}
}
}
func doubleCheckSentiment(tweet: Tweet) {
// Does some on-device NLP using Apple's generic framework NaturalLanguage.
if sentimentScore <= -0.8 { // Once again, the tweet is negative.
tripleCheckSentiment(tweet: tweet)
}
}
func tripleCheckSentiment(tweet: Tweet) {
NetworkingAPI.checkNegativeAzureSentiment(tweet: tweet, completion: {
json in
if json["value"]["sentiment"].int! == 2 { // We confirm the tweet is negative.
Common.negativeTweets.append(tweet)
}
}
}
// This function gets called when the view appears & at a bunch of different occasions.
func fetchAndAnalyze() {
var friendsAnalyzed = 0
tweetManager.fetchTweets(completion: {
success in
friendsAnalyzed += 1 // Every time completion hits, it means one friend was analyzed.
if friendsAnalyzed == Common.listOfFriends.count { // Done analyzing all friends.
self.tableView.reloadData() // Refresh & show the tweets in Common.negativeTweets in table.
}
}
我知道这很长&我深表歉意,但如果我能在这方面得到一些帮助,我将非常感激!顺便说一句,请原谅我使用@escaping&所有这些,我对处理异步API调用相当陌生
谢谢
**EDIT,在实现了jawadAli的解决方案后,由于某种原因,该解决方案在某些情况下有效,我注意到以下模式:**
想象一下,我在朋友列表中添加了一个朋友。然后我刷新,它调用fetchAndAnalyze()
。我们在调用的日志中看到。
&函数调用结束时,没有发现负面推文。就在这件事发生之后,我们从API调用中得到一个结果,其中一条推文被发现为负面消息
如果我再次刷新,则会显示该tweet。有什么线索吗?此函数有问题。在第一次横切for loop时,您的完成将被解雇
func fetchTweets(completion: @escaping (Bool) -> Void) {
let myGroup = DispatchGroup()
for friend in Common.listOfFriends {
myGroup.enter()
NetworkingAPI.getUserTimeline(screenName: friend.handle, completion: {
success in
self.parseTweets()
self.analyze()
myGroup.leave()
})
}
}
myGroup.notify(queue: DispatchQueue.main) {
completion(true)
})
还要在主线程上重新加载数据
DispatchQueue.main.async {
self.tableView.reloadData()
}
注意:您需要相应地处理成功和失败案例。。我只是想知道如何使用dispatchGroup来同步呼叫…据我所知,您在
fetchTweets
函数中多次调用NetworkingAPI.getUserTimeline
,但您没有等待所有响应完成。@omerfarukozturk,是的!这就是我所理解的&似乎是什么导致了这个问题。我尝试了很多不同的方法,你能告诉我如何解决这个问题吗?你可以用以下方法解决它,@omerfarukozturklet myGroup=DispatchGroup()for friend in Common.listOfFriends{myGroup.enter()NetworkingAPI.getUserTimeline(屏幕名:friend.handle,完成:{Success in self.parseTweets()self.Analysis()count+=1如果count==Common.listOfFriends.count{myGroup.leave()}}}}myGroup.notify(queue:DispatchQueue.main,execute:{completion(true)})
它没有工作,很遗憾。当打印count
时,我会在API调用打印其JSON之前达到最大值。顺便说一句,您需要调用leave()
在每次完成时,不仅仅是当count==Common.listofriends.count
时。非常感谢,这帮了我很大的忙。我仍然需要了解DispatchGroup是如何工作的,但这对我来说是个窍门!嗯,这很奇怪。似乎如果我的列表中有多个朋友,它工作得很好。但是,如果我只有一个朋友,它仍然会给我带来更多的快乐同样的问题。它不应该…调试它来检查发生了什么。你能看看编辑并告诉我你是否有任何线索正在发生什么吗?这是我能够调试的。