Last active
February 13, 2018 15:39
-
-
Save curtclifton/1988cdeaf0704853dbd058b864854957 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NetworkIndicatorManager.swift | |
// Indicator | |
// | |
// Created by Curt Clifton on 5/17/16. | |
// Copyright © 2016 curtclifton.net. All rights reserved. | |
// | |
import Foundation | |
import UIKit | |
/// The class of opaque token objects vended by `NetworkIndicatorManager`. | |
public class NetworkIndicatorToken: AnyObject { | |
} | |
/// A simple wrapper class so we can put weak references inside a Swift array. | |
private class WeakWrapper<Wrapped: AnyObject> { | |
weak var wrapped: Wrapped? | |
/// Becomes true when `wrapped` is deallocated. | |
var isCleared: Bool { | |
return wrapped == nil | |
} | |
init(wrapping: Wrapped) { | |
self.wrapped = wrapping | |
} | |
} | |
/// A singleton instance, accessible via `sharedManager`, allows clients to control the network activity indicator. | |
/// | |
/// - SeeAlso: | |
/// - `beganNetworkTask()` | |
/// - `beganNetworkTask(forToken:)` | |
/// - `finishedNetworkTask(forToken:)` | |
public class NetworkIndicatorManager { | |
/// The singleton instance. | |
public static var sharedManager = NetworkIndicatorManager() | |
public var completionCheckInterval: NSTimeInterval = 1.0 | |
private var keepAliveTokens: [WeakWrapper<AnyObject>] = [] | |
private var pollingTimer: NSTimer? = nil | |
private var isPollingConfigured: Bool { return pollingTimer != nil } | |
/// Shows the network activity indicator if it isn't already showing, returning an opaque token that can be used to signal the end of the activity. | |
/// | |
/// Clients can manually signal the end of the task by calling `finishedNetworkTask(forToken:)` passing the returned token. Alternatively, a client can simply hold a reference to the token. When the reference goes out of scope or is released, the task for the token will end automatically. | |
/// - SeeAlso: | |
/// - `beganNetworkTask(forToken:)` | |
/// - `finishedNetworkTask(forToken:)` | |
public func beganNetworkTask() -> NetworkIndicatorToken { | |
let token = NetworkIndicatorToken() | |
beganNetworkTask(withToken: token) | |
return token | |
} | |
/// Shows the network activity indicator if it isn't already showing, using `token` to signal the end of the activity. | |
/// | |
/// Clients can manually signal the end of the task by calling `finishedNetworkTask(forToken:)` passing the same `token` used to begin the task. Alternatively, a client can simply hold a reference to the token. When the reference goes out of scope or is released, the task for the token will end automatically. | |
/// - SeeAlso: | |
/// - `beganNetworkTask()` | |
/// - `finishedNetworkTask(forToken:)` | |
public func beganNetworkTask(withToken token: AnyObject) { | |
// Redispatch async to main queue so access to `keepAliveTokens` is single threaded | |
guard NSThread.isMainThread() else { | |
NSOperationQueue.mainQueue().addOperationWithBlock { | |
self.beganNetworkTask(withToken: token) | |
} | |
return | |
} | |
let wrappedToken = WeakWrapper(wrapping: token) | |
keepAliveTokens.append(wrappedToken) | |
updateIndicator() | |
} | |
/// Signals that the task associated with `token` has finished, hiding the network activity indicator if no other tasks are still active. | |
/// - SeeAlso: | |
/// - `beganNetworkTask()` | |
/// - `beganNetworkTask(forToken:)` | |
public func finishedNetworkTask(forToken token: AnyObject) { | |
// Redispatch async to main queue so access to `keepAliveTokens` is single threaded | |
guard NSThread.isMainThread() else { | |
NSOperationQueue.mainQueue().addOperationWithBlock { | |
self.finishedNetworkTask(forToken: token) | |
} | |
return | |
} | |
keepAliveTokens = keepAliveTokens.filter { wrappedToken in | |
return !wrappedToken.isCleared && !(wrappedToken.wrapped === token) | |
} | |
} | |
// MARK: - Private API | |
private func updateIndicator() { | |
// Redispatch async to main queue so access to `keepAliveTokens` is single threaded | |
guard NSThread.isMainThread() else { | |
NSOperationQueue.mainQueue().addOperationWithBlock { | |
self.updateIndicator() | |
} | |
return | |
} | |
keepAliveTokens = keepAliveTokens.filter { wrappedToken in | |
!wrappedToken.isCleared | |
} | |
let shouldShowActivity = !keepAliveTokens.isEmpty | |
UIApplication.sharedApplication().networkActivityIndicatorVisible = shouldShowActivity | |
// Update polling timer | |
if !isPollingConfigured && shouldShowActivity { | |
pollingTimer = NSTimer.scheduledTimerWithTimeInterval(completionCheckInterval, target: self, selector: #selector(timerFired(_:)), userInfo: nil, repeats: true) | |
} else if isPollingConfigured && !shouldShowActivity { | |
pollingTimer?.invalidate() | |
pollingTimer = nil | |
} | |
} | |
private dynamic func timerFired(timer: NSTimer) { | |
updateIndicator() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment