forked from AcaMate/AcaMate_iOS
[✨] 기능 추가 업데이트 - (API 통신, 푸시, 카카오 로그인, 버전 비교)
This commit is contained in:
parent
67bc5bcac9
commit
33c7682801
63
1. View/ContentView.swift
Normal file
63
1. View/ContentView.swift
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// ContentView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/26/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(systemName: "globe")
|
||||
.imageScale(.large)
|
||||
.foregroundStyle(.tint)
|
||||
Text("Hello, world!")
|
||||
.background(Color("Point/Dark"))
|
||||
Button {
|
||||
SNSLogin().login(type: .Kakao)
|
||||
|
||||
// self.versionCheck()
|
||||
|
||||
} label : {
|
||||
Text("카카오")
|
||||
}
|
||||
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
func versionCheck() {
|
||||
loadAPIData(url: "https://devacamate.ipstein.myds.me",
|
||||
path: "/api/v1/in/app/version",
|
||||
method: .get,
|
||||
parameters: [ "type": "I"],
|
||||
decodingType: APIResponse<VersionData>.self)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
printLog("ERROR: \(error)")
|
||||
case .finished:
|
||||
printLog("Version call Successed")
|
||||
}
|
||||
} receiveValue: { data in
|
||||
guard let responseData = data as? APIResponse<VersionData> else {return}
|
||||
printLog(responseData.status.toStringDict())
|
||||
printLog(responseData.data.toStringDict())
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
43
1. View/Intro&Login/IntroView.swift
Normal file
43
1. View/Intro&Login/IntroView.swift
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// IntroView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
struct IntroView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
.frame(height: 100)
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.background(.white)
|
||||
.border(.black)
|
||||
.padding()
|
||||
Spacer()
|
||||
HStack(spacing: 4) {
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 24, height: 24)
|
||||
Text("STEIN")
|
||||
.font(.nps(font: .bold, size: 16))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
}
|
||||
.padding(.bottom,12)
|
||||
Text("Copyright © Team.Stein")
|
||||
.font(.nps(font: .regular, size: 14))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
.padding(.bottom,50)
|
||||
}
|
||||
.fullView(.Normal.normal)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
IntroView()
|
||||
}
|
45
1. View/Intro&Login/LoginView.swift
Normal file
45
1. View/Intro&Login/LoginView.swift
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// LoginView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LoginView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.background(.white)
|
||||
.border(.black)
|
||||
.padding(.bottom, 84)
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
HStack(spacing: 12) {
|
||||
Image("Kakao_Icon")
|
||||
.resizable()
|
||||
.frame(width: 32, height: 32)
|
||||
Text("카카오 계정으로 시작하기")
|
||||
.font(.nps(font: .regular, size: 16))
|
||||
.foregroundStyle(Color(.Text.black))
|
||||
}
|
||||
.padding(12)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.foregroundStyle(Color(.Other.yellow))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.fullView(.Normal.normal)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LoginView()
|
||||
}
|
125
AcaMate/0. Setup/AppDelegate.swift
Normal file
125
AcaMate/0. Setup/AppDelegate.swift
Normal file
|
@ -0,0 +1,125 @@
|
|||
//
|
||||
// AppDelegate.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/26/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
|
||||
import KakaoSDKCommon
|
||||
import KakaoSDKAuth
|
||||
|
||||
|
||||
|
||||
|
||||
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
|
||||
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
printLog("Start Set AppDelegate")
|
||||
|
||||
if let kakaoAppKey = KEY.loadKey(for: "kakaoAppKey") {
|
||||
printLog("KAKAO APP KEY : \(kakaoAppKey)")
|
||||
KakaoSDK.initSDK(appKey: kakaoAppKey)
|
||||
}
|
||||
|
||||
//MARK: - Set Notification
|
||||
let center = UNUserNotificationCenter.current()
|
||||
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
|
||||
center.requestAuthorization(options: authOptions) { granted, error in
|
||||
if let error = error {
|
||||
printLog("인증 오류: \(error.localizedDescription)")
|
||||
}
|
||||
if granted {
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.registerForRemoteNotifications()
|
||||
}
|
||||
} else {
|
||||
printLog("알람 권한 해제.")
|
||||
}
|
||||
}
|
||||
center.delegate = self
|
||||
|
||||
//MARK: - 네트워크 모니터 초기화
|
||||
// _ = NetworkMonitor.shared
|
||||
|
||||
printLog("End Set AppDelegate")
|
||||
return true
|
||||
}
|
||||
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
|
||||
// if (AuthApi.isKakaoTalkLoginUrl(url)) {
|
||||
// return AuthController.handleOpenUrl(url: url)
|
||||
// }
|
||||
//
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
func registerForRemoteNotifications() {
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
|
||||
print("Permission granted: \(granted)")
|
||||
if let error = error {
|
||||
printLog("권한 요청 실패 \(error)")
|
||||
}
|
||||
printLog("권한 : \(granted)")
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.registerForRemoteNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
// 디바이스 토큰 등록 성공 시
|
||||
func application(_ application: UIApplication,didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02.2hX", $1)})
|
||||
printLog("APNs 디바이스 토큰: \(deviceTokenString)")
|
||||
// 서버로 디바이스 토큰 전달 로직 추가
|
||||
}
|
||||
|
||||
// 디바이스 토큰 등록 실패 시
|
||||
func application(_ application: UIApplication,didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
||||
printLog("APNs 등록 실패: \(error)")
|
||||
}
|
||||
|
||||
// 앱 켜져있을때 알럿 받으면 (오는게 확인되면 바로 뜨는 곳)
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
|
||||
let userInfo = notification.request.content.userInfo
|
||||
|
||||
if let apsData = userInfo["aps"] as? [AnyHashable: Any],
|
||||
let badge = apsData["badge"] as? Int {
|
||||
printLog("\(apsData)")
|
||||
do {
|
||||
try await UNUserNotificationCenter.current().setBadgeCount(badge)
|
||||
} catch {
|
||||
//오류
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 14.0, *) {
|
||||
return [[.list,.banner,.sound]]
|
||||
} else {
|
||||
return[[.alert,.sound]]
|
||||
}
|
||||
}
|
||||
|
||||
// 수신 받은 Notification을 터치해 앱으로 진입했을 때 호출
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
let userInfo = response.notification.request.content.userInfo
|
||||
if let apsData = userInfo["aps"] as? [AnyHashable: Any] {
|
||||
if let alert = apsData["alert"] as? [AnyHashable: Any] {
|
||||
printLog("apsData: \(apsData)")
|
||||
printLog("alert: \(alert)")
|
||||
if let param = alert["parameter"] {
|
||||
printLog(param as? String)
|
||||
}
|
||||
// viewModel.setBadge()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
9
AcaMate/0. Setup/SceneDelegate.swift
Normal file
9
AcaMate/0. Setup/SceneDelegate.swift
Normal file
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// SceneDelegate.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/26/24.
|
||||
//
|
||||
|
||||
//import Foundation
|
||||
// 필요시 구현하는걸로
|
45
AcaMate/1. View/1. Intro & Login/LoginView.swift
Normal file
45
AcaMate/1. View/1. Intro & Login/LoginView.swift
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// LoginView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LoginView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.background(.white)
|
||||
.border(.black)
|
||||
.padding(.bottom, 84)
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
HStack(spacing: 12) {
|
||||
Image("Kakao_Icon")
|
||||
.resizable()
|
||||
.frame(width: 32, height: 32)
|
||||
Text("카카오 계정으로 시작하기")
|
||||
.font(.nps(font: .regular, size: 16))
|
||||
.foregroundStyle(Color(.Text.black))
|
||||
}
|
||||
.padding(12)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.foregroundStyle(Color(.Other.yellow))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.fullView(.Normal.normal)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LoginView()
|
||||
}
|
43
AcaMate/1. View/IntroView.swift
Normal file
43
AcaMate/1. View/IntroView.swift
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// IntroView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
struct IntroView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
.frame(height: 100)
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.background(.white)
|
||||
.border(.black)
|
||||
.padding()
|
||||
Spacer()
|
||||
HStack(spacing: 4) {
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 24, height: 24)
|
||||
Text("STEIN")
|
||||
.font(.nps(font: .bold, size: 16))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
}
|
||||
.padding(.bottom,12)
|
||||
Text("Copyright © Team.Stein")
|
||||
.font(.nps(font: .regular, size: 14))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
.padding(.bottom,50)
|
||||
}
|
||||
.fullView(.Normal.normal)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
IntroView()
|
||||
}
|
63
AcaMate/1. 이건?/ContentView.swift
Normal file
63
AcaMate/1. 이건?/ContentView.swift
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// ContentView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/26/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(systemName: "globe")
|
||||
.imageScale(.large)
|
||||
.foregroundStyle(.tint)
|
||||
Text("Hello, world!")
|
||||
.background(Color("Point/Dark"))
|
||||
Button {
|
||||
SNSLogin().login(type: .Kakao)
|
||||
|
||||
// self.versionCheck()
|
||||
|
||||
} label : {
|
||||
Text("카카오")
|
||||
}
|
||||
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
func versionCheck() {
|
||||
loadAPIData(url: "https://devacamate.ipstein.myds.me",
|
||||
path: "/api/v1/in/app/version",
|
||||
method: .get,
|
||||
parameters: [ "type": "I"],
|
||||
decodingType: APIResponse<VersionData>.self)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
printLog("ERROR: \(error)")
|
||||
case .finished:
|
||||
printLog("Version call Successed")
|
||||
}
|
||||
} receiveValue: { data in
|
||||
guard let responseData = data as? APIResponse<VersionData> else {return}
|
||||
printLog(responseData.status.toStringDict())
|
||||
printLog(responseData.data.toStringDict())
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
43
AcaMate/1. 이건?/IntroView.swift
Normal file
43
AcaMate/1. 이건?/IntroView.swift
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// IntroView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
struct IntroView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
.frame(height: 100)
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.background(.white)
|
||||
.border(.black)
|
||||
.padding()
|
||||
Spacer()
|
||||
HStack(spacing: 4) {
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 24, height: 24)
|
||||
Text("STEIN")
|
||||
.font(.nps(font: .bold, size: 16))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
}
|
||||
.padding(.bottom,12)
|
||||
Text("Copyright © Team.Stein")
|
||||
.font(.nps(font: .regular, size: 14))
|
||||
.foregroundStyle(Color(.Text.detail))
|
||||
.padding(.bottom,50)
|
||||
}
|
||||
.fullView(.Normal.normal)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
IntroView()
|
||||
}
|
45
AcaMate/1. 이건?/LoginView.swift
Normal file
45
AcaMate/1. 이건?/LoginView.swift
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// LoginView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LoginView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Image("Team_Icon")
|
||||
.resizable()
|
||||
.frame(width: 200, height: 200)
|
||||
.background(.white)
|
||||
.border(.black)
|
||||
.padding(.bottom, 84)
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
HStack(spacing: 12) {
|
||||
Image("Kakao_Icon")
|
||||
.resizable()
|
||||
.frame(width: 32, height: 32)
|
||||
Text("카카오 계정으로 시작하기")
|
||||
.font(.nps(font: .regular, size: 16))
|
||||
.foregroundStyle(Color(.Text.black))
|
||||
}
|
||||
.padding(12)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.foregroundStyle(Color(.Other.yellow))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.fullView(.Normal.normal)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LoginView()
|
||||
}
|
424
AcaMate/2. Common/SwiftUI_Prefix.swift
Normal file
424
AcaMate/2. Common/SwiftUI_Prefix.swift
Normal file
|
@ -0,0 +1,424 @@
|
|||
//
|
||||
// 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<T>(_ 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)
|
||||
""")
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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 {
|
||||
var result = false
|
||||
if self == "" { return result }
|
||||
if (self.range(of: filter, options: .regularExpression) != nil) {
|
||||
result = true
|
||||
} else {
|
||||
result = false
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/// 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 Encodable {
|
||||
// 내부 데이터들 전체 호출
|
||||
func toStringDict() -> [String:String] {
|
||||
let mirror = Mirror(reflecting: self)
|
||||
var result = [String:String]()
|
||||
|
||||
for child in mirror.children {
|
||||
if let key = child.label {
|
||||
if let value = child.value as? CustomStringConvertible {
|
||||
result[key] = value.description
|
||||
} else {
|
||||
result[key] = "\(child.value)"
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ANNOTATION
|
||||
|
||||
/// 사용방법
|
||||
/// @UserDefault (key: "keyName", defaultValue: "default") var 변수
|
||||
/// print(변수)
|
||||
/// - 위에처럼 하게 되면 UserDefaults에 저장된 key 값을 갖고있는걸 보여주든가 기본 값 보여줌
|
||||
/// 변수 = 변경값
|
||||
/// - 위에처럼 하게되면 UserDefaults에 해당 key 값에 변경값을 저장하고 들고 있음
|
||||
@propertyWrapper
|
||||
struct UserDefault<T> {
|
||||
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)
|
||||
}
|
||||
}
|
37
AcaMate/3. Controller/APIController.swift
Normal file
37
AcaMate/3. Controller/APIController.swift
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// APIController.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/26/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
import Alamofire
|
||||
|
||||
public func loadAPIData<T: Decodable>(url: String, path: String,
|
||||
method: HTTPMethod = .get,
|
||||
parameters: [String: String],
|
||||
headers: HTTPHeaders = [:],
|
||||
decodingType: T.Type) -> Future<Any, Error> {
|
||||
return Future { promise in
|
||||
AF.request("\(url)\(path)",
|
||||
method: method,
|
||||
parameters: parameters,
|
||||
headers: headers
|
||||
)
|
||||
.validate(statusCode: 200 ..< 300)
|
||||
.responseString { response in
|
||||
printLog(response)
|
||||
}
|
||||
.responseDecodable(of: decodingType) { response in
|
||||
switch response.result {
|
||||
case .success(let value):
|
||||
promise(.success(value))
|
||||
case .failure(let error):
|
||||
promise(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
AcaMate/3. Controller/KeyController.swift
Normal file
36
AcaMate/3. Controller/KeyController.swift
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// Key.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/26/24.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
|
||||
struct appKey: Codable {
|
||||
let kakaoAppKey: String
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct KEY {
|
||||
static func loadKey(for keyName: String) -> String? {
|
||||
guard let path = Bundle.main.path(forResource: "KEY", ofType: "json") else {
|
||||
// 파일을 찾을 수 없음
|
||||
print("파일이 없습니다.")
|
||||
return nil
|
||||
}
|
||||
do {
|
||||
let data = try Data(contentsOf: URL(fileURLWithPath: path))
|
||||
|
||||
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
let value = json[keyName] as? String {
|
||||
return value
|
||||
}
|
||||
} catch {
|
||||
print("파싱 중 에러 발생")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
187
AcaMate/3. Controller/LoginController.swift
Normal file
187
AcaMate/3. Controller/LoginController.swift
Normal file
|
@ -0,0 +1,187 @@
|
|||
//
|
||||
// LoginController.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/26/24.
|
||||
//
|
||||
|
||||
import Combine
|
||||
|
||||
import KakaoSDKCommon
|
||||
import KakaoSDKAuth
|
||||
import KakaoSDKUser
|
||||
|
||||
import Alamofire
|
||||
|
||||
class SNSLogin {
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
func login(type: SNSLoginType){
|
||||
switch type {
|
||||
case .Kakao:
|
||||
self.checkKakaoToken()
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
printLog("KAKAO LOGIN ERROR: \(error)")
|
||||
case .finished: break
|
||||
}
|
||||
} receiveValue: { snsId in
|
||||
printLog("로그인 완료 \(snsId)")
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
case .Apple: break
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func logout(type: SNSLoginType) {
|
||||
switch type {
|
||||
case .Kakao:
|
||||
UserApi.shared.logout { error in
|
||||
if let error = error {
|
||||
printLog(error)
|
||||
}
|
||||
else {
|
||||
printLog("LOGOUT SUCCESS")
|
||||
}
|
||||
}
|
||||
case .Apple: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//MARK: - KAKAO LOGIN
|
||||
extension SNSLogin {
|
||||
/// 토큰을 가지고 있나 확인
|
||||
private func checkKakaoToken() -> Future<SNSID, Error> {
|
||||
return Future { promise in
|
||||
// 토큰 확인
|
||||
if AuthApi.hasToken() {
|
||||
printLog("토큰 있음")
|
||||
self.analysisKakaoToken()
|
||||
.flatMap{ token in
|
||||
self.generateSNSID(token)
|
||||
}
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
promise(.failure(error))
|
||||
case .finished: break
|
||||
}
|
||||
} receiveValue: { data in
|
||||
promise(.success(data))
|
||||
}
|
||||
.store(in: &self.cancellables)
|
||||
}
|
||||
else {
|
||||
// 처음 설치하거나 하면 여기로 들어오게 됨 -> 당연히 없으니까 바로
|
||||
printLog("토큰 없음")
|
||||
self.loginKakao()
|
||||
.flatMap { token in
|
||||
self.generateSNSID(token)
|
||||
}
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
promise(.failure(error))
|
||||
case .finished: break
|
||||
}
|
||||
} receiveValue: { data in
|
||||
promise(.success(data))
|
||||
}
|
||||
.store(in: &self.cancellables)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func generateSNSID(_ token: String) -> Future<SNSID, Error> {
|
||||
return Future { promise in
|
||||
var snsId = SNSID()
|
||||
UserApi.shared.me { user, error in
|
||||
if let error = error {
|
||||
promise(.failure(error))
|
||||
}
|
||||
else if let user = user, let id = user.id{
|
||||
snsId.acctType = "kakao"
|
||||
snsId.snsId = "\(id)"
|
||||
snsId.snsToken = "\(token)"
|
||||
if let email = user.kakaoAccount?.email {
|
||||
snsId.snsEmail = email
|
||||
}
|
||||
promise(.success(snsId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// access 토큰의 유효성을 검증
|
||||
private func analysisKakaoToken() -> Future<String, Error> {
|
||||
return Future { promise in
|
||||
UserApi.shared.accessTokenInfo { tokenInfo, error in
|
||||
if let error = error {
|
||||
printLog("토큰 유효성 체크 실패 - 로그인 동작 필요")
|
||||
if let sdkError = error as? SdkError, sdkError.isInvalidTokenError() == true {
|
||||
// 로그인이 필요
|
||||
self.loginKakao()
|
||||
// 로그인 후 동작이 sink에서 처리 될것
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
promise(.failure(error))
|
||||
case .finished: break
|
||||
// 로그 인 후 추가 동작 할 거 있나? 일단 토큰 받아서 만들어 보내는건 Value에서 함
|
||||
}
|
||||
} receiveValue: { token in
|
||||
printLog("로그인 완료 - 토큰 받아옴 : \(token)")
|
||||
promise(.success(token))
|
||||
}
|
||||
.store(in: &self.cancellables)
|
||||
}
|
||||
else {
|
||||
// 그 외 기타 에러
|
||||
promise(.failure(error))
|
||||
}
|
||||
}
|
||||
else {
|
||||
printLog("토큰 유효성 체크 성공")
|
||||
// 토큰 유효성이 체크 성공, 로그인 불필요 - 해당 토큰으로 카카오 API 호출 가능
|
||||
if let tokenInfo = tokenInfo,
|
||||
let token = Auth.shared.tokenManager.getToken()?.accessToken {
|
||||
promise(.success(token))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loginKakao() -> Future<String, Error> {
|
||||
return Future { promise in
|
||||
if (UserApi.isKakaoTalkLoginAvailable()) { // 카카오톡 앱 실행 가능
|
||||
UserApi.shared.loginWithKakaoTalk { oauthToken, error in
|
||||
if let error = error { // 로그인과정에서 오류 발생
|
||||
promise(.failure(error))
|
||||
}
|
||||
else if let oauthToken = oauthToken { // 정상 로그인 완료
|
||||
promise(.success(oauthToken.accessToken))
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // 카카오톡 앱 실행 불가
|
||||
UserApi.shared.loginWithKakaoAccount { oauthToken, error in
|
||||
if let error = error { // 로그인과정에서 오류 발생
|
||||
promise(.failure(error))
|
||||
}
|
||||
else if let oauthToken = oauthToken { // 정상 로그인 완료
|
||||
promise(.success(oauthToken.accessToken))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
AcaMate/4. Model/API Response.swift
Normal file
27
AcaMate/4. Model/API Response.swift
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// API Response.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/29/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class APIResponse<T: Codable>: Codable {
|
||||
let status: Status
|
||||
let data: T
|
||||
}
|
||||
|
||||
class Status: Codable {
|
||||
let code: String
|
||||
let message: String
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
||||
class VersionData: Codable {
|
||||
let os_type, final_ver, dev_ver, force_ver: String
|
||||
let choice_update_yn: Bool
|
||||
}
|
||||
|
||||
|
8
AcaMate/4. Model/Alert.swift
Normal file
8
AcaMate/4. Model/Alert.swift
Normal file
|
@ -0,0 +1,8 @@
|
|||
//
|
||||
// Alert.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
27
AcaMate/4. Model/SNS Data.swift
Normal file
27
AcaMate/4. Model/SNS Data.swift
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// SNSID.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
enum SNSLoginType{
|
||||
case Kakao
|
||||
case Apple
|
||||
}
|
||||
|
||||
struct SNSID: Codable {
|
||||
var acctType, snsId, snsToken, snsEmail: String
|
||||
|
||||
init(acctType: String = "", snsId: String = "", snsToken: String = "", snsEmail: String = "") {
|
||||
self.acctType = acctType
|
||||
self.snsId = snsId
|
||||
self.snsToken = snsToken
|
||||
self.snsEmail = snsEmail
|
||||
}
|
||||
}
|
||||
|
||||
|
30
AcaMate/5. Modifier/Font.swift
Normal file
30
AcaMate/5. Modifier/Font.swift
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Font.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
}
|
13
AcaMate/5. Modifier/Text.swift
Normal file
13
AcaMate/5. Modifier/Text.swift
Normal file
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// Text.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
|
||||
extension Text {
|
||||
|
||||
}
|
20
AcaMate/5. Modifier/View.swift
Normal file
20
AcaMate/5. Modifier/View.swift
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// View.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/1/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
func fullView(_ backColor: Color) -> some View{
|
||||
return self
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(backColor)
|
||||
}
|
||||
|
||||
func endTextEditing() {
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.573",
|
||||
"green" : "0.584",
|
||||
"red" : "0.745"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.267",
|
||||
"green" : "0.271",
|
||||
"red" : "0.349"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.976",
|
||||
"green" : "0.976",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.765",
|
||||
"green" : "0.776",
|
||||
"red" : "0.992"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.529",
|
||||
"green" : "0.545",
|
||||
"red" : "0.541"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.247",
|
||||
"green" : "0.255",
|
||||
"red" : "0.251"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.973",
|
||||
"green" : "0.973",
|
||||
"red" : "0.973"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.706",
|
||||
"green" : "0.725",
|
||||
"red" : "0.722"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.549",
|
||||
"green" : "0.643",
|
||||
"red" : "0.525"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.255",
|
||||
"green" : "0.302",
|
||||
"red" : "0.243"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.973",
|
||||
"green" : "0.984",
|
||||
"red" : "0.969"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.733",
|
||||
"green" : "0.859",
|
||||
"red" : "0.698"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.557",
|
||||
"green" : "0.608",
|
||||
"red" : "0.706"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.259",
|
||||
"green" : "0.282",
|
||||
"red" : "0.329"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.973",
|
||||
"green" : "0.980",
|
||||
"red" : "0.996"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.741",
|
||||
"green" : "0.812",
|
||||
"red" : "0.941"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.620",
|
||||
"green" : "0.655",
|
||||
"red" : "0.690"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.290",
|
||||
"green" : "0.306",
|
||||
"red" : "0.322"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.984",
|
||||
"green" : "0.988",
|
||||
"red" : "0.992"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.824",
|
||||
"green" : "0.875",
|
||||
"red" : "0.922"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.961",
|
||||
"green" : "0.471",
|
||||
"red" : "0.204"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.773",
|
||||
"green" : "0.827",
|
||||
"red" : "0.878"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.400",
|
||||
"green" : "0.765",
|
||||
"red" : "0.396"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.239",
|
||||
"green" : "0.306",
|
||||
"red" : "0.918"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0",
|
||||
"green" : "204",
|
||||
"red" : "255"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.737",
|
||||
"green" : "0.631",
|
||||
"red" : "0.573"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.345",
|
||||
"green" : "0.294",
|
||||
"red" : "0.267"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.984",
|
||||
"red" : "0.976"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.980",
|
||||
"green" : "0.839",
|
||||
"red" : "0.761"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.404",
|
||||
"green" : "0.400",
|
||||
"red" : "0.349"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.188",
|
||||
"green" : "0.188",
|
||||
"red" : "0.161"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.953",
|
||||
"green" : "0.953",
|
||||
"red" : "0.945"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.537",
|
||||
"green" : "0.533",
|
||||
"red" : "0.463"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.231",
|
||||
"green" : "0.298",
|
||||
"red" : "0.365"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.106",
|
||||
"green" : "0.137",
|
||||
"red" : "0.165"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.929",
|
||||
"green" : "0.941",
|
||||
"red" : "0.949"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.306",
|
||||
"green" : "0.396",
|
||||
"red" : "0.475"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.000",
|
||||
"red" : "0.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.329",
|
||||
"green" : "0.329",
|
||||
"red" : "0.329"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.557",
|
||||
"green" : "0.557",
|
||||
"red" : "0.557"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
6
AcaMate/6. Resources/Assets.xcassets/Contents.json
Normal file
6
AcaMate/6. Resources/Assets.xcassets/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
21
AcaMate/6. Resources/Assets.xcassets/Image Folder/Kakao_Icon.imageset/Contents.json
vendored
Normal file
21
AcaMate/6. Resources/Assets.xcassets/Image Folder/Kakao_Icon.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "KakaoIcon.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Kakao_Icon.imageset/KakaoIcon.png
vendored
Normal file
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Kakao_Icon.imageset/KakaoIcon.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
21
AcaMate/6. Resources/Assets.xcassets/Image Folder/MI.imageset/Contents.json
vendored
Normal file
21
AcaMate/6. Resources/Assets.xcassets/Image Folder/MI.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "MI.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/MI.imageset/MI.png
vendored
Normal file
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/MI.imageset/MI.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Team_Icon.imageset/1024.png
vendored
Normal file
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Team_Icon.imageset/1024.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
21
AcaMate/6. Resources/Assets.xcassets/Image Folder/Team_Icon.imageset/Contents.json
vendored
Normal file
21
AcaMate/6. Resources/Assets.xcassets/Image Folder/Team_Icon.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
AcaMate/6. Resources/Fonts/NPSfont_bold.otf
Normal file
BIN
AcaMate/6. Resources/Fonts/NPSfont_bold.otf
Normal file
Binary file not shown.
BIN
AcaMate/6. Resources/Fonts/NPSfont_extrabold.otf
Normal file
BIN
AcaMate/6. Resources/Fonts/NPSfont_extrabold.otf
Normal file
Binary file not shown.
BIN
AcaMate/6. Resources/Fonts/NPSfont_regular.otf
Normal file
BIN
AcaMate/6. Resources/Fonts/NPSfont_regular.otf
Normal file
Binary file not shown.
BIN
AcaMate/6. Resources/Fonts/NotoSansKR-VariableFont_wght.ttf
Normal file
BIN
AcaMate/6. Resources/Fonts/NotoSansKR-VariableFont_wght.ttf
Normal file
Binary file not shown.
BIN
AcaMate/6. Resources/Images/KakaoIcon.png
Normal file
BIN
AcaMate/6. Resources/Images/KakaoIcon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
BIN
AcaMate/6. Resources/Images/MI.png
Normal file
BIN
AcaMate/6. Resources/Images/MI.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
AcaMate/6. Resources/Images/Team_Icon.png
Normal file
BIN
AcaMate/6. Resources/Images/Team_Icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
4
AcaMate/7. Private/KEY.json
Normal file
4
AcaMate/7. Private/KEY.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"kakaoAppKey": "e52f2f7d553a752a80983f8dba49b580"
|
||||
}
|
||||
|
12
AcaMate/AcaMate.entitlements
Normal file
12
AcaMate/AcaMate.entitlements
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.developer.applesignin</key>
|
||||
<array>
|
||||
<string>Default</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -7,11 +7,21 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
import KakaoSDKCommon
|
||||
import KakaoSDKAuth
|
||||
|
||||
@main
|
||||
struct AcaMateApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
IntroView().onOpenURL { url in
|
||||
if (AuthApi.isKakaoTalkLoginUrl(url)) {
|
||||
_ = AuthController.handleOpenUrl(url: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
//
|
||||
// ContentView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 11/26/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(systemName: "globe")
|
||||
.imageScale(.large)
|
||||
.foregroundStyle(.tint)
|
||||
Text("Hello, world!")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
47
AcaMate/Info.plist
Normal file
47
AcaMate/Info.plist
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>kakaoe52f2f7d553a752a80983f8dba49b580</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>kakaokompassauth</string>
|
||||
</array>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>ipstein.myds.me</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>NPSfont_regular.otf</string>
|
||||
<string>NPSfont_bold.otf</string>
|
||||
<string>NPSfont_extrabold.otf</string>
|
||||
<string>NotoSansKR-VariableFont_wght.ttf</string>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
<string>fetch</string>
|
||||
<string>external-accessory</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
18
AcaMate/IntroView.swift
Normal file
18
AcaMate/IntroView.swift
Normal file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// IntroView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/2/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct IntroView: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
IntroView()
|
||||
}
|
97
AcaMate/LaunchScreen.storyboard
Normal file
97
AcaMate/LaunchScreen.storyboard
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<customFonts key="customFonts">
|
||||
<array key="NPSfont_bold.otf">
|
||||
<string>NPS-font-Bold</string>
|
||||
</array>
|
||||
<array key="NPSfont_regular.otf">
|
||||
<string>NPS-font-Regular</string>
|
||||
</array>
|
||||
</customFonts>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
|
||||
<rect key="frame" x="0.0" y="832" width="393" height="0.0"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="AcaMate" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
|
||||
<rect key="frame" x="0.0" y="263.66666666666669" width="393" height="43"/>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="36"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Copyright © Team.Stein" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DB7-ZE-OmZ">
|
||||
<rect key="frame" x="110.33333333333333" y="747.66666666666663" width="172.66666666666669" height="20.333333333333371"/>
|
||||
<fontDescription key="fontDescription" name="NPS-font-Regular" family="NPS font" pointSize="14"/>
|
||||
<color key="textColor" name="Text/Detail"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="e1n-TP-Snx">
|
||||
<rect key="frame" x="159" y="711.66666666666663" width="75.333333333333314" height="24"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Team_Icon" translatesAutoresizingMaskIntoConstraints="NO" id="Jut-ME-xPR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="24" id="ZYt-gO-7Dd"/>
|
||||
<constraint firstAttribute="height" constant="24" id="n0u-p8-9ce"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="STEIN" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D4H-4L-gwl">
|
||||
<rect key="frame" x="28.000000000000004" y="0.33333333333337123" width="47.333333333333343" height="23.333333333333332"/>
|
||||
<fontDescription key="fontDescription" name="NPS-font-Bold" family="NPS font" pointSize="16"/>
|
||||
<color key="textColor" name="Text/Black"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
|
||||
<color key="backgroundColor" name="Normal/Normal"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
|
||||
<constraint firstItem="Bcu-3y-fUS" firstAttribute="bottom" secondItem="DB7-ZE-OmZ" secondAttribute="bottom" constant="50" id="BAj-IX-P9u"/>
|
||||
<constraint firstItem="DB7-ZE-OmZ" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="FfN-i1-tv7"/>
|
||||
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
|
||||
<constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="SfN-ll-jLj"/>
|
||||
<constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
|
||||
<constraint firstItem="DB7-ZE-OmZ" firstAttribute="top" secondItem="e1n-TP-Snx" secondAttribute="bottom" constant="12" id="adk-eM-96E"/>
|
||||
<constraint firstItem="e1n-TP-Snx" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="ifu-zm-USe"/>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="52.671755725190835" y="374.64788732394368"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Team_Icon" width="144" height="144"/>
|
||||
<namedColor name="Normal/Normal">
|
||||
<color red="0.92199999094009399" green="0.875" blue="0.82400000095367432" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
<namedColor name="Text/Black">
|
||||
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
<namedColor name="Text/Detail">
|
||||
<color red="0.32899999618530273" green="0.32899999618530273" blue="0.32899999618530273" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
</resources>
|
||||
</document>
|
18
AcaMate/LoginView.swift
Normal file
18
AcaMate/LoginView.swift
Normal file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// LoginVIew.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by Sean Kim on 12/2/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LoginVIew: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LoginVIew()
|
||||
}
|
Loading…
Reference in New Issue
Block a user