What I want to achieve is to wait for all service calls to complete. I know that it can be done with GCD, but I'm looking for more Object Oriented Approach. Here is what I've so far:
First services should notify delegate for their completion, so we will need a protocol for that:
protocol ParallelServiceDelegate: class {
func serviceDidCompleted()
}
Services are Alamofire requests and we are getting their response as:
enum ServiceResult {
case Success(NSDictionary)
case Failure(NSError)
}
My design is to add Facade (wrapper) over this methods. This is the abstract:
import ObjectMapper
protocol ParallelService: class {
associatedtype ItemType: Mappable
var item: ItemType? { get }
var error: NSError? { get }
var isCompleted: Bool { get }
weak var delegate: ParallelServiceDelegate? { get set }
// TODO: Pass params
func start()
func handleRequestCompletion(result: ServiceResult)
}
private var psiAssociationKey: UInt8 = 0
private var pseAssociationKey: UInt8 = 0
private var psdAssociationKey: UInt8 = 0
extension ParallelService {
var item: ItemType? {
return _item
}
var error: NSError? {
return _error
}
var isCompleted: Bool {
return item != nil || error != nil
}
weak var delegate: ParallelServiceDelegate? {
get {
let object = objc_getAssociatedObject(self, &psdAssociationKey)
let wrapper = object as? WeakWrapper<ItemType?>
return wrapper?.value as? ParallelServiceDelegate
}
set(newValue) {
objc_setAssociatedObject(self,
&psdAssociationKey,
WeakWrapper(value: newValue),
.OBJC_ASSOCIATION_RETAIN)
}
}
func handleRequestCompletion(result: ServiceResult) {
switch result {
case .Success(let json):
_item = map(json, object: ItemType.self)
case .Failure(let error):
_error = error
}
}
// Degfault is nothing
func start() {}
// MARK: - Private
private var _item: ItemType? {
get {
let object = objc_getAssociatedObject(self, &psiAssociationKey)
let wrapper = object as? WeakWrapper<ItemType?>
return wrapper?.value as? ItemType
}
set(newValue) {
objc_setAssociatedObject(self,
&psiAssociationKey,
WeakWrapper(value: newValue),
.OBJC_ASSOCIATION_RETAIN)
}
}
private var _error: NSError? {
get {
let object = objc_getAssociatedObject(self, &pseAssociationKey)
return object as? NSError
}
set(newValue) {
objc_setAssociatedObject(self,
&pseAssociationKey,
newValue,
.OBJC_ASSOCIATION_RETAIN)
}
}
}
And Specific Service Facade implementation:
class EmployeesParallelService: ParallelService {
typealias ItemType = Employee
func start() {
Service.emploeesList(callback: handleRequestCompletion)
}
}
class InformationParallelService: ParallelService {
typealias ItemType = Information
func start() {
Service.information(callback: handleRequestCompletion)
}
}
Service caller - Knows nothing about services it just starts all and waits for them to complete:
class ParallelServiceCaller {
private var services: [ParallelService] = []
// MARK: - Lifecycle
func addParallelService(service: ParallelService) {
service.delegate = self
self.services.append(service)
}
// MARK: - Public
func start() {
for service in services {
service.start()
}
}
}
extension ParallelServiceCaller: ParallelServiceDelegate {
func serviceDidCompleted() {
for service in services {
// !!! wait
if !service.isCompleted {
return
}
}
// TODO: Notify delegate
}
}
Latter I want to use it like this:
let caller = ParallelServiceCaller()
caller.addParallelService(EmployeesParallelService())
caller.addParallelService(InformationParallelService())
caller.start()
However I got problem in the implementation of the ParallelServiceCaller class. I'm getting the following error:
Protocol 'ParallelService' can only be used as a generic constraint
because it has Self or associated type requirements
Any idea how to avoid this error?
Update 07/07/16:
I'm still not able to understand how to use PATs. However I took slightly different approach and now I'm using visitor pattern. Here is my playground code, it may be helpful for someone:
//: Playground - noun: a place where people can play
import UIKit
// Mocks
enum ServiceResult {
case Success(NSDictionary)
case Failure(NSError)
}
protocol Mappable { }
typealias CompletionHandlerType = (result: ServiceResult) -> Void
class Service {
class func emploeesList(start: Int? = nil,
max: Int? = nil,
timestamp: Int? = nil,
callback: CompletionHandlerType) {
callback(result: .Success(NSDictionary()))
}
class func information(timestamp: Int? = nil,
callback: CompletionHandlerType) {
callback(result: .Failure(NSError(domain: "", code: 1, userInfo: nil)))
}
}
class EmployeesList: Mappable {}
class Information: Mappable {}
// Actual Implementation
// Visitor
protocol ParallelServiceCallerProtocol {
func call(service: EmployeesListParallelService)
func call(service: InformationParallelService)
}
// Element
protocol ParallelServiceProtocol {
func start(visitor: ParallelServiceCallerProtocol)
}
// Concrete Elements
class EmployeesListParallelService: ParallelServiceProtocol {
func start(visitor: ParallelServiceCallerProtocol) { visitor.call(self) }
}
class InformationParallelService: ParallelServiceProtocol {
func start(visitor: ParallelServiceCallerProtocol) { visitor.call(self) }
}
// Concrete Visitor Delegate - defines callback for async tasks
protocol ParallelServiceCallerDelegateProtocol: class {
func didCompleteParellelServiceWithResult(service: ParallelServiceProtocol, result: ServiceResult)
}
// Concrete Visitor - make API calls
class ParallelServiceCaller <T: ParallelServiceCallerDelegateProtocol>: ParallelServiceCallerProtocol {
private unowned let delegate: T
init(delegate: T) {
self.delegate = delegate
}
func call(service: EmployeesListParallelService) {
Service.emploeesList { [unowned self] (result) in
self.delegate.didCompleteParellelServiceWithResult(service, result: result)
}
}
func call(service: InformationParallelService) {
Service.information { (result) in
self.delegate.didCompleteParellelServiceWithResult(service, result: result)
}
}
}
// Service Result In Context
enum SynchronizationServiceResult {
case Employees(ServiceResult)
case Information(ServiceResult)
}
// Concrete Visitor - Wraps API Result And Gives Context
class ParallelServiceParser: ParallelServiceCallerProtocol {
var result: SynchronizationServiceResult?
private let serviceResult: ServiceResult
init(serviceResult: ServiceResult) {
self.serviceResult = serviceResult
}
func call(service: EmployeesListParallelService) {
result = .Employees(serviceResult)
}
func call(service: InformationParallelService) {
result = .Information(serviceResult)
}
}
// Delegate that notifies for completion of all calls
protocol ParallelServiceManagerDelegateProtocol: class {
func didCompleteAllServicesWithResults(results: [SynchronizationServiceResult])
}
// Manager - starts all calls and adds context to returned results - knows nothing about calls
class ParallelServiceManager<T where T: ParallelServiceManagerDelegateProtocol> {
private let services: [ParallelServiceProtocol]
private unowned let delegate: T
// Keep Caller Visitors in Memory or they will be dealocated
private var callers: [ParallelServiceCaller<ParallelServiceManager>] = []
private var completed: [SynchronizationServiceResult] = [] {
didSet {
if completed.count == services.count {
self.delegate.didCompleteAllServicesWithResults(completed)
self.callers.removeAll()
}
}
}
init(services: [ParallelServiceProtocol], delegate: T) {
self.services = services
self.delegate = delegate
}
func start() {
visitAllServices { (service) in
let caller =
ParallelServiceCaller<ParallelServiceManager>(delegate: self)
service.start(caller)
self.callers.append(caller)
}
}
private func visitAllServices(perform: ParallelServiceProtocol -> () ) {
for service in self.services {
perform(service)
}
}
}
extension ParallelServiceManager: ParallelServiceCallerDelegateProtocol {
func didCompleteParellelServiceWithResult(service: ParallelServiceProtocol,
result: ServiceResult) {
// No need to persist parser visitor
let caller = ParallelServiceParser(serviceResult: result)
service.start(caller)
completed.append(caller.result!)
}
}
// Example Usage
class SynchronizationService {
private lazy var services: [ParallelServiceProtocol] = {
return [EmployeesListParallelService(), InformationParallelService()]
}()
func start() {
let manager = ParallelServiceManager<SynchronizationService>(services: services, delegate: self)
manager.start()
}
}
extension SynchronizationService: ParallelServiceManagerDelegateProtocol {
func didCompleteAllServicesWithResults(results: [SynchronizationServiceResult]) {
for result in results {
switch result {
case .Employees(let result):
// TODO:
print("\(result)") // Should Return Success
case .Information(let result):
// TODO:
print("\(result)") // Should Return Failure
}
}
}
}
let sync = SynchronizationService()
sync.start()
Related
I have ChatsocketIO class which handles all the SocketIO functions related to the chat functionality of the app. The skeleton of that class looks like below.
My Question is when the Socket receives a message it fires the "socket!.on("msg")", However i am confused how to pass this back to the view controller class which i am calling this API class. I have added the View Controller class as well below..
class ChatServiceAPI {
// MARK: - Properties
var manager: SocketManager? = nil
var socket: SocketIOClient? = nil
//Chat Variables
var items:[Message] = []
// MARK: - Life Cycle
init() {
setupSocket()
setupSocketEvents()
socket?.connect()
}
static let shared = ChatServiceAPI();
func stop() {
socket?.removeAllHandlers()
}
// MARK: - Socket Setup
func setupSocket() {
socket = manager!.defaultSocket;
}
func setupSocketEvents() {
if socket!.status != .connected{
socket!.connect()
}
socket!.on(clientEvent: .connect) { (data, emitter) in
print("==connecting==");
}
socket!.on("msg") { (data, emitter) in
let mesage = Message()
let jsonObject = JSON(data[0])
let messageString: String? = jsonObject["msg"].string
let userIDFrom: Int? = jsonObject["profile"]["id"].int
if(userIDFrom != Int(self.userIDTo)) {
return
}
if( userIDFrom == nil)
{
return
}
mesage.data = JSON(["from_user": self.userIDTo, "to_user": self.userID, "msg": self.convertStringToHtmlCode(msg: messageString ?? ""), "created": Date().description(with: .current)])
self.items.insert(mesage, at: 0)
//self.viewMessagesList.reload(messages: self.items, conversation: self.conversation)
}
socket!.on("disconnect") { (data, emitter) in
print("===disconnect==");
self.isConnected = false
}
socket!.connect();
}
// MARK: - Socket Emits
func register(user: String) {
socket?.emit("add user", user)
}
func send(message: String, toUser: String) {
let data = NSMutableDictionary()
data.setValue(message, forKey: "msg")
data.setValue(toUser.lowercased(), forKey: "to")
socket!.emit("send", data);
return;
}
}
In My View Controller, I have something like below, I want to pass ChatSocketAPI's "self.items" to the below-calling controller, when a msg comes, I am confused about how to do this?
import UIKit
import SocketIO
import SwiftyJSON
import EZAlertController
class MessagingViewController: UIViewController {
let viewMessagesList = MessagesListViewController()
let bottomMenuChatView = BottomMenuChatView(frame: CGRect.zero)
var isAnimation = false
let emojiView = EmojiView(frame: CGRect.zero)
func setupSocketIO() {
ChatServiceAPI.init();
self.socket = ChatServiceAPI.shared.socket;
}
override func viewDidLoad() {
super.viewDidLoad()
self.setupSocketIO()
ChatServiceAPI.shared.page = 0;
ChatServiceAPI.shared.items = []
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.setupNavigation()
if Reachability.isConnectedToNetwork() == false {
EZAlertController.alert("", message: "Please check your internet connection")
return
}
// bottom menu setup
bottomMenuChatView.btnSendMessage.addTarget(self, action: #selector(tappedBtnSendMessage(btn:)), for: .touchUpInside)
bottomMenuChatView.btnEmoji.addTarget(self, action: #selector(tappedBtnEmoji(btn:)), for: .touchUpInside)
bottomMenuChatView.textFieldMessage.delegate = self
}
Make your MessagingViewController a listener for the ChatServiceAPI. You can implement that like this:
Create a protocol like the following:
protocol MessageListener: AnyObject {
func messageReceived(text: String)
}
and make you controller conform to it:
extension MessagingViewController: MessageListener {
func messageReceived(text: String) {
// do whatever you need with the message
}
}
Then, in the ChatServiceAPI you can create a var named listeners:
private var listeners: [MessageListener] = []
and methods for adding and removing listeners:
func add(listener: MessageListener) {
self.listeners.append(listener)
}
func remove(listener: MessageListener) {
self.listeners.remove(listener)
}
For the last part, in your ChatServiceAPI, when the "msg" event is received, you need to send the message to all of your registered listeners. So, something like this:
socket!.on("msg") { (data, emitter) in
...
for listener in self.listeners {
listener.messageReceived(text: ...)
}
}
Now you also need to register your viewController as a listener. So you would call ChatServiceAPI.shared.add(listener: self) in your viewDidLoad.
Don't forget to also call ChatServiceAPI.shared.remove(listener: self) to prevent memory leaks.
You can back data to your view controller by writing socket.ON inside a function that have escaping block.
Here is my sample code that I use
func getResponse(completion: #escaping(_ resMessage: String) -> Void) {
guard let socket = manager?.defaultSocket else {
return
}
socket.on(myEventName) { (dataArray, socketAck) -> Void in
guard let data = UIApplication.jsonData(from: dataArray[0]) else {
return
}
do {
let responseMsg = try JSONDecoder().decode(String.self, from: data)
completion(responseMsg)
} catch let error {
print("Something happen wrong here...\(error)")
completion("")
}
}
}
Then you can call this function getResponse in viewDidLoad inside your viewController. Like this -
self.socketInstance.getResponse { socketResponse in
// socketResponse is the response
// this will execute when you get new response from socket.ON
}
I'm trying to get my head around Swift delegates and stole/knocked-up a Playground but cannot seem to get the delegate function to be called.
protocol fBookDelegate:class {
func processData(data: String)
}
class fBook {
weak var delegate: fBookDelegate?
init() {
print("initialising fBook")
delegate?.processData(data: "hello world")
print("we should have printed")
}
}
class fMain: fBookDelegate {
init() {
print("initialising fMain")
let getfBook = fBook()
getfBook.delegate = self
print("all done let's rumble")
}
func processData(data: String) {
print("processing data from fBook with \(data)")
}
}
var controller = fMain()
Can anybody spot my mistake ?
All I get for output is
initialising fMain
initialising fBook
we should have printed
all done let's rumble
You can use like this :
import UIKit
protocol fBookDelegate:class {
func processData(data: String)
}
class fBook {
init(delegate: fBookDelegate?) {
print("initialising fBook")
delegate?.processData(data: "hello world")
print("we should have printed")
}
}
class fMain: fBookDelegate {
init() {
print("initialising fMain")
let getfBook = fBook(delegate: self)
print("all done let's rumble")
}
func processData(data: String) {
print("processing data from fBook with \(data)")
}
}
var controller = fMain()
output :
initialising fMain
initialising fBook
processing data from fBook with hello world
we should have printed
all done let's rumble
Here's one option for using your delegate:
protocol fBookDelegate:class {
func processData(data: String)
}
class fBook {
weak var delegate: fBookDelegate?
init() {
print("initialising fBook")
}
func talkToMe() {
delegate?.processData(data: "hello world")
}
}
class fMain: fBookDelegate {
init() {
print("initialising fMain")
let getfBook = fBook()
getfBook.delegate = self
getfBook.talkToMe()
print("all done let's rumble")
}
func processData(data: String) {
print("processing data from fBook with \(data)")
}
}
var controller = fMain()
Another would be a custom init method that takes the delegate as a parameter.
I have a class like this
// a.swift
public class ComServiceString<Result>: ComService<Result> {
override func execute() {
if (method == Method.GET) {
manager.GET(getUrl(),
parameters: nil,
success: { (operation: AFHTTPRequestOperation!,responseObject: AnyObject!) in
self.onSuccessListener?(self.parseResult(responseObject.description))
},
failure: { (operation: AFHTTPRequestOperation!,error: NSError!) in
println("Error: " + error.localizedDescription)
var errorSd = ComServiceError(error.localizedDescription)
if (operation.response != nil) {
errorSd.errorCode = operation.response.statusCode
}
self.onFailedListener?(errorSd)
}
)
}
}
func parseResult(result: String) -> Result? {
fatalError("This method need to be implemented")
}
}
and I extend it to a new class like this
// b.swift:
public class BranchListService<T>: ComServiceString<BranchListModel> {
override func parseResult(result: String) -> BranchListModel? {
let branchListMode = BranchListModel(stringResult: result)
return branchListMode
}
}
my BranchListModel looks like this
public class BranchListModel {
public var total = 0
public var stringResult = ""
init() {}
init (stringResult result: String) {
self.stringResult = result
if let jsonArray = JSONUtil.stringToJSONArray(result) {
if let resultObject = jsonArray[0] as? NSDictionary {
if let total = resultObject["total"] as? NSString {
self.total = total.integerValue;
}
}
}
}
}
but I got BAD_ACCESS error in this line:
let branchListMode = BranchListModel(stringResult: self.resultString)
inside parseResult function on b.swift. I'm still learning this language with trying to convert my java code to swift. Do you guys know what's wrong from the code?
I think there is a bug of the Swift compiler. I've tested a relatively-simplified code below and that crashed in the same way.
class ResultParser<T> {
func parse(result: String) -> T? { abort() }
}
class MyResultParser<T>: ResultParser<Int> {
override func parse(result: String) -> Int? {
return result.toInt()
}
}
let parser: ResultParser<Int> = MyResultParser<()>()
parser.parse("2525")
At the moment, the Swift compiler cannot correctly treat virtual functions directly receiving and/or returning values, whose type is generic. I guess the compiler regards T as a pointer (object) value whatever an actual substituted type is.
I've found a workaround to do nearly the same thing.
class Box<T> {
let raw: T
init(_ raw: T) { self.raw = raw }
}
class ResultParser<T> {
func parse(result: String) -> Box<T?> { abort() }
}
class MyResultParser<T>: ResultParser<Int> {
override func parse(result: String) -> Box<Int?> {
return Box(result.toInt())
}
}
In the code above, the returned value is wrapped in the Box<>. Box<> is an object, so that works no matter whether or not the compiler can tell value types from object types.
I'm encountering a problem where a property of an instance of a class I've created is seemingly losing reference to one its values.
Essentially I have a class like this:
class Channel {
var callbacks: [String: (JSON) -> Void]
var subscribed = false
let name: String
init(name: String) {
self.name = name
self.callbacks = [:]
}
func bind(eventName: String, callback: (JSON) -> Void) {
self.callbacks[eventName] = callback
}
func handleEvent(eventName: String, eventData: String) {
if let cb = self.callbacks[eventName] {
let json = JSON(object: eventData)
cb(json)
}
}
}
and then inside a ViewController I have the following code:
class ViewController: UIViewController {
let wSock = wSocket(key: "afa4d38348f89ba9c398")
func channelSetup() {
var ch = wSock.subscribe("test-channel")
ch.bind("test-event", { (data: JSON) -> Void in
println("I'm the callback getting called")
})
println(ch.callbacks)
}
override func viewDidLoad() {
super.viewDidLoad()
channelSetup()
}
}
In the println of ch.callbacks it shows that there is a key-value pair in the dictionary.
However, when the channel receives an event later on when there is a message received over the socket, the callback is no longer there. In terms of code, here is the code in full:
import UIKit
class ViewController: UIViewController {
let wSock = wSocketClient(key: "afa4d38348f89ba9c398")
func channelSetup() {
var ch = wSock.subscribe("test-channel")
ch.bind("test-event", { (data: JSON) -> Void in
println("I'm the callback getting called")
})
println(ch.callbacks)
}
override func viewDidLoad() {
super.viewDidLoad()
channelSetup()
}
}
class wSocketClient {
let connection: Connection
init(key: String, encrypted: Bool = false) {
var url = "SOCKET_URL"
connection = Connection(url: url)
}
func subscribe(channelName: String) -> Channel {
return self.connection.addChannel(channelName)
}
func connect() {
self.connection.open()
}
}
class Connection: WebSocketDelegate {
let url: String
lazy var socket: WebSocket = { [unowned self] in
return self.connectInternal()
}()
let connected = false
var channels = Channels()
init(url: String) {
self.url = url
}
func addChannel(channelName: String) -> Channel {
return Channel(name: channelName)
}
func open() {
if self.connected {
return
} else {
self.socket = connectInternal()
}
}
func connectInternal() -> WebSocket {
let ws = WebSocket(url: NSURL(string: self.url)!)
ws.delegate = self
ws.connect()
return ws
}
func websocketDidReceiveMessage(text: String) {
let data = (text as NSString).dataUsingEncoding(NSUTF8StringEncoding)
let json = JSON(data: data!)
if let channelName = json["channel"].stringValue {
if let internalChannel = self.channels.find(channelName) {
if let eName = json["event"].stringValue {
if let eData = json["data"].stringValue {
internalChannel.handleEvent(eName, eventData: eData) // this is the part of the code where the channel should eventually call the callback
}
}
}
}
}
}
class Channel {
var callbacks: [String: (JSON) -> Void]
var subscribed = false
let name: String
init(name: String) {
self.name = name
self.callbacks = [:]
}
func bind(eventName: String, callback: (JSON) -> Void) {
self.callbacks[eventName] = callback
}
func handleEvent(eventName: String, eventData: String) {
if let cb = self.callbacks[eventName] { // here self.callbacks is empty and the callback has disappeared
let json = JSON(object: eventData)
cb(json)
}
}
}
class Channels {
var channels = [String: Channel]()
func add(channelName: String) -> Channel {
if let channel = self.channels[channelName] {
return channel
} else {
let newChannel = Channel(name: channelName)
self.channels[channelName] = newChannel
return newChannel
}
}
func find(channelName: String) -> Channel? {
return self.channels[channelName]
}
}
So basically when the WebSocket receives some data that is for the given channel, it should check for the event name, and if there is a callback with that event name, call the callback associated to that event name. However, the channel apparently has no callbacks when the handleEvent method is called, even though at the bottom of viewDidLoad it shows as having a callback in the callbacks property for the channel.
Any ideas as to where / why the callback is disappearing?
Update
I've now tried moving the definition of the channel, ch outside of the channelSetup function so it's like this, but with no luck:
class ViewController: UIViewController {
let wSock = wSocket(key: "afa4d38348f89ba9c398")
var ch: Channel = nil
func channelSetup() {
ch = wSock.subscribe("test-channel")
ch.bind("test-event", { (data: JSON) -> Void in
println("I'm the callback getting called")
})
println(ch.callbacks)
}
override func viewDidLoad() {
super.viewDidLoad()
channelSetup()
}
}
I've solved this but not because it was something going on in Swift that I didn't understand. Instead it was just that the way that I had setup the code meant that there were duplicate channel objects being created and the callback was being added to only one of the channels.
I'm trying to implement an image downloader class. It's a singleton implementation. The idea is to ask the instance to download an image and register as an observer for that image download. This is what I came up with so far:
public protocol ImageDownloaderDelegate {
func imageDownloadFinished(success: Bool)
}
public class ImageDownloader {
var list: [Int64] = []
var observer: [Int64:[ImageDownloaderDelegate]] = [:]
var downloading: Bool = false
public func downloadImageWithId(immutableId: Int64, delegate: ImageDownloaderDelegate) {
// Add Id to download list
if (!contains(list, immutableId)) {
list.append(immutableId)
}
// Add Observer
var observerList = observer[immutableId]
if (observerList == nil) {
observerList = [delegate]
} else if (!contains(observerList, delegate)) {
observerList!.append(delegate)
}
observer[immutableId] = observerList
// Start to download
if (!downloading) {
self.downloadNextImage()
}
}
private func downloadNextImage() {
...
}
/// A shared instance of the class
public class var defaultImageDownloader: ImageDownloader {
struct Singleton {
static let instance = ImageDownloader()
}
return Singleton.instance
}
}
I get the following error:
'ImageDownloaderDelegate' is not convertible to 'S.Generator.Element -> L'
Any help is much appreciated
You're not passing the correct arguments to the contains function, which expects a collection and a predicate (closure).
You need to do
public protocol ImageDownloaderDelegate : class {
func imageDownloadFinished(success: Bool)
}
public class ImageDownloader {
var list: [Int64] = []
var observer: [Int64:[ImageDownloaderDelegate]] = [:]
var downloading: Bool = false
public func downloadImageWithId(immutableId: Int64, delegate: ImageDownloaderDelegate) {
// Add Id to download list
if (!contains(list, immutableId)) {
list.append(immutableId)
}
// Add Observer
var observerList = observer[immutableId]
if (observerList == nil) {
observerList = [delegate]
} else if !contains(observerList!, { observer in observer === delegate }) {
observerList!.append(delegate)
}
observer[immutableId] = observerList
// Start to download
if (!downloading) {
self.downloadNextImage()
}
}
private func downloadNextImage() {
...
}
/// A shared instance of the class
public class var defaultImageDownloader: ImageDownloader {
struct Singleton {
static let instance = ImageDownloader()
}
return Singleton.instance
}
}