-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
279 additions
and
95 deletions.
There are no files selected for viewing
18 changes: 11 additions & 7 deletions
18
.../UpdateAvailableKit/Extensions/File.swift → ...dateAvailableKit/Extensions/Bundle+.swift
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
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
16 changes: 10 additions & 6 deletions
16
Sources/UpdateAvailableKit/Models/ITunesLookupResponse.swift
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
16 changes: 10 additions & 6 deletions
16
Sources/UpdateAvailableKit/Models/ITunesLookupResult.swift
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
18 changes: 10 additions & 8 deletions
18
Sources/UpdateAvailableKit/Models/LookupCachableResponse.swift
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
20 changes: 12 additions & 8 deletions
20
Sources/UpdateAvailableKit/Models/UpdateAvailableResult.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,17 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Swapnanil Dhol on 17/04/22. | ||
// | ||
/***************************************************************************** | ||
* UpdateAvailableResult.swift | ||
* UpdateAvailableKit | ||
***************************************************************************** | ||
* Copyright (c) 2022 Swapnanil Dhol. All rights reserved. | ||
* | ||
* Authors: Swapnanil Dhol <swapnanildhol # gmail.com> | ||
* | ||
* Refer to the COPYING file of the official project for license. | ||
*****************************************************************************/ | ||
|
||
import Foundation | ||
|
||
public enum UpdateAvailableResult { | ||
public enum UpdateAvailableResult: Equatable { | ||
case updateAvailable(newVersion: String) | ||
case noUpdatedAvailable | ||
case noUpdatesAvailable | ||
} |
66 changes: 66 additions & 0 deletions
66
Sources/UpdateAvailableKit/UpdateAvailableManager+Async.swift
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/***************************************************************************** | ||
* UpdateAvailableManager+Async.swift | ||
* UpdateAvailableKit | ||
***************************************************************************** | ||
* Copyright (c) 2022 Swapnanil Dhol. All rights reserved. | ||
* | ||
* Authors: Rudrank Riyam <rudrankriyam # gmail.com> | ||
* Swapnanil Dhol <swapnanildhol # gmail.com> | ||
* | ||
* Refer to the COPYING file of the official project for license. | ||
*****************************************************************************/ | ||
|
||
|
||
import Foundation | ||
|
||
// MARK: - Async Alternative | ||
extension UpdateAvailableManager { | ||
|
||
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) | ||
private func queryITunesForNewResponse( | ||
with bundleID: String = Bundle.main.bundleIdentifier | ||
) async throws -> ITunesLookupResponse { | ||
guard let url: URL = .createITunesLookupURL(with: bundleID) else { | ||
throw URLError(.badURL) | ||
} | ||
let (data, _) = try await URLSession.shared.data(from: url) | ||
let response = try JSONDecoder().decode(ITunesLookupResponse.self, from: data) | ||
let cachableReponse: LookupCachableResponse = .init( | ||
expiryDate: Date().addingTimeInterval(3600), | ||
response: response | ||
) | ||
guard let cachedResponseData = try? JSONEncoder().encode(cachableReponse) else { | ||
throw URLError(.backgroundSessionWasDisconnected) | ||
} | ||
UserDefaults.standard.set(cachedResponseData, forKey: self.cacheKey) | ||
return response | ||
} | ||
|
||
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) | ||
public func checkForVersionUpdate( | ||
with bundleID: String = Bundle.main.bundleIdentifier, | ||
currentVersion: String = Bundle.main.releaseVersionNumber, | ||
useCache: Bool = true | ||
) async throws -> UpdateAvailableResult { | ||
if useCache { | ||
if let cachedData = UserDefaults.standard.data(forKey: cacheKey) { | ||
let response = try JSONDecoder().decode(LookupCachableResponse.self, from: cachedData) | ||
if let currentAppStoreVersion = response.response.results?.first?.version, | ||
Date() < response.expiryDate { | ||
return isAppStoreVersionGreaterThanCurrentVersion( | ||
appStoreVersion: currentAppStoreVersion, | ||
currentVersion: currentVersion | ||
) | ||
} | ||
} | ||
} | ||
let response = try await queryITunesForNewResponse(with: bundleID) | ||
if let currentAppStoreVersion = response.results?.first?.version { | ||
return isAppStoreVersionGreaterThanCurrentVersion( | ||
appStoreVersion: currentAppStoreVersion, | ||
currentVersion: currentVersion | ||
) | ||
} | ||
fatalError("Have to Handle this edge case.") | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,101 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Swapnanil Dhol on 17/04/22. | ||
// | ||
/***************************************************************************** | ||
* UpdateAvailableManager.swift | ||
* UpdateAvailableKit | ||
***************************************************************************** | ||
* Copyright (c) 2022 Swapnanil Dhol. All rights reserved. | ||
* | ||
* Authors: Swapnanil Dhol <swapnanildhol # gmail.com> | ||
* Rudrank Riyam <rudrankriyam # gmail.com> | ||
* | ||
* Refer to the COPYING file of the official project for license. | ||
*****************************************************************************/ | ||
|
||
import Foundation | ||
|
||
public final class UpdateAvailableManager { | ||
|
||
public static let shared = UpdateAvailableManager() | ||
private let cacheKey = "UpdateAvailableManager.ITunesCachedData" | ||
let cacheKey = "UpdateAvailableManager.ITunesCachedData" | ||
private init() { } | ||
|
||
// MARK: - Version Update | ||
// MARK: - Completion Handler Based Methods | ||
|
||
private func queryITunesForNewResponse( | ||
with bundleID: String = Bundle.main.bundleIdentifier, | ||
completion: @escaping((Result<ITunesLookupResponse, Error>) -> Void) | ||
) { | ||
guard let url: URL = .createITunesLookupURL(with: bundleID) else { | ||
completion(.failure(URLError(.badURL))) | ||
return | ||
} | ||
URLSession.shared.dataTask(with: url) { data, _, _ in | ||
guard let data = data else { return } | ||
guard let response = try? JSONDecoder().decode(ITunesLookupResponse.self, from: data) else { return } | ||
let cachableReponse: LookupCachableResponse = .init( | ||
expiryDate: Date().addingTimeInterval(3600), | ||
response: response | ||
) | ||
guard let cachedResponseData = try? JSONEncoder().encode(cachableReponse) else { return } | ||
UserDefaults.standard.set(cachedResponseData, forKey: self.cacheKey) | ||
completion(.success(response)) | ||
} | ||
.resume() | ||
} | ||
|
||
public func checkForVersionUpdate( | ||
with bundleID: String = Bundle.main.bundleIdentifier, | ||
currentVersion: String = Bundle.main.releaseVersionNumber, | ||
useCache: Bool = true, | ||
completion: @escaping((Result<UpdateAvailableResult, Error>) -> Void) | ||
) { | ||
guard let url: URL = .createURLForITunesLookup() else { return } | ||
if useCache { | ||
if let cachedData = UserDefaults.standard.data(forKey: cacheKey), | ||
let response = try? JSONDecoder().decode(LookupCachableResponse.self, from: cachedData), | ||
let currentAppStoreVersion = response.response.results?.first?.version, | ||
Date() < response.expiryDate { | ||
if self.isAppStoreVersionGreaterThanCurrentVersion(appStoreVersion: currentAppStoreVersion) { | ||
completion(.success(.updateAvailable(newVersion: currentAppStoreVersion))) | ||
} else { | ||
completion(.success(.noUpdatedAvailable)) | ||
} | ||
let successResult = self.isAppStoreVersionGreaterThanCurrentVersion( | ||
appStoreVersion: currentAppStoreVersion, | ||
currentVersion: currentVersion | ||
) | ||
completion(.success(successResult)) | ||
return | ||
} | ||
} | ||
URLSession.shared.dataTask(with: url) { data, _, _ in | ||
guard let data = data else { return } | ||
guard let response = try? JSONDecoder().decode(ITunesLookupResponse.self, from: data) else { return } | ||
guard let currentAppStoreVersion = response.results?.first?.version else { return } | ||
if self.isAppStoreVersionGreaterThanCurrentVersion(appStoreVersion: currentAppStoreVersion) { | ||
completion(.success(.updateAvailable(newVersion: currentAppStoreVersion))) | ||
} else { | ||
completion(.success(.noUpdatedAvailable)) | ||
queryITunesForNewResponse(with: bundleID) { result in | ||
switch result { | ||
case .success(let response): | ||
if let currentAppStoreVersion = response.results?.first?.version { | ||
let successResult = self.isAppStoreVersionGreaterThanCurrentVersion( | ||
appStoreVersion: currentAppStoreVersion, | ||
currentVersion: currentVersion | ||
) | ||
completion(.success(successResult)) | ||
} | ||
case .failure: | ||
break | ||
} | ||
let cachableReponse: LookupCachableResponse = .init( | ||
expiryDate: Date().addingTimeInterval(3600), | ||
response: response | ||
) | ||
guard let cachedResponseData = try? JSONEncoder().encode(cachableReponse) else { return } | ||
UserDefaults.standard.set(cachedResponseData, forKey: self.cacheKey) | ||
} | ||
.resume() | ||
} | ||
|
||
// MARK: - Utility Methods | ||
|
||
private func isAppStoreVersionGreaterThanCurrentVersion(appStoreVersion: String) -> Bool { | ||
let currentVersion = Bundle.main.releaseVersionNumber | ||
public func isAppStoreVersionGreaterThanCurrentVersion( | ||
appStoreVersion: String, | ||
currentVersion: String | ||
) -> UpdateAvailableResult { | ||
let currentVersionComponents = currentVersion.components(separatedBy: ".") | ||
let appStoreVersionComponents = appStoreVersion.components(separatedBy: ".") | ||
var index = 0 | ||
while index < appStoreVersionComponents.count { | ||
let appStoreComponent = Int(appStoreVersionComponents[index]) ?? 0 | ||
let currentVersionComponent = Int(currentVersionComponents[index]) ?? 0 | ||
if appStoreComponent > currentVersionComponent { | ||
return true | ||
return .updateAvailable(newVersion: appStoreVersion) | ||
} else if appStoreComponent < currentVersionComponent { | ||
return false | ||
return .noUpdatesAvailable | ||
} | ||
index += 1 | ||
} | ||
return false | ||
return .noUpdatesAvailable | ||
} | ||
} |
Oops, something went wrong.