В настоящее время я сталкиваюсь с ошибкой, из-за которой первоначальный счетчик лайков для этих цитат по умолчанию равен 0 и обновляется до правильного значения только тогда, когда я нажимаю кнопку «Мне нравится». Очевидно, мой метод likeQuoteAction
работает правильно, и я предполагаю, что метод getLikeCountForQuote
работает, потому что его ответ от моего API имеет код состояния 200:
Исходный код для этого запроса GET:
// GET /quoteLikes/:id - get the number of likes for a specific quote by ID
r.GET("/quoteLikes/:id", func(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.Atoi(idStr)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "Invalid quote ID."})
return
}
var likes int
err = db.QueryRow("SELECT likes FROM quotes WHERE id = $1", id).Scan(&likes)
if err == sql.ErrNoRows {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"message": "Quote not found."})
return
} else if err != nil {
log.Println(err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": "Failed to retrieve likes count from the database."})
return
}
c.IndentedJSON(http.StatusOK, gin.H{"id": id, "likes": likes})
})
Это структура, которую я использую для заполнения цитат текстом, автором и лайками. Здесь мне нужно что-то изменить, чтобы начальный подсчет лайков был точным:
struct SingleQuoteView: View {
@EnvironmentObject var sharedVars: SharedVarsBetweenTabs
let quote: Quote
@AppStorage("likedQuotes", store: UserDefaults(suiteName: "group.selectedSettings"))
private var likedQuotesData: Data = Data()
@AppStorage("bookmarkedQuotes", store: UserDefaults(suiteName: "group.selectedSettings"))
private var bookmarkedQuotesData: Data = Data()
@State private var isLiked: Bool = false
@State private var isBookmarked: Bool = false
@State private var likes: Int = 0 // Assuming this is what it's grabbing for the value, without updating until the like button is pressed
@State private var isLiking: Bool = false
init(quote: Quote) {
self.quote = quote
self._isBookmarked = State(initialValue: isQuoteBookmarked(quote))
self._isLiked = State(initialValue: isQuoteLiked(quote))
}
private func getQuoteLikeCountMethod(completion: @escaping (Int) -> Void) {
getLikeCountForQuote(quoteGiven: quote) { likeCount in
completion(likeCount)
}
}
private func getLikeCountForQuote(quoteGiven: Quote, completion: @escaping (Int) -> Void) {
guard let url = URL(string: "omitted for stackoverflow") else {
completion(0)
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let likeCount = json["likeCount"] as? Int {
completion(likeCount)
} else {
completion(0)
}
}.resume()
}
var body: some View {
VStack {
HStack {
Text("\"\(quote.text)\"")
.font(.title3)
.foregroundColor(colorPalettes[safe: sharedVars.colorPaletteIndex]?[1] ?? .white)
.padding(.bottom, 2)
.frame(alignment: .leading)
Spacer()
}
if let author = quote.author, author != "Unknown Author", !author.isEmpty, author != "NULL", author != "" {
HStack {
Spacer()
Text("— \(author)")
.font(.body)
.foregroundColor(colorPalettes[safe: sharedVars.colorPaletteIndex]?[2] ?? .white)
.padding(.bottom, 5)
.frame(alignment: .trailing)
}
}
HStack {
HStack {
Button(action: {
likeQuoteAction()
toggleLike()
}) {
Image(uiImage: resizeImage(UIImage(systemName: isLiked ? "heart.fill" : "heart")!, targetSize: CGSize(width: 75, height: 27))!)
.foregroundColor(isLiked ? .yellow : .gray)
}
// Display the like count next to the heart button
Text("\(likes)")
}
Button(action: {
toggleBookmark()
}) {
Image(uiImage: resizeImage(UIImage(systemName: isBookmarked ? "bookmark.fill" : "bookmark")!, targetSize: CGSize(width: 75, height: 27))!)
.foregroundColor(isBookmarked ? .yellow : .gray)
}
if #available(iOS 16.0, *) {
let authorForSharing = (quote.author != "Unknown Author" && quote.author != "NULL" && quote.author != "" && quote.author != nil) ? quote.author : ""
let wholeAuthorText = (authorForSharing != "") ? "\n— \(authorForSharing ?? "Unknown Author")" : ""
ShareLink(item: URL(string: "https://apps.apple.com/us/app/quote-droplet/id6455084603")!, message: Text("From the Quote Droplet app:\n\n\"\(quote.text)\"\(wholeAuthorText)")) {
Image(uiImage: resizeImage(UIImage(systemName: "square.and.arrow.up")!, targetSize: CGSize(width: 75, height: 27))!)
}
} else {
// Fallback on earlier versions
}
Spacer()
}
}
.padding()
.background(ColorPaletteView(colors: [colorPalettes[safe: sharedVars.colorPaletteIndex]?[0] ?? Color.clear]))
.cornerRadius(20)
.shadow(radius: 5)
.padding(.horizontal)
.onAppear {
isBookmarked = isQuoteBookmarked(quote)
getQuoteLikeCountMethod { fetchedLikeCount in
likes = fetchedLikeCount
}
isLiked = isQuoteLiked(quote)
}
}
private func toggleBookmark() {
isBookmarked.toggle()
var bookmarkedQuotes = getBookmarkedQuotes()
if isBookmarked {
bookmarkedQuotes.append(quote)
} else {
bookmarkedQuotes.removeAll { $0.id == quote.id }
}
saveBookmarkedQuotes(bookmarkedQuotes)
}
private func toggleLike() {
isLiked.toggle()
var likedQuotes = getLikedQuotes()
if isLiked {
likedQuotes.append(quote)
} else {
likedQuotes.removeAll { $0.id == quote.id }
}
saveLikedQuotes(likedQuotes)
}
private func likeQuoteAction() {
guard !isLiking else { return }
isLiking = true
// Check if the quote is already liked
let isAlreadyLiked = isQuoteLiked(quote)
// Call the like/unlike API based on the current like status
if isAlreadyLiked {
unlikeQuote(quoteID: quote.id) { updatedQuote, error in
DispatchQueue.main.async {
if let updatedQuote = updatedQuote {
// Update likes count
self.likes = updatedQuote.likes ?? 0
}
self.isLiking = false
}
}
} else {
likeQuote(quoteID: quote.id) { updatedQuote, error in
DispatchQueue.main.async {
if let updatedQuote = updatedQuote {
// Update likes count
self.likes = updatedQuote.likes ?? 0
}
self.isLiking = false
}
}
}
}
private func isQuoteLiked(_ quote: Quote) -> Bool {
return getLikedQuotes().contains(where: { $0.id == quote.id })
}
private func getLikedQuotes() -> [Quote] {
if let quotes = try? JSONDecoder().decode([Quote].self, from: likedQuotesData) {
return quotes
}
return []
}
private func saveLikedQuotes(_ quotes: [Quote]) {
if let data = try? JSONEncoder().encode(quotes) {
likedQuotesData = data
}
}
private func isQuoteBookmarked(_ quote: Quote) -> Bool {
return getBookmarkedQuotes().contains(where: { $0.id == quote.id })
}
private func getBookmarkedQuotes() -> [Quote] {
if let quotes = try? JSONDecoder().decode([Quote].self, from: bookmarkedQuotesData) {
return quotes
}
return []
}
private func saveBookmarkedQuotes(_ quotes: [Quote]) {
if let data = try? JSONEncoder().encode(quotes) {
bookmarkedQuotesData = data
}
}
}
Для контекста каждый из этих SingleQuoteView
соответствует одной из цитат в моем приложении:
Пожалуйста, дайте мне знать, если необходимы дополнительные разъяснения.
Справедливый комментарий @workingdogsupportUkraine. Да, это довольно расплывчатый вопрос, особенно если учесть, что он подключается не только к проекту Swift, но и к моему API. Однако я почти уверен, что проблема связана только с частью Swift. Чтобы прояснить, почему существует два метода получения лайков, это потому, что один предназначен для цитат, понравившихся пользователю, которые хранятся в настройках пользователя по умолчанию, как и цитаты, добавленные в закладки. Другой метод получения лайков связан с подсчетом лайков для конкретной цитаты, и именно здесь возникает проблема.
@workingdogsupportUkraine В настоящее время то, что я получаю, распечатывая likes
из .onAppear()
, когда я первоначально загружаю цитаты, следующее (значения лайков для каждой из 4 загруженных цитат): лайков 0 лайков 0 лайков 0 лайков 0 Затем, после того, как мне понравилась цитата , он отображается правильно, как счетчик (8) в текстовом поле. Хотя, когда я переключаю TabView
и возвращаюсь к этому TabView
, он печатает это: лайков 0 лайков 0 лайков 0 лайков 8 Очевидно, здесь он получает правильное количество лайков после того, как ему понравилось, но, как ни странно, на самом деле он ставит все 0 как текстовые значения для каждой цитаты считаются.
Я не вижу TabViews
в вашем коде, поэтому невозможно сказать, что происходит, в частности, как вы передаете соответствующие переменные. Я предполагаю, что в .onAppear
вы печатаете с замыканием getQuoteLikeCountMethod { ... print(...) }
, потому что вне его вы всегда получите ноль.
Прежде чем if let data = data, let json = try?...
добавьте отпечаток для отладки. Вам нужно посмотреть, что происходит: print("Получено для: (url.absoluteString): (ответ) и строковых данных: (String(data: data ?? Date(),coding: .utf8)")` и посмотреть, есть ли они действительно называются и каковы возвращаемые значения...
@workingdogsupportUkraine Я намеренно не включил его, чтобы сосредоточиться на том, что имеет отношение к моему вопросу. Представления вкладок просто соответствуют кнопкам вкладок, которые вы видите внизу скриншота моего симулятора iPhone. Передается 0 переменных, влияющих на эту ошибку, поскольку я передаю только одну, относящуюся к цветам (см. код, sharedVars.colorPaletteIndex
.
@workingdogsupportUkraine Я напечатал значение в конце .onAppear
, после строки isLiked = isQuoteLiked(quote)
.
func getQuoteLikeCountMethod(...)
является асинхронным, поэтому попробуйте печатать внутри замыкания, например: getQuoteLikeCountMethod { fetchedLikeCount in likes = fetchedLikeCount print("----> likes: \(likes)") }
а не после, как вы говорите.
Привет @workingdogsupportUkraine, извините за поздний ответ; Я был занят своей повседневной работой. Распечатывая количество лайков в onAppear()
, я действительно получаю правильные значения (например, 17), когда переключаю представление вкладок обратно в это представление «Капельки». Однако когда я печатаю внутри getQuoteLikeCountMethod
, как вы недавно прокомментировали, счетчик лайков всегда равен 0, и это значения, отображаемые рядом с кнопками лайков на экране.
@workingdogsupportUkraine Хотя это происходит только после того, как я нажимаю кнопки «Нравится», а затем переключаюсь и возвращаюсь к этому представлению. Когда я впервые открываю приложение, счетчик лайков в getQuoteLikeMethod
и в начале метода .onAppear()
всегда равен 0. @Larme Я попробовал ваш оператор печати, но получил несколько синтаксических ошибок, поэтому их изменение на следующее решило их: print("Got for: \(url.absoluteString): \(String(describing: response)) and stringified data: \(String(data: data ?? Data(), encoding: .utf8) ?? "nil")")
@Larme На самом деле я получил правильный ответ, распечатанный вместе с вашим оператором печати (который я немного изменил в своем комментарии выше): Got for: {censored URL for stackoverflow}/quoteLikes/232: Optional(<NSHTTPURLResponse: 0x6000002c67e0> { URL:{censored URL for stackoverflow}/quoteLikes/232 } { Status Code: 200, Headers { "likes": 16
Однако, к сожалению, он отображает только количество лайков, равное 0, используя код, который я предоставил в вопросе. Как вы думаете, какое изменение мне нужно внести, чтобы оно действительно правильно отображало количество лайков, полученное из моего API?
Я понятия не имею о Swift, но, может быть, вы читаете неправильное свойство JSON? API возвращается c.IndentedJSON(http.StatusOK, gin.H{"id": id, "likes": likes})
>> {"id": 123, "likes": 5}
. Код Swift пытается прочитать likeCount
, которого не существует let likeCount = json["likeCount"] as? Int
, так и должно быть let likeCount = json["likes"] as? Int
@wakumaku Отличный улов! Это то, что решило мою проблему. Не могли бы вы опубликовать это как ответ, чтобы я мог наградить вас наградой?
Я понятия не имею о Swift, но, может быть, вы читаете неправильное свойство JSON?
API возвращает:
c.IndentedJSON(http.StatusOK, gin.H{"id": id, "likes": likes})
// {"id": 123, "likes": 5}
Код Swift пытается прочитать LikeCount:
let likeCount = json["likeCount"] as? Int
Так и должно быть let likeCount = json["likes"] as? Int
Похоже на несоответствие API между клиентом и сервером.
Вы ожидаете
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let likeCount = json["likeCount"] as? Int {
в клиенте, но отправка
c.IndentedJSON(http.StatusOK, gin.H{"id": id, "likes": likes})
с сервера - было бы полезно увидеть весь ответ выше.
Чтобы избежать таких проблем, вы можете использовать OpenAPI описание вашего сервиса, например, используя Swift OpenAPI Generator на клиенте и swag на сервере.
Слишком много недостающих частей, чтобы можно было скомпилировать и запустить тест вашего кода. Кажется, у вас есть два способа получить
likes
: один с помощью.onAppear
с помощьюgetQuoteLikeCountMethod
, который является просто бесполезной оберткой вокругgetLikeCountForQuote
, а другой с помощьюButton(action: { likeQuoteAction() ...
, который, я думаю, отличается. Вы можете попробовать добавить print("--> нравится: \(лайки)") в.onAppear
и других местах, чтобы увидеть, получите ли вы то, что ожидаетеlikes
. Тогда покажи нам, что у тебя получится.