From 3a6b88a0bcf46997ba13c8b2d8b7a5d5f54d2522 Mon Sep 17 00:00:00 2001 From: Yoorim Choi Date: Thu, 7 Sep 2023 17:36:02 +0900 Subject: [PATCH] Remove `FEATURE_RN_FRIENDS` flag (#264) * RN flag on * `window` is needed * Remove `FEATURE_RN_FRIENDS` flag --- SNUTT-2022/SNUTT.xcodeproj/project.pbxproj | 3 +- .../SNUTT/AppState/AppEnvironment.swift | 82 +++------ .../SNUTT/Services/FriendsService.swift | 157 +++++++++--------- .../SNUTT/ViewModels/FriendsViewModel.swift | 33 ++-- SNUTT-2022/SNUTT/Views/AppDelegate.swift | 6 +- SNUTT-2022/SNUTT/Views/SNUTTView.swift | 28 ++-- .../SNUTT/Views/Scenes/FriendsScene.swift | 82 ++++----- 7 files changed, 168 insertions(+), 223 deletions(-) diff --git a/SNUTT-2022/SNUTT.xcodeproj/project.pbxproj b/SNUTT-2022/SNUTT.xcodeproj/project.pbxproj index 0c767e5a..224d4d88 100644 --- a/SNUTT-2022/SNUTT.xcodeproj/project.pbxproj +++ b/SNUTT-2022/SNUTT.xcodeproj/project.pbxproj @@ -2013,7 +2013,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.wafflestudio.snutt.dev"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.wafflestudio.snutt.dev"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG FEATURE_RN_FRIENDS"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = targeted; SWIFT_VERSION = 5.0; @@ -2057,6 +2057,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development com.wafflestudio.snutt.dev"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.wafflestudio.snutt.dev"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = targeted; SWIFT_VERSION = 5.0; diff --git a/SNUTT-2022/SNUTT/AppState/AppEnvironment.swift b/SNUTT-2022/SNUTT/AppState/AppEnvironment.swift index 68f851cd..7fbad1b1 100644 --- a/SNUTT-2022/SNUTT/AppState/AppEnvironment.swift +++ b/SNUTT-2022/SNUTT/AppState/AppEnvironment.swift @@ -26,9 +26,7 @@ extension AppEnvironment { let popupService: PopupServiceProtocol let etcService: EtcServiceProtocol let vacancyService: VacancyServiceProtocol - #if FEATURE_RN_FRIENDS - let friendsService: FriendsServiceProtocol - #endif + let friendsService: FriendsServiceProtocol } } @@ -135,33 +133,19 @@ extension AppEnvironment { let popupService = PopupService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories) let etcService = EtcService(appState: appState, webRepositories: webRepositories) let vacancyService = VacancyService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories) - #if FEATURE_RN_FRIENDS - let friendsService = FriendsService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories) - return .init(timetableService: timetableService, - userService: userService, - lectureService: lectureService, - searchService: searchService, - globalUIService: globalUIService, - courseBookService: courseBookService, - authService: authService, - notificationService: notificationService, - popupService: popupService, - etcService: etcService, - vacancyService: vacancyService, - friendsService: friendsService) - #else - return .init(timetableService: timetableService, - userService: userService, - lectureService: lectureService, - searchService: searchService, - globalUIService: globalUIService, - courseBookService: courseBookService, - authService: authService, - notificationService: notificationService, - popupService: popupService, - etcService: etcService, - vacancyService: vacancyService) - #endif + let friendsService = FriendsService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories) + return .init(timetableService: timetableService, + userService: userService, + lectureService: lectureService, + searchService: searchService, + globalUIService: globalUIService, + courseBookService: courseBookService, + authService: authService, + notificationService: notificationService, + popupService: popupService, + etcService: etcService, + vacancyService: vacancyService, + friendsService: friendsService) } } @@ -179,32 +163,18 @@ extension EnvironmentValues { #if DEBUG extension AppEnvironment.Services { @MainActor static func preview(appState: AppState) -> Self { - #if FEATURE_RN_FRIENDS - .init(timetableService: FakeTimetableService(), - userService: FakeUserService(), - lectureService: FakeLectureService(), - searchService: FakeSearchService(), - globalUIService: GlobalUIService(appState: appState, localRepositories: .init(userDefaultsRepository: UserDefaultsRepository(storage: .preview)), webRepositories: nil), - courseBookService: FakeCourseBookService(), - authService: FakeAuthService(), - notificationService: FakeNotificationService(), - popupService: FakePopupService(), - etcService: FakeEtcService(), - vacancyService: FakeVacancyService(), - friendsService: FakeFriendsService()) - #else - .init(timetableService: FakeTimetableService(), - userService: FakeUserService(), - lectureService: FakeLectureService(), - searchService: FakeSearchService(), - globalUIService: GlobalUIService(appState: appState, localRepositories: .init(userDefaultsRepository: UserDefaultsRepository(storage: .preview)), webRepositories: nil), - courseBookService: FakeCourseBookService(), - authService: FakeAuthService(), - notificationService: FakeNotificationService(), - popupService: FakePopupService(), - etcService: FakeEtcService(), - vacancyService: FakeVacancyService()) - #endif + .init(timetableService: FakeTimetableService(), + userService: FakeUserService(), + lectureService: FakeLectureService(), + searchService: FakeSearchService(), + globalUIService: GlobalUIService(appState: appState, localRepositories: .init(userDefaultsRepository: UserDefaultsRepository(storage: .preview)), webRepositories: nil), + courseBookService: FakeCourseBookService(), + authService: FakeAuthService(), + notificationService: FakeNotificationService(), + popupService: FakePopupService(), + etcService: FakeEtcService(), + vacancyService: FakeVacancyService(), + friendsService: FakeFriendsService()) } } #endif diff --git a/SNUTT-2022/SNUTT/Services/FriendsService.swift b/SNUTT-2022/SNUTT/Services/FriendsService.swift index f3405fcb..ca54f4db 100644 --- a/SNUTT-2022/SNUTT/Services/FriendsService.swift +++ b/SNUTT-2022/SNUTT/Services/FriendsService.swift @@ -5,109 +5,106 @@ // Created by user on 2023/08/11. // -#if FEATURE_RN_FRIENDS - import Alamofire - import Foundation +import Alamofire +import Foundation - @MainActor - protocol FriendsServiceProtocol: Sendable { - @discardableResult func fetchReactNativeBundleUrl() async throws -> URL - } +@MainActor +protocol FriendsServiceProtocol: Sendable { + @discardableResult func fetchReactNativeBundleUrl() async throws -> URL +} - struct FriendsService: FriendsServiceProtocol, ConfigsProvidable { - var appState: AppState - var webRepositories: AppEnvironment.WebRepositories - var localRepositories: AppEnvironment.LocalRepositories +struct FriendsService: FriendsServiceProtocol, ConfigsProvidable { + var appState: AppState + var webRepositories: AppEnvironment.WebRepositories + var localRepositories: AppEnvironment.LocalRepositories - private let rnBundlePath = "ReactNativeBundles" + private let rnBundlePath = "ReactNativeBundles" - private var vacancyRepository: any VacancyRepositoryProtocol { - webRepositories.vacancyRepository - } + private var vacancyRepository: any VacancyRepositoryProtocol { + webRepositories.vacancyRepository + } - private var configRepository: any ConfigRepositoryProtocol { - webRepositories.configRepository - } + private var configRepository: any ConfigRepositoryProtocol { + webRepositories.configRepository + } - private var userDefaultsRepository: any UserDefaultsRepositoryProtocol { - localRepositories.userDefaultsRepository - } + private var userDefaultsRepository: any UserDefaultsRepositoryProtocol { + localRepositories.userDefaultsRepository + } - // MARK: RN Bundle Management + // MARK: RN Bundle Management - // TODO: refactor with modularization; move to infra layer + // TODO: refactor with modularization; move to infra layer - func fetchReactNativeBundleUrl() async throws -> URL { - let configsDto = try await fetchConfigs() - guard let remoteUrlString = configsDto.reactNativeBundleFriends?.iosBundleSrc, - let remoteUrl = URL(string: remoteUrlString) - else { - throw STError(.INVALID_RN_BUNDLE) - } + func fetchReactNativeBundleUrl() async throws -> URL { + let configsDto = try await fetchConfigs() + guard let remoteUrlString = configsDto.reactNativeBundleFriends?.iosBundleSrc, + let remoteUrl = URL(string: remoteUrlString) + else { + throw STError(.INVALID_RN_BUNDLE) + } - let localUrl = constructLocalCacheUrl(from: remoteUrl) - if FileManager.default.fileExists(atPath: localUrl.path) { - return localUrl - } - clearReactNativeBundlesCache() // always maintain the latest bundle file - let downloadUrl = try await downloadFile(from: remoteUrl) - return downloadUrl + let localUrl = constructLocalCacheUrl(from: remoteUrl) + if FileManager.default.fileExists(atPath: localUrl.path) { + return localUrl } + clearReactNativeBundlesCache() // always maintain the latest bundle file + let downloadUrl = try await downloadFile(from: remoteUrl) + return downloadUrl + } - private var bundlesURL: URL { - guard let cacheURL = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else { - return FileManager.default.temporaryDirectory - } - return cacheURL.appendingPathComponent(rnBundlePath) + private var bundlesURL: URL { + guard let cacheURL = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else { + return FileManager.default.temporaryDirectory } + return cacheURL.appendingPathComponent(rnBundlePath) + } - private func constructLocalCacheUrl(from remoteUrl: URL) -> URL { - var bundlesURL = self.bundlesURL - for component in remoteUrl.pathComponents.dropFirst() { - bundlesURL = bundlesURL.appendingPathComponent(component) - } - return bundlesURL + private func constructLocalCacheUrl(from remoteUrl: URL) -> URL { + var bundlesURL = self.bundlesURL + for component in remoteUrl.pathComponents.dropFirst() { + bundlesURL = bundlesURL.appendingPathComponent(component) } + return bundlesURL + } - private func clearReactNativeBundlesCache() { - do { - let fileURLs = try FileManager.default.contentsOfDirectory(at: bundlesURL, - includingPropertiesForKeys: nil, - options: .skipsHiddenFiles) - for fileURL in fileURLs { - try FileManager.default.removeItem(at: fileURL) - } - } catch { - // pass + private func clearReactNativeBundlesCache() { + do { + let fileURLs = try FileManager.default.contentsOfDirectory(at: bundlesURL, + includingPropertiesForKeys: nil, + options: .skipsHiddenFiles) + for fileURL in fileURLs { + try FileManager.default.removeItem(at: fileURL) } + } catch { + // pass } + } - func downloadFile(from remoteUrl: URL) async throws -> URL { - return try await withCheckedThrowingContinuation { continuation in - let destinationBlock: DownloadRequest.Destination = { _, _ in - let cacheURL = constructLocalCacheUrl(from: remoteUrl) - return (cacheURL, [.removePreviousFile, .createIntermediateDirectories]) - } - AF.download(remoteUrl, to: destinationBlock).response { response in - switch response.result { - case let .success(url): - guard let url else { - continuation.resume(throwing: STError(.INVALID_RN_BUNDLE)) - return - } - continuation.resume(returning: url) - case let .failure(error): - continuation.resume(throwing: error) + func downloadFile(from remoteUrl: URL) async throws -> URL { + return try await withCheckedThrowingContinuation { continuation in + let destinationBlock: DownloadRequest.Destination = { _, _ in + let cacheURL = constructLocalCacheUrl(from: remoteUrl) + return (cacheURL, [.removePreviousFile, .createIntermediateDirectories]) + } + AF.download(remoteUrl, to: destinationBlock).response { response in + switch response.result { + case let .success(url): + guard let url else { + continuation.resume(throwing: STError(.INVALID_RN_BUNDLE)) + return } + continuation.resume(returning: url) + case let .failure(error): + continuation.resume(throwing: error) } } } } +} - struct FakeFriendsService: FriendsServiceProtocol { - func fetchReactNativeBundleUrl() async throws -> URL { - throw STError(.INVALID_RN_BUNDLE) - } +struct FakeFriendsService: FriendsServiceProtocol { + func fetchReactNativeBundleUrl() async throws -> URL { + throw STError(.INVALID_RN_BUNDLE) } - -#endif +} diff --git a/SNUTT-2022/SNUTT/ViewModels/FriendsViewModel.swift b/SNUTT-2022/SNUTT/ViewModels/FriendsViewModel.swift index dacb6e2f..f45acbe3 100644 --- a/SNUTT-2022/SNUTT/ViewModels/FriendsViewModel.swift +++ b/SNUTT-2022/SNUTT/ViewModels/FriendsViewModel.swift @@ -5,26 +5,23 @@ // Created by 박신홍 on 2023/07/15. // -#if FEATURE_RN_FRIENDS - import Foundation +import Foundation - class FriendsViewModel: BaseViewModel, ObservableObject { - override init(container: DIContainer) { - super.init(container: container) - } +class FriendsViewModel: BaseViewModel, ObservableObject { + override init(container: DIContainer) { + super.init(container: container) + } - var accessToken: String? { - appState.user.accessToken - } + var accessToken: String? { + appState.user.accessToken + } - func fetchReactNativeBundleUrl() async -> URL? { - do { - return try await services.friendsService.fetchReactNativeBundleUrl() - } catch { - services.globalUIService.presentErrorAlert(error: error) - } - return nil + func fetchReactNativeBundleUrl() async -> URL? { + do { + return try await services.friendsService.fetchReactNativeBundleUrl() + } catch { + services.globalUIService.presentErrorAlert(error: error) } + return nil } - -#endif +} diff --git a/SNUTT-2022/SNUTT/Views/AppDelegate.swift b/SNUTT-2022/SNUTT/Views/AppDelegate.swift index cb8aa651..1a681da6 100644 --- a/SNUTT-2022/SNUTT/Views/AppDelegate.swift +++ b/SNUTT-2022/SNUTT/Views/AppDelegate.swift @@ -15,10 +15,8 @@ import UIKit /// /// See [here](https://www.raywenderlich.com/20201639-firebase-cloud-messaging-for-ios-push-notifications) for more information about FCM configuration. class AppDelegate: NSObject, UIApplicationDelegate { - #if DEBUG - /// `ReactNativeDevKit` requires this property to be declared. - var window: UIWindow? - #endif + /// `ReactNativeKit` and `ReactNativeDevKit` requires this property to be declared. + var window: UIWindow? var firebaseConfigName: String { #if DEBUG diff --git a/SNUTT-2022/SNUTT/Views/SNUTTView.swift b/SNUTT-2022/SNUTT/Views/SNUTTView.swift index ed842435..d75d7725 100644 --- a/SNUTT-2022/SNUTT/Views/SNUTTView.swift +++ b/SNUTT-2022/SNUTT/Views/SNUTTView.swift @@ -45,11 +45,7 @@ struct SNUTTView: View { ReviewScene(viewModel: .init(container: viewModel.container), isMainWebView: true) } TabScene(tabType: .friends) { - #if FEATURE_RN_FRIENDS - FriendsScene(viewModel: .init(container: viewModel.container)) - #else - FriendsScene() - #endif + FriendsScene(viewModel: .init(container: viewModel.container)) } TabScene(tabType: .settings) { SettingScene(viewModel: .init(container: viewModel.container)) @@ -76,11 +72,9 @@ struct SNUTTView: View { group.addTask { await viewModel.fetchVacancyLectures() } - #if FEATURE_RN_FRIENDS - group.addTask { - await viewModel.fetchReactNativeBundleIfNeeded() - } - #endif + group.addTask { + await viewModel.fetchReactNativeBundleIfNeeded() + } }) } @@ -217,15 +211,13 @@ extension SNUTTView { } } - #if FEATURE_RN_FRIENDS - func fetchReactNativeBundleIfNeeded() async { - do { - try await services.friendsService.fetchReactNativeBundleUrl() - } catch { - // pass - } + func fetchReactNativeBundleIfNeeded() async { + do { + try await services.friendsService.fetchReactNativeBundleUrl() + } catch { + // pass } - #endif + } } } diff --git a/SNUTT-2022/SNUTT/Views/Scenes/FriendsScene.swift b/SNUTT-2022/SNUTT/Views/Scenes/FriendsScene.swift index 49b65f7a..ed457784 100644 --- a/SNUTT-2022/SNUTT/Views/Scenes/FriendsScene.swift +++ b/SNUTT-2022/SNUTT/Views/Scenes/FriendsScene.swift @@ -9,62 +9,52 @@ import ReactNativeKit import SwiftUI struct FriendsScene: View { - #if FEATURE_RN_FRIENDS - var viewModel: FriendsViewModel - #endif + var viewModel: FriendsViewModel @State private var bundleUrl: URL? @Environment(\.colorScheme) var colorScheme var body: some View { let _ = debugChanges() - #if FEATURE_RN_FRIENDS - Group { - if let token = viewModel.accessToken, let bundleUrl { - ZStack { - STColor.systemBackground - .ignoresSafeArea() - RNFriendsView(accessToken: token, bundleUrl: bundleUrl, colorScheme: colorScheme) - .id(colorScheme) - } - } else { - ProgressView() + Group { + if let token = viewModel.accessToken, let bundleUrl { + ZStack { + STColor.systemBackground + .ignoresSafeArea() + RNFriendsView(accessToken: token, bundleUrl: bundleUrl, colorScheme: colorScheme) + .id(colorScheme) } + } else { + ProgressView() } - .task { -// bundleUrl = URL(string: "http://localhost:8081/index.bundle?platform=ios")! - bundleUrl = await viewModel.fetchReactNativeBundleUrl() - } - #else - WIPFriendsView() - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(STColor.systemBackground) - #endif + } + .task { + // bundleUrl = URL(string: "http://localhost:8081/index.bundle?platform=ios")! + bundleUrl = await viewModel.fetchReactNativeBundleUrl() + } } } -#if FEATURE_RN_FRIENDS - struct RNFriendsView: UIViewRepresentable { - let accessToken: String - let bundleUrl: URL - let colorScheme: ColorScheme - private let moduleName = "friends" - - private var initialProps: [String: Any] { - var props: [String: Any] = AppMetadata.asDictionary() - props["x-access-token"] = accessToken - props["theme"] = colorScheme.description - props["allowFontScaling"] = false - return props - } - - func makeUIView(context _: Context) -> UIView { - return makeReactView(bundleURL: bundleUrl, - moduleName: moduleName, - initialProperties: initialProps, - backgroundColor: UIColor(STColor.systemBackground)) - } +struct RNFriendsView: UIViewRepresentable { + let accessToken: String + let bundleUrl: URL + let colorScheme: ColorScheme + private let moduleName = "friends" + + private var initialProps: [String: Any] { + var props: [String: Any] = AppMetadata.asDictionary() + props["x-access-token"] = accessToken + props["theme"] = colorScheme.description + props["allowFontScaling"] = false + return props + } - func updateUIView(_: UIView, context _: Context) {} + func makeUIView(context _: Context) -> UIView { + return makeReactView(bundleURL: bundleUrl, + moduleName: moduleName, + initialProperties: initialProps, + backgroundColor: UIColor(STColor.systemBackground)) } -#endif + + func updateUIView(_: UIView, context _: Context) {} +}