skip to Main Content

I’m making an app with SwiftUI and UIkit, I use UIkit for the main app controller and navigation, and I use SwiftUI for app design.

The app works very well, but I’m worried about the memory leaks. This is because the ViewModels I use to pass data between views don’t call desinit whene the view disappears. I know that in SwiftUI views are not disposed immediately, but since I’m using UIKit to navigate I don’t know what the problem is.

//The ViewModel for each user fetched
internal class UserViewModel: ObservableObject, Identifiable {
    
    //MARK: - Propeties var currentListener: ListenerRegistration?
    
    @Published var request: Request?
    @Published var user: User
    
    init(user: User) {
        self.user = user
        getRequest()
        fetchAdmins()
    }
    
    deinit {
        //Dosnt get called removeListener()
    }
    
    func getRequest() {
        guard let uid = Auth.auth().currentUser?.uid else {return}
        guard let id = id else {return}
        
        self.currentListener = Collections.requests(id).document(uid).addSnapshotListener { snapshot, error in
            
            if let error = error {
                print(error.localizedDescription)
                return
            }
            
            if ((snapshot?.exists) != nil) {
                if let request = try? snapshot!.data(as: Request.self) {
                    DispatchQueue.main.async {
                        self.request = request
                    }
                }
            }
        }
    }
    
    func removeListener() {
        self.currentListener?.remove()
    }
}
}

//The ViewModel to fetch all the users ViewModels
class UsersViewModel: ObservableObject {
    @Published var users = [UserViewModel]()
    
    func fetch() {
        DispatchQueue.global(qos: .background).async {
            Collections.users.getDocuments(completion: { snapshot, err in
                
                guard let documents = snapshot?.documents else { return } let users = documents.compactMap({ try? $0.data(as: User.self) })
                
                users.forEach { user in
                    let vm = UserViewModel(user: user)
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                        self.users.append(vm)
                    }
                }
            })
        }
    } }

//Show the users cells with the ViewModel
struct HomeView: View {
    @ObservedObject var usersViewModels: UsersViewModel
    
    //MARK: - Init
    init() {
        self.usersViewModels = UsersViewModel()
    }
    
    var body: some View {
        ListView(content: {
            ForEach(usersViewModels) { usersViewModel in
                UserCell(viewModel: usersViewModel).id(user.id)
            }
        })
    }
}

This is how I navigate between controllers and views of my app. I don’t use NavigationLinks:

    public static func push<Content: View>(view: Content) {
        DispatchQueue.main.async {
 guard let tabBarController = UIApplication.rootViewController as? UITabBarController, let navigationController = tabBarController.selectedViewController as? UINavigationController else { return nil }

            if let navigationController = UIApplication.getCurrentNavigationController() {
                navigationController.pushViewController(HostingController(content: view), animated: true)
            }
        }
    }

Does anyone know if this method that I am using to navigate can cause me memory problems? And you know why my app doesn’t reduce its memory every time I close a window, it just increases more and more.

Memory Leak Xcode Tool

2

Answers


  1. The disappearing does not mean it is no longer in memory.

    It looks like you keep pushing them onto the navigation stack which increases their retain count.

    Login or Signup to reply.
  2. You’ve got a memory leak here:

    struct HomeView: View {
        @ObservedObject var usersViewModels: UsersViewModel
        
        //MARK: - Init
        init() {
            self.usersViewModels = UsersViewModel() // here
        }
    

    View structs must not init objects because the View struct is recreated every state change thus the object is being constantly init.

    SwiftUI is all about taking advantage of value semantics, try to use @State with value types (or group them in a struct) in the View struct for your view data.

    Model data structs go in a singleton ObservableObject supplied to the Views using .environmentObject.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search