Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sujileelea committed Mar 5, 2024
1 parent 472e9c3 commit f4cad1e
Show file tree
Hide file tree
Showing 59 changed files with 7,902 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
64 changes: 64 additions & 0 deletions Manager/StateManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// StateManager.swift
// mocacong
//
// Created by Suji Lee on 2023/07/01.
//

import SwiftUI
import Combine

class StateManager: ObservableObject {

static let shared = StateManager()

@Published private var _isLoggedIn: Bool = false
@Published private var _isAgreed: Bool = false
@Published private var _tokenExpired: Bool = false
@Published private var _serverDown: Bool = false
@Published private var _userReportCount: Int = 0

var isLoggedIn: Bool {
get {
return self._isLoggedIn
}
set {
self._isLoggedIn = newValue
}
}
var isAgreed: Bool {
get {
return self._isAgreed
}
set {
self._isAgreed = newValue
}
}
var tokenExpired: Bool {
get {
return self._tokenExpired
}
set {
self._tokenExpired = newValue
}
}
var serverDown: Bool {
get {
return self._serverDown
}
set {
self._serverDown = newValue
}
}
var userReportCount: Int {
get {
return self._userReportCount
}
set {
self._userReportCount = newValue
}
}

init() {}

}
127 changes: 127 additions & 0 deletions Manager/TokenManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//
// TokenManager.swift
// mocacong
//
// Created by Suji Lee on 2023/06/28.
//

import Foundation
import Combine
import CryptoKit
import SwiftKeychainWrapper

class TokenManager: ObservableObject {

var cancellables = Set<AnyCancellable>()

static let shared = TokenManager()
private let keychain = KeychainWrapper.standard
private let key = SymmetricKey(size: .bits256)

private init() {}

func isAccessTokenPresent() -> Bool {
if let _ = keychain.string(forKey: "access_token") {
// 데이터가 있을 경우
return true
} else {
// 데이터가 nil일 경우
return false
}
}

func saveToken(token: String) {
keychain.set(token, forKey: "access_token")
}

func getToken() -> String? {
return keychain.string(forKey: "access_token")
}

func saveRefreshToken(token: String) {
keychain.set(token, forKey: "refresh_token")
}

func getRefreshToken() -> String? {
return keychain.string(forKey: "refresh_token")

}

func logoutUser() {
let accessRemoved = KeychainWrapper.standard.removeObject(forKey: "access_token")
let refreshRemoved = KeychainWrapper.standard.removeObject(forKey: "refresh_token")
if accessRemoved && refreshRemoved {
print("All tokens removed successfully.")
} else {
print("Failed to remove some tokens.")
}
StateManager.shared.isLoggedIn = false
}

func deleteUserAccount() {
logoutUser()
}

func refreshAccessToken(accessInfo: AccessInfo) {
guard let url = URL(string: "\(requestURL)/login/reissue") else {
fatalError("Invalid Url")
}

var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
let jsonData = try JSONEncoder().encode(accessInfo)
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
} catch {

}

URLSession.shared.dataTaskPublisher(for: request)
.receive(on: DispatchQueue.main)
.tryMap { data, response -> Data in
guard let httpResponse = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
switch httpResponse.statusCode {
case 200:
print("토큰 리프레시 통신 200")
case 401:
StateManager.shared.tokenExpired = true
print("토큰 리프레시 통신 401")
case 500 :
print("토큰 리프레시 서버 에러 500")
StateManager.shared.serverDown = true
default:
print("토큰 리프레시 상태코드: \(httpResponse.statusCode)")
}
return data
}
.decode(type: AccessInfo.self, decoder: JSONDecoder())
.sink { completion in
switch completion {
case .failure(let error):
if let urlError = error as? URLError {
switch urlError.code {
case .notConnectedToInternet, .networkConnectionLost, .cannotConnectToHost:
StateManager.shared.serverDown = true // Handle connection-related errors
default:
break // Handle other errors if needed
}
}
print("Error: \(error)")
case .finished:
break
}
} receiveValue: { data in
print("토큰 리프레시 반환 데이터 : ", data)
if let userReportCount = data.userReportCount {
StateManager.shared.userReportCount = userReportCount
}
if let accessToken = data.accessToken {
TokenManager.shared.saveToken(token: accessToken)
}
}
.store(in: &self.cancellables)
}
}
13 changes: 13 additions & 0 deletions Map/Model/Filter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Filter.swift
// mocacong
//
// Created by Suji Lee on 2023/06/19.
//

import Foundation

struct Filter: Codable, Hashable {
var mapIds: [String]?
var userReportCount: Int?
}
93 changes: 93 additions & 0 deletions Map/Model/Place.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// Place.swift
// mocacong
//
// Created by Suji Lee on 2023/04/11.
//

import Foundation
import MapKit

final class PlaceAnnotation: NSObject, MKAnnotation {
let title: String?
let coordinate: CLLocationCoordinate2D

init(place: Place) {
self.title = place.placeName
self.coordinate = CLLocationCoordinate2D(latitude: Double(place.y)!, longitude: Double(place.x)!)
}
}

struct KakaoResponse: Codable, Hashable {
var meta: Meta?
var documents: Array<Place>?
}

struct CafePlace: Codable, Hashable, Identifiable {
var id: String
var addressName: String
var phone: String
var placeName: String
var roadAddressName: String
var x: String
var y: String
var solo: Bool = false
var group: Bool = false
var favorite: Bool = false
}

struct Place: Codable, Hashable, Identifiable {
var addressName: String = ""
var categoryGroupCode: String = ""
var categoryGroup_name: String = ""
var category_name: String = ""
var distance: String = ""
var id: String = ""
var phone: String = ""
var placeName: String = ""
var placeUrl: String = ""
var roadAddressName: String = ""
var x: String = ""
var y: String = ""

enum CodingKeys: String, CodingKey {
case addressName = "address_name"
case categoryGroupCode = "category_group_code"
case categoryGroup_name = "category_group_name"
case category_name = "category_name"
case distance = "distance"
case id = "id"
case phone = "phone"
case placeName = "place_name"
case placeUrl = "place_url"
case roadAddressName = "road_address_name"
case x = "x"
case y = "y"
}
}

struct Meta: Codable, Hashable {
var isEnd: Bool?
var pageableCount: Int?
var totalCount: Int?
var sameName: SameName?

enum CodingKeys: String, CodingKey {
case isEnd = "is_end"
case pageableCount = "pageable_count"
case totalCount = "total_count"
case sameName = "same_name"
}

struct SameName: Codable, Hashable {
var region: [String]?
var keyword: String?
var selectedRegion: String?

enum CodingKeys: String, CodingKey {
case region = "region"
case keyword = "keyword"
case selectedRegion = "selected_region"
}
}
}
95 changes: 95 additions & 0 deletions Map/View/CafeAnnotation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// CustomAnnotation.swift
// mocacong
//
// Created by Suji Lee on 2023/05/14.
//

import SwiftUI

struct CafeAnnotation: View {

@ObservedObject var memberVM: MemberViewModel
@ObservedObject var cafeVM: CafeViewModel
@ObservedObject var mapVM: MapViewModel
@StateObject var previewVM: PreviewViewModel = PreviewViewModel()
@ObservedObject var myVM: MyViewModel
@Binding var currentMode: CurrentMode
@State var targetPlace: CafePlace
@State var cafeToPost: Cafe = Cafe()
@State var showModal: Bool = false
@Binding var profileImageData: Data?

@State var showCafePage: Bool = false

var body: some View {
VStack(spacing: 0) {
NavigationLink(destination: CafePageView(memberVM: memberVM, cafeVM: cafeVM, myVM: myVM), isActive: $showCafePage)
{
Button(action: {
cafeToPost.name = targetPlace.placeName
cafeToPost.mapId = targetPlace.id
cafeToPost.phoneNumber = targetPlace.phone
cafeToPost.roadAddress = targetPlace.roadAddressName

cafeVM.cafeData = cafeToPost
cafeVM.cafeMapId = targetPlace.id

postCafe()

if let token = TokenManager.shared.getToken() {
previewVM.fetchCafePreview(accessToken: token, mapId: targetPlace.id)
}
}, label: {
if targetPlace.favorite {
AnnotationMark(type: "FavoriteAnnotation")
} else if targetPlace.solo || targetPlace.group {
AnnotationMark(type: "MocafeAnnotation")
} else {
if targetPlace.id == "388741564" {
AnnotationMark(type: "MocafeAnnotation")
} else {
AnnotationMark(type: "CafeAnnotation")
}
}
})
}
Text(targetPlace.placeName)
.font(.system(size: 12, weight: .medium))
.foregroundColor(.black)
}
.sheet(isPresented: $showModal) {
CafePreviewModal(memberVM: memberVM, cafeVM: cafeVM, previewVM: previewVM, showCafePage: $showCafePage)
.presentationDetents([.large, .fraction(0.385)])
}
.onAppear {
if currentMode == .search {
print("타겟 장소 : ", targetPlace)
}
}
}

@ViewBuilder
func AnnotationMark(type: String) -> some View {
Image(type)
.resizable()
.scaledToFit()
.frame(width: type == "MocafeAnnotation" ? 26 : 23, height: type == "MocafeAnnotation" ? 26 : 23)
}

func postCafe() {
cafeVM.postNewCafe(cafeToPost: cafeToPost)
.sink(receiveCompletion: { completion in
switch completion {
case .failure(let error):
print("카페 등록 비동기 error : \(error)")
case .finished:
print("카페 등록 비동기 성공")
break
}
}, receiveValue: { data in
showModal = true
})
.store(in: &mapVM.cancellables)
}
}
Loading

0 comments on commit f4cad1e

Please sign in to comment.