diff --git a/IntentsExtension/IntentHandler.swift b/IntentsExtension/IntentHandler.swift index f8615baf..6fe01160 100644 --- a/IntentsExtension/IntentHandler.swift +++ b/IntentsExtension/IntentHandler.swift @@ -17,18 +17,6 @@ class IntentHandler: INExtension { // This is the default implementation. If you want different objects to handle different intents, // you can override this and return the handler you want for that particular intent. - cardListFetchWithAPI { [weak self] result in - switch result { - case .success(let result): - if let result { - self?.cardItems = result.data - print(self?.cardItems) - } - case .failure(let err): - print(err) - } - } - return self } } @@ -36,38 +24,54 @@ class IntentHandler: INExtension { extension IntentHandler: MyCardIntentHandling { // 내 명함 목록 선택할 때 호출. func provideMyCardOptionsCollection(for intent: MyCardIntent, with completion: @escaping (INObjectCollection?, Error?) -> Void) { - cardListFetchWithAPI { [weak self] result in + cardListFetchWithAPI { result in switch result { case .success(let result): - if let result { - self?.cardItems = result.data - - if let cardItems = self?.cardItems { - let myCards = cardItems.map { card in - let myCard = MyCard(identifier: card.cardUUID, display: card.cardName) - myCard.userName = card.userName - myCard.cardImage = card.cardImage - - return myCard - } - let collection = INObjectCollection(items: myCards) - completion(collection, nil) + if let cardItems = result?.data { + let myCards = cardItems.map { card in + let myCard = MyCard(identifier: card.cardUUID, display: card.cardName) + myCard.userName = card.userName + myCard.cardImage = card.cardImage + + return myCard } + let collection = INObjectCollection(items: myCards) + completion(collection, nil) } case .failure(let err): print(err) + completion(nil, nil) } } } - // 위젯 편집할때 호출. 기본값 설정. + // 위젯 추가할때 호출. 기본값 설정. func defaultMyCard(for intent: MyCardIntent) -> MyCard? { var myCard: MyCard? - if let cardItems { - myCard = MyCard(identifier: cardItems[0].cardUUID, display: cardItems[0].cardName) + let group = DispatchGroup() + + DispatchQueue.global().async(group: group) { [weak self] in + group.enter() + + self?.cardListFetchWithAPI { [weak self] result in + switch result { + case .success(let result): + if let result { + self?.cardItems = result.data + myCard = MyCard(identifier: self?.cardItems?[0].cardUUID ?? "", display: self?.cardItems?[0].cardName ?? "") + myCard?.userName = self?.cardItems?[0].userName + myCard?.cardImage = self?.cardItems?[0].cardImage + } + case .failure(let err): + print(err) + } + group.leave() + } } + _ = group.wait(timeout: .now() + 60) + return myCard } } @@ -85,7 +89,7 @@ extension IntentHandler { guard let url = URL(string: "http://3.35.107.3:8080/api/v1/card") else { return } var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "GET" - urlRequest.addValue("Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEwfQ.rq__Rzvunpxq3paeo2fK4i_oeupyDlHp3q1RW6uSHSQ", forHTTPHeaderField: "Authorization") + urlRequest.addValue("Bearer \(UserDefaults.appGroup.string(forKey: "accessToken") ?? "")", forHTTPHeaderField: "Authorization") print("😀", UserDefaults.appGroup.string(forKey: "accessToken") ?? "") URLSession.shared.dataTask(with: urlRequest) { data, response, error in @@ -103,7 +107,13 @@ extension IntentHandler { message: result?.message ?? "none message"))) } else { if let result { - completion(.success(result)) + if result.status != 200 { + completion(.failure(WidgetError.networkFail(status: status, + code: result.code ?? "none code", + message: result.message ?? "none message"))) + } else { + completion(.success(result)) + } } else { completion(.failure(WidgetError.decodeFail(status: status))) } diff --git a/NADA-iOS-forRelease.xcodeproj/project.pbxproj b/NADA-iOS-forRelease.xcodeproj/project.pbxproj index 314955f5..b130f520 100644 --- a/NADA-iOS-forRelease.xcodeproj/project.pbxproj +++ b/NADA-iOS-forRelease.xcodeproj/project.pbxproj @@ -3,11 +3,10 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 53; objects = { /* Begin PBXBuildFile section */ - 2566DBA723D1370275ECE593 /* Pods_NADA_iOS_forRelease.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D86FBA2B7966CFE6EEF0E7E8 /* Pods_NADA_iOS_forRelease.framework */; }; 39007F2C27080D8200E7143E /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39007F2B27080D8200E7143E /* UIViewController+Extension.swift */; }; 3903CC202769F4F40094C458 /* EmptyCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3903CC1E2769F4F40094C458 /* EmptyCardCell.swift */; }; 3903CC212769F4F40094C458 /* EmptyCardCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3903CC1F2769F4F40094C458 /* EmptyCardCell.xib */; }; @@ -218,6 +217,7 @@ F8FC43BA26C022900033E151 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8FC43B926C022900033E151 /* ViewController.swift */; }; F8FC43BC26C022A20033E151 /* Storyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8FC43BB26C022A20033E151 /* Storyboard.swift */; }; F8FC43BF26C025180033E151 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = F8FC43BE26C025180033E151 /* .swiftlint.yml */; }; + FF46241630BB3F2D09D5A1AF /* Pods_NADA_iOS_forRelease.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F6A2AA0066BA1941116A267 /* Pods_NADA_iOS_forRelease.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -261,7 +261,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 00BC6C525F13C9651D73976F /* Pods-NADA-iOS-forRelease.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NADA-iOS-forRelease.debug.xcconfig"; path = "Target Support Files/Pods-NADA-iOS-forRelease/Pods-NADA-iOS-forRelease.debug.xcconfig"; sourceTree = ""; }; + 0F6A2AA0066BA1941116A267 /* Pods_NADA_iOS_forRelease.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NADA_iOS_forRelease.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B19E918E93248BE054C89A /* Pods-NADA-iOS-forRelease.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NADA-iOS-forRelease.debug.xcconfig"; path = "Target Support Files/Pods-NADA-iOS-forRelease/Pods-NADA-iOS-forRelease.debug.xcconfig"; sourceTree = ""; }; + 19298D75B81F3260A870AB0B /* Pods-NADA-iOS-forRelease.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NADA-iOS-forRelease.release.xcconfig"; path = "Target Support Files/Pods-NADA-iOS-forRelease/Pods-NADA-iOS-forRelease.release.xcconfig"; sourceTree = ""; }; 39007F2B27080D8200E7143E /* UIViewController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = ""; }; 3903CC1E2769F4F40094C458 /* EmptyCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCardCell.swift; sourceTree = ""; }; 3903CC1F2769F4F40094C458 /* EmptyCardCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmptyCardCell.xib; sourceTree = ""; }; @@ -371,15 +373,12 @@ 77F2C0EA27632A91007641E3 /* CardHarmonyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardHarmonyViewController.swift; sourceTree = ""; }; 77F2C0EC27632AA7007641E3 /* CardHarmony.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CardHarmony.storyboard; sourceTree = ""; }; 77F47D92276C79B600414659 /* Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; }; - 78FC1ADEA3CAB995C08D47DB /* Pods-NADA-iOS-forRelease.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NADA-iOS-forRelease.release.xcconfig"; path = "Target Support Files/Pods-NADA-iOS-forRelease/Pods-NADA-iOS-forRelease.release.xcconfig"; sourceTree = ""; }; - D86FBA2B7966CFE6EEF0E7E8 /* Pods_NADA_iOS_forRelease.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NADA_iOS_forRelease.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F805588429C993E7002E8EA3 /* UpdateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateViewController.swift; sourceTree = ""; }; F80C679129F21BB7002C5ECC /* onboarding01.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = onboarding01.json; sourceTree = ""; }; F80C679229F21BB7002C5ECC /* onboarding02.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = onboarding02.json; sourceTree = ""; }; F81171FF27383097002742CF /* ChangeGroupRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeGroupRequest.swift; sourceTree = ""; }; F822E7A82709CEB60020452C /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; F8268DB827730B0100BF114B /* FirstCardAlertBottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstCardAlertBottomSheetViewController.swift; sourceTree = ""; }; - F82AF6A029FBBAF50051545B /* IntentsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IntentsExtension.entitlements; sourceTree = ""; }; F82AF6A129FBBEE50051545B /* UserDefaults+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Extension.swift"; sourceTree = ""; }; F82FEB4A27639F3100DA7847 /* MainCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainCardCell.swift; sourceTree = ""; }; F82FEB4B27639F3100DA7847 /* MainCardCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainCardCell.xib; sourceTree = ""; }; @@ -510,7 +509,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2566DBA723D1370275ECE593 /* Pods_NADA_iOS_forRelease.framework in Frameworks */, + FF46241630BB3F2D09D5A1AF /* Pods_NADA_iOS_forRelease.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -520,8 +519,8 @@ 186051B2C99DBAECC539DAC9 /* Pods */ = { isa = PBXGroup; children = ( - 00BC6C525F13C9651D73976F /* Pods-NADA-iOS-forRelease.debug.xcconfig */, - 78FC1ADEA3CAB995C08D47DB /* Pods-NADA-iOS-forRelease.release.xcconfig */, + 13B19E918E93248BE054C89A /* Pods-NADA-iOS-forRelease.debug.xcconfig */, + 19298D75B81F3260A870AB0B /* Pods-NADA-iOS-forRelease.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -748,7 +747,7 @@ F838B661298E5C5300D84340 /* SwiftUI.framework */, F87D2229298ECAFB001A882B /* Intents.framework */, F87D2234298ECAFB001A882B /* IntentsUI.framework */, - D86FBA2B7966CFE6EEF0E7E8 /* Pods_NADA_iOS_forRelease.framework */, + 0F6A2AA0066BA1941116A267 /* Pods_NADA_iOS_forRelease.framework */, ); name = Frameworks; sourceTree = ""; @@ -1517,13 +1516,13 @@ isa = PBXNativeTarget; buildConfigurationList = F8FC439626C01CDE0033E151 /* Build configuration list for PBXNativeTarget "NADA-iOS-forRelease" */; buildPhases = ( - 63B126249E7D65994A592714 /* [CP] Check Pods Manifest.lock */, + 968E60C36CA3B9A2790CB1AA /* [CP] Check Pods Manifest.lock */, F8FC437E26C01CDD0033E151 /* Sources */, F8FC437F26C01CDD0033E151 /* Frameworks */, F8FC438026C01CDD0033E151 /* Resources */, F8FC43BD26C0244D0033E151 /* ShellScript */, F838B673298E5C5400D84340 /* Embed Foundation Extensions */, - 2FD8F2B3BF35A3CA09A8E81C /* [CP] Embed Pods Frameworks */, + 3DFB37457B9A5C839910082B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1543,8 +1542,9 @@ F8FC437A26C01CDD0033E151 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1420; - LastUpgradeCheck = 1250; + LastUpgradeCheck = 1430; TargetAttributes = { F838B65D298E5C5300D84340 = { CreatedOnToolsVersion = 14.2; @@ -1669,7 +1669,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 2FD8F2B3BF35A3CA09A8E81C /* [CP] Embed Pods Frameworks */ = { + 3DFB37457B9A5C839910082B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1686,7 +1686,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NADA-iOS-forRelease/Pods-NADA-iOS-forRelease-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 63B126249E7D65994A592714 /* [CP] Check Pods Manifest.lock */ = { + 968E60C36CA3B9A2790CB1AA /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -2278,7 +2278,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.2; + MARKETING_VERSION = 1.0.3; PRODUCT_BUNDLE_IDENTIFIER = "YJC.NADA-iOS-forRelease"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development YJC.NADA-iOS-forRelease"; @@ -2307,7 +2307,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.2; + MARKETING_VERSION = 1.0.3; PRODUCT_BUNDLE_IDENTIFIER = "YJC.NADA-iOS-forRelease"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AppStore YJC.NADA-iOS-forRelease"; diff --git a/NADA-iOS-forRelease.xcodeproj/xcshareddata/xcschemes/IntentsExtension.xcscheme b/NADA-iOS-forRelease.xcodeproj/xcshareddata/xcschemes/IntentsExtension.xcscheme index e859c310..d93c9ea0 100644 --- a/NADA-iOS-forRelease.xcodeproj/xcshareddata/xcschemes/IntentsExtension.xcscheme +++ b/NADA-iOS-forRelease.xcodeproj/xcshareddata/xcschemes/IntentsExtension.xcscheme @@ -1,6 +1,6 @@ - NSLocationWhenInUseUsageDescription - "사용자의 위치를 받아오려고 합니다." - NSLocationAlwaysAndWhenInUseUsageDescription - "사용자의 위치를 받아오려고 합니다." CFBundleDevelopmentRegion ko_KR CFBundleDisplayName @@ -52,6 +48,10 @@ NSCameraUsageDescription QR코드를 인식하여 명함을 추가하기 위해 카메라 권한 허용이 필요해요. + NSLocationAlwaysAndWhenInUseUsageDescription + "사용자의 위치를 받아오려고 합니다." + NSLocationWhenInUseUsageDescription + "사용자의 위치를 받아오려고 합니다." NSPhotoLibraryAddUsageDescription 명함을 이미지로 저장하기 위해 갤러리 권한 허용이 필요해요. NSPhotoLibraryUsageDescription diff --git a/NADA-iOS-forRelease/Resouces/Constants/UserDefaults.swift b/NADA-iOS-forRelease/Resouces/Constants/UserDefaults.swift index ce9e93cb..44501beb 100644 --- a/NADA-iOS-forRelease/Resouces/Constants/UserDefaults.swift +++ b/NADA-iOS-forRelease/Resouces/Constants/UserDefaults.swift @@ -12,13 +12,15 @@ extension Const { static let darkModeState = "darkModeState" static let userID = "userID" static let isFirstCard = "isFirstCard" - static let isOnboarding = "isOnboarding" + static let isOnboarding = "isOnboardingAvailable" static let firstCardID = "firstCardID" static let isAppleLogin = "isAppleLogin" static let isKakaoLogin = "isKakaoLogin" static let dynamicLinkCardUUID = "dynamicLinkCardUUID" - // TODO: - KeyChain 적용 - static let accessToken = "accessToken" + static let accessToken = "AccessToken" + static let openQRCodeWidget = "openQRCodeWidget" + static let openMyCardWidget = "openMyCardWidget" + static let widgetCardUUID = "widgetCardUUID" static let refreshToken = "refreshToken" } } diff --git a/NADA-iOS-forRelease/Resouces/Extensions/UserDefaults+Extension.swift b/NADA-iOS-forRelease/Resouces/Extensions/UserDefaults+Extension.swift index 50258ba6..dfc14713 100644 --- a/NADA-iOS-forRelease/Resouces/Extensions/UserDefaults+Extension.swift +++ b/NADA-iOS-forRelease/Resouces/Extensions/UserDefaults+Extension.swift @@ -8,5 +8,5 @@ import Foundation extension UserDefaults { - static var appGroup = UserDefaults(suiteName: "group.NADA-iOS-forRelease")! + static var appGroup = UserDefaults(suiteName: "group.YJC.NADA-iOS-forRelease")! } diff --git a/NADA-iOS-forRelease/Sources/SceneDelegate.swift b/NADA-iOS-forRelease/Sources/SceneDelegate.swift index 96fa7187..a3156d1b 100644 --- a/NADA-iOS-forRelease/Sources/SceneDelegate.swift +++ b/NADA-iOS-forRelease/Sources/SceneDelegate.swift @@ -5,6 +5,7 @@ // Created by kimhyungyu on 2021/08/08. // +import Photos import UIKit import FirebaseDynamicLinks @@ -16,7 +17,23 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? let defaults = UserDefaults.standard + private let myCardURL = "openMyCardWidget" + private let qrCodeURL = "openQRCodeWidget" + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + + if let url = connectionOptions.urlContexts.first?.url { + if url.absoluteString == qrCodeURL { + UserDefaults.standard.setValue(true, forKey: Const.UserDefaultsKey.openQRCodeWidget) + } else if url.absoluteString.starts(with: myCardURL) { + guard let queryItems = URLComponents(string: url.absoluteString)?.queryItems, + let cardUUID = queryItems.filter({ $0.name == "cardUUID" }).first?.value else { return } + + UserDefaults.standard.setValue(true, forKey: Const.UserDefaultsKey.openMyCardWidget) + UserDefaults.standard.setValue(cardUUID, forKey: Const.UserDefaultsKey.widgetCardUUID) + } + } + guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(frame: windowScene.coordinateSpace.bounds) @@ -45,7 +62,63 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { - if let url = URLContexts.first?.url { + guard let url = URLContexts.first?.url, + let urlComponents = URLComponents(string: url.absoluteString) else { return } + + if qrCodeURL == url.absoluteString { + // qr code 위젯. + switch AVCaptureDevice.authorizationStatus(for: .video) { + case .denied: + window?.rootViewController?.makeOKCancelAlert(title: "카메라 권한이 허용되어 있지 않아요.", + message: "QR코드 인식을 위해 카메라 권한이 필요합니다. 앱 설정으로 이동해 허용해 주세요.", + okAction: { _ in UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)}, + cancelAction: nil, + completion: nil) + case .authorized: + guard let nextVC = UIStoryboard.init(name: Const.Storyboard.Name.qrScan, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.qrScanViewController) as? QRScanViewController else { return } + nextVC.modalPresentationStyle = .overFullScreen + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) { + let topVC = UIApplication.mostTopViewController() + topVC?.present(nextVC, animated: true) + } + case .notDetermined: + AVCaptureDevice.requestAccess(for: .video) { granted in + if granted { + DispatchQueue.main.async { + guard let nextVC = UIStoryboard.init(name: Const.Storyboard.Name.qrScan, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.qrScanViewController) as? QRScanViewController else { return } + nextVC.modalPresentationStyle = .overFullScreen + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) { + let topVC = UIApplication.mostTopViewController() + topVC?.present(nextVC, animated: true) + } + } + } + } + default: + break + } + } else if url.absoluteString.starts(with: myCardURL) { + // 내 명함 위젯. + guard let queryItems = urlComponents.queryItems, + let cardUUID = queryItems.filter({ $0.name == "cardUUID" }).first?.value else { return } + + let nextVC = CardShareBottomSheetViewController() + .setTitle("명함공유") + .setHeight(606.0) + + cardDetailFetchWithAPI(cardUUID: cardUUID) { cardDataModel in + nextVC.isActivate = false + nextVC.modalPresentationStyle = .overFullScreen + nextVC.cardDataModel = cardDataModel + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) { + let topVC = UIApplication.mostTopViewController() + topVC?.present(nextVC, animated: true) + } + } + } else { if (AuthApi.isKakaoTalkLoginUrl(url)) { _ = AuthController.handleOpenUrl(url: url) } @@ -107,3 +180,27 @@ extension SceneDelegate { return cardUUID } } + +// MARK: - Network + +extension SceneDelegate { + private func cardDetailFetchWithAPI(cardUUID: String, completion: @escaping (Card) -> Void) { + CardAPI.shared.cardDetailFetch(cardUUID: cardUUID) { response in + switch response { + case .success(let data): + if let cardDataModel = data as? Card { + completion(cardDataModel) + } + print("cardDetailFetchWithAPI - success") + case .requestErr(let message): + print("cardDetailFetchWithAPI - requestErr", message) + case .pathErr: + print("cardDetailFetchWithAPI - pathErr") + case .serverErr: + print("cardDetailFetchWithAPI - serverErr") + case .networkFail: + print("deleteGroupWithAPI - networkFail") + } + } + } +} diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/Home/VC/HomeViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/Home/VC/HomeViewController.swift index 42687fc1..b7b9266b 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/Home/VC/HomeViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/Home/VC/HomeViewController.swift @@ -5,6 +5,7 @@ // Created by Yi Joon Choi on 2023/02/06. // +import Photos import UIKit import RxSwift @@ -66,7 +67,7 @@ final class HomeViewController: UIViewController { super.viewDidLoad() setLayout() bindActions() - checkUpdateVersion() + checkUpdateVersionAndSetting() } override func viewWillAppear(_ animated: Bool) { @@ -175,7 +176,7 @@ extension HomeViewController { }.disposed(by: self.disposeBag) } - private func checkUpdateVersion() { + private func checkUpdateVersionAndSetting() { updateUserInfoFetchWithAPI { [weak self] checkUpdateNote in if !checkUpdateNote { self?.updateNoteFetchWithAPI { [weak self] updateNote in @@ -186,12 +187,36 @@ extension HomeViewController { if let dynamicLinkCardUUID = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.dynamicLinkCardUUID) { self?.checkDynamicLink(dynamicLinkCardUUID) } + + if UserDefaults.standard.bool(forKey: Const.UserDefaultsKey.openQRCodeWidget) { + self?.presentQRScanVC() + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.openQRCodeWidget) + } + + if UserDefaults.standard.bool(forKey: Const.UserDefaultsKey.openMyCardWidget), + let widgetCardUUID = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.widgetCardUUID) { + self?.presentCardShareBottomSheetVC(with: widgetCardUUID) + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.openMyCardWidget) + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.widgetCardUUID) + } } } } else { if let dynamicLinkCardUUID = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.dynamicLinkCardUUID) { self?.checkDynamicLink(dynamicLinkCardUUID) } + + if UserDefaults.standard.bool(forKey: Const.UserDefaultsKey.openQRCodeWidget) { + self?.presentQRScanVC() + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.openQRCodeWidget) + } + + if UserDefaults.standard.bool(forKey: Const.UserDefaultsKey.openMyCardWidget), + let widgetCardUUID = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.widgetCardUUID) { + self?.presentCardShareBottomSheetVC(with: widgetCardUUID) + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.openMyCardWidget) + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.widgetCardUUID) + } } } } @@ -232,6 +257,47 @@ extension HomeViewController { cardDetailVC.cardDataModel = cardDataModel self.present(cardDetailVC, animated: true) } + + private func presentQRScanVC() { + switch AVCaptureDevice.authorizationStatus(for: .video) { + case .denied: + makeOKCancelAlert(title: "카메라 권한이 허용되어 있지 않아요.", + message: "QR코드 인식을 위해 카메라 권한이 필요합니다. 앱 설정으로 이동해 허용해 주세요.", + okAction: { _ in UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)}, + cancelAction: nil, + completion: nil) + case .authorized: + guard let nextVC = UIStoryboard.init(name: Const.Storyboard.Name.qrScan, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.qrScanViewController) as? QRScanViewController else { return } + nextVC.modalPresentationStyle = .overFullScreen + self.present(nextVC, animated: true, completion: nil) + case .notDetermined: + AVCaptureDevice.requestAccess(for: .video) { granted in + if granted { + DispatchQueue.main.async { + guard let nextVC = UIStoryboard.init(name: Const.Storyboard.Name.qrScan, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.qrScanViewController) as? QRScanViewController else { return } + nextVC.modalPresentationStyle = .overFullScreen + self.present(nextVC, animated: true, completion: nil) + } + } + } + default: + break + } + } + + private func presentCardShareBottomSheetVC(with cardUUID: String) { + self.cardDetailFetchWithAPI(cardUUID: cardUUID) { [weak self] cardDataModel in + let nextVC = CardShareBottomSheetViewController() + .setTitle("명함공유") + .setHeight(606.0) + + nextVC.isActivate = false + nextVC.modalPresentationStyle = .overFullScreen + nextVC.cardDataModel = cardDataModel + + self?.present(nextVC, animated: true) + } + } } // MARK: - Network diff --git a/Podfile.lock b/Podfile.lock index 1a7a2874..a1f91842 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,17 +1,17 @@ PODS: - Alamofire (5.6.4) - - Firebase/CoreOnly (10.8.0): - - FirebaseCore (= 10.8.0) - - Firebase/DynamicLinks (10.8.0): + - Firebase/CoreOnly (10.9.0): + - FirebaseCore (= 10.9.0) + - Firebase/DynamicLinks (10.9.0): - Firebase/CoreOnly - - FirebaseDynamicLinks (~> 10.8.0) - - FirebaseCore (10.8.0): + - FirebaseDynamicLinks (~> 10.9.0) + - FirebaseCore (10.9.0): - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/Logger (~> 7.8) - - FirebaseCoreInternal (10.8.0): + - FirebaseCoreInternal (10.9.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseDynamicLinks (10.8.0): + - FirebaseDynamicLinks (10.9.0): - FirebaseCore (~> 10.0) - FlexLayout (1.3.31) - GoogleUtilities/Environment (7.11.1): @@ -32,7 +32,7 @@ PODS: - KakaoSDKUser (2.15.0): - KakaoSDKAuth (= 2.15.0) - Kingfisher (7.6.2) - - lottie-ios (4.1.3) + - lottie-ios (4.2.0) - Moya (15.0.0): - Moya/Core (= 15.0.0) - Moya/Core (15.0.0): @@ -122,10 +122,10 @@ SPEC REPOS: SPEC CHECKSUMS: Alamofire: 4e95d97098eacb88856099c4fc79b526a299e48c - Firebase: b49ef44e5ec9a3d0c1f6450f410337e97872279c - FirebaseCore: e78636a990b54be427ce300aa09a0e359f4e0e87 - FirebaseCoreInternal: fa2899eb1f340054858d289e5a0fb935a0a74e52 - FirebaseDynamicLinks: 8c7e16503fd057c44727b6bc8dc8e7ff2a667955 + Firebase: bd152f0f3d278c4060c5c71359db08ebcfd5a3e2 + FirebaseCore: b68d3616526ec02e4d155166bbafb8eca64af557 + FirebaseCoreInternal: d2b4acb827908e72eca47a9fd896767c3053921e + FirebaseDynamicLinks: 8cb66c4f403aa6ddf86ff3bc3c383a652f344ce9 FlexLayout: 8010187077ecf09710cdf0e9c8ffe2c9b92ec5db GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749 IQKeyboardManagerSwift: c7955c0bdbf7b2eb29bb7daaa44e3d90f55a9a85 @@ -133,7 +133,7 @@ SPEC CHECKSUMS: KakaoSDKCommon: 89d863b7f34398e6fb93a0acb28a204908551a39 KakaoSDKUser: a997ca5c4c18ece2ab30646ff74841bc29d4399d Kingfisher: 6c5449c6450c5239166510ba04afe374a98afc4f - lottie-ios: d0954d3150061f662ed0adf96ef98d7421864c47 + lottie-ios: 809ecf2d460ed650a6aed7aa88b2ec45fab4779c Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667 PinLayout: f8a677ce0cd1cfe96b58435d029b4ceb4ce9c04c diff --git a/Widgets/Resource/Widgets.intentdefinition b/Widgets/Resource/Widgets.intentdefinition index 969bf8f1..6a4b22df 100644 --- a/Widgets/Resource/Widgets.intentdefinition +++ b/Widgets/Resource/Widgets.intentdefinition @@ -9,7 +9,7 @@ INIntentDefinitionNamespace 88xZPY INIntentDefinitionSystemVersion - 22D68 + 22E261 INIntentDefinitionToolsBuildVersion 14E222b INIntentDefinitionToolsVersion @@ -19,6 +19,8 @@ INIntentCategory information + INIntentDescription + 내 명함 INIntentDescriptionID tVvJ9c INIntentEligibleForWidgets diff --git a/Widgets/WidgetsBundle/MyCardWidget.swift b/Widgets/WidgetsBundle/MyCardWidget.swift index d2b8d842..4550c4f3 100644 --- a/Widgets/WidgetsBundle/MyCardWidget.swift +++ b/Widgets/WidgetsBundle/MyCardWidget.swift @@ -11,7 +11,7 @@ import Intents struct MyCardProvider: IntentTimelineProvider { func placeholder(in context: Context) -> MyCardEntry { - MyCardEntry(date: Date(), widgetCard: WidgetCard(cardID: "", title: "일이삼사오육칠팔구", userName: "일이삼사오육", backgroundImage: UIImage())) + MyCardEntry(date: Date(), widgetCard: WidgetCard(cardUUID: "", title: "일이삼사오육칠팔구", userName: "일이삼사오육", backgroundImage: UIImage())) } func getSnapshot(for configuration: MyCardIntent, in context: Context, completion: @escaping (MyCardEntry) -> Void) { @@ -19,7 +19,7 @@ struct MyCardProvider: IntentTimelineProvider { if let card = configuration.myCard { entry = MyCardEntry(date: Date(), - widgetCard: WidgetCard(cardID: card.identifier ?? "", + widgetCard: WidgetCard(cardUUID: card.identifier ?? "", title: card.displayString, userName: card.userName ?? "", backgroundImage: fetchImage(card.cardImage ?? ""))) @@ -39,7 +39,7 @@ struct MyCardProvider: IntentTimelineProvider { for hourOffset in 0 ..< 5 { let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate) ?? Date() let entry = MyCardEntry(date: entryDate, - widgetCard: WidgetCard(cardID: card.identifier ?? "", + widgetCard: WidgetCard(cardUUID: card.identifier ?? "", title: card.displayString, userName: card.userName ?? "", backgroundImage: fetchImage(card.cardImage ?? ""))) @@ -68,7 +68,7 @@ extension MyCardProvider { } struct WidgetCard { - let cardID: String + let cardUUID: String let title: String let userName: String let backgroundImage: UIImage @@ -120,7 +120,7 @@ struct MyCardEntryView: View { } } } - .widgetURL(URL(string: "openMyCardWidget")) + .widgetURL(URL(string: "openMyCardWidget://?cardUUID=\(widgetCard.cardUUID)")) } else { Image("widgetEmpty") .resizable() @@ -148,7 +148,7 @@ struct MyCardWidget_Previews: PreviewProvider { static var previews: some View { MyCardEntryView(entry: MyCardEntry(date: Date(), widgetCard: nil)) .previewContext(WidgetPreviewContext(family: .systemSmall)) - MyCardEntryView(entry: MyCardEntry(date: Date(), widgetCard: WidgetCard(cardID: Card.mockData[0].cardUUID, title: Card.mockData[0].cardName, userName: Card.mockData[0].userName, backgroundImage: UIImage(named: Card.mockData[2].cardImage) ?? UIImage()))) + MyCardEntryView(entry: MyCardEntry(date: Date(), widgetCard: WidgetCard(cardUUID: Card.mockData[0].cardUUID, title: Card.mockData[0].cardName, userName: Card.mockData[0].userName, backgroundImage: UIImage(named: Card.mockData[2].cardImage) ?? UIImage()))) .previewContext(WidgetPreviewContext(family: .systemSmall)) } }