Skip to content

Bump target to iOS7, introduce nullability, always async, and separate storage from queue #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions EDQueue.podspec
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Pod::Spec.new do |s|
s.name = 'EDQueue'
s.version = '0.7.1'
s.version = '0.7.3'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are going to break backwards compatibility (min platform to iOS 7) we should bump the major version number as per semver. That would make this 1.0.0. 😄

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1!

s.license = 'MIT'
s.summary = 'A persistent background job queue for iOS.'
s.homepage = 'https://github.com/thisandagain/queue'
s.authors = {'Andrew Sliwinski' => '[email protected]', 'Francois Lambert' => '[email protected]'}
s.source = { :git => 'https://github.com/thisandagain/queue.git', :tag => 'v0.7.1' }
s.platform = :ios, '5.0'
s.homepage = 'https://github.com/gelosi/queue'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we move the location of the homepage and repo?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've set it to mine now to use it already, but if you will accept the changes in the end (I'm still on it) I'd be happy to keep original link to repo.

s.authors = {'Andrew Sliwinski' => '[email protected]', 'Francois Lambert' => '[email protected]', 'Oleg Shanyuk' => '[email protected]'}
s.source = { :git => 'https://github.com/gelosi/queue.git', :tag => 'v0.7.3' }
s.platform = :ios, '7.0'
s.source_files = 'EDQueue'
s.library = 'sqlite3.0'
s.requires_arc = true
s.dependency 'FMDB', '~> 2.0'
s.dependency 'FMDB', '~> 2.1'
end
39 changes: 28 additions & 11 deletions EDQueue/EDQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
// Copyright (c) 2012 Andrew Sliwinski. All rights reserved.
//

#import <UIKit/UIKit.h>
@import Foundation;

#import "EDQueueJob.h"
#import "EDQueuePersistentStorageProtocol.h"

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSInteger, EDQueueResult) {
EDQueueResultSuccess = 0,
Expand All @@ -22,30 +27,42 @@ extern NSString *const EDQueueJobDidSucceed;
extern NSString *const EDQueueJobDidFail;
extern NSString *const EDQueueDidDrain;

@protocol EDQueueDelegate;
@interface EDQueue : NSObject
@class EDQueue;

+ (EDQueue *)sharedInstance;
@protocol EDQueueDelegate <NSObject>
- (void)queue:(EDQueue *)queue processJob:(EDQueueJob *)job completion:(EDQueueCompletionBlock)block;
@end

@interface EDQueue : NSObject

@property (nonatomic, weak) id<EDQueueDelegate> delegate;
@property (nonatomic, strong, readonly) id<EDQueuePersistentStorage> storage;

/**
* Returns true if Queue is running (e.g. not stopped).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Thanks

*/
@property (nonatomic, readonly) BOOL isRunning;
/**
* Returns true if Queue is performing Job right now
*/
@property (nonatomic, readonly) BOOL isActive;
/**
* Retry limit for failing tasks (will be elimitated and moved to Job later)
*/
@property (nonatomic) NSUInteger retryLimit;

- (void)enqueueWithData:(id)data forTask:(NSString *)task;
- (instancetype)initWithPersistentStore:(id<EDQueuePersistentStorage>)persistentStore;

- (void)enqueueJob:(EDQueueJob *)job;
- (void)start;
- (void)stop;
- (void)empty;

- (BOOL)jobExistsForTask:(NSString *)task;
- (BOOL)jobIsActiveForTask:(NSString *)task;
- (NSDictionary *)nextJobForTask:(NSString *)task;
- (nullable EDQueueJob *)nextJobForTask:(NSString *)task;

@end

@protocol EDQueueDelegate <NSObject>
@optional
- (EDQueueResult)queue:(EDQueue *)queue processJob:(NSDictionary *)job;
- (void)queue:(EDQueue *)queue processJob:(NSDictionary *)job completion:(EDQueueCompletionBlock)block;
@end

NS_ASSUME_NONNULL_END
128 changes: 64 additions & 64 deletions EDQueue/EDQueue.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,55 +15,31 @@
NSString *const EDQueueJobDidFail = @"EDQueueJobDidFail";
NSString *const EDQueueDidDrain = @"EDQueueDidDrain";

@interface EDQueue ()
{
BOOL _isRunning;
BOOL _isActive;
NSUInteger _retryLimit;
}

@property (nonatomic) EDQueueStorageEngine *engine;
@property (nonatomic, readwrite) NSString *activeTask;
static NSString *const EDQueueNameKey = @"name";
static NSString *const EDQueueDataKey = @"data";

@end

//
NS_ASSUME_NONNULL_BEGIN

@implementation EDQueue
@interface EDQueue ()

@synthesize isRunning = _isRunning;
@synthesize isActive = _isActive;
@synthesize retryLimit = _retryLimit;
@property (nonatomic, readwrite, nullable) NSString *activeTask;

#pragma mark - Singleton
@end

+ (EDQueue *)sharedInstance
{
static EDQueue *singleton = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{
singleton = [[self alloc] init];
});
return singleton;
}

#pragma mark - Init
@implementation EDQueue

- (id)init
- (instancetype)initWithPersistentStore:(id<EDQueuePersistentStorage>)persistentStore
{
self = [super init];
if (self) {
_engine = [[EDQueueStorageEngine alloc] init];
_retryLimit = 4;
_storage = persistentStore;
}
return self;
}

- (void)dealloc
{
self.delegate = nil;
_engine = nil;
}

#pragma mark - Public methods

Expand All @@ -75,10 +51,9 @@ - (void)dealloc
*
* @return {void}
*/
- (void)enqueueWithData:(id)data forTask:(NSString *)task
- (void)enqueueJob:(EDQueueJob *)job
{
if (data == nil) data = @{};
[self.engine createJob:data forTask:task];
[self.storage createJob:job];
[self tick];
}

Expand All @@ -91,7 +66,7 @@ - (void)enqueueWithData:(id)data forTask:(NSString *)task
*/
- (BOOL)jobExistsForTask:(NSString *)task
{
BOOL jobExists = [self.engine jobExistsForTask:task];
BOOL jobExists = [self.storage jobExistsForTask:task];
return jobExists;
}

Expand All @@ -115,9 +90,9 @@ - (BOOL)jobIsActiveForTask:(NSString *)task
*
* @return {NSArray}
*/
- (NSDictionary *)nextJobForTask:(NSString *)task
- (nullable EDQueueJob *)nextJobForTask:(NSString *)task
{
NSDictionary *nextJobForTask = [self.engine fetchJobForTask:task];
EDQueueJob *nextJobForTask = [self.storage fetchNextJobForTask:task];
return nextJobForTask;
}

Expand All @@ -131,7 +106,10 @@ - (void)start
if (!self.isRunning) {
_isRunning = YES;
[self tick];
[self performSelectorOnMainThread:@selector(postNotification:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:EDQueueDidStart, @"name", nil, @"data", nil] waitUntilDone:false];

NSDictionary *object = @{ EDQueueNameKey : EDQueueDidStart };

[self postNotificationOnMainThread:object];
}
}

Expand All @@ -145,7 +123,9 @@ - (void)stop
{
if (self.isRunning) {
_isRunning = NO;
[self performSelectorOnMainThread:@selector(postNotification:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:EDQueueDidStop, @"name", nil, @"data", nil] waitUntilDone:false];

NSDictionary *object = @{ EDQueueNameKey : EDQueueDidStop };
[self postNotificationOnMainThread:object];
}
}

Expand All @@ -159,7 +139,7 @@ - (void)stop
*/
- (void)empty
{
[self.engine removeAllJobs];
[self.storage removeAllJobs];
}


Expand All @@ -174,57 +154,71 @@ - (void)tick
{
dispatch_queue_t gcd = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(gcd, ^{
if (self.isRunning && !self.isActive && [self.engine fetchJobCount] > 0) {
if (self.isRunning && !self.isActive && [self.storage jobCount] > 0) {
// Start job
_isActive = YES;
id job = [self.engine fetchJob];
self.activeTask = [(NSDictionary *)job objectForKey:@"task"];
EDQueueJob *job = [self.storage fetchNextJob];
self.activeTask = job.task;

// Pass job to delegate
if ([self.delegate respondsToSelector:@selector(queue:processJob:completion:)]) {
[self.delegate queue:self processJob:job completion:^(EDQueueResult result) {
[self processJob:job withResult:result];
self.activeTask = nil;
}];
} else {
EDQueueResult result = [self.delegate queue:self processJob:job];
[self processJob:job withResult:result];
self.activeTask = nil;
}
}
});
}

- (void)processJob:(NSDictionary*)job withResult:(EDQueueResult)result
- (void)processJob:(EDQueueJob*)job withResult:(EDQueueResult)result
{
// Check result
switch (result) {
case EDQueueResultSuccess:
[self performSelectorOnMainThread:@selector(postNotification:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:EDQueueJobDidSucceed, @"name", job, @"data", nil] waitUntilDone:false];
[self.engine removeJob:[job objectForKey:@"id"]];

[self postNotificationOnMainThread:@{
EDQueueNameKey : EDQueueJobDidSucceed,
EDQueueDataKey : job
}];

[self.storage removeJob:job];
break;

case EDQueueResultFail:
[self performSelectorOnMainThread:@selector(postNotification:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:EDQueueJobDidFail, @"name", job, @"data", nil] waitUntilDone:true];
NSUInteger currentAttempt = [[job objectForKey:@"attempts"] intValue] + 1;

[self postNotificationOnMainThread:@{
EDQueueNameKey : EDQueueJobDidFail,
EDQueueDataKey : job
}];

NSUInteger currentAttempt = job.attempts.integerValue + 1;

if (currentAttempt < self.retryLimit) {
[self.engine incrementAttemptForJob:[job objectForKey:@"id"]];
[self.storage incrementAttemptForJob:job];
} else {
[self.engine removeJob:[job objectForKey:@"id"]];
[self.storage removeJob:job];
}
break;
case EDQueueResultCritical:
[self performSelectorOnMainThread:@selector(postNotification:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:EDQueueJobDidFail, @"name", job, @"data", nil] waitUntilDone:false];

[self postNotificationOnMainThread:@{
EDQueueNameKey : EDQueueJobDidFail,
EDQueueDataKey : job
}];

[self errorWithMessage:@"Critical error. Job canceled."];
[self.engine removeJob:[job objectForKey:@"id"]];
[self.storage removeJob:job];
break;
}

// Clean-up
_isActive = NO;

// Drain
if ([self.engine fetchJobCount] == 0) {
[self performSelectorOnMainThread:@selector(postNotification:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:EDQueueDidDrain, @"name", nil, @"data", nil] waitUntilDone:false];
if ([self.storage jobCount] == 0) {

[self postNotificationOnMainThread:@{
EDQueueNameKey : EDQueueDidDrain,
}];
} else {
[self performSelectorOnMainThread:@selector(tick) withObject:nil waitUntilDone:false];
}
Expand All @@ -239,9 +233,13 @@ - (void)processJob:(NSDictionary*)job withResult:(EDQueueResult)result
*
* @return {void}
*/
- (void)postNotification:(NSDictionary *)object
- (void)postNotificationOnMainThread:(NSDictionary *)object
{
[[NSNotificationCenter defaultCenter] postNotificationName:[object objectForKey:@"name"] object:[object objectForKey:@"data"]];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:[object objectForKey:EDQueueNameKey]
object:[object objectForKey:EDQueueDataKey]];
});

}

/**
Expand All @@ -257,3 +255,5 @@ - (void)errorWithMessage:(NSString *)message
}

@end

NS_ASSUME_NONNULL_END
35 changes: 35 additions & 0 deletions EDQueue/EDQueueJob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// EDQueueJob.h
// queue
//
// Created by Oleg Shanyuk on 18/02/16.
// Copyright © 2016 DIY, Co. All rights reserved.
//

@import Foundation;

NS_ASSUME_NONNULL_BEGIN

@interface EDQueueJob : NSObject

@property(nonatomic, readonly) NSString *task;
@property(nonatomic, readonly) NSDictionary *userInfo;

@property(nonatomic, readonly, nullable) NSNumber *jobID;
@property(nonatomic, readonly, nullable) NSNumber *attempts;
@property(nonatomic, readonly, nullable) NSString *timeStamp;

- (instancetype)initWithTask:(NSString *)task
userInfo:(nullable NSDictionary *)userInfo
jobID:(nullable NSNumber *)jobID
atempts:(nullable NSNumber *)attemps
timeStamp:(nullable NSString *)timeStamp;

- (instancetype)initWithTask:(NSString *)task
userInfo:(nullable NSDictionary *)userInfo;

- (instancetype)init NS_UNAVAILABLE;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to mark +new as unavailable as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌🏻


@end

NS_ASSUME_NONNULL_END

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line missing

Loading