При переходе к домашнему представлению загрузка занимает много времени, а при переходе обратно из FullPostCell на перехоIOS

Программируем под IOS
Ответить Пред. темаСлед. тема
Anonymous
 При переходе к домашнему представлению загрузка занимает много времени, а при переходе обратно из FullPostCell на перехо

Сообщение Anonymous »

Главное представление (или любое представление с заполненными сообщениями) работает очень медленно, и после отладки я обнаружил, что фильтрация НЕ является причиной проблемы. По какой-то причине на симуляторе он не медленный, но на реальном устройстве он очень медленный, особенно после загрузки постов.
Я тоже отлаживал кучу раз, но до сих пор не могу в этом разобраться проблема, в отладках ничего не указывает на ее причину, но я думаю, что это проблема с узким местом.

Код: Выделить всё

import SwiftUI

struct HomeView: View {
@EnvironmentObject var authViewModel: AuthViewModel
@StateObject var viewModel = PostViewModel()
@StateObject var followingViewModel = FollowingViewModel()
@StateObject var followViewModel = FollowViewModel()
@StateObject var profileViewModel = ProfileViewModel()
@StateObject var likeViewModel = LikeViewModel()
@StateObject var appData: AppData
@State private var selectedHomeTab: Int = 0
@State private var isLoading: Bool = true
@State private var lastViewedPostId: String? {
didSet {
if let id = lastViewedPostId {
UserDefaults.standard.set(id, forKey: "lastViewedPostId")
}
}
}
var body: some View {
VStack {
ToolbarModifier(viewTitle: .constant("Home"), appData: appData)
VStack {
HStack(spacing: 87) {
Text("For You")
.foregroundStyle(selectedHomeTab == 0 ? Color.black : Color.gray)
.font(.system(size: 15, weight: .semibold))
.offset(y: 10)
.onTapGesture {
withAnimation(.easeIn(duration: 0.2)) {
selectedHomeTab = 0
}
}
Text("Following")
.foregroundStyle(selectedHomeTab == 1 ? Color.black : Color.gray)
.font(.system(size: 15, weight: .semibold))
.offset(y: 10)
.onTapGesture {
withAnimation(.easeIn(duration: 0.2)) {
selectedHomeTab = 1
}
}
}
Rectangle()
.frame(width: 50, height: 5)
.foregroundStyle(Color(hex: "#5CE1E6"))
.cornerRadius(3)
.offset(x: selectedHomeTab == 0 ? -77.4 : 70.2, y: 7)
}
.animation(.easeIn(duration: 0.1), value: selectedHomeTab)
.padding(.top, 3)

TabView(selection: $selectedHomeTab) {
ScrollViewReader { scrollProxy in
ScrollView(showsIndicators: false) {
VStack(spacing: 14) {
if viewModel.posts.count > 0 {

let viewWeight: Double = 0.5
let timestampWeight: Double = 1.2
let likesWeight: Double = 0.9

let cutoffDate = Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date()

let sortedPosts = viewModel.posts
.lazy
.filter { !$0.user.privateProfile && $0.post.parentId.isEmpty && $0.post.groupId.isEmpty }
.sorted { (post1, post2) -> Bool in
let likesCount1 = likeViewModel.likes.filter { $0.like.postId == post1.post.id }.count
let likesCount2 = likeViewModel.likes.filter { $0.like.postId == post2.post.id }.count

let score1 = Double(post1.post.views) * viewWeight
+ post1.post.timestamp.timeIntervalSince(cutoffDate) / 3600 * timestampWeight
+ Double(likesCount1) * likesWeight

let score2 = Double(post2.post.views) * viewWeight
+ post2.post.timestamp.timeIntervalSince(cutoffDate) / 3600 * timestampWeight
+ Double(likesCount2) * likesWeight

return score1 >  score2
}

ForEach(sortedPosts) { post in
PostCell(postWithUser: post, appData: appData)
.id(post.id)
.onAppear {
lastViewedPostId = post.id
}
}
.padding(.top, 17)

} else {
LoadingModifier()
.padding(.top, 230)
}
}
}
.tag(0)
.onAppear {
viewModel.fetchPosts()
if let savedId = lastViewedPostId {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
withAnimation(.easeIn) {
scrollProxy.scrollTo(savedId, anchor: .center)
}
}
}
}
}

ScrollView(showsIndicators: false) {
if !isLoading {
VStack(spacing: 10) {
if viewModel.posts.count != 0 {
let followingPosts = viewModel.posts
.filter { $0.post.parentId.isEmpty && $0.post.groupId.isEmpty }
.filter { postWithUser in
followingViewModel.followings.contains { $0.user.id == postWithUser.user.id }
}
.sorted(by: { $0.post.timestamp >  $1.post.timestamp })
if !followingPosts.isEmpty {
ForEach(followingPosts, id: \.post.id) { postWithUser in
PostCell(postWithUser: postWithUser, appData: appData)
}
.padding(.top, 17)
}
} else {
Image(systemName: "person.fill")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30)
.fontWeight(.bold)
.foregroundStyle(Color(UIColor.systemGray2))
.padding(.top, 120)
Text("Your not following anyone yet.")
.foregroundStyle(Color(UIColor.systemGray2))
.font(.system(size: 16, weight: .semibold))
.padding(.bottom)
if appData.showFollowRecomendations {
ZStack {
Rectangle()
.foregroundStyle(Color(UIColor.systemGray6))
.frame(maxWidth: .infinity)
.frame(height: 245)
.cornerRadius(7)
.padding(.horizontal, 16)
VStack(spacing: 7) {
HStack {
Text("Frequently followed")
.foregroundStyle(Color(UIColor.systemGray2))
.font(.system(size: 14, weight: .regular))

Spacer()

Image(systemName: "xmark")
.resizable()
.scaledToFit()
.frame(width: 14, height: 14)
.fontWeight(.bold)
.foregroundStyle(Color(UIColor.systemGray2))
.onTapGesture {
appData.showFollowRecomendations = false
}
}
.padding(.bottom, 7)
ForEach(profileViewModel.users
.sorted(by: { user1, user2 in
let user1FollowerCount = followViewModel.follows.filter { $0.follow.currentUserId == user1.id }.count
let user2FollowerCount = followViewModel.follows.filter { $0.follow.currentUserId == user2.id }.count
return user1FollowerCount >  user2FollowerCount
}).prefix(3)) { user in
if user.id != profileViewModel.id {
NavigationLink(destination: UserProfileView(user: user)) {
ProfileCell(user: user)
}
} else {
NavigationLink(destination: ProfileView(appData: appData)) {
ProfileCell(user: user)
}
}
}
}
.padding(.horizontal, 43)
}
}
}
}
} else {
LoadingModifier()
.padding(.top, 80)
}
}
.tag(1)
.onAppear {
followingViewModel.fetchFollowings()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
isLoading = false
}
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.refreshable {
viewModel.refreshPosts()
viewModel.fetchPosts()
profileViewModel.fetchUsers()
}
}
.navigationBarBackButtonHidden(true)
.navigationTitle("")
.onAppear {
viewModel.fetchPosts()
profileViewModel.fetchUsers()
likeViewModel.fetchLikes()
appData.showMainNavBar = true
lastViewedPostId = UserDefaults.standard.string(forKey: "lastViewedPostId")

}
}
}

#Preview {
HomeView(appData: AppData())
}

Код: Выделить всё

import Foundation
import SwiftUI
import Firebase

class PostViewModel: ObservableObject {
@Published var posts: [PostWithUser] = []

let db = Firestore.firestore()

@MainActor
func fetchPosts() {
PostService.shared.fetchPosts { result in
DispatchQueue.main.async {
switch result {
case .success(let posts):
self.posts = posts
case .failure(let error):
print("Failed to fetch posts: \(error)")
}
}
}
}

func refreshPosts() {
PostService.shared.refreshPosts { result in
DispatchQueue.main.async {
switch result {
case .success(let posts):
self.posts = posts
case .failure(let error):
print("Failed to fetch posts: \(error)")
}
}
}
}

private func fetchUserProfiles(for posts: [Post]) {
let userIds = Set(posts.map { $0.userId })
UserService.shared.fetchUsers(userIds: Array(userIds)) { result in
DispatchQueue.main.async {
switch result {
case .success(let users):
let userDict = Dictionary(uniqueKeysWithValues: users.map { ($0.id, $0) })
self.posts = posts.map { post in
if let user = userDict[post.userId] {
return PostWithUser(post: post, user: user)
} else {
return PostWithUser(post: post, user: MockData.mockUser)
}
}
case .failure(let error):
print("Failed to fetch user profiles: \(error)")
}
}
}
}

func deletePost(postId: String) {
PostService.shared.deletePost(postId:  postId) { result in
switch result {
case .success:
print("post deleted")
case .failure(let error):
print("Failed to delete post: \(error.localizedDescription)")
}
}
}
}

Код: Выделить всё

import Firebase
import FirebaseFirestore
import FirebaseStorage
import SwiftUI

class PostService {
static let shared = PostService()
let db = Firestore.firestore()
let postsRef = Firestore.firestore().collection("posts")
let usersRef = Firestore.firestore().collection("users")
let storageRef = Storage.storage().reference()
private var cachedPosts: [PostWithUser] = []

func savePost(text: String, image: UIImage?, videoURL: URL?, isPinned: Bool, parentId: String, groupId: String, completion: @escaping (Result) -> Void) {
guard let currentUser = Auth.auth().currentUser else {
completion(.failure(NSError(domain: "PostService", code: -1, userInfo: nil)))
return
}

let uid = currentUser.uid
let newPostRef = postsRef.document()

if let image = image {
uploadImage(image, postId: newPostRef.documentID) { result in
switch result {
case .success(let imageUrl):
if let videoURL = videoURL {
self.uploadVideo(videoURL, postId: newPostRef.documentID) { videoResult in
switch videoResult {
case .success(let videoUrl):
self.createPost(text: text, imageUrl: imageUrl, videoUrl: videoUrl, isPinned: isPinned, parentId: parentId, postId: newPostRef.documentID, groupId: groupId, uid: uid, completion: completion)
case .failure(let error):
completion(.failure(error))
}
}
} else {
self.createPost(text: text, imageUrl: imageUrl, videoUrl: "", isPinned: isPinned, parentId: parentId, postId: newPostRef.documentID, groupId: groupId, uid: uid, completion: completion)
}
case .failure(let error):
completion(.failure(error))
}
}
} else if let videoURL = videoURL {
uploadVideo(videoURL, postId: newPostRef.documentID) { result in
switch result {
case .success(let videoUrl):
self.createPost(text: text, imageUrl: "", videoUrl: videoUrl, isPinned: isPinned, parentId: parentId, postId: newPostRef.documentID, groupId: groupId, uid: uid, completion: completion)
case .failure(let error):
completion(.failure(error))
}
}
} else {
self.createPost(text: text, imageUrl: "", videoUrl: "", isPinned: isPinned, parentId: parentId, postId: newPostRef.documentID, groupId: groupId, uid: uid, completion: completion)
}
}

private func uploadImage(_ image: UIImage, postId: String, completion: @escaping (Result) -> Void) {
guard let imageData = image.jpegData(compressionQuality: 0.8) else {
completion(.failure(NSError(domain: "PostService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Image conversion failed."])))
return
}

let imageRef = storageRef.child("post_images/\(postId).jpg")

imageRef.putData(imageData, metadata: nil) { metadata, error in
if let error = error {
completion(.failure(error))
return
}

imageRef.downloadURL { url, error in
if let error = error {
completion(.failure(error))
} else if let imageUrl = url?.absoluteString {
completion(.success(imageUrl))
} else {
completion(.failure(NSError(domain: "PostService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to get download URL."])))
}
}
}
}

private func uploadVideo(_ videoURL: URL, postId: String, completion: @escaping (Result) ->  Void) {
let videoRef = storageRef.child("post_videos/\(postId).mp4")

print("Uploading video to \(videoRef.fullPath)")

do {
// Ensure the video file is accessible
let videoData = try Data(contentsOf: videoURL)

// Upload video using data
videoRef.putData(videoData, metadata: nil) { metadata, error in
if let error = error {
print("Failed to upload video: \(error.localizedDescription)")
completion(.failure(error))
return
}

videoRef.downloadURL { url, error in
if let error = error {
print("Failed to get video URL: \(error.localizedDescription)")
completion(.failure(error))
} else if let videoUrl = url?.absoluteString {
print("Video uploaded successfully: \(videoUrl)")
completion(.success(videoUrl))
} else {
print("Failed to get video URL: Unknown error")
completion(.failure(NSError(domain: "PostService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to get download URL."])))
}
}
}
} catch {
print("Failed to access video file: \(error.localizedDescription)")
completion(.failure(error))
}
}

private func createPost(text: String, imageUrl: String, videoUrl: String, isPinned: Bool, parentId: String, postId: String, groupId: String, uid: String, completion: @escaping (Result) -> Void) {
let postData: [String: Any] = [
"userId": uid,
"text": text,
"imageUrl": imageUrl,
"videoUrl": videoUrl,
"parentId": parentId,
"groupId": groupId,
"isPinned": isPinned,
"timestamp": FieldValue.serverTimestamp(),
"views": 0
]

postsRef.document(postId).setData(postData) { error in
if let error = error {
completion(.failure(error))
} else {
completion(.success(()))
}
}
}

func incrementPostViewCount(postId: String) {
let postRef = postsRef.document(postId)
postRef.updateData(["views": FirebaseFirestore.FieldValue.increment(Int64(1))]) { error in
if let error = error {
print("Error updating views: \(error.localizedDescription)")
} else {
print("Views updated successfully!")
}
}
}

func fetchPosts(completion: @escaping (Result) ->  Void) {
if !cachedPosts.isEmpty {
completion(.success(cachedPosts))
return
}
postsRef.order(by: "timestamp", descending: true).getDocuments { snapshot, error in
if let error = error {
completion(.failure(error))
return
}

guard let documents = snapshot?.documents else {
completion(.success([]))
return
}

let group = DispatchGroup()
var postsWithUsers: [PostWithUser] = []

for document in documents {
group.enter()
let data = document.data()
guard let userId = data["userId"] as? String,
let parentId = data["parentId"] as? String,
let groupId = data["groupId"] as? String,
let text = data["text"] as? String,
let imageUrl = data["imageUrl"] as? String,
let videoUrl = data["videoUrl"] as? String,
let isPinned = data["isPinned"] as? Bool,
let timestamp = data["timestamp"] as? Timestamp,
let views = data["views"] as? Int else {
group.leave()
continue
}

let post = Post(id: document.documentID, userId: userId, parentId: parentId, groupId: groupId, text: text, imageUrl: imageUrl, videoUrl: videoUrl, timestamp: timestamp.dateValue(), isPinned: isPinned, likedBy: [], views: views)

self.usersRef.document(userId).getDocument { userDocument, error in
defer { group.leave() }

if let userDocument = userDocument, let userData = userDocument.data() {
let user = User(
id: userId,
username: userData["username"] as? String ?? "",
bio: userData["bio"] as? String ?? "",
profilePictureUrl: userData["profileImageUrl"] as? String ?? "",
privateProfile: userData["privateProfile"] as? Bool ?? false,
privateFollowerList: userData["privateFollowerList"] as? Bool ?? false,
privateFollowingList: userData["privateFollowingList"] as? Bool ?? false,
privateReplies: userData["privateReplies"] as? Bool ?? false,
privateLikes: userData["privateLikes"] as? Bool ?? false

)
postsWithUsers.append(PostWithUser(post: post, user: user))
} else {
print("Failed to fetch user data for userId: \(userId), error: \(String(describing: error))")
}
}
}

group.notify(queue: .main) {
self.cachedPosts = postsWithUsers
completion(.success(postsWithUsers))
}
}
}

func refreshPosts(completion: @escaping (Result) -> Void) {
cachedPosts = []
fetchPosts { result in
print("posts refreshed")
}
}

func deletePost(postId: String, completion: @escaping (Result) ->  Void) {
let postRef = postsRef.document(postId)

postRef.getDocument { document, error in
if let error = error {
completion(.failure(error))
return
}

guard let document = document, document.exists else {
completion(.failure(NSError(domain: "PostService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Post not found."])))
return
}

let data = document.data()
if data?["imageUrl"] is String {
let imageRef = self.storageRef.child("post_images/\(postId).jpg")
imageRef.delete { error in
if let error = error {
print("Failed to delete image: \(error.localizedDescription)")
}
}
}

postRef.delete { error in
if let error = error {
completion(.failure(error))
} else {
self.cachedPosts.removeAll { $0.post.id == postId }
completion(.success(()))
}
}
}
}
}

Я уже несколько часов сталкиваюсь с этой проблемой. Может кто-нибудь помочь?

Подробнее здесь: https://stackoverflow.com/questions/786 ... en-navigat
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «IOS»