Search code examples
iosxcodeswiftuiswitch-statementcloudkit

Let warning in nested CloudKit Query makes var never change


I get the Warning "Variable 'userArray' was never mutated; consider changing to 'let' constant" in my code and my variable isn't being changed for some reason? I included some print statements to try and figure out what is running and what isn't. Here is what Prints: "Users appended - []", "Found ["Test User"]"

static func getBossSubs(bossID: String, completion: @escaping (Result<UserNames, Error>) ->
    ()) {
    let pred = NSPredicate(format: "uniqueID = %@", bossID)
    let sort = NSSortDescriptor(key: "creationDate", ascending: false)
    let query = CKQuery(recordType: RecordType.Users, predicate: pred)
    query.sortDescriptors = [sort]
    
    let operation = CKQueryOperation(query: query)
    operation.desiredKeys = ["subscribedBosses"]
    operation.resultsLimit = 50
    
    operation.recordFetchedBlock = { record in
        DispatchQueue.main.async {
            guard let subs = record["subscribedBosses"] as? [String] else {
                print("Error at screenName")
                
                completion(.failure(CloudKitHelperError.castFailure))
                return
            }
            var userArray = UserNames() //Error that it should be a LET is here. 
            for boss in subs{
                CloudKitHelper.getBossScreenName(bossID: boss) { (result) in
                    
                    switch result{
                        
                    case .success(let name):
                        userArray.names.append(name) //works fine
                        print("Found \(userArray.names)") //Prints a name
                    case .failure(let er):
                        completion(.failure(er))
                    }
                    
                }
                print("does this run?") // Only runs if No Name
                completion(.success(userArray)) // contains no name or doesn't run?
            }
            
        }
    }
    operation.queryCompletionBlock = { (_, err) in
        DispatchQueue.main.async {
            if let err = err {
                completion(.failure(err))
                return
            }
        }
        
    }
    CKContainer.default().publicCloudDatabase.add(operation)
}

I call the code like this:

.onAppear {
            // MARK: - fetch from CloudKit
            self.userList.names = []
            let myUserID = UserDefaults.standard.string(forKey: self.signInWithAppleManager.userIdentifierKey)!
            // get my subs projects
            CloudKitHelper.getBossSubs(bossID: myUserID) { (results) in
                switch results{
                    
                case .success(let user):
                    print("Users appended")
                    self.userList.names = user.names
                case .failure(let er):
                    print(er.localizedDescription)
                }
            }
        }

Solution

  • As you have it, userArray is an object:

    var userArray = UserNames()

    When you append stuff to the names property of the object, you aren't mutating the object, you are changing the content of one of its properties. If you aren't mutating the object (i.e. changing what properties it has), you should use let.

    Does anything break if you just change it to let like Xcode is suggesting?

    let userArray = UserNames()