Skip to content
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

Provide quick and easy way to run background repository sync tasks. #61

Open
levibostian opened this issue Jul 24, 2019 · 1 comment
Open
Milestone

Comments

@levibostian
Copy link
Owner

It's a common task for Teller users to run iOS background fetch with Teller repositories so that they can keep repository data always up-to-date.

The current method is hands on and a roadblock for devs. It requires some work:

extension AppDelegate {

    func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        let backgroundFetchResult = Wendy.shared.backgroundFetchRunTasks(application, performFetchWithCompletionHandler: completionHandler)

        // ??? What do we do about other SDKs that run background commands and return a UIBackgroundFetchResult?

        // ??? What should a Teller repository sync return as a UIBackgroundFetchResult? Currently, it's up to the developer to implement this themselves. 
        repositorySyncService.sync { (success) in
            completionHandler(backgroundFetchResult)
        }
    }

}

import RxSwift

protocol RepositorySyncService {
    func sync(onComplete: @escaping (_ success: Bool) -> Void)
}

class TellerRepositorySyncService: RepositorySyncService {

    private let reposRepository: ReposRepository
    private let githubUsernameRepository: GitHubUsernameRepository

    private let disposeBag = DisposeBag()

    init(reposRepository: ReposRepository,
         githubUsernameRepository: GitHubUsernameRepository) {
        self.reposRepository = reposRepository
        self.githubUsernameRepository = githubUsernameRepository
    }

    func sync(onComplete: @escaping (_ success: Bool) -> Void) {
        if let usernameReposToFetch = githubUsernameRepository.dataSource.value {
            reposRepository.requirements = ReposDataSource.GetDataRequirements(githubUsername: usernameReposToFetch)

            try! reposRepository.refresh(force: false)
                .subscribe(onSuccess: { (result) in
                    onComplete(result.didSucceed())
                }).disposed(by: disposeBag)
        }
    }

}

As you can see from the example above, there are some problems currently:

  1. What do developers do when trying to combine 2+ separate UIBackgroundFetchResult?
  2. Teller does not provide a way to create a UIBackgroundFetchResult for you. You must do it yourself. It would be great to do this work for you.
@levibostian
Copy link
Owner Author

Here is an idea (untested) for this:

import Foundation
import RxSwift
import Teller

protocol RepositorySyncService {
    func syncRepositories(onComplete: @escaping ([RefreshResult]) -> Void)
    func sync() -> UIBackgroundFetchResult
}

class TellerRepositorySyncService: RepositorySyncService {
    private let driveRepository: DriveRepository

    private let syncQueue = DispatchQueue(label: "RepositorySyncService.syncQueue")
    private let syncDispatchGroup = DispatchGroup()

    private let disposeBag = DisposeBag()

    init(driveRepository: DriveRepository) {
        self.driveRepository = driveRepository
    }

    func syncRepositories(onComplete: @escaping ([RefreshResult]) -> Void) {
        var fetchResults: [RefreshResult] = []

        driveRepository.requirements = DriveDataSource.GetDataRequirements()

        try! driveRepository.refresh(force: false)
            .subscribe(onSuccess: { result in
                fetchResults.append(result)

                onComplete(fetchResults)
            }).disposed(by: disposeBag)
    }

    func sync() -> UIBackgroundFetchResult {
        return syncQueue.sync {
            syncDispatchGroup.enter()
            var backgroundFetchResult: UIBackgroundFetchResult!

            syncRepositories(onComplete: { refreshResults in
                let numberSuccessfulFetches = refreshResults.filter { $0.didSucceed() }.count
                let numberSkippedTasks = refreshResults.filter { $0.didSkip() }.count
                let numberFailedTasks = refreshResults.filter { $0.didFail() }.count

                if refreshResults.isEmpty || numberSkippedTasks == refreshResults.count {
                    backgroundFetchResult = .noData
                } else if numberSuccessfulFetches >= numberFailedTasks {
                    backgroundFetchResult = .newData
                } else {
                    backgroundFetchResult = .failed
                }

                self.syncDispatchGroup.leave()
            })

            _ = syncDispatchGroup.wait(timeout: .distantFuture)
            return backgroundFetchResult
        }
    }
}

@levibostian levibostian added this to the 0.6.0 milestone Nov 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant