Skip to content

Commit

Permalink
Merge branch 'release/1.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
SwapnanilDhol committed Apr 18, 2022
2 parents c2a8dbd + 96f134f commit 85857c2
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
//
// File.swift
//
//
// Created by Swapnanil Dhol on 17/04/22.
//
/*****************************************************************************
* Bundle+.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

extension Bundle {
public extension Bundle {
var releaseVersionNumber: String {
guard let shortVersionString = infoDictionary?["CFBundleShortVersionString"] as? String else { return " " }
return shortVersionString
Expand Down
22 changes: 14 additions & 8 deletions Sources/UpdateAvailableKit/Extensions/URL+.swift
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
//
// File.swift
//
//
// Created by Swapnanil Dhol on 17/04/22.
//
/*****************************************************************************
* URL+.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

extension URL {

static func createURLForITunesLookup() -> URL? {
static func createITunesLookupURL(
with bundleID: String = Bundle.main.bundleIdentifier
) -> URL? {
var urlComponents = URLComponents()
urlComponents.scheme = "https"
urlComponents.host = "itunes.apple.com"
urlComponents.path = "/lookup"
urlComponents.queryItems = [
.init(name: "bundleId", value: Bundle.main.bundleIdentifier)
.init(name: "bundleId", value: bundleID)
]
return urlComponents.url
}
Expand Down
16 changes: 10 additions & 6 deletions Sources/UpdateAvailableKit/Models/ITunesLookupResponse.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
//
// File.swift
//
//
// Created by Swapnanil Dhol on 17/04/22.
//
/*****************************************************************************
* ITunesLookupResponse.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

Expand Down
16 changes: 10 additions & 6 deletions Sources/UpdateAvailableKit/Models/ITunesLookupResult.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
//
// File.swift
//
//
// Created by Swapnanil Dhol on 17/04/22.
//
/*****************************************************************************
* ITunesLookupResult.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

Expand Down
18 changes: 10 additions & 8 deletions Sources/UpdateAvailableKit/Models/LookupCachableResponse.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//
// File.swift
//
//
// Created by Swapnanil Dhol on 17/04/22.
//

/*****************************************************************************
* LookupCachableResponse.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

// MARK: - LookupCachableResponse
struct LookupCachableResponse: Codable {
let expiryDate: Date
let response: ITunesLookupResponse
Expand Down
20 changes: 12 additions & 8 deletions Sources/UpdateAvailableKit/Models/UpdateAvailableResult.swift
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 Sources/UpdateAvailableKit/UpdateAvailableManager+Async.swift
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.")
}
}
95 changes: 61 additions & 34 deletions Sources/UpdateAvailableKit/UpdateAvailableManager.swift
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
}
}
Loading

0 comments on commit 85857c2

Please sign in to comment.