Coder Social home page Coder Social logo

lucas34 / swiftqueue Goto Github PK

View Code? Open in Web Editor NEW
403.0 8.0 43.0 792 KB

Job Scheduler for IOS with Concurrent run, failure/retry, persistence, repeat, delay and more

License: MIT License

Swift 98.87% Ruby 1.13%
job scheduler swift operation queue persist operationqueue persistence delay job-scheduler

swiftqueue's Introduction

SwiftQueue

Schedule tasks with constraints made easy.

Awesome platform swift Swift codecov pod Carthage compatible Swift Package Manager compatible Documentation

SwiftQueue is a job scheduler for iOS inspired by popular android libraries like android-priority-jobqueue or android-job. It allows you to run your tasks with run and retry constraints.

Library will rely on Operation and OperationQueue to make sure all tasks will run in order. Don't forget to check our WIKI.

Features

  • Sequential or Concurrent execution
  • Persistence
  • Cancel all, by id or by tag
  • Start / Stop queue

Job Constraints:

  • Delay
  • Deadline
  • Timeout
  • Internet
  • Charging
  • Single instance in queue
  • Retry: Max count, exponential backoff
  • Periodic: Max run, interval delay
  • Experimental Foreground or Background execution

Requirements

  • iOS 8.0+, watchOS 2.0+, macOS 10.10+, tvOS 9.0+
  • Xcode 11.0

Installation

SwiftPackageManager (SPM)

To integrate using Apple's Swift package manager, add the following as a dependency to your Package.swift:

.package(url: "https://github.com/lucas34/SwiftQueue.git", .upToNextMajor(from: "4.0.0"))

Carthage

SwiftQueue is carthage compatible. Add the following entry in your Cartfile:

github "lucas34/SwiftQueue"

Then run carthage update.

CocoaPods

You can use CocoaPods to install SwiftQueue by adding it to your Podfile:

platform :ios, '8.0'
use_frameworks!
pod 'SwiftQueue'

In your application, simply import the library

import SwiftQueue

Example

This example will simply wrap an api call. Create your custom job by extending Job with onRun, onRetry and onRemove callbacks.

// A job to send a tweet
class SendTweetJob: Job {
    
    // Type to know which Job to return in job creator
    static let type = "SendTweetJob"
    // Param
    private let tweet: [String: Any]

    required init(params: [String: Any]) {
        // Receive params from JobBuilder.with()
        self.tweet = params
    }

    func onRun(callback: JobResult) {
        let api = Api()
        api.sendTweet(data: tweet).execute(onSuccess: {
            callback.done(.success)
        }, onError: { error in
            callback.done(.fail(error))
        })
    }

    func onRetry(error: Error) -> RetryConstraint {
        // Check if error is non fatal
        return error is ApiError ? RetryConstraint.cancel : RetryConstraint.retry(delay: 0) // immediate retry
    }

    func onRemove(result: JobCompletion) {
        // This job will never run anymore  
        switch result {
            case .success:
                // Job success
            break
            
            case .fail(let error):
                // Job fail
            break
       
        }
    }
}

Create your SwiftQueueManager and keep the reference. If you want to cancel a job it has to be done with the same instance.

let manager = SwiftQueueManagerBuilder(creator: TweetJobCreator()).build()

Schedule your job and specify the constraints.

JobBuilder(type: SendTweetJob.type)
        // Requires internet to run
        .internet(atLeast: .cellular)
        // params of my job
        .with(params: ["content": "Hello world"])
        // Add to queue manager
        .schedule(manager: manager)

Bind your job type with an actual instance.

class TweetJobCreator: JobCreator {

    // Base on type, return the actual job implementation
    func create(type: String, params: [String: Any]?) -> Job {
        // check for job and params type
        if type == SendTweetJob.type  {
            return SendTweetJob(params: params)
        } else {
            // Nothing match
            // You can use `fatalError` or create an empty job to report this issue.
            fatalError("No Job !")
        }
    }
}

3rd Party Extensions

Contributors

We would love you for the contribution to SwiftQueue, check the LICENSE file for more info.

License

Distributed under the MIT license. See LICENSE for more information.

swiftqueue's People

Contributors

ad-sho-loko avatar arthurdapaz avatar bdargan avatar dependabot[bot] avatar foxware00 avatar giannicarlo avatar grighakobian avatar hang321 avatar lucas34 avatar moyoteg avatar tavernari avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftqueue's Issues

Retry

Hi,

I just wanted to ask how to go about executing a job only once.

I tried implementing the onRetry(error: Error) method with a return of RetryConstraint.cancel

Also, I built the job with the retry option set at .limited(0.0) and .limited(-1.0).

By looking at the console when executing queued jobs, it appears that the job's onRun method gets executed twice.

Hope you can help.

Regards,
Dennis

Crashing in JobBuilder create with custom params

Im getting a crash on the assert in this function in the JobBuilder class.

    /// Custom parameters will be forwarded to create method
    public func with(params: [String: Any]) -> Self {
        assert(JSONSerialization.isValidJSONObject(params))
        info.params = params
        return self
    }

Before moving to the new code I was passing things like delegates and callbacks to my job but since those are not valid JSON but conform to Any it crashes on this assertion.

I'm not sure if this is intended or not but it does mention "Validate JSON argument" int the 1.4.0 change log so maybe. If it was intended, is there a better way to get callback blocks and updates from a job?

*Breaking Changes* Improve completion callback

In order to make the completion callback more extensible, I would like to implement as follow

// Sucess
onDone(result: JobCompleted.success)
// fail
onDone(result: JobCompleted.withError(error))

SwiftQueue is requires minimum iOS 10.3??

When i try to import SwiftQueue using Carthage its asking minimum deployment target should be iOS 10.3. My target was iOS 9.0.

Can some one give some idea what changes i need to do, to support minimum iOS 9.0.

Cancel all jobs under a specific queue while multiple queues are in progress

Hello Lucas,

I would request your support for the below scenario.

I have set up the download queue as below

Initiated one queue with queueName and tag (q1) which in turn create multiple jobs with the job name and tag name. Tag name(tag_q1) is same for all the jobs under the first queue (q1)

Initiated 2nd queue with queueName and tag (q2) which in turn create multiple jobs with the job name and tag name. Tag name(tag_q2) is same for all the jobs under the 2nd queue (q2)

Both queues are running in parallel, now I have to delete q1 and its jobs with tag name tag_q1.

I tried to cancel the jobs using the "cancelOperations(tag:"tag_q1"), but its not cancelling the job even one single job.

Kindly share your comments on how to this situation with SwiftQueue.

Job Completion

How can I subscribe to a Job's callback?

i.e. I execute my Job in a View Model and I want to know the result of the job inside the View Model

Change error to enum

We will refractor error to follow enum Pattern

ContraintError will be removed
All errors will be thrown under SwiftQueueError

  • TaskAlreadyExist -> SwiftQueueError.Duplicate
  • DealineError -> SwiftQueueError.Deadline
  • Canceled -> SwiftQueueError.Canceled

Only one job are called

Hello, i'm using SwiftQueue to createa upload/download queue in my app.
When i try to run the queue return allways the same image for all objects in que

Can you help me?
I have arround 500 images to upload but queue only run the same job for te 500 images.
What i'm doing wrong, im using the last version of pod.
`import Foundation
import SwiftQueue
import Crashlytics
import Alamofire

class MIImagesInQueueDownload: NSObject, NSCoding {
var url: String?

init(url: String?) {
    self.url = url
}

func encode(with aCoder: NSCoder) {
    aCoder.encode(url, forKey: "url")
}

required init(coder aDecoder: NSCoder) {
    if let url = aDecoder.decodeObject(forKey: "url") as? String {
        self.url = url
    }
}

static func ==(lhs: MIImagesInQueueDownload, rhs: MIImagesInQueueDownload) -> Bool {
    return lhs.url ?? "" == rhs.url ?? ""
}

}

class MIImagesInQueueUpload: NSObject, NSCoding {
var filePath: String = ""
var fileName: String = ""
var assignmentUUID: String = ""
var url:String = ""

init(filePath: String, fileName: String, assignmentUUID: String, url:String) {
    self.fileName = fileName
    self.filePath = filePath
    self.assignmentUUID = assignmentUUID
    self.url = url
}

func encode(with aCoder: NSCoder) {
    aCoder.encode(filePath, forKey: "filePath")
    aCoder.encode(fileName, forKey: "fileName")
    aCoder.encode(assignmentUUID, forKey: "assignmentUUID")
    aCoder.encode(url, forKey: "url")
}

required init(coder aDecoder: NSCoder) {
    if let filename = aDecoder.decodeObject(forKey: "fileName") as? String {
        self.fileName = filename
    }
    
    if let filePath = aDecoder.decodeObject(forKey: "filePath") as? String {
        self.filePath = filePath
    }
    
    if let uuid = aDecoder.decodeObject(forKey: "assignmentUUID") as? String {
        self.assignmentUUID = uuid
    }
    
    if let url = aDecoder.decodeObject(forKey: "url") as? String {
        self.url = url
    }
}

static func ==(lhs: MIImagesInQueueUpload, rhs: MIImagesInQueueUpload) -> Bool {
    return lhs.fileName == rhs.fileName && lhs.assignmentUUID == rhs.assignmentUUID && lhs.filePath == rhs.filePath && lhs.url == rhs.url
}

}

public struct ImageError: Error {
let msg: String
}

public struct UploadError: Error {
let msg: String
}

extension ImageError: LocalizedError {
public var errorDescription: String? {
return NSLocalizedString(msg, comment: "")
}
}

extension UploadError: LocalizedError {
public var errorDescription: String? {
return NSLocalizedString(msg, comment: "")
}
}

class downloadOperation: Job {
let image: MIImagesInQueueDownload
static let type = "downloadImage"

required init(image: MIImagesInQueueDownload) {
    self.image = image
}

func onRun(callback: JobResult) {
    self.downloadImage(with: self.image) { (error) in
        if let error = error {
            callback.done(.fail(error))
        } else {
            callback.done(.success)
        }
    }
}

func onRetry(error: Error) -> RetryConstraint {
    if error is UploadError {
        return RetryConstraint.retry(delay: 3600)
    } else {
        return RetryConstraint.cancel
    }
}

func onRemove(result: JobCompletion) {
    // This job will never run anymore
    switch result {
    case .success:
        // Job success
        break
        
    case .fail(let error):
        Logger.shared.log(message: error.localizedDescription, vc: "ImageDownload JOB")
        break
        
    }
}

private func downloadImage(with image:MIImagesInQueueDownload, onComplete:@escaping onCompleteupload) {
    guard let urlImage = image.url?.encodeURL() else { return }
    MIImageQueue.shared.imageDownloader.downloadImage(with: urlImage, options: [], progressBlock: nil) { (image, error, url, data) in
        Logger.shared.log(message: error?.localizedDescription ?? "", vc: "imageDowload")
        guard let image = image else {
            onComplete(ImageError(msg: "missing file at server"))
            return
        }
        guard let url = url else {
            onComplete(ImageError(msg: "missing file at server"))
            return
        }
        MIHelper.shared.storeImage(image: image, fileURL: url.absoluteString, fileName: url.lastPathComponent, execute: { (imageDevice) in
            onComplete(nil)
        })
    }
}

}

class uploadOperation: Job {
let image: MIImagesInQueueUpload
lazy var uploadQueue: DispatchQueue = {
var queue = DispatchQueue(label: "com.uscope.photoId.ImagesUpload",
qos: DispatchQoS.background,
attributes: .concurrent,
autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit,
target: DispatchQueue.global(qos: .background))
return queue
}()

static let type = "uploadImage"

required init(image: MIImagesInQueueUpload) {
    self.image = image
    print(image.filePath)
}

func onRun(callback: JobResult) {
    print(image.fileName)
    self.uploadImage(with: self.image) { (error) in
        if let error = error {
            callback.done(.fail(error))
        } else {
            callback.done(.success)
        }
    }
}

func onRetry(error: Error) -> RetryConstraint {
    if error is UploadError || error is ImageError {
        return RetryConstraint.retry(delay: 3600)
    } else {
        return RetryConstraint.cancel
    }
}

func onRemove(result: JobCompletion) {
    // This job will never run anymore
    switch result {
    case .success:
        // Job success
        break
        
    case .fail(let error):
        Logger.shared.log(message: error.localizedDescription, vc: "ImageUpload JOB")
        break
        
    }
}

fileprivate func uploadImage(with imageQueue:MIImagesInQueueUpload, onComplete:@escaping onCompleteupload){
    let object = imageQueue
    if let filePathURL = URL(string: imageQueue.filePath), let profileIdToSet = imageQueue.url.getProfileID() {
        let filePath = filePathURL.imagesFolderPath()
        if filePath.extists() {
            let parameters:Parameters = ["filename": object.fileName, "sourceFileUri": filePath.absoluteString, "assignmentId": object.assignmentUUID, "profileId": profileIdToSet]
            let parametersLog:[String:String] = ["filename": object.fileName, "assignmentId": object.assignmentUUID, "profileId": profileIdToSet]
            print(parameters)
            Answers.logCustomEvent(withName: "image upload", customAttributes: parametersLog)
            MIConnnection.shared.alamofireManager.upload(multipartFormData: { (multipartFormData) in
                multipartFormData.append(filePath, withName: "bill", fileName: object.fileName.isEmpty ? filePath.getImageName() : object.fileName, mimeType: "image/jpeg")
            }, with: APINetworkRouter.upload(parameters: parameters), encodingCompletion: { [weak self] (result) in
                guard let strongSelf = self else { return }
                switch result {
                case .success(let upload, _, _):
                    upload.uploadProgress(closure: { (progress) in
                        Logger.shared.log(message: "Upload progress \(progress.fractionCompleted)", vc: String(describing: self))
                    })
                    upload.customValidation().responseJSON(queue: strongSelf.uploadQueue, completionHandler: { (response) in
                        switch response.result {
                        case .success(let responseJSON):
                            guard let responseObject = responseJSON as? DefaultDict else { return }
                            guard let success = responseObject["success"]?.boolValue else { return }
                            guard let message = responseObject["message"] as? String else { return }
                            if success {
                                Answers.logCustomEvent(withName: "image upload success", customAttributes: ["success" : success, "message": message])
                                let objectNotification = ["url": imageQueue.url.getImageNameWhitoutURL(), "assignmnentId" : object.assignmentUUID]
                                NotificationCenter.default.post(name: Notifications.didChangeUpload, object: nil, userInfo:objectNotification)
                                DispatchQueue.main.async {
                                    onComplete(nil)
                                }
                            } else {
                                DispatchQueue.main.async {
                                    onComplete(UploadError(msg: message))
                                }
                                Answers.logCustomEvent(withName: "image upload fail", customAttributes: ["success" : success, "message": message])
                                return
                            }
                        case .failure(let error):
                            if isDebug {
                                error.manageAlamofireError()
                            }
                            DispatchQueue.main.async {
                                onComplete(UploadError(msg: error.localizedDescription))
                            }
                            return
                        }
                    })
                case .failure(let error):
                    if isDebug {
                        error.manageAlamofireError()
                    }
                    onComplete(error)
                    return
                }
            })
        } else {
            DispatchQueue.main.async {
                onComplete(ImageError(msg: "missing file at disk"))
            }
            Answers.logCustomEvent(withName: "image upload fail", customAttributes: ["error" : "missing file at disk"])
        }
    }
}

}

class UploaderDownloaderManager {
static let shared:UploaderDownloaderManager = UploaderDownloaderManager()
let queueManager = SwiftQueueManagerBuilder(creator: mainJobCreator()).set(logger: ConsoleLogger()).build()

func uploadImage(with imageQueue:MIImagesInQueueUpload) {
    if let user = MIFirebaseDatabaseManager.shared.userApp {
        JobBuilder(type: uploadOperation.type)
            // Prevent adding the same job multiple times
            .singleInstance(forId: imageQueue.url)
            // Different group name will run in parallel
            // But one by one for a group
            .group(name: "uploadPicture")
            // Sending tweet will require internet. At least cellular.
            // Will also be executed if connected to wifi.
            .internet(atLeast: user.cellarSync ? .any : .wifi)
            // Content of your tweet. Can be a class, struct or anything
            .with(params: ["fileURI" : imageQueue.url, "filePath" : imageQueue.filePath, "fileName" : imageQueue.fileName, "assignmentId" : imageQueue.assignmentUUID, "profileId": user.userID])
            // unlimited retry by default
            .retry(limit: .limited(10))
            // set persist by default
            .persist(required: true)
            // set periodic by default
            //                .periodic()
            // Cancel the job if it's not completed after one week
            .deadline(date: Date(timeIntervalSinceNow: 604_800))
            // Insert to queue
            .schedule(manager: self.queueManager)
    }
}

func downloadImage(with imageQueue:MIImagesInQueueDownload) {
    if let user = MIFirebaseDatabaseManager.shared.userApp {
        JobBuilder(type: downloadOperation.type)
            // Prevent adding the same job multiple times
            .singleInstance(forId: imageQueue.url ?? "")
            // Different group name will run in parallel
            // But one by one for a group
            .group(name: "downloadPicture")
            // Sending tweet will require internet. At least cellular.
            // Will also be executed if connected to wifi.
            .internet(atLeast: user.cellarSync ? .any : .wifi)
            // Content of your tweet. Can be a class, struct or anything
            .with(params: ["fileURI" : imageQueue.url ?? ""])
            // unlimited retry by default
            .retry(limit: .limited(5))
            // set persist by default
            .persist(required: true)
            // set periodic by default
            .periodic()
            // Cancel the job if it's not completed after 24 hours
            .deadline(date: Date(timeIntervalSinceNow: 86_400))
            // Insert to queue
            .schedule(manager: self.queueManager)
    }
}

func downloadFromURL(with url:URL) {
    if let user = MIFirebaseDatabaseManager.shared.userApp {
        JobBuilder(type: downloadOperation.type)
            // Prevent adding the same job multiple times
            .singleInstance(forId: url.absoluteString)
            // Different group name will run in parallel
            // But one by one for a group
            .group(name: "downloadPicture")
            // Sending tweet will require internet. At least cellular.
            // Will also be executed if connected to wifi.
            .internet(atLeast: user.cellarSync ? .any : .wifi)
            // Content of your tweet. Can be a class, struct or anything
            .with(params: ["fileURI" : url.absoluteString])
            // unlimited retry by default
            .retry(limit: .limited(5))
            // set persist by default
            .persist(required: true)
            // set periodic by default
            .periodic()
            // Cancel the job if it's not completed after 24 hours
            .deadline(date: Date(timeIntervalSinceNow: 86_400))
            // Insert to queue
            .schedule(manager: self.queueManager)
    }
}

}

class mainJobCreator: JobCreator {
func create(type: String, params: [String : Any]?) -> Job {
// check for job and params type
if type == downloadOperation.type {
return downloadOperation(image: MIImagesInQueueDownload(url: params?["fileURI"] as? String ?? ""))
} else if type == uploadOperation.type {
return uploadOperation(image: MIImagesInQueueUpload(filePath: params?["filePath"] as? String ?? "", fileName: params?["fileName"] as? String ?? "", assignmentUUID: params?["assignmentId"] as? String ?? "", url: params?["fileURI"] as? String ?? ""))
} else {
fatalError("No Job !")
}
}
}

`

Run timeout

Specify a timeout for each run. This will prevent the job to get stuck.

Facing issue in syncronisation

Hi,
I am using JobCreator with custom params as below
let swiftQueueManager = SwiftQueueManagerBuilder(creator: JobCreator(otherParams : otherParams, completion:completionForJob)).build()

First thing is I run this line of code every time i.e. this instance is created every time as the params values is different always and there is no way to set JobCreator in ManagerBuilder other than initialisation.
After 3-4 such runs, the jobs get executed but not in synchronised manner.

Do I need to keep the instance of manager on class level? If yes, then how can I set job creator in the manager as it has different params every time.

And how is job removed after successful run? Do I manually need to call remove function after execution in onRun() method?

Please respond.
Thanks

Carthage

Make it compatible with Carthage.

Cancel all operations with SwiftQueueManager reference

I wanted to cancel all the operations which is scheduled or in execution without the SwiftQueueManager reference.

is there any way?

Scenario: Image is being upload mean while i am logging out from my app, so i wanna cancel all the uploading operations.

how to queue when internet not reacheable and how to dequeue when internet aceess

hi thank for this library
this is my class. i am using Alamofire for http request.

`class SendJobLocation: Job {
    // Type to know which Job to return in job creator
    static let type = "SendJobLocation"
    // Param
    private let loc: [String: Any]
    required init(Location: [String: Any]) {
        // Receive params from JobBuilder.with()
        self.loc = Location
    }
    func onRun(callback: JobResult) {
        let url = myurl
        let token = UserDefaults.standard.string(forKey: "token") as! String//?.data(using: .utf8)?.base64EncodedString()
        let auth = "PT-\(UserDefaults.standard.string(forKey: "myuuid")!):\(token)".data(using: .utf8)?.base64EncodedString()
        let headerInsert : HTTPHeaders = ["Authorization" : "Basic \(auth!)"]
        print("Parameter is : \(loc) \n and header is : \(headerInsert)")
        Alamofire.request(url, method: .post, parameters: loc, encoding: JSONEncoding.default, headers: headerInsert).validate(statusCode: 200..<300).responseJSON { response in
            let json = JSON(response.data!)
            callback.done(JobCompletion.success)
            print( "JSON AFTER SEND  TO SERVER IS : \(json)")
        }
    }
    
    func onRetry(error: Error) -> RetryConstraint {
       
        return RetryConstraint.retry(delay: 0)
    }
    func onRemove(result: JobCompletion) {
        switch result {
        case .success:
            print("job success")
            break
        case .fail(let erorr):
            print(erorr)
            break
        }
    }
}
class LocationCreator: JobCreator {
    func create(type: String, params: [String : Any]?) -> Job {
        if type == SendJobLocation.type{
            print("location creat")
            return SendJobLocation(Location: params!)
        }else{
            
            fatalError("No Job !")
        }
    }
}`

and in ViewController use this line of code

`if internetReachability.isReachable {
            print("Internet Ok")
            JobBuilder(type: SendJobLocation.type).internet(atLeast: .any).with(params: self.locationParameter).schedule(manager: managerQueue)
           // self.SendLocation(parameter: self.locationParameter)
        }else{
            print("Internet Failed")
            JobBuilder(type: SendJobLocation.type).internet(atLeast: .any).retry(limit: .limited(3)).with(params: self.locationParameter).schedule(manager: managerQueue)
        }

when internet is running erveything is ok but when internet not reachable message not in queue and when internet comes back not dequeue ! how can this ?
thank for all

Start / Stop queue

Create a function to start or pause execution. Will makes testing easier.

Does it run even when the app is not running?

I want to know if it will still run the scheduled job even when the app is terminated or not even running?
Also tell me if it will still able to run the scheduled job even after device restart?

Missing CFBundleVersion?

I'm attempting to build a production version of an app which has SwiftQueue as a dependency. When I do so and attempt to validate it with iTunes Connect I receive an error that...

SwiftQueue framework is invalid. The Info.plist file is missing the required key: CFBundleVersion

In the SwiftQueue.xcodeproj/SwiftQueue_Info.plist file CFBundleVersion is mapped to a build variable called CURRENT_PROJECT_VERSION. In the build settings it looks like CURRENT_PROJECT_VERSION isn't actually set to anything which I assume isn't deliberate.

Am I misunderstanding or does that need to be added to the source?

Question: How do we get a Job counts that keep and finished currently in SqOperationQueue?

I'm thinking about multiple Images uploading service with SwiftQueue.
For example, If I take three photos, three jobs would insert into queue like 0/3 .
As soon as I insert into queue, the first job will execute like 1/3 .

Then, in order to show the progress of jobs to User, I'd like to know jobs that keep and finished currently in SqOperationQueue.
Screen Shot 2019-05-06 at 4 19 31 PM

If I get a job counts that keep and finished currently in SqOperationQueue, I can show the progress of jobs to User.
Please tell meπŸ™

Thank you ahead of time.

Run daily at 4 am

I want to run the job every day between 4 and 5 am. What should I do?

SwiftQueueManager callback completion

Add a callback completion in SwiftQueue manager.

Sometimes we need to know exactly when the job is done.
For example when using background fetch in iOS. The job will still go trough the Queue.

Logo Design for SwiftQueue

Hi @lucas34, my name is Zuur W. I'm a graphic designer and I'd like to collaborate on your open source project and propose free logo/icon design for your project. If it's something you're interested in, please, let me know!
Best Regards
Zuur

Suspending a job that has a delay

I stumbled upon a problem I didn't think about before. All my jobs are being run after a delay. I thought I'd be able to suspend them whenever I need to, but it looks like the current implementation is actually creating the Operation and in its run method it's just waiting the time set by the delay. That way, even if the OperationQueue is being suspended, all of the tasks that I queued are already being run and can't be suspended.

Have you got any idea how I could change this to make operations with delays suspendable?
Cheers!

Question: Allowing at most 2 jobs of the same type

I found func singleInstance(forId: String, override: Bool = false) -> Self to allow only 1 job of a type but my use case is a bit different.

I do a sync with the backend and during the app lifetime there are requests for that sync coming in. The idea is that when a sync is already running, it should be not canceled and another sync should be scheduled. But when one sync is already running and another one is already scheduled, there is no point in scheduling a third sync.

Basically I need the queue to always have at most 1 running and 1 scheduled operation. Is this possible?

Async loading

Loading serialised task may be a time consuming operation. Should found a way to allow this to run asynchronously.
But newly added task should still be scheduled AFTER.

Question regarding failed jobs

First, thank you for the wonderful library!

Let's say I have a job group like this:

JobBuilder(type: CatJob.type)
            .with(params: [CatJob.PARAM_CAT_ID: catId])
            .singleInstance(forId: catId)
            // Requires internet to run
            .internet(atLeast: .cellular)
            //  will run in parallel to other group names, in sequence with other device info
            .group(name: "cats")
            .retry(limit: .limited(10))
            // Add to queue manager
            .schedule(manager: queueManager)

and inside the job, we have something like this:

func onRetry(error: Error) -> RetryConstraint {
        if let err = error as? SyncError {
            switch (err) {
            case .apiError(let httpStatus):
                if httpStatus == 400 {
                    return RetryConstraint.cancel
                }
            case .localError:
                // do nothing, fall through to below
                break
            }
        }
        return RetryConstraint.exponential(initial: 10) 
    }

And now I add 3 cat jobs. The first one succeeds, but the second one fails.

Will the library continue to retry the second cat job before proceeding to cat job #3? From what I can tell, this seems to be the case. Is it possible to configure the library to continue to process all of the cat jobs and simply push the failing ones to the bottom of the queue?

Thanks!

Swift package manager

Update project structure to follow swift package manager rule.
Release lib on this platform

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.