Search code examples
swiftviewmodel

Can I place my code in a viewmodel if that code has references to the view?


I have a view with alot of logic code that would be better of in a viewmodel. The problem is that the code has references to the view which means that the viewmodel would need an object from the view to work properly. If I understand the concept of a viewmodel correctly, having a two-way dependency defeats the whole purpose of a viewmodel

As you can see from the code below it contains the line addChild(x) as well as .removeFromParent. So if moved to a viewmodel the code would have to be something like: mainView.addChild(x). It works sure enough, but my question is if it's appropriate. If not, should I just keep the code in the view, even though it takes up alot of space and has alot of logic in it?

These functions are used in the native touchDown() function:

func characterIsSelected(i: Int, j: Int) {
    //Check Accessibility of Tile
    if !gameBoardArray[i][j].isAccessibilityElement {
        hasBeenClickedOnce = true
        characterIsSelected = true
        characterVM.getGameBoardPosition().fillColor = SKColor.brown
        print("NOT ACCESSIBLE")
        return
    }else {
        print("Moving")
        viewModel.placeCharacter(row: i, col: j)
        buttonIsAvailable = false

        //SEE HERE
        guiButton.removeFromParent()
        enemyProximityCheck(i: i, j: j)
    }
}

func enemyProximityCheck(i: Int, j: Int){
    print("ENEMY PROXIMITY")
    //SCAN VICINITY OF CHARACTER. IF ENEMY IS ON TILE, GOTO COMBAT
    for ii in -1...1 {
        for jj in -1...1 {
            //gameBoardArray[i + (ii)][j + (jj)].fillColor = SKColor.blue
            if(i + (ii) < 0 || j + (jj) < 0 ) {
                print("LOW")
            }
            else if(i + (ii) > gameBoardArray.count - 1 || j + (jj) > gameBoardArray.count - 1) {
                print("HIGH")
            }
            else {
                if (gameBoardArray[i + (ii)][j + (jj)].contains(enemyVM.getCurrentEnemyPosition() ) ) {
                    print("AAARGH AN ENEMY!")

                    //SEE HERE
                    addChild(guiButton)
                    buttonIsAvailable = true
                    //enemyHasBeenSpotted = true
                }
            }
        }
    }//End for
}

Solution

  • Generally if you’re using a “view model”, the view model would not generally be manipulating the view directly. It would simply communicate the “encountered enemy” (or whatever) event to the “view” (which many of us would consider to be both the UIView hierarchy and the view controller, too), which would then be responsible for manipulating the view(s) accordingly.

    Unfortunately, it’s hard to say precisely how the view model would do this in your case, on the basis of what you’ve outlined. Some use the term “view model” loosely, so there are a couple of possible interpretations:

    1. One approach is to have the view observe (through some “binding” mechanism) some property in the view model, and when the view model updates that state, the view would then add/configure the appropriate views/subviews accordingly. (This is binding/observing is arguably an essential aspect of any “view model” based approach.)

    2. Other approaches include protocol-delegate or closure patterns for the view model to communicate that UI updates to the view. (This is arguably more of a MVP sort of approach, though I’ve seen teams use the term “view model” in this context, too.)

    But the consistent pattern is that the view model should generally not be reaching in and manipulating the view hierarchy itself. It should simply communicate (one way or the other) the state change, and the “view” would take it over from there.

    FYI, I think Medium’s iOS Architecture Patterns is illuminating when talking about these various patterns in the context of iOS apps.