diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..60e76aaf --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,25 @@ +## ๐ŸŒฑ ์ž‘์—…ํ•œ ๋‚ด์šฉ + + + +- ์ž‘์—… ๋‚ด์šฉ 1 + +## ๐ŸŒฑ PR Point + + + +- PR Point 1 + +## ๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท + + + +| ๊ตฌํ˜„ ๋‚ด์šฉ | ์Šคํฌ๋ฆฐ์ƒท | +| :-------------: | :----------: | +| ex. ๋กœ๊ทธ์ธ ํ™”๋ฉด | ํŒŒ์ผ์ฒจ๋ถ€๋ฐ”๋žŒ | + +## ๐Ÿ“ฎ ๊ด€๋ จ ์ด์Šˆ + + + +- Resolved: #์ด์Šˆ๋ฒˆํ˜ธ diff --git a/.gitignore b/.gitignore index 2de5e93b..33136889 100644 --- a/.gitignore +++ b/.gitignore @@ -106,4 +106,5 @@ iOSInjectionProject/ /*.gcno **/xcshareddata/WorkspaceSettings.xcsettings -# End of https://www.toptal.com/developers/gitignore/api/xcode,swift,cocoapods \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/xcode,swift,cocoapods +.DS_Store diff --git a/Runnect-iOS/.swiftlint.yml b/Runnect-iOS/.swiftlint.yml new file mode 100644 index 00000000..b4b8db1c --- /dev/null +++ b/Runnect-iOS/.swiftlint.yml @@ -0,0 +1,16 @@ +disabled_rules: + - trailing_whitespace + - identifier_name + - line_length + - type_name + - legacy_constructor + - unused_setter_value + +included: + - Runnect-iOS +excluded: + # AppDelegate, SceneDelegate file ๋ฌด์‹œ + - Runnect-iOS/Global/Supports/AppDelegate.swift + - Runnect-iOS/Global/Supports/SceneDelegate.swift + +force_cast: warning diff --git a/Runnect-iOS/Podfile b/Runnect-iOS/Podfile new file mode 100644 index 00000000..f82d3dbf --- /dev/null +++ b/Runnect-iOS/Podfile @@ -0,0 +1,16 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '14.0' + +target 'Runnect-iOS' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + pod 'NMapsMap' + pod 'Kingfisher', '~> 7.0' + pod 'SnapKit', '~> 5.6.0' + pod 'Moya', '~> 15.0' + + + # Pods for Runnect-iOS + +end diff --git a/Runnect-iOS/Podfile.lock b/Runnect-iOS/Podfile.lock new file mode 100644 index 00000000..f73983c6 --- /dev/null +++ b/Runnect-iOS/Podfile.lock @@ -0,0 +1,38 @@ +PODS: + - Alamofire (5.6.4) + - Kingfisher (7.4.1) + - Moya (15.0.0): + - Moya/Core (= 15.0.0) + - Moya/Core (15.0.0): + - Alamofire (~> 5.0) + - NMapsGeometry (1.0.1) + - NMapsMap (3.16.1): + - NMapsGeometry + - SnapKit (5.6.0) + +DEPENDENCIES: + - Kingfisher (~> 7.0) + - Moya (~> 15.0) + - NMapsMap + - SnapKit (~> 5.6.0) + +SPEC REPOS: + trunk: + - Alamofire + - Kingfisher + - Moya + - NMapsGeometry + - NMapsMap + - SnapKit + +SPEC CHECKSUMS: + Alamofire: 4e95d97098eacb88856099c4fc79b526a299e48c + Kingfisher: cd762a593a61b2fbecf7645c00f9a801a3ebfc9c + Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee + NMapsGeometry: 53c573ead66466681cf123f99f698dc8071a4b83 + NMapsMap: 926c3a303d381a24bec8da3cd6e198f50af93ae9 + SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25 + +PODFILE CHECKSUM: f23513cb80e72754bbf29355e1160abe4b35c783 + +COCOAPODS: 1.11.3 diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj new file mode 100644 index 00000000..dd2eb573 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -0,0 +1,778 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 0AEBD608F3973389E8E1C6D6 /* Pods_Runnect_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 015778D02D5CDE0838284CD7 /* Pods_Runnect_iOS.framework */; }; + CE4545C9295D7AF4003201E1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4545C8295D7AF4003201E1 /* AppDelegate.swift */; }; + CE4545CB295D7AF4003201E1 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4545CA295D7AF4003201E1 /* SceneDelegate.swift */; }; + CE4545CD295D7AF4003201E1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4545CC295D7AF4003201E1 /* ViewController.swift */; }; + CE4545D2295D7AF5003201E1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE4545D1295D7AF5003201E1 /* Assets.xcassets */; }; + CE4545D5295D7AF5003201E1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE4545D3295D7AF5003201E1 /* LaunchScreen.storyboard */; }; + CE6655BF295D82E200C64E12 /* .gitkeep in Resources */ = {isa = PBXBuildFile; fileRef = CE6655BE295D82E200C64E12 /* .gitkeep */; }; + CE6655C8295D849F00C64E12 /* StringLiterals.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655C7295D849F00C64E12 /* StringLiterals.swift */; }; + CE6655CA295D84DD00C64E12 /* UserDefaultKeyList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655C9295D84DD00C64E12 /* UserDefaultKeyList.swift */; }; + CE6655CD295D856300C64E12 /* KeyPathFindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655CC295D856300C64E12 /* KeyPathFindable.swift */; }; + CE6655D0295D85FF00C64E12 /* CancelBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655CF295D85FF00C64E12 /* CancelBag.swift */; }; + CE6655D2295D862A00C64E12 /* Publisher+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655D1295D862A00C64E12 /* Publisher+Driver.swift */; }; + CE6655D4295D865B00C64E12 /* Publisher+UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655D3295D865B00C64E12 /* Publisher+UIControl.swift */; }; + CE6655D7295D86F900C64E12 /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655D6295D86F900C64E12 /* String+.swift */; }; + CE6655D9295D871B00C64E12 /* URL+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655D8295D871B00C64E12 /* URL+.swift */; }; + CE6655DC295D873500C64E12 /* UIButton+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655DB295D873500C64E12 /* UIButton+.swift */; }; + CE6655DE295D877F00C64E12 /* UIColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655DD295D877F00C64E12 /* UIColor+.swift */; }; + CE6655E0295D87D200C64E12 /* UIDevice+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655DF295D87D200C64E12 /* UIDevice+.swift */; }; + CE6655E2295D87EB00C64E12 /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655E1295D87EB00C64E12 /* UIImage+.swift */; }; + CE6655E4295D884600C64E12 /* UILabel+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655E3295D884600C64E12 /* UILabel+.swift */; }; + CE6655E6295D887F00C64E12 /* UIStackView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655E5295D887F00C64E12 /* UIStackView+.swift */; }; + CE6655E8295D889600C64E12 /* UISwitch+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655E7295D889600C64E12 /* UISwitch+.swift */; }; + CE6655EA295D88B200C64E12 /* UITabBar+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655E9295D88B200C64E12 /* UITabBar+.swift */; }; + CE6655EC295D88D000C64E12 /* UITableView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655EB295D88D000C64E12 /* UITableView+.swift */; }; + CE6655EE295D88E600C64E12 /* UITextField+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655ED295D88E600C64E12 /* UITextField+.swift */; }; + CE6655F0295D891B00C64E12 /* UITextView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655EF295D891B00C64E12 /* UITextView+.swift */; }; + CE6655F2295D894D00C64E12 /* UIView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655F1295D894D00C64E12 /* UIView+.swift */; }; + CE6655F4295D898400C64E12 /* UIViewController+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655F3295D898400C64E12 /* UIViewController+.swift */; }; + CE6655F6295D90B600C64E12 /* addToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655F5295D90B600C64E12 /* addToolBar.swift */; }; + CE6655F8295D90CF00C64E12 /* adjusted+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655F7295D90CF00C64E12 /* adjusted+.swift */; }; + CE6655FA295D90E000C64E12 /* applyShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655F9295D90E000C64E12 /* applyShadow.swift */; }; + CE6655FC295D90F500C64E12 /* calculatePastTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655FB295D90F500C64E12 /* calculatePastTime.swift */; }; + CE6655FE295D912300C64E12 /* calculateTopInset.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655FD295D912300C64E12 /* calculateTopInset.swift */; }; + CE665600295D915D00C64E12 /* getClassName.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6655FF295D915D00C64E12 /* getClassName.swift */; }; + CE665602295D918000C64E12 /* JsonCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE665601295D918000C64E12 /* JsonCoder.swift */; }; + CE665604295D91B100C64E12 /* makeAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE665603295D91B100C64E12 /* makeAlert.swift */; }; + CE665606295D91C500C64E12 /* makeVibrate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE665605295D91C500C64E12 /* makeVibrate.swift */; }; + CE665608295D921500C64E12 /* setImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE665607295D921500C64E12 /* setImage.swift */; }; + CE66560A295D924A00C64E12 /* Result+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE665609295D924A00C64E12 /* Result+.swift */; }; + CE66560C295D928300C64E12 /* setRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE66560B295D928300C64E12 /* setRootViewController.swift */; }; + CE66560E295D92A500C64E12 /* setStatusBarBackgroundColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE66560D295D92A500C64E12 /* setStatusBarBackgroundColor.swift */; }; + CE665610295D92C200C64E12 /* setTextLineHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE66560F295D92C200C64E12 /* setTextLineHeight.swift */; }; + CE665612295D92E400C64E12 /* UserDefaultWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE665611295D92E400C64E12 /* UserDefaultWrapper.swift */; }; + CE665615295D989A00C64E12 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = CE665614295D989A00C64E12 /* .swiftlint.yml */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 015778D02D5CDE0838284CD7 /* Pods_Runnect_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runnect_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3C3033C911343B5C57EB68E7 /* Pods-Runnect-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runnect-iOS.debug.xcconfig"; path = "Target Support Files/Pods-Runnect-iOS/Pods-Runnect-iOS.debug.xcconfig"; sourceTree = ""; }; + CE4545C5295D7AF4003201E1 /* Runnect-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Runnect-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + CE4545C8295D7AF4003201E1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + CE4545CA295D7AF4003201E1 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + CE4545CC295D7AF4003201E1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + CE4545D1295D7AF5003201E1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CE4545D4295D7AF5003201E1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + CE4545D6295D7AF5003201E1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CE6655B8295D81C900C64E12 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; + CE6655BC295D82CF00C64E12 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; + CE6655BD295D82D800C64E12 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; + CE6655BE295D82E200C64E12 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; + CE6655C0295D82F000C64E12 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; + CE6655C1295D82F700C64E12 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; + CE6655C5295D83B700C64E12 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; + CE6655C6295D83BD00C64E12 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; + CE6655C7295D849F00C64E12 /* StringLiterals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringLiterals.swift; sourceTree = ""; }; + CE6655C9295D84DD00C64E12 /* UserDefaultKeyList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultKeyList.swift; sourceTree = ""; }; + CE6655CC295D856300C64E12 /* KeyPathFindable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPathFindable.swift; sourceTree = ""; }; + CE6655CF295D85FF00C64E12 /* CancelBag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelBag.swift; sourceTree = ""; }; + CE6655D1295D862A00C64E12 /* Publisher+Driver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+Driver.swift"; sourceTree = ""; }; + CE6655D3295D865B00C64E12 /* Publisher+UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+UIControl.swift"; sourceTree = ""; }; + CE6655D6295D86F900C64E12 /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; + CE6655D8295D871B00C64E12 /* URL+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+.swift"; sourceTree = ""; }; + CE6655DB295D873500C64E12 /* UIButton+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+.swift"; sourceTree = ""; }; + CE6655DD295D877F00C64E12 /* UIColor+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+.swift"; sourceTree = ""; }; + CE6655DF295D87D200C64E12 /* UIDevice+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+.swift"; sourceTree = ""; }; + CE6655E1295D87EB00C64E12 /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = ""; }; + CE6655E3295D884600C64E12 /* UILabel+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+.swift"; sourceTree = ""; }; + CE6655E5295D887F00C64E12 /* UIStackView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+.swift"; sourceTree = ""; }; + CE6655E7295D889600C64E12 /* UISwitch+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISwitch+.swift"; sourceTree = ""; }; + CE6655E9295D88B200C64E12 /* UITabBar+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITabBar+.swift"; sourceTree = ""; }; + CE6655EB295D88D000C64E12 /* UITableView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+.swift"; sourceTree = ""; }; + CE6655ED295D88E600C64E12 /* UITextField+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+.swift"; sourceTree = ""; }; + CE6655EF295D891B00C64E12 /* UITextView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+.swift"; sourceTree = ""; }; + CE6655F1295D894D00C64E12 /* UIView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+.swift"; sourceTree = ""; }; + CE6655F3295D898400C64E12 /* UIViewController+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+.swift"; sourceTree = ""; }; + CE6655F5295D90B600C64E12 /* addToolBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = addToolBar.swift; sourceTree = ""; }; + CE6655F7295D90CF00C64E12 /* adjusted+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "adjusted+.swift"; sourceTree = ""; }; + CE6655F9295D90E000C64E12 /* applyShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = applyShadow.swift; sourceTree = ""; }; + CE6655FB295D90F500C64E12 /* calculatePastTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = calculatePastTime.swift; sourceTree = ""; }; + CE6655FD295D912300C64E12 /* calculateTopInset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = calculateTopInset.swift; sourceTree = ""; }; + CE6655FF295D915D00C64E12 /* getClassName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = getClassName.swift; sourceTree = ""; }; + CE665601295D918000C64E12 /* JsonCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsonCoder.swift; sourceTree = ""; }; + CE665603295D91B100C64E12 /* makeAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = makeAlert.swift; sourceTree = ""; }; + CE665605295D91C500C64E12 /* makeVibrate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = makeVibrate.swift; sourceTree = ""; }; + CE665607295D921500C64E12 /* setImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = setImage.swift; sourceTree = ""; }; + CE665609295D924A00C64E12 /* Result+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+.swift"; sourceTree = ""; }; + CE66560B295D928300C64E12 /* setRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = setRootViewController.swift; sourceTree = ""; }; + CE66560D295D92A500C64E12 /* setStatusBarBackgroundColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = setStatusBarBackgroundColor.swift; sourceTree = ""; }; + CE66560F295D92C200C64E12 /* setTextLineHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = setTextLineHeight.swift; sourceTree = ""; }; + CE665611295D92E400C64E12 /* UserDefaultWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultWrapper.swift; sourceTree = ""; }; + CE665614295D989A00C64E12 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + E837271A78E1C0A0C30789BF /* Pods-Runnect-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runnect-iOS.release.xcconfig"; path = "Target Support Files/Pods-Runnect-iOS/Pods-Runnect-iOS.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE4545C2295D7AF4003201E1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0AEBD608F3973389E8E1C6D6 /* Pods_Runnect_iOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3F7098551CF7A77F3FE7EB2B /* Pods */ = { + isa = PBXGroup; + children = ( + 3C3033C911343B5C57EB68E7 /* Pods-Runnect-iOS.debug.xcconfig */, + E837271A78E1C0A0C30789BF /* Pods-Runnect-iOS.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 4BAB0487E0060209ADC1C28C /* Frameworks */ = { + isa = PBXGroup; + children = ( + 015778D02D5CDE0838284CD7 /* Pods_Runnect_iOS.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + CE4545BC295D7AF4003201E1 = { + isa = PBXGroup; + children = ( + CE665614295D989A00C64E12 /* .swiftlint.yml */, + CE4545C7295D7AF4003201E1 /* Runnect-iOS */, + CE4545C6295D7AF4003201E1 /* Products */, + 3F7098551CF7A77F3FE7EB2B /* Pods */, + 4BAB0487E0060209ADC1C28C /* Frameworks */, + ); + sourceTree = ""; + }; + CE4545C6295D7AF4003201E1 /* Products */ = { + isa = PBXGroup; + children = ( + CE4545C5295D7AF4003201E1 /* Runnect-iOS.app */, + ); + name = Products; + sourceTree = ""; + }; + CE4545C7295D7AF4003201E1 /* Runnect-iOS */ = { + isa = PBXGroup; + children = ( + CE6655AA295D7FAE00C64E12 /* Global */, + CE6655A9295D7FAA00C64E12 /* Network */, + CE6655A8295D7F7D00C64E12 /* Presentation */, + CE4545D6295D7AF5003201E1 /* Info.plist */, + ); + path = "Runnect-iOS"; + sourceTree = ""; + }; + CE6655A8295D7F7D00C64E12 /* Presentation */ = { + isa = PBXGroup; + children = ( + CE6655AB295D7FBC00C64E12 /* TabBar */, + CE6655C2295D836D00C64E12 /* SignIn */, + ); + path = Presentation; + sourceTree = ""; + }; + CE6655A9295D7FAA00C64E12 /* Network */ = { + isa = PBXGroup; + children = ( + CE6655B0295D800700C64E12 /* Dto */, + CE6655AF295D7FF600C64E12 /* Foundation */, + CE6655AE295D7FF000C64E12 /* Service */, + CE6655AD295D7FEA00C64E12 /* Router */, + CE6655AC295D7FE400C64E12 /* Model */, + ); + path = Network; + sourceTree = ""; + }; + CE6655AA295D7FAE00C64E12 /* Global */ = { + isa = PBXGroup; + children = ( + CE6655B6295D803C00C64E12 /* UIComponents */, + CE6655B5295D803800C64E12 /* Literal */, + CE6655B4295D803400C64E12 /* Resource */, + CE6655B3295D802800C64E12 /* Supports */, + CE6655B2295D801A00C64E12 /* Extension */, + CE6655B1295D801700C64E12 /* Utils */, + CE6655CB295D851800C64E12 /* Protocols */, + ); + path = Global; + sourceTree = ""; + }; + CE6655AB295D7FBC00C64E12 /* TabBar */ = { + isa = PBXGroup; + children = ( + CE4545CC295D7AF4003201E1 /* ViewController.swift */, + ); + path = TabBar; + sourceTree = ""; + }; + CE6655AC295D7FE400C64E12 /* Model */ = { + isa = PBXGroup; + children = ( + CE6655C1295D82F700C64E12 /* .gitkeep */, + ); + path = Model; + sourceTree = ""; + }; + CE6655AD295D7FEA00C64E12 /* Router */ = { + isa = PBXGroup; + children = ( + CE6655C0295D82F000C64E12 /* .gitkeep */, + ); + path = Router; + sourceTree = ""; + }; + CE6655AE295D7FF000C64E12 /* Service */ = { + isa = PBXGroup; + children = ( + CE6655BE295D82E200C64E12 /* .gitkeep */, + ); + path = Service; + sourceTree = ""; + }; + CE6655AF295D7FF600C64E12 /* Foundation */ = { + isa = PBXGroup; + children = ( + CE6655BD295D82D800C64E12 /* .gitkeep */, + ); + path = Foundation; + sourceTree = ""; + }; + CE6655B0295D800700C64E12 /* Dto */ = { + isa = PBXGroup; + children = ( + CE6655BC295D82CF00C64E12 /* .gitkeep */, + ); + path = Dto; + sourceTree = ""; + }; + CE6655B1295D801700C64E12 /* Utils */ = { + isa = PBXGroup; + children = ( + CE6655F5295D90B600C64E12 /* addToolBar.swift */, + CE6655F7295D90CF00C64E12 /* adjusted+.swift */, + CE6655F9295D90E000C64E12 /* applyShadow.swift */, + CE6655FB295D90F500C64E12 /* calculatePastTime.swift */, + CE6655FD295D912300C64E12 /* calculateTopInset.swift */, + CE6655FF295D915D00C64E12 /* getClassName.swift */, + CE665601295D918000C64E12 /* JsonCoder.swift */, + CE665603295D91B100C64E12 /* makeAlert.swift */, + CE665605295D91C500C64E12 /* makeVibrate.swift */, + CE665607295D921500C64E12 /* setImage.swift */, + CE66560B295D928300C64E12 /* setRootViewController.swift */, + CE66560D295D92A500C64E12 /* setStatusBarBackgroundColor.swift */, + CE66560F295D92C200C64E12 /* setTextLineHeight.swift */, + CE665611295D92E400C64E12 /* UserDefaultWrapper.swift */, + ); + path = Utils; + sourceTree = ""; + }; + CE6655B2295D801A00C64E12 /* Extension */ = { + isa = PBXGroup; + children = ( + CE6655DA295D872900C64E12 /* UIKit+ */, + CE6655D5295D86EB00C64E12 /* Foundation+ */, + CE6655CE295D85E900C64E12 /* Combine+ */, + ); + path = Extension; + sourceTree = ""; + }; + CE6655B3295D802800C64E12 /* Supports */ = { + isa = PBXGroup; + children = ( + CE4545D3295D7AF5003201E1 /* LaunchScreen.storyboard */, + CE4545C8295D7AF4003201E1 /* AppDelegate.swift */, + CE4545CA295D7AF4003201E1 /* SceneDelegate.swift */, + ); + path = Supports; + sourceTree = ""; + }; + CE6655B4295D803400C64E12 /* Resource */ = { + isa = PBXGroup; + children = ( + CE4545D1295D7AF5003201E1 /* Assets.xcassets */, + ); + path = Resource; + sourceTree = ""; + }; + CE6655B5295D803800C64E12 /* Literal */ = { + isa = PBXGroup; + children = ( + CE6655C7295D849F00C64E12 /* StringLiterals.swift */, + CE6655C9295D84DD00C64E12 /* UserDefaultKeyList.swift */, + ); + path = Literal; + sourceTree = ""; + }; + CE6655B6295D803C00C64E12 /* UIComponents */ = { + isa = PBXGroup; + children = ( + CE6655B8295D81C900C64E12 /* .gitkeep */, + ); + path = UIComponents; + sourceTree = ""; + }; + CE6655C2295D836D00C64E12 /* SignIn */ = { + isa = PBXGroup; + children = ( + CE6655C4295D838100C64E12 /* Views */, + CE6655C3295D837800C64E12 /* VC */, + ); + path = SignIn; + sourceTree = ""; + }; + CE6655C3295D837800C64E12 /* VC */ = { + isa = PBXGroup; + children = ( + CE6655C6295D83BD00C64E12 /* .gitkeep */, + ); + path = VC; + sourceTree = ""; + }; + CE6655C4295D838100C64E12 /* Views */ = { + isa = PBXGroup; + children = ( + CE6655C5295D83B700C64E12 /* .gitkeep */, + ); + path = Views; + sourceTree = ""; + }; + CE6655CB295D851800C64E12 /* Protocols */ = { + isa = PBXGroup; + children = ( + CE6655CC295D856300C64E12 /* KeyPathFindable.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + CE6655CE295D85E900C64E12 /* Combine+ */ = { + isa = PBXGroup; + children = ( + CE6655CF295D85FF00C64E12 /* CancelBag.swift */, + CE6655D1295D862A00C64E12 /* Publisher+Driver.swift */, + CE6655D3295D865B00C64E12 /* Publisher+UIControl.swift */, + ); + path = "Combine+"; + sourceTree = ""; + }; + CE6655D5295D86EB00C64E12 /* Foundation+ */ = { + isa = PBXGroup; + children = ( + CE6655D6295D86F900C64E12 /* String+.swift */, + CE6655D8295D871B00C64E12 /* URL+.swift */, + CE665609295D924A00C64E12 /* Result+.swift */, + ); + path = "Foundation+"; + sourceTree = ""; + }; + CE6655DA295D872900C64E12 /* UIKit+ */ = { + isa = PBXGroup; + children = ( + CE6655DB295D873500C64E12 /* UIButton+.swift */, + CE6655DD295D877F00C64E12 /* UIColor+.swift */, + CE6655DF295D87D200C64E12 /* UIDevice+.swift */, + CE6655E1295D87EB00C64E12 /* UIImage+.swift */, + CE6655E3295D884600C64E12 /* UILabel+.swift */, + CE6655E5295D887F00C64E12 /* UIStackView+.swift */, + CE6655E7295D889600C64E12 /* UISwitch+.swift */, + CE6655E9295D88B200C64E12 /* UITabBar+.swift */, + CE6655EB295D88D000C64E12 /* UITableView+.swift */, + CE6655ED295D88E600C64E12 /* UITextField+.swift */, + CE6655EF295D891B00C64E12 /* UITextView+.swift */, + CE6655F1295D894D00C64E12 /* UIView+.swift */, + CE6655F3295D898400C64E12 /* UIViewController+.swift */, + ); + path = "UIKit+"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CE4545C4295D7AF4003201E1 /* Runnect-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE4545D9295D7AF5003201E1 /* Build configuration list for PBXNativeTarget "Runnect-iOS" */; + buildPhases = ( + 5E54A0A4C236151ED373DD47 /* [CP] Check Pods Manifest.lock */, + CE665613295D980700C64E12 /* ShellScript */, + CE4545C1295D7AF4003201E1 /* Sources */, + CE4545C2295D7AF4003201E1 /* Frameworks */, + CE4545C3295D7AF4003201E1 /* Resources */, + CF0A7C4CF1329EF14A755C46 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Runnect-iOS"; + productName = "Runnect-iOS"; + productReference = CE4545C5295D7AF4003201E1 /* Runnect-iOS.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE4545BD295D7AF4003201E1 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1410; + LastUpgradeCheck = 1410; + TargetAttributes = { + CE4545C4295D7AF4003201E1 = { + CreatedOnToolsVersion = 14.1; + }; + }; + }; + buildConfigurationList = CE4545C0295D7AF4003201E1 /* Build configuration list for PBXProject "Runnect-iOS" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE4545BC295D7AF4003201E1; + productRefGroup = CE4545C6295D7AF4003201E1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CE4545C4295D7AF4003201E1 /* Runnect-iOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CE4545C3295D7AF4003201E1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE665615295D989A00C64E12 /* .swiftlint.yml in Resources */, + CE6655BF295D82E200C64E12 /* .gitkeep in Resources */, + CE4545D5295D7AF5003201E1 /* LaunchScreen.storyboard in Resources */, + CE4545D2295D7AF5003201E1 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 5E54A0A4C236151ED373DD47 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runnect-iOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + CE665613295D980700C64E12 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 12; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; + CF0A7C4CF1329EF14A755C46 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runnect-iOS/Pods-Runnect-iOS-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runnect-iOS/Pods-Runnect-iOS-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runnect-iOS/Pods-Runnect-iOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CE4545C1295D7AF4003201E1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE665604295D91B100C64E12 /* makeAlert.swift in Sources */, + CE6655EA295D88B200C64E12 /* UITabBar+.swift in Sources */, + CE665602295D918000C64E12 /* JsonCoder.swift in Sources */, + CE4545CD295D7AF4003201E1 /* ViewController.swift in Sources */, + CE6655F4295D898400C64E12 /* UIViewController+.swift in Sources */, + CE6655CD295D856300C64E12 /* KeyPathFindable.swift in Sources */, + CE6655E4295D884600C64E12 /* UILabel+.swift in Sources */, + CE6655FA295D90E000C64E12 /* applyShadow.swift in Sources */, + CE665606295D91C500C64E12 /* makeVibrate.swift in Sources */, + CE66560A295D924A00C64E12 /* Result+.swift in Sources */, + CE66560E295D92A500C64E12 /* setStatusBarBackgroundColor.swift in Sources */, + CE6655D7295D86F900C64E12 /* String+.swift in Sources */, + CE4545C9295D7AF4003201E1 /* AppDelegate.swift in Sources */, + CE6655C8295D849F00C64E12 /* StringLiterals.swift in Sources */, + CE6655E0295D87D200C64E12 /* UIDevice+.swift in Sources */, + CE6655E8295D889600C64E12 /* UISwitch+.swift in Sources */, + CE6655F6295D90B600C64E12 /* addToolBar.swift in Sources */, + CE6655F0295D891B00C64E12 /* UITextView+.swift in Sources */, + CE6655EE295D88E600C64E12 /* UITextField+.swift in Sources */, + CE6655F8295D90CF00C64E12 /* adjusted+.swift in Sources */, + CE4545CB295D7AF4003201E1 /* SceneDelegate.swift in Sources */, + CE6655FE295D912300C64E12 /* calculateTopInset.swift in Sources */, + CE6655D0295D85FF00C64E12 /* CancelBag.swift in Sources */, + CE6655DC295D873500C64E12 /* UIButton+.swift in Sources */, + CE6655D4295D865B00C64E12 /* Publisher+UIControl.swift in Sources */, + CE6655EC295D88D000C64E12 /* UITableView+.swift in Sources */, + CE66560C295D928300C64E12 /* setRootViewController.swift in Sources */, + CE6655D9295D871B00C64E12 /* URL+.swift in Sources */, + CE6655DE295D877F00C64E12 /* UIColor+.swift in Sources */, + CE665608295D921500C64E12 /* setImage.swift in Sources */, + CE665612295D92E400C64E12 /* UserDefaultWrapper.swift in Sources */, + CE665610295D92C200C64E12 /* setTextLineHeight.swift in Sources */, + CE6655E2295D87EB00C64E12 /* UIImage+.swift in Sources */, + CE6655D2295D862A00C64E12 /* Publisher+Driver.swift in Sources */, + CE6655E6295D887F00C64E12 /* UIStackView+.swift in Sources */, + CE6655CA295D84DD00C64E12 /* UserDefaultKeyList.swift in Sources */, + CE6655F2295D894D00C64E12 /* UIView+.swift in Sources */, + CE665600295D915D00C64E12 /* getClassName.swift in Sources */, + CE6655FC295D90F500C64E12 /* calculatePastTime.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + CE4545D3295D7AF5003201E1 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CE4545D4295D7AF5003201E1 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + CE4545D7295D7AF5003201E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CE4545D8295D7AF5003201E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CE4545DA295D7AF5003201E1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3C3033C911343B5C57EB68E7 /* Pods-Runnect-iOS.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Runnect-iOS/Info.plist"; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.runnect.Runnect-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CE4545DB295D7AF5003201E1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E837271A78E1C0A0C30789BF /* Pods-Runnect-iOS.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Runnect-iOS/Info.plist"; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.runnect.Runnect-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE4545C0295D7AF4003201E1 /* Build configuration list for PBXProject "Runnect-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE4545D7295D7AF5003201E1 /* Debug */, + CE4545D8295D7AF5003201E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE4545D9295D7AF5003201E1 /* Build configuration list for PBXNativeTarget "Runnect-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE4545DA295D7AF5003201E1 /* Debug */, + CE4545DB295D7AF5003201E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE4545BD295D7AF4003201E1 /* Project object */; +} diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Runnect-iOS/Runnect-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Runnect-iOS/Runnect-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Runnect-iOS/Runnect-iOS.xcworkspace/contents.xcworkspacedata b/Runnect-iOS/Runnect-iOS.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..07cc0133 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Runnect-iOS/Runnect-iOS.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Runnect-iOS/Runnect-iOS.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/CancelBag.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/CancelBag.swift new file mode 100644 index 00000000..d42da6fe --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/CancelBag.swift @@ -0,0 +1,25 @@ +// +// CancelBag.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Combine + +class CancelBag { + var subscriptions = Set() + + func cancel() { + subscriptions.forEach { $0.cancel() } + subscriptions.removeAll() + } + + init() { } +} + +extension AnyCancellable { + func store(in cancelBag: CancelBag) { + cancelBag.subscriptions.insert(self) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/Publisher+Driver.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/Publisher+Driver.swift new file mode 100644 index 00000000..db865d74 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/Publisher+Driver.swift @@ -0,0 +1,27 @@ +// +// Publisher+Driver.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Combine +import Foundation + +public typealias Driver = AnyPublisher + +public extension Publisher { + func asDriver() -> Driver { + return self.catch { _ in Empty() } + .receive(on: RunLoop.main) + .eraseToAnyPublisher() + } + + static func just(_ output: Output) -> Driver { + return Just(output).eraseToAnyPublisher() + } + + static func empty() -> Driver { + return Empty().eraseToAnyPublisher() + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/Publisher+UIControl.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/Publisher+UIControl.swift new file mode 100644 index 00000000..57298e79 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/Combine+/Publisher+UIControl.swift @@ -0,0 +1,63 @@ +// +// Publisher+UIControl.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit +import Combine + +protocol CombineCompatible { } + +extension UIControl: CombineCompatible { } + +extension CombineCompatible where Self: UIControl { + func publisher(for events: UIControl.Event) -> UIControlPublisher { + return UIControlPublisher(control: self, events: events) + } +} + +final class UIControlSubscription: Subscription where SubscriberType.Input == Control { + private var subscriber: SubscriberType? + private let control: Control + + init(subscriber: SubscriberType, control: Control, event: UIControl.Event) { + self.subscriber = subscriber + self.control = control + control.addTarget(self, action: #selector(eventHandler), for: event) + } + + func request(_ demand: Subscribers.Demand) { + + } + + func cancel() { + subscriber = nil + } + + @objc private func eventHandler() { + _ = subscriber?.receive(control) + } +} + +struct UIControlPublisher: Publisher { + + public typealias Output = Control + public typealias Failure = Never + + let control: Control + let controlEvents: UIControl.Event + + init(control: Control, events: UIControl.Event) { + self.control = control + self.controlEvents = events + } + + func receive(subscriber: S) where S: Subscriber, + S.Failure == UIControlPublisher.Failure, + S.Input == UIControlPublisher.Output { + let subscription = UIControlSubscription(subscriber: subscriber, control: control, event: controlEvents) + subscriber.receive(subscription: subscription) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/Result+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/Result+.swift new file mode 100644 index 00000000..60dd7648 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/Result+.swift @@ -0,0 +1,26 @@ +// +// Result+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Foundation + +extension Result { + @discardableResult + func success(_ successHandler: (Success) -> Void) -> Result { + if case .success(let value) = self { + successHandler(value) + } + return self + } + + @discardableResult + func `catch`(_ failureHandler: (Failure) -> Void) -> Result { + if case .failure(let error) = self { + failureHandler(error) + } + return self + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/String+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/String+.swift new file mode 100644 index 00000000..a26b84d4 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/String+.swift @@ -0,0 +1,67 @@ +// +// String+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension String { + + /// String์„ UIImage๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ + func makeImage() -> UIImage? { + if let data = Data(base64Encoded: self, options: .ignoreUnknownCharacters) { + return UIImage(data: data) + } + return nil + } + + /// ์„œ๋ฒ„์—์„œ ๋“ค์–ด์˜จ Date String์„ Date ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ + private func toDate() -> Date { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + dateFormatter.timeZone = TimeZone(identifier: "KST") + if let date = dateFormatter.date(from: self) { + return date + } else { + print("toDate() convert error") + return Date() + } + } + + /// serverTimeToString์˜ ์šฉ๋„ ์ •์˜ + enum TimeStringCase { + case forNotification + case forDefault + } + + /// ์„œ๋ฒ„์—์„œ ๋“ค์–ด์˜จ Date String์„ UI์— ์ ์šฉ ๊ฐ€๋Šฅํ•œ String ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ + func serverTimeToString(forUse: TimeStringCase) -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yy/MM/dd" + + let currentTime = Int(Date().timeIntervalSince1970) + + switch forUse { + case .forNotification: + let getTime = self.toDate().timeIntervalSince1970 + let displaySec = currentTime - Int(getTime) + let displayMin = displaySec / 60 + let displayHour = displayMin / 60 + let displayDay = displayHour / 24 + + if displayDay >= 1 { + return dateFormatter.string(from: self.toDate()) + } else if displayHour >= 1 { + return "\(displayHour)์‹œ๊ฐ„ ์ „" + } else if displayMin >= 1 { + return "\(displayMin)๋ถ„ ์ „" + } else { + return "1๋ถ„ ์ „" + } + case .forDefault: + return dateFormatter.string(from: self.toDate()) + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/URL+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/URL+.swift new file mode 100644 index 00000000..e06d6dca --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/Foundation+/URL+.swift @@ -0,0 +1,20 @@ +// +// URL+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Foundation + +extension URL { + /// - Description: URL์— ํ•œ๊ตญ์–ด๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ, ํผ์„ผํŠธ ๋ฌธ์ž๋กœ ์น˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค + static func decodeURL(urlString: String) -> URL? { + if let url = URL(string: urlString) { + return url + } else { + let encodedString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? urlString + return URL(string: encodedString) + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIButton+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIButton+.swift new file mode 100644 index 00000000..70dcfdce --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIButton+.swift @@ -0,0 +1,92 @@ +// +// UIButton+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIButton { + + /// ๋ฒ„ํŠผ Background Color๋ฅผ ์ƒํƒœ๋ณ„๋กœ ์ง€์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ + func setBackgroundColor(_ color: UIColor, for state: UIControl.State) { + let minimumSize: CGSize = CGSize(width: 1.0, height: 1.0) + + UIGraphicsBeginImageContext(minimumSize) + + if let context = UIGraphicsGetCurrentContext() { + context.setFillColor(color.cgColor) + context.fill(CGRect(origin: .zero, size: minimumSize)) + } + + let colorImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + self.clipsToBounds = true + self.setBackgroundImage(colorImage, for: state) + } + + func setUnderline() { + guard let title = title(for: .normal) else { return } + let attributedString = NSMutableAttributedString(string: title) + attributedString.addAttribute(.underlineStyle, + value: NSUnderlineStyle.single.rawValue, + range: NSRange(location: 0, length: title.count) + ) + setAttributedTitle(attributedString, for: .normal) + } +} + +extension UIButton { + + /** + button์— ๋Œ€ํ•ด addTargetํ•ด์„œ ์ผ์ผ์ด ์ฒ˜๋ฆฌ์•ˆํ•˜๊ณ , closure ํ˜•ํƒœ๋กœ ๋™์ž‘์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ extension์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค + press๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ์•ˆ์— ๋ฒ„ํŠผ์ด ๋ˆŒ๋ ธ์„ ๋•Œ, ๋™์ž‘ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. + + clicked(completion : @escaping ((Bool) -> Void)) ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•ด, + ๋ฒ„ํŠผ์ด ๋ˆŒ๋ ธ์„๋•Œ, ์ค„์–ด๋“ค์—ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ ๋Š˜์–ด๋‚˜๋Š” (Popping)ํšจ๊ณผ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ + ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฒ„ํŠผ์ด ๋ˆŒ๋ ธ๋‹ค๋Š” ์ธํ„ฐ๋ ‰์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค! + + ์ง„๋™์€ ์„ ํƒ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ฐ”๊พธ์—ˆ์Šต๋‹ˆ๋‹ค. + + iOS14๋ถ€ํ„ฐ๋Š” UIAction์˜ addAction์ด ๊ฐ€๋Šฅ + iOS13๊นŒ์ง€๋Š” NSObjectํ˜•ํƒœ๋กœ ๋“ฑ๋กํ•ด์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ถ„๊ธฐ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. + */ + + func press(vibrate: Bool = false, + animated: Bool = true, + for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping() -> Void) { + if #available(iOS 14.0, *) { + self.addAction(UIAction { _ in closure() + if animated { self.clickedAnimation(vibrate: vibrate) } + }, for: controlEvents) + } else { + @objc class ClosureSleeve: NSObject { + let closure: () -> Void + init(_ closure: @escaping() -> Void) { self.closure = closure } + @objc func invoke() { closure() } + } + let sleeve = ClosureSleeve(closure) + self.addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents) + objc_setAssociatedObject(self, "\(UUID())", sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) + } + } + + /** + ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด์„œ Poppin ํšจ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. + - Description: + ์ค„์–ด๋“œ๋Š” ์ •๋„๋ฅผ ์กฐ์ ˆํ•˜๊ณ ์‹ถ๋‹ค๋ฉด ,ScaleX,Y๊ฐ’์„ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค(์ตœ๋Œ€๊ฐ’ 1). + ๋‚ฎ์„์ˆ˜๋ก ๋งŽ์ด ์ค„์–ด๋“ฆ. + */ + func clickedAnimation(vibrate: Bool) { + if vibrate { makeVibrate(degree: .light) } + UIView.animate(withDuration: 0.1, animations: { + self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95) }, completion: { _ in + UIView.animate(withDuration: 0.1, animations: { + self.transform = CGAffineTransform.identity + }) + } + ) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIColor+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIColor+.swift new file mode 100644 index 00000000..3ac1d752 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIColor+.swift @@ -0,0 +1,26 @@ +// +// UIColor+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIColor { + convenience init(red: Int, green: Int, blue: Int) { + assert(red >= 0 && red <= 255, "Invalid red component") + assert(green >= 0 && green <= 255, "Invalid green component") + assert(blue >= 0 && blue <= 255, "Invalid blue component") + + self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0) + } + + convenience init(rgb: Int) { + self.init( + red: (rgb >> 16) & 0xFF, + green: (rgb >> 8) & 0xFF, + blue: rgb & 0xFF + ) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIDevice+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIDevice+.swift new file mode 100644 index 00000000..e4113cda --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIDevice+.swift @@ -0,0 +1,66 @@ +// +// UIDevice+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIDevice { + static let iOSVersion = "\(current.systemName) \(current.systemVersion)" + + private static var hardwareString: String { + var systemInfo = utsname() + uname(&systemInfo) + let machineMirror = Mirror(reflecting: systemInfo.machine) + let model = machineMirror.children.reduce("") { identifier, element in + guard let value = element.value as? Int8, value != 0 else { return identifier } + return identifier + String(UnicodeScalar(UInt8(value))) + } + return model + } + + /// Referenced the following URL. + /// [List of iOS and iPadOS devices](https://en.wikipedia.org/wiki/List_of_iOS_and_iPadOS_devices) + private static var modelDictionary: [String: String] { + return [ + "i386": "Simulator", // 32 bit + "x86_64": "Simulator", // 64 bit + "iPhone8,1": "iPhone 6S", + "iPhone8,2": "iPhone 6S Plus", + "iPhone8,4": "iPhone SE 1st generation", + "iPhone9,1": "iPhone 7", + "iPhone9,3": "iPhone 7", + "iPhone9,2": "iPhone 7 Plus", + "iPhone9,4": "iPhone 7 Plus", + "iPhone10,1": "iPhone 8", + "iPhone10,4": "iPhone 8", + "iPhone10,2": "iPhone 8 Plus", + "iPhone10,5": "iPhone 8 Plus", + "iPhone10,3": "iPhone X", + "iPhone10,6": "iPhone X", + "iPhone11,2": "iPhone XS", + "iPhone11,4": "iPhone XS Max", + "iPhone11,6": "iPhone XS Max", + "iPhone11,8": "iPhone XR", + "iPhone12,1": "iPhone 11", + "iPhone12,3": "iPhone 11 Pro", + "iPhone12,5": "iPhone 11 Pro Max", + "iPhone12,8": "iPhone SE 2nd generation", + "iPhone13,1": "iPhone 12 Mini", + "iPhone13,2": "iPhone 12", + "iPhone13,3": "iPhone 12 Pro", + "iPhone13,4": "iPhone 12 Pro Max", + "iPhone14,4": "iPhone 13 Mini", + "iPhone14,5": "iPhone 13", + "iPhone14,2": "iPhone 13 Pro", + "iPhone14,3": "iPhone 13 Pro Max", + "iPhone14,6": "iPhone SE 3nd generation" + ] + } + + static var iPhoneModel: String { + return modelDictionary[hardwareString] ?? "Unknown iPhone - \(hardwareString)" + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIImage+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIImage+.swift new file mode 100644 index 00000000..50a36e0a --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIImage+.swift @@ -0,0 +1,36 @@ +// +// UIImage+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIImage { + /// ์ด๋ฏธ์ง€์˜ ์šฉ๋Ÿ‰์„ ์ค„์ด๊ธฐ ์œ„ํ•ด์„œ ๋ฆฌ์‚ฌ์ด์ฆˆ. + /// - ๊ฐ€๋กœ, ์„ธ๋กœ ์ค‘ ์งง์€ ๊ฒƒ์ด 720 ๋ณด๋‹ค ์ž‘๋‹ค๋ฉด ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜. + /// - ๊ฐ€๋กœ, ์„ธ๋กœ ์ค‘ ์งง์€ ๊ฒƒ์ด 720 ๋ณด๋‹ค ํฌ๋‹ค๋ฉด 720 ์œผ๋กœ ๋ฆฌ์‚ฌ์ด์ฆˆํ•ด์„œ ๋ฐ˜ํ™˜. + func resize() -> UIImage { + let width = self.size.width + let height = self.size.height + let resizeLength: CGFloat = 720.0 + + var scale: CGFloat + + if height >= width { + scale = width <= resizeLength ? 1 : resizeLength / width + } else { + scale = height <= resizeLength ? 1 :resizeLength / height + } + + let newHeight = height * scale + let newWidth = width * scale + let size = CGSize(width: newWidth, height: newHeight) + let render = UIGraphicsImageRenderer(size: size) + let renderImage = render.image { _ in + self.draw(in: CGRect(origin: .zero, size: size)) + } + return renderImage + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UILabel+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UILabel+.swift new file mode 100644 index 00000000..a4133e34 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UILabel+.swift @@ -0,0 +1,140 @@ +// +// UILabel+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UILabel { + + /// ํ–‰๊ฐ„ ์กฐ์ • ๋ฉ”์„œ๋“œ + func setLineSpacing(lineSpacing: CGFloat) { + if let text = self.text { + let attributedStr = NSMutableAttributedString(string: text) + let style = NSMutableParagraphStyle() + style.lineSpacing = lineSpacing + attributedStr.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: NSMakeRange(0, attributedStr.length)) + self.attributedText = attributedStr + } + } + + func setLineSpacingWithChaining(lineSpacing: CGFloat) -> UILabel { + let label = self + if let text = self.text { + let style = NSMutableParagraphStyle() + style.lineSpacing = lineSpacing + let attributes: [NSAttributedString.Key: Any] = [ + .paragraphStyle: style + ] + label.attributedText = NSAttributedString(string: text, attributes: attributes) + } + return label + } + + /// ์ž๊ฐ„ ์„ค์ • ๋ฉ”์„œ๋“œ + func setCharacterSpacing(_ spacing: CGFloat) { + let attributedStr = NSMutableAttributedString(string: self.text ?? "") + attributedStr.addAttribute(NSAttributedString.Key.kern, value: spacing, range: NSMakeRange(0, attributedStr.length)) + self.attributedText = attributedStr + } + + /// ์ž๊ฐ„๊ณผ ํ–‰๊ฐ„์„ ๋ชจ๋‘ ์กฐ์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ + func setLineAndCharacterSpacing(lineSpacing: CGFloat, characterSpacing: CGFloat) { + if let text = self.text { + let attributedStr = NSMutableAttributedString(string: text) + let style = NSMutableParagraphStyle() + style.lineSpacing = lineSpacing + attributedStr.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: NSMakeRange(0, attributedStr.length)) + attributedStr.addAttribute(NSAttributedString.Key.kern, value: characterSpacing, range: NSMakeRange(0, attributedStr.length)) + self.attributedText = attributedStr + } + } + + /// ๋ผ๋ฒจ ์ผ๋ถ€ font ๋ณ€๊ฒฝํ•ด์ฃผ๋Š” ํ•จ์ˆ˜ + /// - targerString์—๋Š” ๋ฐ”๊พธ๊ณ ์ž ํ•˜๋Š” ํŠน์ • ๋ฌธ์ž์—ด์„ ๋„ฃ์–ด์ฃผ์„ธ์š” + /// - font์—๋Š” targetString์— ์ ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” UIFont๋ฅผ ๋„ฃ์–ด์ฃผ์„ธ์š” + func partFontChange(targetString: String, font: UIFont) { + let fullText = self.text ?? "" + let range = (fullText as NSString).range(of: targetString) + let attributedString = NSMutableAttributedString(string: fullText) + attributedString.addAttribute(.font, value: font, range: range) + self.attributedText = attributedString + } + + /// ๋ผ๋ฒจ ์ผ๋ถ€ textColor ๋ณ€๊ฒฝํ•ด์ฃผ๋Š” ํ•จ์ˆ˜ + /// - targetString์—๋Š” ๋ฐ”๊พธ๊ณ ์ž ํ•˜๋Š” ํŠน์ • ๋ฌธ์ž์—ด์„ ๋„ฃ์–ด์ฃผ์„ธ์š” + /// - textColor์—๋Š” targetString์— ์ ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ํŠน์ • UIColor์— ๋„ฃ์–ด์ฃผ์„ธ์š” + func partColorChange(targetString: String, textColor: UIColor) { + let fullText = self.text ?? "" + let range = (fullText as NSString).range(of: targetString) + let attributedString = NSMutableAttributedString(string: fullText) + attributedString.addAttribute(.foregroundColor, value: textColor, range: range) + self.attributedText = attributedString + } + + func htmlToString(_ targetString: String) -> NSAttributedString? { + let text = targetString + + guard let data = text.data(using: .utf8) else { + return NSAttributedString() + } + do { + return try NSAttributedString(data: data, + options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], + documentAttributes: nil) + } catch { + return NSAttributedString() + } + } + + func setAttributedText(targetFontList: [String: UIFont], + targetColorList: [String: UIColor]) { + let fullText = self.text ?? "" + let attributedString = NSMutableAttributedString(string: fullText) + for dic in targetFontList { + let range = (fullText as NSString).range(of: dic.key) + attributedString.addAttribute(.font, value: dic.value, range: range) + } + + for dic in targetColorList { + let range = (fullText as NSString).range(of: dic.key) + attributedString.addAttribute(.foregroundColor, value: dic.value, range: range) + } + self.attributedText = attributedString + } + + /// ๋ผ๋ฒจ ๋‚ด์˜ ํŠน์ • ๋ฌธ์ž์—ด์˜ CGRect ์„ ๋ฐ˜ํ™˜ + /// - Parameter subText: CGRect ์„ ์•Œ๊ณ  ์‹ถ์€ ํŠน์ • ๋ฌธ์ž์—ด. + func rectFromString(with subText: String) -> CGRect? { + guard let attributedText = attributedText else { return nil } + guard let labelText = self.text else { return nil } + + // ์ „์ฒด ํ…์ŠคํŠธ(labelText)์—์„œ subText๋งŒํผ์˜ range๋ฅผ ๊ตฌํ•ฉ๋‹ˆ๋‹ค. + guard let subRange = labelText.range(of: subText) else { return nil } + let range = NSRange(subRange, in: labelText) + + // attributedText๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ NSTextStorage๋ฅผ ์„ ์–ธํ•˜๊ณ  NSLayoutManager๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. + let layoutManager = NSLayoutManager() + let textStorage = NSTextStorage(attributedString: attributedText) + textStorage.addLayoutManager(layoutManager) + + // instrinsicContentSize๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ NSTextContainer๋ฅผ ์„ ์–ธํ•˜๊ณ  + let textContainer = NSTextContainer(size: intrinsicContentSize) + // ์ •ํ™•ํ•œ CGRect๋ฅผ ๊ตฌํ•ด์•ผํ•˜๋ฏ€๋กœ padding ๊ฐ’์€ 0์„ ์ค๋‹ˆ๋‹ค. + textContainer.lineFragmentPadding = 0.0 + // layoutManager์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. + layoutManager.addTextContainer(textContainer) + + var glyphRange = NSRange() + // ์ฃผ์–ด์ง„ ๋ฒ”์œ„(rage)์— ๋Œ€ํ•œ ์‹ค์งˆ์ ์ธ glyphRange๋ฅผ ๊ตฌํ•ฉ๋‹ˆ๋‹ค. + layoutManager.characterRange( + forGlyphRange: range, + actualGlyphRange: &glyphRange + ) + + // textContainer ๋‚ด์˜ ์ง€์ •๋œ glyphRange์— ๋Œ€ํ•œ CGRect ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. + return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIStackView+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIStackView+.swift new file mode 100644 index 00000000..26f27a83 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIStackView+.swift @@ -0,0 +1,16 @@ +// +// UIStackView+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIStackView { + func addArrangedSubviews(_ views: UIView...) { + for view in views { + self.addArrangedSubview(view) + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UISwitch+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UISwitch+.swift new file mode 100644 index 00000000..afaf389c --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UISwitch+.swift @@ -0,0 +1,37 @@ +// +// UISwitch+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UISwitch { + + func totalSize(width: CGFloat, height: CGFloat) { + + let standardHeight: CGFloat = 31 + let standardWidth: CGFloat = 51 + + let heightRatio = height / standardHeight + let widthRatio = width / standardWidth + + transform = CGAffineTransform(scaleX: widthRatio, y: heightRatio) + } + + func thumbSize(scaleX: CGFloat, scaleY: CGFloat) { + if let thumb = self.subviews[0].subviews[1].subviews[2] as? UIImageView { + thumb.transform = CGAffineTransform(scaleX: scaleX, y: scaleY) + } + + self.subviews[0].subviews[0].layer.cornerRadius = 0 + } + + func backgroundCornerRadius(cornerRadius: CGFloat) { + // deselected + self.subviews[0].subviews[0].layer.cornerRadius = cornerRadius + // selected + self.subviews[0].subviews[1].layer.cornerRadius = cornerRadius + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITabBar+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITabBar+.swift new file mode 100644 index 00000000..3fe75ab5 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITabBar+.swift @@ -0,0 +1,17 @@ +// +// UITabBar+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UITabBar { + + static func clearShadow() { + UITabBar.appearance().shadowImage = UIImage() + UITabBar.appearance().backgroundImage = UIImage() + UITabBar.appearance().backgroundColor = UIColor.white + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITableView+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITableView+.swift new file mode 100644 index 00000000..88722077 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITableView+.swift @@ -0,0 +1,27 @@ +// +// UITableView+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UITableView { + + /// TableView ํ•˜๋‹จ ๋นˆ ์…€ ์—†์• ๋Š” ๋ฉ”์„œ๋“œ + func setBottomEmptyView() { + let dummyView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) + self.tableFooterView = dummyView + } + + func fitContentInset(inset: UIEdgeInsets!) { + self.contentInset = inset + self.scrollIndicatorInsets = inset + } + + /// TableView ๋งˆ์ง€๋ง‰ separator, ๋นˆ ์…€ separator ์ˆจ๊ธฐ๋Š” ๋ฉ”์„œ๋“œ + func removeSeparatorsOfEmptyCellsAndLastCell() { + tableFooterView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 0, height: 1))) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITextField+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITextField+.swift new file mode 100644 index 00000000..0eb4d443 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITextField+.swift @@ -0,0 +1,37 @@ +// +// UITextField+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UITextField { + func addLeftPadding(width: CGFloat) { + let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: self.frame.height)) + self.leftView = paddingView + self.leftViewMode = .always + } + + func addRightPadding(width: CGFloat) { + let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: self.frame.height)) + self.rightView = paddingView + self.rightViewMode = .always + } + + /// UITextField์˜ ์ƒํƒœ๋ฅผ ๋ฆฌํ„ดํ•จ + var isEmpty: Bool { + if text?.isEmpty ?? true { + return true + } + return false + } + + /// ์ž๊ฐ„ ์„ค์ • ๋ฉ”์„œ๋“œ + func setCharacterSpacing(_ spacing: CGFloat) { + let attributedStr = NSMutableAttributedString(string: self.text ?? "") + attributedStr.addAttribute(NSAttributedString.Key.kern, value: spacing, range: NSMakeRange(0, attributedStr.length)) + self.attributedText = attributedStr + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITextView+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITextView+.swift new file mode 100644 index 00000000..37053082 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UITextView+.swift @@ -0,0 +1,54 @@ +// +// UITextView+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UITextView { + + /// ์ž๊ฐ„ ์„ค์ • ๋ฉ”์„œ๋“œ + func setCharacterSpacing(_ spacing: CGFloat) { + let attributedStr = NSMutableAttributedString(string: self.text ?? "") + attributedStr.addAttribute(NSAttributedString.Key.kern, value: spacing, range: NSMakeRange(0, attributedStr.length)) + self.attributedText = attributedStr + } + + /// ์„ธ๋กœ ์ค‘์•™์ •๋ ฌ ๋ฉ”์„œ๋“œ + func centerVertically() { + let fittingSize = CGSize(width: bounds.width, height: CGFloat.greatestFiniteMagnitude) + let size = sizeThatFits(fittingSize) + let topOffset = (bounds.size.height - size.height * zoomScale) / 2 + let positiveTopOffset = max(1, topOffset) + contentOffset.y = -positiveTopOffset + } + + /// line ์ˆ˜ ์นด์šดํŠธ ๋ฉ”์„œ๋“œ + func numberOfLines() -> Int { + let layoutManager = layoutManager + let numberOfGlyphs = layoutManager.numberOfGlyphs + var lineRange: NSRange = NSMakeRange(0, 1) + var index = 0 + var numberOfLines = 0 + + while index < numberOfGlyphs { + layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange) + index = NSMaxRange(lineRange) + numberOfLines += 1 + } + return numberOfLines + } + + /// ํ–‰๊ฐ„ ์กฐ์ • ๋ฉ”์„œ๋“œ + func setLineSpacing(lineSpacing: CGFloat) { + if let text = self.text { + let attributeString = NSMutableAttributedString(string: text) + let style = NSMutableParagraphStyle() + style.lineSpacing = lineSpacing + attributeString.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: NSMakeRange(0, attributeString.length)) + self.attributedText = attributeString + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIView+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIView+.swift new file mode 100644 index 00000000..5b0f12ee --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIView+.swift @@ -0,0 +1,51 @@ +// +// UIView+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIView { + + // UIView ์—ฌ๋Ÿฌ ๊ฐœ ์ธ์ž๋กœ ๋ฐ›์•„์„œ ํ•œ ๋ฒˆ์— addSubview + func addSubviews(_ views: UIView...) { + views.forEach { self.addSubview($0) } + } + + func addSubviewFromNib(view: UIView) { + guard let view = Bundle.main.loadNibNamed(view.className, owner: self, options: nil)?.first as? UIView else { return } + view.frame = bounds + view.clipsToBounds = true + addSubview(view) + } + + func setGradient() { + let gradient = CAGradientLayer() + gradient.frame = bounds + gradient.colors = [UIColor.init(white: 1, alpha: 0).cgColor, UIColor.init(white: 1, alpha: 1).cgColor] + gradient.locations = [0.0, 0.8, 1.0] + gradient.startPoint = CGPoint(x: 1.0, y: 0.2) + gradient.endPoint = CGPoint(x: 1.0, y: 1) + layer.insertSublayer(gradient, at: 0) + } + + func roundCorners(cornerRadius: CGFloat, maskedCorners: CACornerMask) { + clipsToBounds = true + layer.cornerRadius = cornerRadius + layer.maskedCorners = CACornerMask(arrayLiteral: maskedCorners) + } +} + +class XibView: UIView { + override init(frame: CGRect) { + super.init(frame: frame) + addSubviewFromNib(view: self) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + addSubviewFromNib(view: self) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIViewController+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIViewController+.swift new file mode 100644 index 00000000..9920f612 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIViewController+.swift @@ -0,0 +1,27 @@ +// +// UIViewController+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIViewController { + + /** + - Description: ํ™”๋ฉด ํ„ฐ์น˜์‹œ ์ž‘์„ฑ ์ข…๋ฃŒ + */ + /// ํ™”๋ฉด ํ„ฐ์น˜์‹œ ์ž‘์„ฑ ์ข…๋ฃŒํ•˜๋Š” ๋ฉ”์„œ๋“œ + open override func touchesBegan(_ touches: Set, with event: UIEvent?) { + self.view.endEditing(true) + } + + /** + - Description: ํ™”๋ฉด ํ„ฐ์น˜์‹œ ํ‚ค๋ณด๋“œ ๋‚ด๋ฆฌ๋Š” Extension + */ + @objc + func dismissKeyboard() { + view.endEditing(true) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Literal/StringLiterals.swift b/Runnect-iOS/Runnect-iOS/Global/Literal/StringLiterals.swift new file mode 100644 index 00000000..2854c91e --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Literal/StringLiterals.swift @@ -0,0 +1,18 @@ +// +// StringLiterals.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Foundation + +struct I18N { + struct Default { + static let error = "์—๋Ÿฌ" + static let networkError = "๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." + static let delete = "์‚ญ์ œ" + static let cancel = "์ทจ์†Œ" + static let ok = "ํ™•์ธ" + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Literal/UserDefaultKeyList.swift b/Runnect-iOS/Runnect-iOS/Global/Literal/UserDefaultKeyList.swift new file mode 100644 index 00000000..c7ed5ade --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Literal/UserDefaultKeyList.swift @@ -0,0 +1,14 @@ +// +// UserDefaultKeyList.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Foundation + +struct UserDefaultKeyList { + struct Auth { + @UserDefaultWrapper(key: "deviceId") public static var deviceId + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Protocols/KeyPathFindable.swift b/Runnect-iOS/Runnect-iOS/Global/Protocols/KeyPathFindable.swift new file mode 100644 index 00000000..4ecc8812 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Protocols/KeyPathFindable.swift @@ -0,0 +1,36 @@ +// +// KeyPathFindable.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Foundation + +@dynamicMemberLookup +public struct KeyFinder { + + private var base: Base + + public init(_ base: Base) { + self.base = base + } + + public subscript(dynamicMember keyPath: ReferenceWritableKeyPath) -> ReferenceWritableKeyPath { + return keyPath + } +} + +public protocol KeyPathFindable { + associatedtype Base: AnyObject + var kf: KeyFinder { get } +} + +public extension KeyPathFindable where Self: AnyObject { + var kf: KeyFinder { + get { KeyFinder(self) } + set { } + } +} + +extension NSObject: KeyPathFindable { } diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AccentColor.colorset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..13613e3e --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Supports/AppDelegate.swift b/Runnect-iOS/Runnect-iOS/Global/Supports/AppDelegate.swift new file mode 100644 index 00000000..3acc8543 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Supports/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// AppDelegate.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/Runnect-iOS/Runnect-iOS/Global/Supports/Base.lproj/LaunchScreen.storyboard b/Runnect-iOS/Runnect-iOS/Global/Supports/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Supports/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Runnect-iOS/Runnect-iOS/Global/Supports/SceneDelegate.swift b/Runnect-iOS/Runnect-iOS/Global/Supports/SceneDelegate.swift new file mode 100644 index 00000000..af639051 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Supports/SceneDelegate.swift @@ -0,0 +1,54 @@ +// +// SceneDelegate.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + + let window = UIWindow(windowScene: windowScene) + window.rootViewController = ViewController() + self.window = window + window.makeKeyAndVisible() + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/.gitkeep b/Runnect-iOS/Runnect-iOS/Global/UIComponents/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/.gitkeep @@ -0,0 +1 @@ + diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/JsonCoder.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/JsonCoder.swift new file mode 100644 index 00000000..5c20ea05 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/JsonCoder.swift @@ -0,0 +1,27 @@ +// +// JsonCoder.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Foundation + +/** + + - Description: + + JsonEncoder์™€ decoder๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ ‘๊ทผํ•˜๋Š” ์šฉ๋„์ž…๋‹ˆ๋‹ค. + 'Json.decoder.decode'์™€ ๊ฐ™์ด ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + +*/ + +public enum Json { + public static let encoder: JSONEncoder = { + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + return encoder + }() + + public static let decoder = JSONDecoder() +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/UserDefaultWrapper.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/UserDefaultWrapper.swift new file mode 100644 index 00000000..3a7d2cf7 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/UserDefaultWrapper.swift @@ -0,0 +1,29 @@ +// +// UserDefaultWrapper.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Foundation + +@propertyWrapper struct UserDefaultWrapper { + + var wrappedValue: T? { + get { + return UserDefaults.standard.object(forKey: self.key) as? T + } + + set { + if newValue == nil { + UserDefaults.standard.removeObject(forKey: key) + } else { UserDefaults.standard.setValue(newValue, forKey: key) } + } + } + + private let key: String + + init(key: String) { + self.key = key + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/addToolBar.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/addToolBar.swift new file mode 100644 index 00000000..2c1e4714 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/addToolBar.swift @@ -0,0 +1,41 @@ +// +// addToolBar.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIViewController { + + func addToolbar(textfields: [UITextField]) { + let toolBarKeyboard = UIToolbar() + toolBarKeyboard.sizeToFit() + let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + let btnDoneBar = UIBarButtonItem(title: "์™„๋ฃŒ", style: .done, target: self, action: #selector(self.dismissKeyBoard)) + toolBarKeyboard.items = [flexSpace, btnDoneBar] + textfields.forEach { + $0.inputAccessoryView = toolBarKeyboard + } + } + + func addToolBar(textView: UITextView) { + let toolBarKeyboard = UIToolbar() + toolBarKeyboard.sizeToFit() + let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + let btnDoneBar = UIBarButtonItem(title: "์™„๋ฃŒ", style: .done, target: self, action: #selector(self.dismissKeyBoard)) + toolBarKeyboard.items = [flexSpace, btnDoneBar] + toolBarKeyboard.tintColor = UIColor.red + textView.inputAccessoryView = toolBarKeyboard + } + + @objc func dismissKeyBoard() { + self.view.endEditing(true) + } + + func addTapGesture() { + let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyBoard)) + self.view.addGestureRecognizer(tap) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/adjusted+.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/adjusted+.swift new file mode 100644 index 00000000..adc093d9 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/adjusted+.swift @@ -0,0 +1,41 @@ +// +// adjusted+.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +/** + - Description: + ์Šคํฌ๋ฆฐ ๋„ˆ๋น„ 375๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋””์ž์ธ์ด ๋‚˜์™”์„ ๋•Œ ํ˜„์žฌ ๊ธฐ๊ธฐ์˜ ์Šคํฌ๋ฆฐ ์‚ฌ์ด์ฆˆ์— ๋น„๋ก€ํ•˜๋Š” ์ˆ˜์น˜๋ฅผ Returnํ•œ๋‹ค. + + - Note: + ๊ธฐ๊ธฐ๋ณ„ ๋Œ€์‘์— ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. + ex) (size: 20.adjusted) + */ + +extension CGFloat { + var adjusted: CGFloat { + let ratio: CGFloat = UIScreen.main.bounds.width / 375 + return self * ratio + } + + var adjustedH: CGFloat { + let ratio: CGFloat = UIScreen.main.bounds.height / 812 + return self * ratio + } +} + +extension Double { + var adjusted: Double { + let ratio: Double = Double(UIScreen.main.bounds.width / 375) + return self * ratio + } + + var adjustedH: Double { + let ratio: Double = Double(UIScreen.main.bounds.height / 812) + return self * ratio + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/applyShadow.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/applyShadow.swift new file mode 100644 index 00000000..392029a7 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/applyShadow.swift @@ -0,0 +1,41 @@ +// +// applyShadow.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +/** + + - Description: + + View์˜ Layer ๊ณ„์ธต(CALayer)์— shadow๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ž…ํž ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. + ํ”ผ๊ทธ๋งˆ์— ๋‚˜์™€์žˆ๋Š” shadow ์†์„ฑ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๊ธฐ์ž…ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค! + + */ + +public extension CALayer { + func applyShadow( + color: UIColor = .black, + alpha: Float = 0.5, + x: CGFloat = 0, + y: CGFloat = 2, + blur: CGFloat = 4, + spread: CGFloat = 0) { + + masksToBounds = false + shadowColor = color.cgColor + shadowOpacity = alpha + shadowOffset = CGSize(width: x, height: y) + shadowRadius = blur / 2.0 + if spread == 0 { + shadowPath = nil + } else { + let dx = -spread + let rect = bounds.insetBy(dx: dx, dy: dx) + shadowPath = UIBezierPath(rect: rect).cgPath + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/calculatePastTime.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/calculatePastTime.swift new file mode 100644 index 00000000..ccde8c97 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/calculatePastTime.swift @@ -0,0 +1,53 @@ +// +// calculatePastTime.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import Foundation + +public func calculatePastTime(date: String) -> String { + + let minute = 60 + let hour = minute * 60 + let day = hour * 60 + let week = day * 7 + + var message: String = "" + + let UTCDate = Date() + let formatter = DateFormatter() + formatter.timeZone = TimeZone(secondsFromGMT: 32400) + formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + let defaultTimeZoneStr = formatter.string(from: UTCDate) + + let format = DateFormatter() + format.dateFormat = "yyyy-MM-dd HH:mm:ss" + format.locale = Locale(identifier: "ko_KR") + + guard let tempDate = format.date(from: date) else {return ""} + let krTime = format.date(from: defaultTimeZoneStr) + + let articleDate = format.string(from: tempDate) + var useTime = Int(krTime!.timeIntervalSince(tempDate)) + useTime -= 32400 + + if useTime < minute { + message = "๋ฐฉ๊ธˆ ์ „" + } else if useTime < hour { + message = String(useTime/minute) + "๋ถ„ ์ „" + } else if useTime < day { + message = String(useTime/hour) + "์‹œ๊ฐ„ ์ „" + } else if useTime < week { + message = String(useTime/day) + "์ผ ์ „" + } else if useTime < week * 4 { + message = String(useTime/week) + "์ฃผ ์ „" + } else { + let timeArray = articleDate.components(separatedBy: " ") + let dateArray = timeArray[0].components(separatedBy: "-") + message = dateArray[1] + "์›” " + dateArray[2] + "์ผ" + } + + return message +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/calculateTopInset.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/calculateTopInset.swift new file mode 100644 index 00000000..19d746b0 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/calculateTopInset.swift @@ -0,0 +1,53 @@ +// +// calculateTopInset.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +/** + - Description: + + ์ƒ๋‹จ์˜ ํƒ‘ safe Area๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค + ๋…ธ์น˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” safe area inset ๋งŒํผ ์Œ์ˆ˜๊ฐ’์„ ์ฃผ๊ณ , + ๋…ธ์น˜๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” -44๋กœ ๊ณ ์ •๊ฐ’์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค + */ + +public extension UIViewController { + func calculateTopInset() -> CGFloat { + let windows = UIApplication.shared.windows[0] + let topInset = windows.safeAreaInsets.top + + if UIDevice.current.hasNotch { + return topInset * -1 + } else { + return -44 + } + } + + func safeAreaBottomInset() -> CGFloat { + if #available(iOS 11.0, *) { + let window = self.view.window + let bottomPadding = window?.safeAreaInsets.bottom + return bottomPadding ?? 0.0 + } else { + return 0.0 + } + } +} + +/** + + - Description: + + ํ•ด๋‹น ๊ธฐ๊ธฐ๊ฐ€, notch๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ bottom safe area inset๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ํŒ๋‹จํ•˜๋Š” ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ์ž…๋‹ˆ๋‹ค. + + */ + +public extension UIDevice { + var hasNotch: Bool { + UIScreen.main.bounds.height > 736 ? true : false + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/getClassName.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/getClassName.swift new file mode 100644 index 00000000..15692eb4 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/getClassName.swift @@ -0,0 +1,26 @@ +// +// getClassName.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +/** + + - Description: + + ๊ฐ VC,TVC,CVC์˜ className์„ String์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. + ์š” ๊ฐ’๋“ค์€ ๋‚˜์ค‘์— Identifier์— ์ž˜ ์จ๋จน์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ^__^ +*/ + +extension NSObject { + public static var className: String { + NSStringFromClass(self.classForCoder()).components(separatedBy: ".").last! + } + + public var className: String { + NSStringFromClass(self.classForCoder).components(separatedBy: ".").last! + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/makeAlert.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/makeAlert.swift new file mode 100644 index 00000000..b7765339 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/makeAlert.swift @@ -0,0 +1,37 @@ +// +// makeAlert.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +/** + + - Description: + + ์š”์ฒญํ•˜๋Š”(OK,์ทจ์†Œ)๋ฒ„ํŠผ๋งŒ ์žˆ๋Š” UIAlertController๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ extension์ž…๋‹ˆ๋‹ค. + + - parameters: + - title: ์•Œ๋ฆผ์ฐฝ์— ๋œจ๋Š” ํƒ€์ดํ‹€ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. + - message: ํƒ€์ดํ‹€ ๋ฐ‘์— ๋œจ๋Š” ๋ฉ”์„ธ์ง€ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. + - okAction: ํ™•์ธ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ ๋™์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. + - cancelAction: ์ทจ์†Œ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ ๋™์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. + - completion: ํ•ด๋‹น UIAlertController๊ฐ€ ๋„์›Œ์กŒ์„ ๋•Œ, ๋™์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. + + + */ +public extension UIViewController { + func makeAlert(title: String, + message: String, + okAction: ((UIAlertAction) -> Void)? = nil, + completion: (() -> Void)? = nil) { + makeVibrate() + let alertViewController = UIAlertController(title: title, message: message, + preferredStyle: .alert) + let okAction = UIAlertAction(title: "ํ™•์ธ", style: .default, handler: okAction) + alertViewController.addAction(okAction) + self.present(alertViewController, animated: true, completion: completion) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/makeVibrate.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/makeVibrate.swift new file mode 100644 index 00000000..630b1e23 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/makeVibrate.swift @@ -0,0 +1,37 @@ +// +// makeVibrate.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +/** + + - Description: + + VC๋‚˜ View ๋‚ด์—์„œ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ํ–…ํ‹ฑ์ด ๋ฐœ์ƒํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. + ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ ์œ ์ €์—๊ฒŒ ํŠน์ • ํ–‰๋™์ด ๋ฐœ์ƒํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๋ ค์ฃผ๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ–…ํ‹ฑ์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. + + - parameters: + - degree: ํ„ฐ์น˜์˜ ์„ธ๊ธฐ ์ •๋„๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณดํ†ต์€ medium,light๋ฅผ ์ œ์ผ ๋งŽ์ด ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค?! + ๋”ฐ๋ผ์„œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธฐ๋ณธ๊ฐ’์„ . medium์œผ๋กœ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. + +*/ + +public extension UIViewController { + + func makeVibrate(degree: UIImpactFeedbackGenerator.FeedbackStyle = .medium) { + let generator = UIImpactFeedbackGenerator(style: degree) + generator.impactOccurred() + } +} + +public extension UIView { + + func makeVibrate(degree: UIImpactFeedbackGenerator.FeedbackStyle = .medium) { + let generator = UIImpactFeedbackGenerator(style: degree) + generator.impactOccurred() + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/setImage.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/setImage.swift new file mode 100644 index 00000000..d05d2898 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/setImage.swift @@ -0,0 +1,54 @@ +// +// setImage.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit +import Kingfisher + +public extension UIImageView { + func setImage(with urlString: String, placeholder: String? = nil, completion: ((UIImage?) -> Void)? = nil) { + let cache = ImageCache.default + if urlString == "" { + // URL ๋นˆ ์ด๋ฏธ์ง€๋กœ ๋„˜๊ฒจ ๋ฐ›์•˜์„ ๊ฒฝ์šฐ, ์•„๋ž˜์— UIImage์— ๊ธฐ๋ณธ ์‚ฌ์ง„์„ ์ถ”๊ฐ€ ํ•˜๋ฉด ๋œ๋‹ค. + self.image = UIImage() + } else { + cache.retrieveImage(forKey: urlString) { result in + result.success { imageCache in + if let image = imageCache.image { + self.image = image + completion?(image) + } else { + self.setNewImage(with: urlString, placeholder: placeholder, completion: completion) + } + }.catch { _ in + self.setNewImage(with: urlString, placeholder: placeholder, completion: completion) + } + } + } + } + + private func setNewImage(with urlString: String, placeholder: String? = "img_placeholder", completion: ((UIImage?) -> Void)? = nil) { + guard let url = URL(string: urlString) else { return } + let resource = ImageResource(downloadURL: url, cacheKey: urlString) + let placeholderImage = UIImage(named: "img_placeholder") + let placeholder = placeholderImage + + self.kf.setImage( + with: resource, + placeholder: placeholder, + options: [ + .scaleFactor(UIScreen.main.scale/4), + .transition(.fade(0.5)), + .cacheMemoryOnly + ], + completionHandler: { result in + result.success { imageResult in + completion?(imageResult.image) + } + } + ) + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/setRootViewController.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/setRootViewController.swift new file mode 100644 index 00000000..09cd12e0 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/setRootViewController.swift @@ -0,0 +1,37 @@ +// +// setRootViewController.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +/** + + - Description: + + RootViewController๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์œ ํ‹ธ์ž…๋‹ˆ๋‹ค. SnapShot์„ ์ฐ์–ด์„œ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. + +*/ +enum ViewControllerUtils { + static func setRootViewController(window: UIWindow, viewController: UIViewController, withAnimation: Bool) { + if !withAnimation { + window.rootViewController = viewController + window.makeKeyAndVisible() + return + } + + if let snapshot = window.snapshotView(afterScreenUpdates: true) { + viewController.view.addSubview(snapshot) + window.rootViewController = viewController + window.makeKeyAndVisible() + + UIView.animate(withDuration: 0.4, animations: { + snapshot.layer.opacity = 0 + }, completion: { _ in + snapshot.removeFromSuperview() + }) + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/setStatusBarBackgroundColor.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/setStatusBarBackgroundColor.swift new file mode 100644 index 00000000..c3c83b1f --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/setStatusBarBackgroundColor.swift @@ -0,0 +1,23 @@ +// +// setStatusBarBackgroundColor.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UIViewController { + public func setStatusBarBackgroundColor(_ color: UIColor?) { + if #available(iOS 13.0, *) { + let window = UIApplication.shared.windows.first + let statusBarManager = window?.windowScene?.statusBarManager + let statusBarView = UIView(frame: statusBarManager?.statusBarFrame ?? .zero) + statusBarView.backgroundColor = color + window?.addSubview(statusBarView) + } else { + let statusBarView = UIApplication.shared.value(forKey: "statusBar") as? UIView + statusBarView?.backgroundColor = color + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/setTextLineHeight.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/setTextLineHeight.swift new file mode 100644 index 00000000..8a207bb4 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/setTextLineHeight.swift @@ -0,0 +1,42 @@ +// +// setTextLineHeight.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +extension UILabel { + public func setTextWithLineHeight(text: String?, lineHeightMultiple: CGFloat) { + if let text = text { + let style = NSMutableParagraphStyle() + style.lineHeightMultiple = lineHeightMultiple + + let attributes: [NSAttributedString.Key: Any] = [ + .paragraphStyle: style + ] + + let attrString = NSAttributedString(string: text, + attributes: attributes) + self.attributedText = attrString + } + } +} + +extension UITextView { + public func setTextWithLineHeight(text: String?, lineHeightMultiple: CGFloat) { + if let text = text { + let style = NSMutableParagraphStyle() + style.lineHeightMultiple = lineHeightMultiple + + let attributes: [NSAttributedString.Key: Any] = [ + .paragraphStyle: style + ] + + let attrString = NSAttributedString(string: text, + attributes: attributes) + self.attributedText = attrString + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Info.plist b/Runnect-iOS/Runnect-iOS/Info.plist new file mode 100644 index 00000000..0eb786dc --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Info.plist @@ -0,0 +1,23 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + + diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/.gitkeep b/Runnect-iOS/Runnect-iOS/Network/Dto/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Runnect-iOS/Runnect-iOS/Network/Foundation/.gitkeep b/Runnect-iOS/Runnect-iOS/Network/Foundation/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Runnect-iOS/Runnect-iOS/Network/Model/.gitkeep b/Runnect-iOS/Runnect-iOS/Network/Model/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/.gitkeep b/Runnect-iOS/Runnect-iOS/Network/Router/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Runnect-iOS/Runnect-iOS/Network/Service/.gitkeep b/Runnect-iOS/Runnect-iOS/Network/Service/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/.gitkeep b/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Runnect-iOS/Runnect-iOS/Presentation/SignIn/Views/.gitkeep b/Runnect-iOS/Runnect-iOS/Presentation/SignIn/Views/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Runnect-iOS/Runnect-iOS/Presentation/TabBar/ViewController.swift b/Runnect-iOS/Runnect-iOS/Presentation/TabBar/ViewController.swift new file mode 100644 index 00000000..36555ce9 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Presentation/TabBar/ViewController.swift @@ -0,0 +1,16 @@ +// +// ViewController.swift +// Runnect-iOS +// +// Created by sejin on 2022/12/29. +// + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .blue + } +}