Swift memory leak while using firebase listener

35 Views Asked by At

My Memory Graph shows 2 FeedViewModels even though it should only be one since I only have 1 Feed created in Firebase

If i printObject feedViewModel.text in the init block breakpoint in my FeedRow it shows -> This is a firebase text 5

But when i printObject feedViewModel.text in the breakpoint where the view actually displays the text it shows -> This is a firebase text 4

Here is my function in the firebase repository: `func fetchAllFeeds(completion: @escaping (Result<[Feed], FirebaseError>) -> Void) { self.feedIdsListener = FirebaseManager.shared.firestore.collection("feeds").addSnapshotListener { querySnapshot, error in if let error { print("Fetch feeds failed: (error)") completion(.failure(.unknown(error))) return }

        guard let documents = querySnapshot?.documents else {
            print("Query Snapshot has no documents")
            completion(.failure(.collectioNotFound))
            return
        }
        
        let allFeeds = documents.compactMap { document in
            try? document.data(as: Feed.self)
        }
        
        completion(.success(allFeeds))
    }
}`

Here is the function in my FeedsViewModel that generates my [FeedViewModel] `private func fetchAllFeeds() { feedRepository.fetchAllFeeds() { [weak self] result in switch result { case .success(let allFeeds): if let user = self?.user { self?.feeds = allFeeds.compactMap { FeedViewModel($0, withUser: user) } self?.feeds.sort(by: { $0.updatedAt > $1.updatedAt })

                if let feeds = self?.feeds {
                    self?.followedFeeds = feeds.filter { user.following.contains($0.creator) }
                    
                    self?.ownFeeds = feeds.filter { $0.creator == user }
                    
                    self?.usedFeeds = feeds.filter { $0.activeUsers.contains(user) }
                }
            }
        case .failure(let error):
            print("Failed fetching feeds: \(error)")
        }
    }
}`

At this point i generate my FeedRows in my FeedView using the data from the FeedsViewModel VStack { ForEach(self.feedsViewModel.feeds) { feed in FeedRow(feed) } } .padding(.horizontal)

Here is my FeedRow `struct FeedRow: View {

@EnvironmentObject var authenticationViewModel: AuthenticationViewModel
@StateObject var feedViewModel: FeedViewModel

@State private var showComments = false
@State private var translationActive: Bool = false

private var textIsTranslated: Bool {
    feedViewModel.translatedText != nil
}

init(_ feedViewModel: FeedViewModel) {
    _feedViewModel = StateObject(wrappedValue: feedViewModel)
}

var body: some View {
    VStack(spacing: 8) {
        HStack(alignment: .top, spacing: 16) {
            ProfilePictureSmall()
            
            VStack(spacing: 8) {
                HStack {
                    Text(self.feedViewModel.creator.realName)
                        .font(.footnote)
                        .fontWeight(.semibold)
                    
                    Spacer()
                    
                    Button(action: {
                        
                    }, label: {
                        Image(systemName: "envelope")
                            .foregroundStyle(LinearGradient(gradient: Gradient(colors: [.blue, .red]), startPoint: .leading, endPoint: .trailing))
                    })
                }
                Text((!self.translationActive ? self.feedViewModel.text : self.feedViewModel.translatedText) ?? "")
                    .font(.footnote)
                    .fontWeight(.thin)
                    .frame(maxWidth: .infinity, alignment: .leading)
                
                HStack(alignment: .bottom, spacing: 16) {
                    VStack {
                        Button(action: {
                            self.feedViewModel.likeFeed()
                        }, label: {
                            Image(systemName: "heart")
                                .foregroundStyle(LinearGradient(gradient: Gradient(colors: [.blue, .red]), startPoint: .leading, endPoint: .trailing))
                        })
                        Text(String(self.feedViewModel.likes.count))
                            .font(.footnote)
                            .fontWeight(.ultraLight)
                    }
                    
                    VStack {
                        Button(action: {
                            self.showComments = true
                        }, label: {
                            Image(systemName: "text.bubble")
                                .foregroundStyle(LinearGradient(gradient: Gradient(colors: [.red, .blue]), startPoint: .leading, endPoint: .trailing))
                        })
                        Text(String(self.feedViewModel.comments.count))
                            .font(.footnote)
                            .fontWeight(.ultraLight)
                    }
                    
                    Spacer()
                    
                    VStack {
                        Button(action: {
                            self.translationActive.toggle()
                        }, label: {
                            Text(self.textIsTranslated ? (self.translationActive ? "Show original" : "Translate") : "No Translation")
                        })
                        .disabled(!self.textIsTranslated)
                        
                        Text(self.feedViewModel.createdAtString)
                            .fontWeight(.ultraLight)
                    }
                    .font(.footnote)
                }
            }
        }
        
        if !self.feedViewModel.images.isEmpty {
            ScrollView(.horizontal, showsIndicators: false) {
                LazyHStack(alignment: .center) {
                    ForEach(self.feedViewModel.images, id: \.self) { url in
                        AsyncImage(
                            url: URL(string: url),
                            content: { image in
                                image
                                    .resizable()
                                    .scaledToFit()
                                    .frame(height: 200)
                                    .clipShape(RoundedRectangle(cornerRadius: 10))
                            },
                            placeholder: {
                                Image(systemName: "network.slash")
                            }
                        )
                    }
                }
                .padding(.horizontal)
            }
            .frame(height: 200)
        }
        
        Divider()
        
        if !self.feedViewModel.richPreviews.isEmpty {
            ForEach(self.feedViewModel.richPreviews) { richLinkPreview in
                RichLinkPreviewView(richPreviewViewModel: richLinkPreview)
            }
        }
    }
    .sheet(isPresented: $showComments, content: {
        CommentsView()
            .environmentObject(self.feedViewModel)
            .presentationDetents([.medium, .large])
    })
    .onAppear {
        self.feedViewModel.translateText()
    }
    .onChange(of: self.textIsTranslated) {
        if self.textIsTranslated {
            self.translationActive = true
        } else {
            self.translationActive = false
        }
    }
}

}`

I already tried [weak self] in the function in the FeedsViewModel in hope it helps but it didnt change anything and the memory leak persists

I fixed it by modyfing the code in the FeedRow like this: @EnvironmentObject var authenticationViewModel: AuthenticationViewModel weak var weakFeedViewModel: FeedViewModel? @ObservedObject var feedViewModel: FeedViewModel

@State private var showComments = false
@State private var translationActive: Bool = false

private var textIsTranslated: Bool {
    feedViewModel.translatedText != nil
}

init(_ feedViewModel: FeedViewModel) {
    weakFeedViewModel = feedViewModel
    _feedViewModel = ObservedObject(wrappedValue: weakFeedViewModel ?? feedViewModel)
}
0

There are 0 best solutions below