diff --git a/.gitignore b/.gitignore index e69de29..504faf8 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +WebAppUIKitBase/SecretCode.swift \ No newline at end of file diff --git a/WebAppUIKitBase/Base.lproj/Main.storyboard b/WebAppUIKitBase/Base.lproj/Main.storyboard deleted file mode 100644 index 25a7638..0000000 --- a/WebAppUIKitBase/Base.lproj/Main.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WebAppUIKitBase/Common/AppDelegate.swift b/WebAppUIKitBase/Common/AppDelegate.swift new file mode 100644 index 0000000..4dd12c0 --- /dev/null +++ b/WebAppUIKitBase/Common/AppDelegate.swift @@ -0,0 +1,33 @@ +// +// AppDelegate.swift +// WebAppUIKitBase +// +// Created by Sean Kim on 10/21/24. +// + +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/WebAppUIKitBase/Common/CommonUtils.swift b/WebAppUIKitBase/Common/CommonUtils.swift new file mode 100644 index 0000000..f62c8e7 --- /dev/null +++ b/WebAppUIKitBase/Common/CommonUtils.swift @@ -0,0 +1,8 @@ +// +// CommonUtils.swift +// WebAppUIKitBase +// +// Created by Sean Kim on 10/21/24. +// + +import Foundation diff --git a/WebAppUIKitBase/Common/Prefix.swift b/WebAppUIKitBase/Common/Prefix.swift new file mode 100644 index 0000000..e9c4a3b --- /dev/null +++ b/WebAppUIKitBase/Common/Prefix.swift @@ -0,0 +1,440 @@ +// +// SwiftUI_Prefix.swift +// PersonalHealthDiary +// +// Created by Sean Kim on 2/20/24. +// +import SwiftUI + +// MARK: - TYPEALIAS +typealias VOID_TO_VOID = () -> () + +// MARK: - VARIABLE +public var APPSTORE_URL = "https://itunes.apple.com/app/" +public var KEYBOARD_UP_HEIGHT: CGFloat = 46.0 + +enum Compare: Int{ + case bigger = 0 + case smaller + case equal + case error +} + +// MARK: - FUNCTION +/// print 를 조금 더 자세하게 표기해주는 함수 +public func printLog(_ object: T, _ file: String = #file, _ function: String = #function, _ line: Int = #line){ +#if DEBUG + let dateString = Date().convertString("yyyy/MM/dd HH:mm:ss:SSS") + Swift.print( +// """ +// __________ __________ +// |* TIME = [\(dateString)] || FILE = [\(file.lastPathComponent)] +// | NAME = [\(function)] || LINE = [\(line)] +// |>>> PRINT = \(object) +//  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ +// """ + """ + __________ __________ __________ __________ + * LOCATION : [\(file.lastPathComponent) : \(line)] - \(function) + | TIME : [\(dateString)] + > NOTE : \(object) +  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ + """ + ) +#else + +#endif +} + +public func getDeviceWidth() -> CGFloat { UIScreen.main.bounds.size.width } +public func getDeviceHeight() -> CGFloat { UIScreen.main.bounds.size.height } + + +/// 탈옥 여부 파악 함수 +public func isIllegalDevice() -> Bool { + func canOpen(path: String) -> Bool { + let file = fopen(path, "r") + guard file != nil else { return false } + fclose(file) + return true + } + + guard let cydiaUrlScheme = NSURL(string: "cydia://package/com.example.package") else { return false } + if UIApplication.shared.canOpenURL(cydiaUrlScheme as URL) { + return true + } + +#if arch(i386) || arch(x86_64) + return false +#endif + + let fileManager = FileManager.default + if fileManager.fileExists(atPath: "/Applications/Cydia.app") || + fileManager.fileExists(atPath: "/Library/MobileSubstrate/MobileSubstrate.dylib") || + fileManager.fileExists(atPath: "/bin/bash") || + fileManager.fileExists(atPath: "/usr/sbin/sshd") || + fileManager.fileExists(atPath: "/etc/apt") || + fileManager.fileExists(atPath: "/usr/bin/ssh") || + fileManager.fileExists(atPath: "/private/var/lib/apt") { + return true + } + if canOpen(path: "/Applications/Cydia.app") || + canOpen(path: "/Library/MobileSubstrate/MobileSubstrate.dylib") || + canOpen(path: "/bin/bash") || + canOpen(path: "/usr/sbin/sshd") || + canOpen(path: "/etc/apt") || + canOpen(path: "/usr/bin/ssh") { + return true + } + let path = "/private/" + NSUUID().uuidString + do { + try "anyString".write(toFile: path, atomically: true, encoding: String.Encoding.utf8) + try fileManager.removeItem(atPath: path) + return true + } catch let error { // 이 부분 + printLog("Jail ERROR: \(error))") + return false + } +} + +public func fontNameCheck() { + for family: String in UIFont.familyNames { + print(family) + for names : String in UIFont.fontNames(forFamilyName: family){ + printLog("\(names)") + } + } +} + +/// a 가 b 보다 클 경우 true 반환 ( 그외 전부 false) +public func isBigger (_ a: Int, _ b: Int) -> Bool { + if a > b { return true } else { return false } +} + +/// JSON 형태의 String 을 받아서 Dictionary 의 형태로 내보냄 +public func jsonToDict(_ input: String) -> [String: Any] { + if let jsonData = input.data(using: .utf8){ + do { + if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] { + return jsonObject + } + } catch let error { // 이 부분 + printLog("JSON ERROR: \(error))") + } + } + return [:] +} + +func versionChange(ver: String) -> [Int] { + return ver.components(separatedBy: ["."]).map {Int($0) ?? 0} +} + +/// a 가 b 보다 OOO 하다. +func compareVersion(_ a: String, _ b: String) -> Compare { + let aList = versionChange(ver: a) + let bList = versionChange(ver: b) + if aList.count != bList.count { return .error } + else { + for i in 0 ..< aList.count { + if aList[i] > bList[i] { return .bigger } + else if aList[i] < bList[i] { return .smaller } + } + return .equal + } +} + +func copyToClipboard(_ text: String){ + UIPasteboard.general.string = text +} + + + +// MARK: - CUSTOM COMPONENTS + + +// MARK: - EXTENSION +extension String { + /// 마지막 경로 구성 요소 + var lastPathComponent: String { + get { + return (self as NSString).lastPathComponent + } + } + + /// 원하는 길이 만큼 절단 + func cut(start: Int, end: Int) -> String { + let startIndex = self.index(self.startIndex,offsetBy: start >= 0 ? start : 0) + let endIndex = self.index(self.startIndex,offsetBy: end >= 0 ? end : 0) + let result: String = "\(self[startIndex ..< endIndex])" + return result + } + + /// 글자 하나 단위로 구분 + func letter() -> [String] { + return self.map { String($0) } + } + + /// 단어 하나 단위로 구문 (띄어쓰기) + func word() -> [String] { + return self.components(separatedBy: " ") + } + + /// Date() 로 변환 -> (Bool, Date) 출력 + func convertDate(_ dateFormat: String = "yyyyMMdd") -> (Bool, Date) { + let dateFormatter = Date().setDateFormatter(dateFormat) + if let convert = dateFormatter.date(from: self) { + return (true, convert) + } else { + return (false, Date()) + } + } + + /// 정규식 체크 + /// + /// 이메일 정규식 예시: "[A-Z0-9a-z._%+-]+@[A-Z0-9a-z._%+-]+\\.[A-Za-z]{2,64}" + /// 비밀번호 정규식 에시: "[A-Z0-9a-z._%+-]{6,12}" + func checkFilter(_ filter: String) -> Bool { + if self == "" { return false } + return self.range(of: filter, options: .regularExpression) != nil + + } + + /// Bool 값에 맞게 DateFormat을 반환 + static func makeDateFormat(year: Bool, month: Bool, day: Bool, dayOfWeek: Bool = false, am_pm: Bool = false, hour: Bool = false,_ fullTime: Bool = true, minute: Bool = false, second: Bool = false, mSecond: Bool = false, mSDigit: Int = 1) -> String { + + var dateFormat = "" + + if year { + dateFormat = dateFormat + "yyyy" + } + if month { + dateFormat = dateFormat + "MM" + } + if day { + dateFormat = dateFormat + "dd" + } + if dayOfWeek { + dateFormat = dateFormat + "EEEEEE" + } + + if am_pm { + dateFormat = dateFormat + "a" + } + + if fullTime { // 24시간 + if hour { + dateFormat = dateFormat + "HH" + } + } else { // 12시간 + if hour { + dateFormat = dateFormat + "hh" + } + } + + if minute { + dateFormat = dateFormat + "mm" + } + + if second { + dateFormat = dateFormat + "ss" + } + + if mSecond { + for _ in 0 ..< mSDigit { + dateFormat = dateFormat + "S" + } + } + + return dateFormat + } + + /// 날짜를 합쳐서 하나의 String으로 반환 + static func combineDate(year: Int, month: Int, day: Int) -> String { + return "\(year * 10000 + month * 100 + day)" + } + + func stringToInt() -> Int { + if let intValue = Int(self) { + return intValue + } else { + return 0 + } + } +} + +extension Date { + /// 연도 표시 + /// + /// 변환에 실패시 현재 날짜의 년 표시 + var year: Int { + let dateFormatter = self.setDateFormatter("yyyy") + if let year = Int(dateFormatter.string(from: self)){ + return year + } else { + return Calendar.current.component(.year, from: self) + } + } + + /// 월 표시 + /// + /// 변환 실패시 현재 날짜의 월 표시 + var month: Int { + let dateFormatter = self.setDateFormatter("MM") + if let month = Int(dateFormatter.string(from: self)) { + return month + } else { + return Calendar.current.component(.month, from: self) + } + } + + /// 일 표시 + /// + /// 변환 실패시 현재 날짜의 월 표시 + var day: Int { + let dateFormatter = self.setDateFormatter("dd") + if let day = Int(dateFormatter.string(from: self)) { + return day + } else { + return Calendar.current.component(.day, from: self) + } + } + + /// 요일 표시 + /// + /// 일요일 부터 시작해 0~6까지 반환 + /// + /// -1 반환시 오류 발생 + var dayOfWeek: Int { + let dateFormatter = self.setDateFormatter("EEEEEE") + let convert = dateFormatter.string(from: self) + switch convert { + case "일": + return 0 + case "월": + return 1 + case "화": + return 2 + case "수": + return 3 + case "목": + return 4 + case "금": + return 5 + case "토": + return 6 + default: + return -1 + } + } + + func setDateFormatter(_ dateFormat: String) -> DateFormatter { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = dateFormat + dateFormatter.timeZone = TimeZone(abbreviation: "KST") + dateFormatter.locale = Locale(identifier: "ko_kr") + return dateFormatter + } + + /// String 값으로 변환 + func convertString(_ dateFormat: String = "yyyyMMdd") -> String { + let dateFormatter = self.setDateFormatter(dateFormat) + return dateFormatter.string(from: self) + } + + + /// 해당 년도가 윤년인지 확인 + /// + /// 값이 정확하지 않으면 현재 날짜로 파악 + func checkLeapMonth() -> Bool { + if self.year % 4 == 0 { + if self.year % 100 == 0 { + if self.year % 400 == 0 { + return true + } + } else { + return true + } + } + return false + } + + /// 해당 월의 날짜 수 확인 + /// + /// 값이 정확하지 않으면 현재 날짜로 파악 + func getLastDayOfMonth() -> Int { + switch self.month { + case 1,3,5,7,8,10,12 : + return 31 + case 2: + if self.checkLeapMonth() { + return 29 + } else { + return 28 + } + case 4,6,9,11: + return 30 + default: + return -1 + } + } +} + + +extension Font { + enum NPS_Font : String { + case regular + case bold + + var value: String { + switch self { + case .regular: + return "NPS-font-Regular" + case .bold: + return "NPS-font-Bold" + } + } + } + + static func nps(font: NPS_Font = .regular, size: CGFloat = 12) -> Font { + return .custom(font.value, size: size) + } + + +} + +extension View { + func endTextEditing() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } +} + +// MARK: - ANNOTATION + +/// 사용방법 +/// @UserDefault (key: "keyName", defaultValue: "default") var 변수 +/// print(변수) +/// - 위에처럼 하게 되면 UserDefaults에 저장된 key 값을 갖고있는걸 보여주든가 기본 값 보여줌 +/// 변수 = 변경값 +/// - 위에처럼 하게되면 UserDefaults에 해당 key 값에 변경값을 저장하고 들고 있음 +@propertyWrapper +struct UserDefault { + private let ud: UserDefaults = .standard + private let key: String + private var defaultValue: T + + var wrappedValue: T { + set { ud.set(newValue, forKey: key) } + get { ud.object(forKey: key) as? T ?? defaultValue } + } + + init(key: String, defaultValue: T) { + self.key = key + self.defaultValue = defaultValue + } + + ///사용법: _변수.removeData() + /// - 중요사항 변수 앞에 _ 무조건 붙여야 함 + func removeData() { + ud.removeObject(forKey: key) + } +} diff --git a/WebAppUIKitBase/Common/Protocol/LoactionProtocol.swift b/WebAppUIKitBase/Common/Protocol/LoactionProtocol.swift new file mode 100644 index 0000000..a351262 --- /dev/null +++ b/WebAppUIKitBase/Common/Protocol/LoactionProtocol.swift @@ -0,0 +1,8 @@ +// +// LoactionProtocol.swift +// WebAppUIKitBase +// +// Created by Sean Kim on 10/21/24. +// + +import Foundation diff --git a/WebAppUIKitBase/SceneDelegate.swift b/WebAppUIKitBase/Common/SceneDelegate.swift similarity index 80% rename from WebAppUIKitBase/SceneDelegate.swift rename to WebAppUIKitBase/Common/SceneDelegate.swift index 097a40a..8838e81 100644 --- a/WebAppUIKitBase/SceneDelegate.swift +++ b/WebAppUIKitBase/Common/SceneDelegate.swift @@ -13,10 +13,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let _ = (scene as? UIWindowScene) else { return } + guard let windowScene = (scene as? UIWindowScene) else { return } + window = UIWindow(frame: UIScreen.main.bounds) + window?.windowScene = windowScene + // 여기서 메인 화면 띄우는 걸 함 + window?.rootViewController = IntroVC() + + window?.makeKeyAndVisible() } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/WebAppUIKitBase/IntroVC.swift b/WebAppUIKitBase/IntroVC.swift new file mode 100644 index 0000000..c03da97 --- /dev/null +++ b/WebAppUIKitBase/IntroVC.swift @@ -0,0 +1,8 @@ +// +// IntroVC.swift +// WebAppUIKitBase +// +// Created by Sean Kim on 10/21/24. +// + +import Foundation diff --git a/WebAppUIKitBase/Scene/AppDelegate.swift b/WebAppUIKitBase/Scene/AppDelegate.swift new file mode 100644 index 0000000..4dd12c0 --- /dev/null +++ b/WebAppUIKitBase/Scene/AppDelegate.swift @@ -0,0 +1,33 @@ +// +// AppDelegate.swift +// WebAppUIKitBase +// +// Created by Sean Kim on 10/21/24. +// + +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/WebAppUIKitBase/ViewController.swift b/WebAppUIKitBase/Scene/IntroVC.swift similarity index 53% rename from WebAppUIKitBase/ViewController.swift rename to WebAppUIKitBase/Scene/IntroVC.swift index ede38f7..1e67d9e 100644 --- a/WebAppUIKitBase/ViewController.swift +++ b/WebAppUIKitBase/Scene/IntroVC.swift @@ -1,5 +1,5 @@ // -// ViewController.swift +// IntroVC.swift // WebAppUIKitBase // // Created by Sean Kim on 10/21/24. @@ -7,13 +7,11 @@ import UIKit -class ViewController: UIViewController { - +class IntroVC: UIViewController { + override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. + + } - - } - diff --git a/WebAppUIKitBase/Scene/MainWebVC.swift b/WebAppUIKitBase/Scene/MainWebVC.swift new file mode 100644 index 0000000..e9b227b --- /dev/null +++ b/WebAppUIKitBase/Scene/MainWebVC.swift @@ -0,0 +1,8 @@ +// +// MainWebVC.swift +// WebAppUIKitBase +// +// Created by Sean Kim on 10/21/24. +// + +import Foundation diff --git a/WebAppUIKitBase/Scene/SceneDelegate.swift b/WebAppUIKitBase/Scene/SceneDelegate.swift new file mode 100644 index 0000000..8838e81 --- /dev/null +++ b/WebAppUIKitBase/Scene/SceneDelegate.swift @@ -0,0 +1,55 @@ +// +// SceneDelegate.swift +// WebAppUIKitBase +// +// Created by Sean Kim on 10/21/24. +// + +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 } + window = UIWindow(frame: UIScreen.main.bounds) + window?.windowScene = windowScene + // 여기서 메인 화면 띄우는 걸 함 + window?.rootViewController = IntroVC() + + 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/WebAppUIKitBase/SecretCode.swift b/WebAppUIKitBase/SecretCode.swift new file mode 100644 index 0000000..612a4ad --- /dev/null +++ b/WebAppUIKitBase/SecretCode.swift @@ -0,0 +1,8 @@ +// +// SecretCode.swift +// WebAppUIKitBase +// +// Created by Sean Kim on 10/21/24. +// + +import Foundation