At the time of writing this, iOS Swift does not provide a queue structure - though they do provide a walk-through. A queue is actually pretty simple. You enqueue (add an item) and dequeue (remove and return) an item (or push and pop for a stack).
A custom queue can have many more features that allow for collection management operations. Using generics, these capabilities can be provided for all nullable types.
The file Queue.swift could be created as follows:
//
// Queue.swift
//
// Created by Christopher Wirz on 5/15/17.
// Copyright © 2017. All rights reserved.
//
import Foundation
public struct Queue<T>{
private var _list = [T?]()
private var _head : Int = 0
private let _queue = DispatchQueue(label: "ThreadSafeCollection.queue", attributes: .concurrent)
private var _gc_size : Int = 32
public var count : Int {
return _list.count - _head
}
public var isEmpty : Bool {
return self.count < 1
}
public mutating func enqueue(_ element: T) {
_queue.sync(flags: .barrier) {
_list.append(element)
}
}
public mutating func dequeue() ->T? {
guard _head < _list.count, let element = _list[_head] else { return nil }
_queue.sync(flags: .barrier) {
self._list[self._head] = nil // Removes the reference
self._head += 1
if self._head >= _gc_size {
self._list.removeFirst(self._head)
self._head = 0
}
}
return element
}
public func peek() ->T? {
if isEmpty {
return nil
} else {
var ret : T? = nil
_queue.sync {
ret = _list[_head]
}
return ret
}
}
subscript(index: Int) ->T? {
get {
let i = index + _head
if i >= _list.count {return nil}
return _list[i]
}
set(newValue) {
let i = index + _head
if i >= _list.count {return}
_queue.sync(flags: .barrier) {
_list[i] = newValue
}
}
}
public mutating func dropWhere(_ pred:(T)->Bool) ->[T] {
var dropped = [T]()
var remaining = [T]()
_queue.sync(flags: .barrier) {
for index in _head..<_list.count {
if let item = _list[index] {
if pred(item) == true {
dropped.append(item)
} else {
remaining.append(item)
}
}
}
_list = remaining
_head = 0
}
return dropped
}
public var garbageCollectSize : Int {
get {
return _gc_size
}
set {
if newValue >= 1 {
_gc_size = newValue
}
}
}
public func any(_ pred:(T)->Bool) -> Bool {
for index in _head..<_list.count {
if let item = _list[index] {
return pred(item) == true
}
}
return false
}
}
This simple queue has many use cases. The primary use case is to reduce network usage by processing one call to the server at a time. In the following example, the web API only allows us to delete one message or thread at a time. Therefore, clearing out my inbox, it will be far more efficient on memory and network to have one network session active with the server at a time.
//
// MessageDeleter.swift
//
// Created by Christopher Wirz on 5/16/17.
// Copyright © 2017. All rights reserved.
//
import Foundation
public class MessageDeleter {
private var _queue = Queue<Message>()
private var _isRunning : Bool = false
public var isRunning : Bool {
return _isRunning
}
public static let instance = MessageDeleter() // Singleton pattern
private init(){}
func deleteMessage(_ message: Message? = nil){
if let m = message {
if !_queue.any({m in
return m.messageId == message?.messageId}) {
_queue.enqueue(m)
}
}
self.runMessages()
}
private func runMessages(){
if _isRunning {return}
_isRunning = true
guard let message = _queue.dequeue() else {_isRunning = false; return}
var dict = ["message_id" : message.messageId]
if message.messageThreadString != "" {
dict["message_thread_string"] = message.messageThreadString
}
WebApi.instance.Message.delete(params: dict){_ in
self._isRunning = false
self.runMessages()
}
}
}