Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1703,7 +1703,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2024.0208.0315;
CURRENT_PROJECT_VERSION = 2024.0312.0041;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8Q4H7X3Q58;
GENERATE_INFOPLIST_FILE = NO;
Expand All @@ -1726,7 +1726,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.runnect.Runnect-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.runnect.Runnect-iOS";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.runnect-Runnect-iOS";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand All @@ -1747,7 +1747,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2024.0208.0315;
CURRENT_PROJECT_VERSION = 2024.0312.0041;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8Q4H7X3Q58;
GENERATE_INFOPLIST_FILE = NO;
Expand All @@ -1770,7 +1770,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.runnect.Runnect-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.runnect.Runnect-iOS";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.runnect-Runnect-iOS";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import UIKit

import SnapKit
import FirebaseDynamicLinks

extension UIViewController {

Expand Down Expand Up @@ -89,10 +90,84 @@ extension UIViewController {
}

extension UIViewController {

/**
### Description: 공유 기능에 해당하는 정보를 넣어줍니다.
2025년 8월 25일에 동적링크는 만기 됩니다.

- courseTitle : 타이틀 이름
- courseId : 코스 아이디
- courseImageURL : 코스 사진
- minimumAppVersion : 공유 기능을 사용할 수 있는 최소 버전
- descriptionText : 내용
- parameter : 공유 기능에 필요한 파라미터
*/
/// 공유 기능 메서드
///
func shareCourse(
courseTitle: String,
courseId: Int,
courseImageURL: String,
minimumAppVersion: String,
descriptionText: String? = nil,
parameter: String
) {
Comment on lines +107 to +114
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

익스텐션으로 따로 빼두는게 좋을 것 같다는 생각을 했는데 역시 명진쌤,,,,, 👍🏻

let dynamicLinksDomainURIPrefix = "https://rnnt.page.link"
let courseParameter = parameter
guard let link = URL(string: "\(dynamicLinksDomainURIPrefix)/?\(courseParameter)=\(courseId)") else {
print("Invalid link.")
return
}

guard let linkBuilder = DynamicLinkComponents(link: link, domainURIPrefix: dynamicLinksDomainURIPrefix) else {
print("Failed to create link builder.")
return
}

linkBuilder.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.runnect.Runnect-iOS")
linkBuilder.iOSParameters?.appStoreID = "1663884202"
linkBuilder.iOSParameters?.minimumAppVersion = minimumAppVersion

linkBuilder.androidParameters = DynamicLinkAndroidParameters(packageName: "com.runnect.runnect")

linkBuilder.socialMetaTagParameters = DynamicLinkSocialMetaTagParameters()
linkBuilder.socialMetaTagParameters?.imageURL = URL(string: courseImageURL)
linkBuilder.socialMetaTagParameters?.title = courseTitle
linkBuilder.socialMetaTagParameters?.descriptionText = descriptionText ?? ""

linkBuilder.shorten { [weak self] url, _, error in
guard let shortDynamicLink = url?.absoluteString else {
if let error = error {
print("Error shortening dynamic link: \(error)")
}
return
}

print("Short URL is: \(shortDynamicLink)")
DispatchQueue.main.async {
self?.presentShareActivity(with: shortDynamicLink)
}
}
}

private func presentShareActivity(with url: String) {
let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self.view
self.present(activityVC, animated: true, completion: nil)
}
}

extension UIViewController {
/**
- Description: 뷰컨에서 GA(구글 애널리틱스) 스크린 , 버튼 이벤트 사용 사는 메서드 입니다.
*/

/// 스크린 이벤트
func analyze(screenName: String) {
GAManager.shared.logEvent(eventType: .screen(screenName: screenName))
}

/// 버튼 이벤트
func analyze(buttonName: String) {
GAManager.shared.logEvent(eventType: .button(buttonName: buttonName))
}
Expand Down
64 changes: 40 additions & 24 deletions Runnect-iOS/Runnect-iOS/Global/Supports/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import FirebaseDynamicLinks
import FirebaseCore
import FirebaseCoreInternal

// 들어온 링크가 공유된 코스인지, 개인 보관함에 있는 코스인지 나타내기 위한 타입입니다.
enum CourseType {
case publicCourse, privateCourse
}
Comment on lines +16 to +18
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sceneDelegate의 역할이 scene의 라이프 사이클을 관리하는 거라서 이 파일 내부에서 CourseType에 대한 정의를 하는게 저희를 제외한 외부한테는 조금 이해하기 어려울 수 있겠다는 생각이 들엇습니다... 장황하게 말하긴 했지만 위치를 옮기거나(근데 옮길 곳이 없는듯..?) 주석을 다는게 어떨까용?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석을 추가 하도록 하겠습니다!


class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?


func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

guard let _ = (scene as? UIWindowScene) else { return }
Expand All @@ -39,30 +43,33 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {

if let incomingURL = userActivity.webpageURL {
let linkHandled = DynamicLinks.dynamicLinks()
DynamicLinks.dynamicLinks()
.handleUniversalLink(incomingURL) { dynamicLink, error in

if let courseId = self.handleDynamicLink(dynamicLink) {
guard let _ = (scene as? UIWindowScene) else { return }
if let (courseType, courseId) = self.handleDynamicLink(dynamicLink) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
let navigationController = UINavigationController()

if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)

switch courseType {
case .publicCourse:
let courseDetailVC = CourseDetailVC()
courseDetailVC.getUploadedCourseDetail(courseId: Int(courseId))

let tabBarController = TabBarController()
let navigationController = UINavigationController(rootViewController: tabBarController)
navigationController.navigationBar.isHidden = true
courseDetailVC.getUploadedCourseDetail(courseId: courseId)
navigationController.pushViewController(courseDetailVC, animated: false)

// 코스 발견 view 로 이동
tabBarController.selectedIndex = 2
window.rootViewController = navigationController
window.makeKeyAndVisible()
self.window = window

case .privateCourse:
let privateCourseDetailVC = RunningWaitingVC()
privateCourseDetailVC.setData(courseId: courseId, publicCourseId: nil)
navigationController.pushViewController(privateCourseDetailVC, animated: false)
}

let tabBarController = TabBarController()
navigationController.navigationBar.isHidden = true
navigationController.viewControllers = [tabBarController, navigationController.viewControllers.last].compactMap { $0 }

tabBarController.selectedIndex = 2
window.rootViewController = navigationController
window.makeKeyAndVisible()
self.window = window
}
}
}
Expand Down Expand Up @@ -106,17 +113,26 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// to restore the scene back to its current state.
}

func handleDynamicLink(_ dynamicLink: DynamicLink?) -> String? {
func handleDynamicLink(_ dynamicLink: DynamicLink?) -> (courseType: CourseType, courseId: Int)? {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guard let link = URL(string: "\(dynamicLinksDomainURIPrefix)/?\(courseParameter)=\(courseId)")
이 부분에서 courseParameter를 지정해준거에 따라 추출해서 courseType을 결정해주는 것으로 이해했는데 맞을까용 ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다. 아래와 같은 코드로 URL Scheme에 해당하는 요소를 빼고 어떤 건지 구별하여 타입을 구분하여 리턴을 하도록 만들었습니다.

item.name == "courseId", item.name == "privateCourseId"

if let dynamicLink = dynamicLink, let url = dynamicLink.url,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems {
var courseId: Int?
var courseType: CourseType?

for item in queryItems {
if item.name == "courseId", let courseId = item.value {
// 동적링크 핸들링 하여 courseId 추출

return courseId
if item.name == "courseId", let id = item.value, let idInt = Int(id) {
courseId = idInt
courseType = .publicCourse
} else if item.name == "privateCourseId", let id = item.value, let idInt = Int(id) {
courseId = idInt
courseType = .privateCourse
}
}

if let courseId = courseId, let courseType = courseType {
return (courseType, courseId)
}
}
return nil
}
Expand Down
6 changes: 4 additions & 2 deletions Runnect-iOS/Runnect-iOS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>2.0.0</string>
<string>2.0.1</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand All @@ -44,7 +46,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>2024.0208.0315</string>
<string>2024.0312.0041</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>kakaokompassauth</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct PrivateCourseResponseDto: Codable {

struct PrivateCourse: Codable {
let id: Int
let isNowUser: Bool?
let title: String
let image, createdAt: String
let distance: Float?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// CourseDetailVC.swift
// Runnect-iOS
//
// Created by 몽이 누나 on 2023/01/05.
// Created by 이명진 on 2023/10/09.
//

import UIKit
Expand All @@ -13,9 +13,6 @@ import NMapsMap
import Moya
import SafariServices
import KakaoSDKCommon
import FirebaseCore
import FirebaseDynamicLinks
import KakaoSDKShare
import KakaoSDKTemplate
import DropDown

Expand Down Expand Up @@ -167,68 +164,26 @@ extension CourseDetailVC {

scrapCourse(scrapTF: !sender.isSelected)
delegate?.didUpdateScrapState(publicCourseId: publicCourseId, isScrapped: !sender.isSelected) /// 코스 발견 UI Update 부분
marathonDelegate?.didUpdateMarathonScrapState(publicCourseId: publicCourseId, isScrapped: !sender.isSelected) // 마라톤 코스 UI Update 부분

/// print("CourseDetailVC 스크랩 탭🔥publicCourseId=\(publicCourseId), isScrapped은 \(!sender.isSelected) 요렇게 변경 ")
marathonDelegate?.didUpdateMarathonScrapState(publicCourseId: publicCourseId, isScrapped: !sender.isSelected) // 마라톤 코스 UI
}

@objc private func shareButtonTapped() {
guard let model = self.uploadedCourseDetailModel else {
return
}

analyze(buttonName: GAEvent.Button.clickShare)

let publicCourse = model.publicCourse
let title = publicCourse.title
let courseId = publicCourse.id // primaryKey
let description = publicCourse.description
let courseImage = publicCourse.image

let dynamicLinksDomainURIPrefix = "https://rnnt.page.link"
guard let link = URL(string: "\(dynamicLinksDomainURIPrefix)/?courseId=\(courseId)") else {
return
}

print("‼️link= \(link)")

guard let linkBuilder = DynamicLinkComponents(link: link, domainURIPrefix: dynamicLinksDomainURIPrefix) else {
return
}

linkBuilder.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.runnect.Runnect-iOS")
linkBuilder.iOSParameters?.appStoreID = "1663884202"
linkBuilder.iOSParameters?.minimumAppVersion = "1.0.4"

linkBuilder.androidParameters = DynamicLinkAndroidParameters(packageName: "com.runnect.runnect")

linkBuilder.socialMetaTagParameters = DynamicLinkSocialMetaTagParameters()
linkBuilder.socialMetaTagParameters?.imageURL = URL(string: courseImage)
linkBuilder.socialMetaTagParameters?.title = title
linkBuilder.socialMetaTagParameters?.descriptionText = description

guard let longDynamicLink = linkBuilder.url else {
return
}
print("The long URL is: \(longDynamicLink)")
analyze(buttonName: GAEvent.Button.clickShare)

/// 짧은 Dynamic Link로 변환하는 부분 입니다.
linkBuilder.shorten { [weak self] url, _, error in // warning 파라미터 와일드 카드
guard let shortDynamicLink = url?.absoluteString else {
if let error = error {
print("❌Error shortening dynamic link: \(error)")
}
return
}

print("🔥The short URL is: \(shortDynamicLink)")

DispatchQueue.main.async {
let activityVC = UIActivityViewController(activityItems: [shortDynamicLink], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self?.view
self?.present(activityVC, animated: true, completion: nil)
}
}
self.shareCourse(
courseTitle: publicCourse.title,
courseId: publicCourse.id,
courseImageURL: publicCourse.image,
minimumAppVersion: "1.0.4",
descriptionText: publicCourse.description,
parameter: "courseId"
)
}

@objc private func pushToUserProfileVC() {
Expand Down Expand Up @@ -479,10 +434,6 @@ extension CourseDetailVC {
$0.width.height.equalTo(37)
}

// profileNameLabel.snp.makeConstraints {
// $0.leading.equalTo(profileImageView.snp.trailing).offset(12)
// }

firstHorizontalDivideLine.snp.makeConstraints {
$0.top.equalTo(mapImageView.snp.bottom).offset(62)
$0.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(14)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,8 @@ extension CourseStorageVC {
guard let self = self else { return }
analyze(buttonName: GAEvent.Button.clickScrapPageStartCourse) // 코스 발견_스크랩코스 상세페이지 시작하기 Event

let title = self.privateCourseList[index].title
let runningWaitingVC = RunningWaitingVC()
runningWaitingVC.setData(courseId: self.privateCourseList[index].id, publicCourseId: nil, courseTitle: title)
runningWaitingVC.setData(courseId: self.privateCourseList[index].id, publicCourseId: nil)

/// 코스 이름을 여기서 가져오는 로직
runningWaitingVC.hidesBottomBarWhenPushed = true
Expand Down
Loading