1

When I run this following function subscribe, with the following main procedure, the desired effect is to pass in a list by reference, and allow the synchronous(or asynchronous) procedure to append elements to the back of the list, acting as a data feed:

// Main procedure
override func viewDidLoad() {
    super.viewDidLoad()
    var queue : [String] = []
    subscribe(prefix: "weight", dataFeed: &queue) // calling procedure here  
}

// Function where the error is occuring
func subscribe(prefix : String, dataFeed : inout [String]){
    ParticleCloud.sharedInstance().subscribeToAllEvents(withPrefix: prefix, handler: { (eventOpt :ParticleEvent?, error : Error?) in
        if let _ = error {
            eprint (message: "could not subscribe to events")
        } else {
            DispatchQueue.main.sync(execute: {
                if let event = eventOpt{
                    if let eventData = event.data {
                        eprint(message: "got event with data \(eventData)")
                        dataFeed.append(eventData) // EXC_BAD_ACCESS (code=1, ...)
                    }
                }
            })
        }
    })
}

Below is a slightly abstracted version(since it uses a specific API that requires username and password to replicate) of the code that is currently causing trouble:

// Function where the error is occuring
func foo(prefix : String, dataFeed : inout [String]){
    API.call(withPrefix: prefix, handler: { (eventOpt :Event?, error : Error?) in
        if let _ = error {
            print("error!")
        } else {
            DispatchQueue.main.sync(execute: {
                if let event = eventOpt{
                    if let eventData = event.data {
                        dataFeed.append(eventData) // EXC_BAD_ACCESS (code=1, ...)
                    }
                }
            })
        }
    })
}

Does anyone know why I'm getting the EXC_BAD_ACCESS (code=1, ...) error here?

OneRaynyDay
  • 3,658
  • 2
  • 23
  • 56
  • 1
    Why are you trying to use an `inout` parameter? Since you are performing an async call, use a completion handler instead. – rmaddy Feb 09 '18 at 06:43
  • @rmaddy could you elaborate on how to use a completion handler in this situation? I've never used one. I would gladly change the interface of the function if it means correctness; I'm just not experienced in swift at all – OneRaynyDay Feb 09 '18 at 06:46
  • because API.call block is executed after foo already completed – Tomasz Czyżak Feb 09 '18 at 06:46
  • There are many examples of using a completion handler. See https://stackoverflow.com/questions/25203556/returning-data-from-async-call-in-swift-function for a few. – rmaddy Feb 09 '18 at 06:48
  • Dude, you should present us a runnable code. Hence we don't know what's happening in `API.call`. Here is my guess: You modify `dataFeed` array in `main thread` so apply `append` method to it should be `thread-safe`. But `eventData` may be not, so you should try set lock to the `eventOpt`. – AntiMoron Feb 09 '18 at 07:04
  • @rmaddy thank you for the link! I'll check it out. – OneRaynyDay Feb 09 '18 at 07:06
  • @AntiMoron I apologize, I usually try to keep it MVCE, but for this situation I don't have an equivalent example where someone else can replicate the results as the setup requires authentication. – OneRaynyDay Feb 09 '18 at 07:06
  • @OneRaynyDay what does the `API.call` do? Is `eventOpt` thread safe during `handler`'s execution? – AntiMoron Feb 09 '18 at 07:09
  • @AntiMoron I believe it it is threadsafe during the handler's execution, since I'm calling `sync` instead of `async`. Is this a false assumption? API.call makes an HTTP call to get incoming events every few seconds, and the handler gets the result of that async call and (tries to) add the event data into a list. – OneRaynyDay Feb 09 '18 at 07:58
  • @OneRaynyDay Then it's wired. Make your code simpler and see whether is `dataFeed` crashing. Try this : `DispatchQueue.main.sync(execute: { dataFeed.append(123) // EXC_BAD_ACCESS} })` – AntiMoron Feb 12 '18 at 03:18

0 Answers0