0

I am trying to figure out how to make the following scenario work.

Bit of background; I am trying to create a multiplayer game using Multipeer Connectivity, for learning purposes. There are two players, each player can drop in and out of the game anytime they want. When a player leaves the game and re-enters the game scene is loaded and sprites are created, meanwhile a timer will start to share x/y locations. And here I have an issue, it sometimes happens that the timer will start sending the locations before the sprites are created, or possibly it happens simultaneously? I am not entirely sure when what happens, but since I sometimes get an exception in 'func B()'- "NSArray mutated while being enumerated", I assume there is a conflict between func A() and func B(). The timer aka func B() starts because the device is receiving a message from the other device to start the timer and thus start sending over the locations. I have tried many things to make sure that they work in tandem, for example;

I have created

//----

let mySpriteQueue = DispatchQueue(label: "mySpriteQueue", attributes: .concurrent)

//and use it in both functions like so;

func B() {
   // sprite are collected and prepared to be send over
   for n in 0...10 {
      mySpriteQueue.sync {
         if let node = self.childnode(withname: "somename \(n)" as? SKSpriteNode {
            Mynode = node
         }
      }


      mySpriteQueue.async(flags: .barrier) {
          tobeSendOver.append(MyNode.location)
      }
   }

}

func A() {

 //Sprites are created here
   for n in 0...10 {
      let sprite = SKSpriteNode(...)
      sprite.name = "somename \(n)"
      mySpriteQueue.async(flags: .barrier) {
         addchild(sprite)
      }
   }

}
//------

I also am trying to work with DispatchGroups in func A() using;

let spriteGroup = DispatchGroup()

and have the sprites created in 
spriteGroup.enter()
//here the sprites are created 
//and added to an array
spriteGroup.leave()

spriteGroup.notify(flags: .barrier, queue: .main){
// here I use addchild to add the nodes to the scene
}

How can I make it work that func B waits for func A to be ready, no matter what circumstance? I could add a delay to my timer, and that works but I for learning purposes I would like to accomplish this with manipulating the threads.

By the way the NSArray mutating exception appears in func B at "if let node = childenode(withname..)"

My assumption is that it is because sprites are created and I am trying to read it meanwhile.

user1973842
  • 113
  • 1
  • 9

1 Answers1

0

If you're looking for thread-safe access to certain properties, you can use this property wrapper:

@propertyWrapper public struct ThreadSafe<Value> {

    private var value: Value
    private let queue: DispatchQueue

    public init(wrappedValue: Value, queue: DispatchQueue) {
        self.value = wrappedValue
        self.queue = queue
    }

    public var wrappedValue: Value {
        get {
            queue.sync { value }
        }
        set {
            queue.sync { value = newValue }
        }
    }
}

And then mark your thread-safe properties like this:

@ThreadSafe(queue: DispatchQueue(label: "com.someQueue"))
private var someProperty = 10
henrik-dmg
  • 1,448
  • 16
  • 23