Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
abc58dd
[Add] #311 App store에 등록된 app id 추가
isakatty May 26, 2024
976c89e
[Add] #311 앱스토어에 등록된 앱의 최신 버전을 가져오는 함수
isakatty May 26, 2024
a215197
[Add] #311 앱스토어에 연결하는 합수
isakatty May 26, 2024
f5dd3c8
[Feat] #311 앱스토어 버전과 프로젝트 버전 비교해서 업데이트 alert 주는 함수
isakatty May 26, 2024
5cefc75
[Chore] #311 completion block -> async await으로 수정 및 관련 코드 수정
isakatty May 26, 2024
b8e4353
[Remove] #311 legacy 주석 삭제
isakatty May 26, 2024
f1f9aad
[Chore] #311 변수명 변경 및 린트 오류 수정, 사용하지 않는 변수 삭제
isakatty May 28, 2024
edffe09
[Chore] Single 형태로 변경
isakatty Jun 24, 2024
f0ba96c
[Feat] AppStore에 올라간 정보를 받는 Endpoint 정의
isakatty Jul 31, 2024
3637db5
[Add] #311 AppInfoResponse, DTO 추가
isakatty Aug 1, 2024
b407074
[Add] #311 AppStoreRepository 생성
isakatty Aug 1, 2024
c09701d
[Remove] #311 레거시 코드 삭제
isakatty Mar 25, 2025
86d9247
[Refactor] #311 Endpoint Scheme 형태 변경 및 앱스토어 링크를 Endpoint 추가
isakatty Mar 25, 2025
01501d0
[Add] #311 네트워크 통신 Single 반환 형태 추가
isakatty Mar 25, 2025
ac159a2
[Add] #311 itunes search api DTO 및 Response 모델 추가
isakatty Mar 25, 2025
a06125a
[Refactor] #311 사용자 앱 버전 반환 형태 변경
isakatty Mar 25, 2025
9df3a73
[Fix] #311 Lint 형태 맞춰 수정
isakatty Mar 25, 2025
63fd2db
[Add] #311 버전 확인을 위한 Repository 추가
isakatty Mar 25, 2025
bed33be
[Add] #311 Version 확인을 위한 UseCase 추가
isakatty Mar 25, 2025
a5df382
[Add] #311 DIContainer에 UseCase 의존성 추가
isakatty Mar 25, 2025
fdc2b47
[Refcator] #311 구조에 맞춰 버전 확인 적용
isakatty Mar 25, 2025
19e2000
Merge branch 'dev' of https://github.com/Pepsi-Club/WhereMyBus-iOS in…
isakatty Mar 25, 2025
7042776
[Refactor] #311 dev에 맞춰 Network 수정
isakatty Mar 25, 2025
e5aa978
[Refactor] #311 오탈자 수정
isakatty Mar 25, 2025
4f28787
[Add] #311 최소버전에 대한 Endpoint 및 DTO 모델
isakatty Mar 26, 2025
3996360
[Refactor] #311 변경된 Endpoint에 맞춰 수정
isakatty Mar 26, 2025
2e11d37
[Add] #311 plist 수정
isakatty Mar 26, 2025
65358b3
[Refactor] #311 버전관리 의존성 주입 주석처리
isakatty Mar 27, 2025
40f3e02
[Refactor] #311 모델 리턴 타입 변경, Comparable 채택
isakatty Mar 27, 2025
28ac0e5
[Refactor] #311 버전 관련 데이터 소스들을 관리하는 Repository 수정
isakatty Mar 27, 2025
ac363dd
[Add] #311 버전을 비교하는 Service 객체 생성
isakatty Mar 27, 2025
9e2a204
[Refactor] #311 버전을 비교하는 로직 수정
isakatty Mar 27, 2025
9cf728c
[Refactor] #311 최소 요구 버전 객체 네이밍 변경
isakatty Mar 27, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ public extension [String: Plist.Value] {
"NMFClientId": "$(NAVERMAP_CLIENT_ID)",
"TERMS_OF_PRIVACY_URL": "$(TERMS_OF_PRIVACY_URL)",
"LOCATION_PRIVACY_URL": "$(LOCATION_PRIVACY_URL)",
"INQURY_URL": "$(INQURY_URL)"
"INQUIRY_URL": "$(INQUIRY_URL)",
"APPSTORE_ID": "$(APPSTORE_ID)",
"DOMAIN_URL": "$(DOMAIN_URL)",
]

static let additionalInfoPlist: Self = [
Expand Down
10 changes: 10 additions & 0 deletions Projects/App/Sources/AppDelegate+Register.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ extension AppDelegate {
= DefaultLocalNotificationService()
let regularAlarmEditingService: RegularAlarmEditingService
= DefaultRegularAlarmEditingService()
// TODO: 추후 의존 주입 형태 변경
// let versionCheckRepository: VersionCheckRepository
// = DefaultVersionCheckRepository(networkService: networkService)

DIContainer.register(
type: FavoritesUseCase.self,
Expand Down Expand Up @@ -92,5 +95,12 @@ extension AppDelegate {
type: RegularAlarmEditingService.self,
regularAlarmEditingService
)

// DIContainer.register(
// type: VersionCheckUseCase.self,
// DefaultVersionCheckUseCase(
// versionCheckRepository: versionCheckRepository
// )
// )
}
}
59 changes: 58 additions & 1 deletion Projects/App/Sources/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import UIKit

import Core
import NetworkService
import Domain
import Data
Expand All @@ -18,9 +19,20 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var appCoordinator: AppCoordinator?
var deeplinkHandler: DeeplinkHandler?

let disposeBag = DisposeBag()

// MARK: 추후 구체타입이 아닌 형태로 변경
private var useCase: VersionCheckUseCase
= DefaultVersionCheckUseCase(
versionCheckRepository: DefaultVersionCheckRepository(
networkService: DefaultNetworkService()
),
forceUpdateService: DefaultForceUpdateService()
)

func scene(
_ scene: UIScene,
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
Expand All @@ -37,6 +49,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
)
appCoordinator?.start()
window?.makeKeyAndVisible()
// 앱 진입할 때 확인
deeplinkHandler = .init(appCoordinator: appCoordinator)
if let url = connectionOptions.urlContexts.first?.url {
deeplinkHandler?.handleUrl(url: url)
Expand All @@ -52,7 +65,9 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func sceneWillResignActive(_ scene: UIScene) {
}

/// 앱이 Foreground로 전환될때 실행될 함수
func sceneWillEnterForeground(_ scene: UIScene) {
checkAndUpdateIfNeeded()
}

func sceneDidEnterBackground(_ scene: UIScene) {
Expand All @@ -66,5 +81,47 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
deeplinkHandler?.handleUrl(url: url)
}
}

private func checkAndUpdateIfNeeded() {
useCase.fetchAppStoreURL()
.subscribe(with: self) { owner, str in
guard let str else { return }
owner.showUpdateAlert(with: str)
} onFailure: { _, error in
print(error)
}
.disposed(by: disposeBag)
}

private func showUpdateAlert(with urlString: String) {
let alert = UIAlertController(
title: "업데이트 알림",
message: "더 나은 서비스를 위해 업데이트 되었어요 ! 업데이트 해주세요.",
preferredStyle: .alert
)

let alertAction = UIAlertAction(
title: "업데이트",
style: .default
) { [weak self] _ in
guard let self else { return }

openAppStore(urlString)
}

alert.addAction(alertAction)

Task { @MainActor in
window?.rootViewController?.present(alert, animated: true)
}
}

private func openAppStore(_ str: String) {
guard let url = URL(string: str) else { return }

if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
}

2 changes: 1 addition & 1 deletion Projects/App/Widget/ArrivalInfo/View/ArrivalInfoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SwiftUI
import WidgetKit
import DesignSystem

@available (iOS 17.0, *)
@available(iOS 17.0, *)
struct ArrivalInfoView: View {
var entry: ArrivalInfoProvider.Entry
@Environment(\.widgetFamily) var widgetFamily
Expand Down
8 changes: 5 additions & 3 deletions Projects/Core/Sources/Extension/String+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ public extension String {
return serverKey
}

static func getCurrentVersion() -> String {
/// 프로젝트 버전
static func getCurrentVersion() -> [Int] {
guard let dictionary = Bundle.main.infoDictionary,
let version = dictionary["CFBundleShortVersionString"] as? String
else { return "" }
return version
else { return [1, 0, 0] }

return version.split(separator: ".").compactMap { Int($0) }
}

static func getDeviceIdentifier() -> String {
Expand Down
43 changes: 43 additions & 0 deletions Projects/Data/Sources/DTO/AppInfoDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// AppInfoDTO.swift
// Data
//
// Created by Jisoo HAM on 8/1/24.
// Copyright © 2024 Pepsi-Club. All rights reserved.
//

import Foundation

import Domain

public struct AppInfoDTO: Decodable {
let resultCount: Int
let results: [AppDetailDTO]
}

extension AppInfoDTO {
var toDomain: AppVersionInfoResponse? {
guard let versionString = results.map({ $0.version }).first
else { return nil }

let versionComponents = versionString.split(separator: ".")
.compactMap { Int($0) }

guard versionComponents.count == 3
else { return nil }

return AppVersionInfoResponse(
major: versionComponents[0],
minor: versionComponents[1],
patch: versionComponents[2]
)
}
}

extension AppInfoDTO {
struct AppDetailDTO: Decodable {
let trackId: Int
/// App Version
let version: String
}
}
35 changes: 35 additions & 0 deletions Projects/Data/Sources/DTO/RequiredVersionDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// RequiredVersionDTO.swift
// Data
//
// Created by Jisoo Ham on 3/26/25.
// Copyright © 2025 Pepsi-Club. All rights reserved.
//

import Foundation

import Domain

public struct RequiredVersionDTO: Decodable {
let version: String

enum CodingKeys: String, CodingKey {
case version = "ver"
}
}

extension RequiredVersionDTO {
var toDomain: AppVersionInfoResponse {
let versionComponents = version.split(separator: ".")
.compactMap { Int($0) }

guard versionComponents.count == 3
else { return AppVersionInfoResponse(major: 1, minor: 2, patch: 5) }

return AppVersionInfoResponse(
major: versionComponents[0],
minor: versionComponents[1],
patch: versionComponents[2]
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//
// DefaultVersionCheckRepository.swift
// Data
//
// Created by Jisoo HAM on 8/1/24.
// Copyright © 2024 Pepsi-Club. All rights reserved.
//

import UIKit

import Core
import Domain
import NetworkService

import RxSwift

public final class DefaultVersionCheckRepository: VersionCheckRepository {
private let networkService: NetworkService

@UserDefaultsWrapper(
key: "ForceUpdate",
defaultValue: ForceUpdate(
version: AppVersionInfoResponse(major: 1, minor: 2, patch: 4),
date: Date(timeIntervalSince1970: 0)
)
)
private var forceUpdateInfo: ForceUpdate

public init(networkService: NetworkService) {
self.networkService = networkService
}

/// 서버로 부터 받은 App의 최소 지원 버전
public func fetchRequiredVersion()
-> Single<Result<AppVersionInfoResponse, Error>> {
return networkService.request(
endPoint: MinVersionEndpoint(domain: getDomainURL()),
responseType: RequiredVersionDTO.self
)
.map { result in
switch result {
case .success(let value):
return .success(value.toDomain)
case .failure(let error):
return .failure(error)
}
}
}

public func getStoreLink() -> String? {
return OpenStoreEndpoint(appStoreID: getAppStoreID()).toURLString
}

public func getAppStoreID() -> String {
guard let appId = Bundle.main.object(
forInfoDictionaryKey: "APPSTORE_ID"
) as? String
else { return "" }

return appId
}

public func getUserAppVersion() -> AppVersionInfoResponse {
guard let dictionary = Bundle.main.infoDictionary,
let version = dictionary["CFBundleShortVersionString"] as? String
else { return AppVersionInfoResponse(major: 1, minor: 0, patch: 0) }

let splitedVersion = version.split(separator: ".")
.compactMap { Int($0) }

return AppVersionInfoResponse(
major: splitedVersion[0],
minor: splitedVersion[1],
patch: splitedVersion[2]
)
}

/// 최소 요구 버전, fetch 받은 날짜를 UserDefaults 저장
public func saveForceUpdateInfo(_ newValue: ForceUpdate) {
forceUpdateInfo = newValue
}

/// UserDefaults 저장된 최소 요구 버전, fetch 받은 날짜
public func getForceUpdateInfo() -> ForceUpdate {
return forceUpdateInfo
}

private func getDomainURL() -> String {
guard let domainURL = Bundle.main.object(
forInfoDictionaryKey: "DOMAIN_URL"
) as? String
else { return "" }

return domainURL
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// DefaultForceUpdateService.swift
// Data
//
// Created by Jisoo Ham on 3/27/25.
// Copyright © 2025 Pepsi-Club. All rights reserved.
//

import Foundation

import Domain

public final class DefaultForceUpdateService: ForceUpdateService {

public init() { }

public func compareVersion(
user: AppVersionInfoResponse,
required: AppVersionInfoResponse
) -> Bool {
return required > user
}
}
22 changes: 22 additions & 0 deletions Projects/Domain/Sources/Entity/ForceUpdate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// ForceUpdate.swift
// Domain
//
// Created by Jisoo Ham on 3/27/25.
// Copyright © 2025 Pepsi-Club. All rights reserved.
//

import Foundation

public struct ForceUpdate: Codable {
let version: AppVersionInfoResponse
let date: Date

public init(
version: AppVersionInfoResponse,
date: Date
) {
self.version = version
self.date = date
}
}
Loading