Cancel NSOperationsQueue - ios

I am trying to cancel my NSOperationsQueue and remove all the operations from the queue. I called the method "cancelAllOperations()" but this doesnt remove the operation from queue, I am aware that this method only adds a flag to the operation.
How do I go about removing all operations from my queue? Or prevent the operation queue from executing operations that have a flag?
private let mediaDownloadOperationQueue: NSOperationQueue = {
let queue = NSOperationQueue()
queue.maxConcurrentOperationCount = 1
queue.qualityOfService = .Background
return queue
}()
func startDownloadProcess() {
guard downloadSwitchState == true else { return }
let context = DataBaseManager.sharedInstance.mainManagedObjectContext
let mediasToDownload = self.listOfMediaToDownload(context)
for media in mediasToDownload {
downloadMedia(media)
}
}
private func downloadMedia(media: Media) {
//check if operation already exist
for operation in mediaDownloadOperationQueue.operations {
let operation = operation as! MediaDownloadOperation
if operation.media.objectID == media.objectID { return }
}
//HERE I am adding the operation to queue
mediaDownloadOperationQueue.addOperation(MediaDownloadOperation(media: media))
}
//EDIT: Here is my MediasDownloadOperation
import Foundation
import Alamofire
class MediaDownloadOperation: BaseAsyncOperation {
let media: Media
init(media: Media) {
self.media = media
super.init()
}
override func main() {
guard let mediaSourceURI = media.sourceURI
else {
self.completeOperation()
return
}
var filePath: NSURL?
let destination: (NSURL, NSHTTPURLResponse) -> (NSURL) = {
(temporaryURL, response) in
if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first, let suggestedFilename = response.suggestedFilename {
filePath = directoryURL.URLByAppendingPathComponent("\(suggestedFilename)")
return filePath!
}
return temporaryURL
}
RequestManager.sharedAlamofireManager.download(.GET, mediaSourceURI, destination: destination).response {
(request, response, data, error) -> Void in
if let filePath = filePath where error == nil {
let saveToCameraRoll = self.media.saveToCameraRoll?.boolValue == true
self.media.fileLocation = filePath.lastPathComponent
self.media.saveToCameraRoll = false
DataBaseManager.sharedInstance.save()
if saveToCameraRoll {
self.media.saveMediaToCameraRoll(nil)
}
}
self.completeOperation()
NSNotificationCenter.defaultCenter().postNotificationName(MediaDownloadManager.MediaIsDownloadedNote, object: nil)
}
}
}

Adding a conditional to test to see if operation has cancelled seemed to have solved the problem:
RequestManager.sharedAlamofireManager.download(.GET, mediaSourceURI, destination: destination).response {
(request, response, data, error) -> Void in
//Only download if logged in
if (self.cancelled == false){
print("RAN operation!")
if let filePath = filePath where error == nil {
let saveToCameraRoll = self.media.saveToCameraRoll?.boolValue == true
self.media.fileLocation = filePath.lastPathComponent
self.media.saveToCameraRoll = false
DataBaseManager.sharedInstance.save()
if saveToCameraRoll {
self.media.saveMediaToCameraRoll(nil)
}
}
self.completeOperation()
NSNotificationCenter.defaultCenter().postNotificationName(MediaDownloadManager.MediaIsDownloadedNote, object: nil)
}else{
print("did not run operation! was cancelled!")
self.completeOperation()
}
}

Related

How to refactor duplicate Firestore document IDs in Swift?

I'm doing my very first IOS app using Cloud Firestore and have to make the same queries to my database repeatedly. I would like to get rid of the duplicate lines of code. This is examples of func where documents ID are duplicated. Also I using other queries as .delete(), .addSnapshotListener(), .setData(). Should I refactor all that queries somehow or leave them because they were used just for one time?
#objc func updateUI() {
inputTranslate.text = ""
inputTranslate.backgroundColor = UIColor.clear
let user = Auth.auth().currentUser?.email
let docRef = db.collection(K.FStore.collectionName).document(user!)
docRef.getDocument { [self] (document, error) in
if let document = document, document.exists {
let document = document
let label = document.data()?.keys.randomElement()!
self.someNewWord.text = label
// Fit the label into screen
self.someNewWord.adjustsFontSizeToFitWidth = true
self.checkButton.isHidden = false
self.inputTranslate.isHidden = false
self.deleteBtn.isHidden = false
} else {
self.checkButton.isHidden = true
self.inputTranslate.isHidden = true
self.deleteBtn.isHidden = true
self.someNewWord.adjustsFontSizeToFitWidth = true
self.someNewWord.text = "Add your first word to translate"
updateUI()
}
}
}
#IBAction func checkButton(_ sender: UIButton) {
let user = Auth.auth().currentUser?.email
let docRef = db.collection(K.FStore.collectionName).document(user!)
docRef.getDocument { (document, error) in
let document = document
let label = self.someNewWord.text!
let currentTranslate = document!.get(label) as? String
let translateField = self.inputTranslate.text!.lowercased().trimmingCharacters(in: .whitespaces)
if translateField == currentTranslate {
self.inputTranslate.backgroundColor = UIColor.green
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in
self.inputTranslate.backgroundColor = UIColor.clear
updateUI()}
} else {
self.inputTranslate.backgroundColor = UIColor.red
self.inputTranslate.shakingAndRedBg()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in
self.inputTranslate.backgroundColor = UIColor.clear
self.inputTranslate.text = ""
}
}
}
}
func deletCurrentWord () {
let user = Auth.auth().currentUser?.email
let docRef = db.collection(K.FStore.collectionName).document(user!)
docRef.getDocument { (document, err) in
let document = document
if let err = err {
print("Error getting documents: \(err)")
} else {
let array = document!.data()
let counter = array!.count
if counter == 1 {
// The whole document will deleted together with a last word in list.
let user = Auth.auth().currentUser?.email
self.db.collection(K.FStore.collectionName).document(user!).delete() { err in
if let err = err {
print("Error removing document: \(err)")
} else {
self.updateUI()
}
}
} else {
// A current word will be deleted
let user = Auth.auth().currentUser?.email
let wordForDelete = self.someNewWord.text!
self.db.collection(K.FStore.collectionName).document(user!).updateData([
wordForDelete: FieldValue.delete()
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
self.updateUI()
}
}
}
}
}
}
Another query example
func loadMessages() {
let user = Auth.auth().currentUser?.email
let docRef = db.collection(K.FStore.collectionName).document(user!)
docRef.addSnapshotListener { (querySnapshot, error) in
self.messages = []
if let e = error {
print(e)
} else {
if let snapshotDocuments = querySnapshot?.data(){
for item in snapshotDocuments {
if let key = item.key as? String, let translate = item.value as? String {
let newMessage = Message(key: key, value: translate)
self.messages.append(newMessage)
}
}
DispatchQueue.main.async {
self.messages.sort(by: {$0.value > $1.value})
self.secondTableView.reloadData()
let indexPath = IndexPath(row: self.messages.count - 1, section: 0)
self.secondTableView.scrollToRow(at: indexPath, at: .top, animated: false)
}
}
}
}
}
}
enum Error {
case invalidUser
case noDocumentFound
}
func fetchDocument(onError: #escaping (Error) -> (), completion: #escaping (FIRQueryDocument) -> ()) {
guard let user = Auth.auth().currentUser?.email else {
onError(.invalidUser)
return
}
db.collection(K.FStore.collectionName).document(user).getDocument { (document, error) in
if let error = error {
onError(.noDocumentFound)
} else {
completion(document)
}
}
}
func updateUI() {
fetchDocument { [weak self] error in
self?.hideShowViews(shouldHide: true, newWordText: nil)
} completion: { [weak self] document in
guard document.exists else {
self?.hideShowViews(shouldHide: true, newWordText: nil)
return
}
self?.hideShowViews(shouldHide: false, newWordText: document.data()?.keys.randomElement())
}
}
private func hideShowViews(shouldHide: Bool, newWordText: String?) {
checkButton.isHidden = shouldHide
inputTranslate.isHidden = shouldHide
deleteBtn.isHidden = shouldHide
someNewWord.adjustsFontSizeToFitWidth = true
someNewWord.text = newWordText ?? "Add your first word to translate"
}
The updateUI method can easily be refactored using a simple guard statement and then taking out the common code into a separate function. I also used [weak self] so that no memory leaks or retain cycles occur.
Now, you can follow the similar approach for rest of the methods.
Use guard let instead of if let to avoid nesting.
Use [weak self] for async calls to avoid memory leaks.
Take out the common code into a separate method and use a Bool flag to hide/show views.
Update for step 3:
You can create methods similar to async APIs for getDocument() or delete() etc and on completion you can update UI or perform any action. You can also create a separate class and move the fetchDocument() and other similar methods in there and use them.

Optional Still Returning Nil After Assigning Value

I am working on a similar feature to 'liking/unliking a post'.
I have an MVVM architecture as;
struct MyStructModel {
var isLiked: Bool? = false
}
class MyStructView {
var isLiked: Bool
init(myStructModel: MyStructModel) {
self.isLiked = myStructModel.isLiked ?? false
}
}
I successfully get the value of whether the post is liked or not here;
func isPostLiked(documentID: String, completion: #escaping (Bool) -> Void) {
guard let authID = auth.id else { return }
let query = reference(to: .users).document(authID).collection("liked").document(documentID)
query.getDocument { (snapshot, error) in
if error != nil {
print(error as Any)
return
}
guard let data = snapshot?.data() else { return }
if let value = data["isLiked"] as? Bool {
completion(value)
} else {
completion(false)
}
}
}
func retrieveReviews(completion: #escaping([MyStructModel]) -> ()) {
var posts = [MyStructModel]()
let query = reference(to: .posts).order(by: "createdAt", descending: true)
query.getDocuments { (snapshot, error) in
if error != nil {
print(error as Any)
return
}
guard let snapshotDocuments = snapshot?.documents else { return }
for document in snapshotDocuments {
if var post = try? JSONDecoder().decodeQuery(MyStructModel.self, fromJSONObject: document.decode()) {
// isLiked is nil here...
self.isPostLiked(documentID: post.documentID!) { (isLiked) in
post.isLiked = isLiked
print("MODEL SAYS: \(post.isLiked!)")
// isLiked is correct value here...
}
posts.append(post)
}
completion(posts)
}
}
}
However, when it gets to my cell the value is still nil.
Adding Cell Code:
var post: MyStructView? {
didSet {
guard let post = post else { return }
print(post.isLiked!)
}
}
Your isLiked property is likely nil in your cells because the retrieveReviews function doesn't wait for the isPostLiked function to complete before completing itself.
You could easily solve this issue by using DispatchGroups. This would allow you to make sure all of your Posts have their isLiked value properly set before being inserted in the array, and then simply use the DispatchGroup's notify block to return all the loaded posts via the completion handler:
func retrieveReviews(completion: #escaping([MyStructModel]) -> ()) {
var posts = [MyStructModel]()
let query = reference(to: .posts).order(by: "createdAt", descending: true)
query.getDocuments { [weak self] (snapshot, error) in
guard let self = self else { return }
if error != nil {
return
}
guard let documents = snapshot?.documents else { return }
let dispatchGroup = DispatchGroup()
for document in documents {
dispatchGroup.enter()
if var post = try? JSONDecoder().decodeQuery(MyStructModel.self, fromJSONObject: document.decode()) {
self.isPostLiked(documentID: post.documentID!) { isLiked in
post.isLiked = isLiked
posts.append(post)
dispatchGroup.leave()
}
}
}
dispatchGroup.notify(queue: .main) {
completion(posts)
}
}
}

App crash : NSInvalidArgumentException - Operation is already enqueued on a queue

I am using Rob's implementation of AsynchronousOperation
Following is my AsynchronousOperation subclass
class AsynOperation : Operation {
// Same implementation as mentioned in Rob's answer
}
I'm trying to make a thumbnail for images/videos. I've wrapped the thumbnail creation functions inside an NSOperation
class AssetDocFetchOperation : AsynOperation, AssetDocGrabberProtocol {
var cacheKey: URL?
var url : String
var image : UIImage?
init(url : String, cacheKey : URL?) {
self.url = url
self.cacheKey = cacheKey
}
}
Image Generator
class AssetImageGrabber : AssetDocFetchOperation{
let client = NetworkClient()
override init(url : String,cacheKey : URL?) {
super.init(url: url,cacheKey : cacheKey)
// name = cacheKey?.absoluteString
}
override func main() {
guard let docURL = URL(string: self.url) else {
self.finish()
return
}
if let cKey = cacheKey ,let imageData = MemoryCache.shareInstance.object(forKey: cKey.absoluteString) {
self.image = UIImage(data: imageData)
self.finish()
return
}
client.shouldCache = false
let service = AssetDocFetchService(url:docURL )
client.request(customHostService: service) {[weak self] (result) in
guard let this = self else {return}
switch result {
case .success(let data) :
if let imageData = data, let i = UIImage(data: imageData){
if let cKey = this.cacheKey {
MemoryCache.shareInstance.set(object: imageData , forKey: cKey.absoluteString, cost: 1)
}
this.image = i
this.finish()
}
case .failure(let e):
print(e)
this.image = nil
this.finish()
}
}
}
override func cancel() {
super.cancel()
client.cancel()
}
}
Video thumbnail creator
class AssetVideoThumbnailMaker : AssetDocFetchOperation {
private var imageGenerator : AVAssetImageGenerator?
override init(url : String,cacheKey : URL?) {
super.init(url: url,cacheKey : cacheKey)
//name = cacheKey?.absoluteString
}
override func main() {
guard let docURL = URL(string: self.url) else {
self.finish()
return
}
if let cKey = cacheKey ,let imageData = MemoryCache.shareInstance.object(forKey: cKey.absoluteString) {
self.image = UIImage(data: imageData)
self.finish()
return
}
generateThumbnail(url: docURL) {[weak self] (image) in
self?.image = image
self?.finish()
}
}
override func cancel() {
super.cancel()
imageGenerator?.cancelAllCGImageGeneration()
}
private func generateThumbnail(url : URL ,completion: #escaping (UIImage?) -> Void) {
let asset = AVAsset(url: url)
imageGenerator = AVAssetImageGenerator(asset: asset)
let time = CMTime(seconds: 1, preferredTimescale: 60)
let times = [NSValue(time: time)]
imageGenerator?.generateCGImagesAsynchronously(forTimes: times, completionHandler: { [weak self] _, image, _, _, _ in
if let image = image {
let uiImage = UIImage(cgImage: image)
if let cKey = self?.cacheKey , let data = uiImage.pngData() {
MemoryCache.shareInstance.set(object: data , forKey: cKey.absoluteString, cost: 1)
}
completion(uiImage)
} else {
completion(nil)
}
})
}
}
I'll create NSOperation from cellForItemAt method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let file : AssetFile = // set the file here
....
let grabOperation = taskMaker(fromDocFile : file)
cell.assetFile = grabOperation
if grabOperation.state.value == .initial {
start(operation: grabOperation)
}
return cell
}
func taskMaker(fromDocFile file : AssetFile)->AssetDocFetchOperation{
switch file.fileType {
case .image:
return AssetImageGrabber(url : file.path ?? "",cacheKey: file.cacheKeyURL)
case .video :
return AssetVideoThumbnailMaker(url : file.path ?? "",cacheKey: file.cacheKeyURL)
}
}
I'll add them to the operation queue as follows
lazy var docsOperations : OperationQueue = {
let queue = OperationQueue()
queue.name = "assetVideoOperations"
queue.maxConcurrentOperationCount = 3
return queue
}()
lazy var docsOperationInProgress : [AssetGrabOperation] = []
func start(operation : AssetGrabOperation){
if !docsOperationInProgress.contains(operation) && !operation.task.isFinished && !operation.task.isExecuting {
docsOperationInProgress.append(operation)
docsOperations.addOperation(operation.task)
}
}
For failed/timed out request, I've retry method
func reloadDocument(atIndex: IndexPath?) {
if let index = atIndex {
let tile : AssetFile = // get file here
let grabOperation = // get current opration
remove(operation: grabOperation)
reloadFile(atIndexPath: index) // i'll recreate the operation with taskMaker(fromDocFile : file)
documentList.reloadItems(at: [index])
}
}
The remove operation method
func remove(operation :AssetGrabOperation ) {
operation.task.cancel()
docsOperationInProgress = docsOperationInProgress.filter({ return $0 == operation ? false : true })
}
The problem is, if I scroll back and forth in my UICollectionView the app crashes throwing the following error. (Sometimes when I call the reloadDocument as well)
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperation:]: operation is already enqueued on a queue'
I've also found a solutions/workaround by checking the names of the operation
let alreadyOn = docsOperations.operations.filter({
return $0.name == operation.task.name
})
// i'll assign the cacheURL to name while the init of opration
if alreadyOn.count == 0 {
docsOperationInProgress.append(operation)
docsOperations.addOperation(operation.task)
}
I'm not sure whether this approch is good or not. What am i doing wrong?
After a long time to fix this issue, I did make an extension for Operation to remove dependencies before starting the new Operation, This could be a workaround solution 😅
extension Operation {
public func removeAllDependencies() {
dependencies.forEach {
$0.removeAllDependencies()
removeDependency($0) }
}
}

Why nsurlsession.datataskwithrequst was canceled

I am using nsurlsession on RxSwift.
I am facing two problems about nsurlsession on RxSwift.
I created Custom Observable.
This Observable has used nsurlsession.
nsurlsession.datataskwithrequst was canceled everytime on RxSwift.
My code is here
func getWorkInfo(request:NSURLRequest,type1:C.Type,type2:W.Type? = nil) -> Observable<(C?,[W]?, NSHTTPURLResponse)>{
return Observable.create { observer in
var d: NSDate?
if Logging.URLRequests(request) {
d = NSDate()
}
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) in
guard let response = response, data = data else {
ColorLogger.defaultInstance?.error(error)
observer.on(.Error(error ?? RxCocoaURLError.Unknown))
return
}
guard let httpResponse = response as? NSHTTPURLResponse else {
observer.on(.Error(RxCocoaURLError.NonHTTPResponse(response: response)))
return
}
guard let jsonString: String = NSString(data:data, encoding:NSUTF8StringEncoding) as? String else{
observer.on(.Error(ApiError.FormatError))
return
}
//カウント系
let countObj = Mapper<C>().map(jsonString)
//一覧系
//二つ目を指定してない場合はnilを返す
if type2 != nil{
let aryObj = Mapper<W>().mapArray(jsonString)
observer.on(.Next(countObj,aryObj, httpResponse))
}else{
observer.on(.Next(countObj,nil, httpResponse))
}
observer.on(.Completed)
}
let t = task
t.resume()
return AnonymousDisposable{task.cancel()}
}
}
Above method was called by here.
func getWorkCount(dicParam: NSDictionary) -> Observable<WorkCount?> {
// URL作成
let strParam = dicParam.urlEncodedString()
let strUrl = Const.ShiftApiBase.SFT_API_DOMAIN+Const.ApiUrl.WORK_COUNT+"?"+strParam
// 求人リストデータを取得
let url = NSURL(string: strUrl)!
let request = NSURLRequest(URL: url)
let client = WorkClient<WorkCount,Work>()
ColorLogger.defaultInstance?.debug(strUrl)
return client.getWorkInfo(request, type1: WorkCount.self)
.observeOn(Dependencies.sharedDependencies.backgroundWorkScheduler)
.catchError{(error) -> Observable<(WorkCount?,[Work]?, NSHTTPURLResponse)> in
print("error")
ColorLogger.defaultInstance?.error("UnknownError")
return Observable.empty()
}
.map { countObj,workObj,httpResponse in
if httpResponse.statusCode != 200 {
throw ApiError.Bad
}
return countObj
}
.observeOn(Dependencies.sharedDependencies.mainScheduler)
}
And My subscribe is here.
/**
検索件数を取得
- parameter param: <#param description#>
*/
func getSearchCount(param: [String:String]){
let dicParam = NSDictionary(dictionary: param)
api.getWorkCount(dicParam)
.catchError{
error -> Observable<WorkCount?> in
switch error{
case ApiError.Bad:
ColorLogger.defaultInstance?.error("status error")
break
case ApiError.FormatError:
ColorLogger.defaultInstance?.error("FormatError")
break
case ApiError.NoResponse:
ColorLogger.defaultInstance?.error("NoResponse")
break
default:
ColorLogger.defaultInstance?.error("UnKnownError")
break
}
return Observable.just(nil)
}
.subscribeNext { [weak self] countObj in
self?.count.value = countObj?.returned_count
}
.addDisposableTo(disposeBag)
}
I have two problems.
1:nsurlsession was canceled every time.I don know reason.
2:Even if I got error on NSURLSession,I could not catch error on "CatchError".
By the way,when i try to use the following code,But nsurlsession might be canceled.
It might be a base nsurlsession on RxSwift.
func getWorkCount4(dicParam: NSDictionary) -> Observable<WorkCount?> {
// URL作成
let strParam = dicParam.urlEncodedString()
let strUrl = Const.ShiftApiBase.SFT_API_DOMAIN+Const.ApiUrl.WORK_COUNT+"?"+strParam
// 求人リストデータを取得
let url = NSURL(string: strUrl)!
let request = NSURLRequest(URL: url)
let session = ApiBase.sharedObj.createNSURLSession()
return NSURLSession.sharedSession().rx_response(request)
.observeOn(Dependencies.sharedDependencies.backgroundWorkScheduler)
.map { data,httpResponse in
if httpResponse.statusCode != Const.HTTP_RESPONSE.HTTP_STATUS_CODE_OK {
throw ApiError.Bad
}
guard let jsonString: String = NSString(data:data, encoding:NSUTF8StringEncoding) as? String else{
throw ApiError.FormatError
}
let countObj = Mapper<WorkCount>().map(jsonString)
return countObj
}
.observeOn(Dependencies.sharedDependencies.mainScheduler)
}
What is this problem?
I could resolve by myself.
Above method does not have any problem.
Below code has problem.
// MARK: - 検索カウント
extension SearchCount{
/// 検索用のViewModel
var searchViewModel:SearchViewModel {
return SearchViewModel()
}
/**
検索の件数を取得
*/
func getSearchCount(){
setApiParameter()
searchViewModel.getSearchCount(apiParam)
}
}
I defined searchViewModel on Class,the i could resolve.

Loading images using UIImageView+AFNetworking Xcode 7

I have been using UIImageView+AFNetworking.swift in my application to download images, but after updating to Xcode 7, it gives me an error.
Here is the full code:
import UIKit
#objc public protocol AFImageCacheProtocol:class{
func cachedImageForRequest(request:NSURLRequest) -> UIImage?
func cacheImage(image:UIImage, forRequest request:NSURLRequest);
}
extension UIImageView {
private struct AssociatedKeys {
static var SharedImageCache = "SharedImageCache"
static var RequestImageOperation = "RequestImageOperation"
static var URLRequestImage = "UrlRequestImage"
}
public class func setSharedImageCache(cache:AFImageCacheProtocol?) {
objc_setAssociatedObject(self, &AssociatedKeys.SharedImageCache, cache, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN))
}
public class func sharedImageCache() -> AFImageCacheProtocol {
struct Static {
static var token : dispatch_once_t = 0
static var defaultImageCache:AFImageCache?
}
dispatch_once(&Static.token, { () -> Void in
Static.defaultImageCache = AFImageCache()
NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationDidReceiveMemoryWarningNotification, object: nil, queue: NSOperationQueue.mainQueue()) { (NSNotification) -> Void in
Static.defaultImageCache!.removeAllObjects()
}
})
return objc_getAssociatedObject(self, &AssociatedKeys.SharedImageCache) as? AFImageCacheProtocol ?? Static.defaultImageCache!
}
private class func af_sharedImageRequestOperationQueue() -> NSOperationQueue {
struct Static {
static var token:dispatch_once_t = 0
static var queue:NSOperationQueue?
}
dispatch_once(&Static.token, { () -> Void in
Static.queue = NSOperationQueue()
Static.queue!.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
})
return Static.queue!
}
private var af_requestImageOperation:(operation:NSOperation?, request: NSURLRequest?) {
get {
let operation:NSOperation? = objc_getAssociatedObject(self, &AssociatedKeys.RequestImageOperation) as? NSOperation
let request:NSURLRequest? = objc_getAssociatedObject(self, &AssociatedKeys.URLRequestImage) as? NSURLRequest
return (operation, request)
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.RequestImageOperation, newValue.operation, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))
objc_setAssociatedObject(self, &AssociatedKeys.URLRequestImage, newValue.request, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
public func setImageWithUrl(url:NSURL, placeHolderImage:UIImage? = nil) {
let request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.addValue("image/*", forHTTPHeaderField: "Accept")
self.setImageWithUrlRequest(request, placeHolderImage: placeHolderImage, success: nil, failure: nil)
}
public func setImageWithUrlRequest(request:NSURLRequest, placeHolderImage:UIImage? = nil,
success:((request:NSURLRequest?, response:NSURLResponse?, image:UIImage, fromCache:Bool) -> Void)?,
failure:((request:NSURLRequest?, response:NSURLResponse?, error:NSError) -> Void)?)
{
self.cancelImageRequestOperation()
if let cachedImage = UIImageView.sharedImageCache().cachedImageForRequest(request) {
if success != nil {
success!(request: nil, response:nil, image: cachedImage, fromCache:true)
}
else {
self.image = cachedImage
}
return
}
if placeHolderImage != nil {
self.image = placeHolderImage
}
self.af_requestImageOperation = (NSBlockOperation(block: { () -> Void in
var response:NSURLResponse?
var error:NSError?
let data: NSData?
do {
data = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response)
} catch let error1 as NSError {
error = error1
data = nil
} catch {
fatalError()
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if request.URL!.isEqual(self.af_requestImageOperation.request?.URL) {
let image:UIImage? = (data != nil ? UIImage(data: data!) : nil)
if image != nil {
if success != nil {
success!(request: request, response: response, image: image!, fromCache:false)
}
else {
self.image = image!
}
UIImageView.sharedImageCache().cacheImage(image!, forRequest: request)
}
else {
if failure != nil {
failure!(request: request, response:response, error: error!)
}
}
self.af_requestImageOperation = (nil, nil)
}
})
}), request)
UIImageView.af_sharedImageRequestOperationQueue().addOperation(self.af_requestImageOperation.operation!)
}
private func cancelImageRequestOperation() {
self.af_requestImageOperation.operation?.cancel()
self.af_requestImageOperation = (nil, nil)
}
}
func AFImageCacheKeyFromURLRequest(request:NSURLRequest) -> String {
return request.URL!.absoluteString
}
class AFImageCache: NSCache, AFImageCacheProtocol {
func cachedImageForRequest(request: NSURLRequest) -> UIImage? {
switch request.cachePolicy {
case NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData,
NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData:
return nil
default:
break
}
return self.objectForKey(AFImageCacheKeyFromURLRequest(request)) as? UIImage
}
func cacheImage(image: UIImage, forRequest request: NSURLRequest) {
self.setObject(image, forKey: AFImageCacheKeyFromURLRequest(request))
}
}
Error image:
Error 1:
objc_setAssociatedObject(self, &AssociatedKeys.SharedImageCache, cache, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN))
Error 2:
objc_setAssociatedObject(self, &AssociatedKeys.RequestImageOperation, newValue.operation, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))
Error 3:
objc_setAssociatedObject(self, &AssociatedKeys.URLRequestImage, newValue.request, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))
Does anyone has any idea how I can fix this?
The reason for this is exactly as the error states; that UInt cannot be created with an argument of type objc_AssociationPolicy.
objc_setAssociatedObject expects the policy parameter to be of type objc_AssociationPolicy, you don't need to convert it to UInt at all.
objc_setAssociatedObject(self, &AssociatedKeys.SharedImageCache, cache, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)

Resources