diff --git a/Austin/Austin.xcodeproj/project.pbxproj b/Austin/Austin.xcodeproj/project.pbxproj index 57ca273..2a7da42 100644 --- a/Austin/Austin.xcodeproj/project.pbxproj +++ b/Austin/Austin.xcodeproj/project.pbxproj @@ -15,6 +15,9 @@ 95C2DE662AD1455500600202 /* KakaoSDKCert in Frameworks */ = {isa = PBXBuildFile; productRef = 95C2DE652AD1455500600202 /* KakaoSDKCert */; }; 95C2DE682AD1455500600202 /* KakaoSDKCertCore in Frameworks */ = {isa = PBXBuildFile; productRef = 95C2DE672AD1455500600202 /* KakaoSDKCertCore */; }; 95C2DE6A2AD1455500600202 /* KakaoSDKCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 95C2DE692AD1455500600202 /* KakaoSDKCommon */; }; + 95C2DF182AD3C6D900600202 /* CoreDataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C2DF172AD3C6D900600202 /* CoreDataView.swift */; }; + 95C2DF1C2AD3C82800600202 /* TestModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 95C2DF1A2AD3C82800600202 /* TestModel.xcdatamodeld */; }; + 95C2DF1E2AD3CA4300600202 /* TestModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C2DF1D2AD3CA4300600202 /* TestModel.swift */; }; F013F3D62ACE973500423AFF /* AustinApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F013F3D52ACE973500423AFF /* AustinApp.swift */; }; F013F3D82ACE973500423AFF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F013F3D72ACE973500423AFF /* ContentView.swift */; }; F013F3DA2ACE973600423AFF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F013F3D92ACE973600423AFF /* Assets.xcassets */; }; @@ -28,6 +31,9 @@ 95C2DE5E2AD141CC00600202 /* LoginKakaoButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginKakaoButton.swift; sourceTree = ""; }; 95C2DE6B2AD15FDF00600202 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 95C2DE6D2AD163A800600202 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; + 95C2DF172AD3C6D900600202 /* CoreDataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataView.swift; sourceTree = ""; }; + 95C2DF1B2AD3C82800600202 /* TestModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TestModel.xcdatamodel; sourceTree = ""; }; + 95C2DF1D2AD3CA4300600202 /* TestModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestModel.swift; sourceTree = ""; }; F013F3D22ACE973500423AFF /* Austin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Austin.app; sourceTree = BUILT_PRODUCTS_DIR; }; F013F3D52ACE973500423AFF /* AustinApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AustinApp.swift; sourceTree = ""; }; F013F3D72ACE973500423AFF /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -65,6 +71,7 @@ children = ( F013F3D52ACE973500423AFF /* AustinApp.swift */, F013F3D72ACE973500423AFF /* ContentView.swift */, + 95C2DF152AD3C6BD00600202 /* CoreData */, 95C2DE582AD134B900600202 /* Login */, ); path = Sources; @@ -88,6 +95,32 @@ path = Login; sourceTree = ""; }; + 95C2DF152AD3C6BD00600202 /* CoreData */ = { + isa = PBXGroup; + children = ( + 95C2DF192AD3C81500600202 /* Model */, + 95C2DF162AD3C6CD00600202 /* View */, + ); + path = CoreData; + sourceTree = ""; + }; + 95C2DF162AD3C6CD00600202 /* View */ = { + isa = PBXGroup; + children = ( + 95C2DF172AD3C6D900600202 /* CoreDataView.swift */, + ); + path = View; + sourceTree = ""; + }; + 95C2DF192AD3C81500600202 /* Model */ = { + isa = PBXGroup; + children = ( + 95C2DF1A2AD3C82800600202 /* TestModel.xcdatamodeld */, + 95C2DF1D2AD3CA4300600202 /* TestModel.swift */, + ); + path = Model; + sourceTree = ""; + }; F013F3C92ACE973500423AFF = { isa = PBXGroup; children = ( @@ -204,11 +237,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 95C2DF1C2AD3C82800600202 /* TestModel.xcdatamodeld in Sources */, 95C2DE5F2AD141CC00600202 /* LoginKakaoButton.swift in Sources */, 95C2DE5A2AD134DF00600202 /* LoginView.swift in Sources */, + 95C2DF1E2AD3CA4300600202 /* TestModel.swift in Sources */, 95C2DE5D2AD13C4200600202 /* LoginAppleButton.swift in Sources */, F013F3D82ACE973500423AFF /* ContentView.swift in Sources */, F013F3D62ACE973500423AFF /* AustinApp.swift in Sources */, + 95C2DF182AD3C6D900600202 /* CoreDataView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -461,6 +497,19 @@ productName = KakaoSDKCommon; }; /* End XCSwiftPackageProductDependency section */ + +/* Begin XCVersionGroup section */ + 95C2DF1A2AD3C82800600202 /* TestModel.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 95C2DF1B2AD3C82800600202 /* TestModel.xcdatamodel */, + ); + currentVersion = 95C2DF1B2AD3C82800600202 /* TestModel.xcdatamodel */; + path = TestModel.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ }; rootObject = F013F3CA2ACE973500423AFF /* Project object */; } diff --git a/Austin/Austin/Sources/AustinApp.swift b/Austin/Austin/Sources/AustinApp.swift index 475138f..916acb7 100644 --- a/Austin/Austin/Sources/AustinApp.swift +++ b/Austin/Austin/Sources/AustinApp.swift @@ -11,6 +11,8 @@ import SwiftUI @main struct AustinApp: App { + let persistentController = PersistenceController.shared + init() { // Kakao SDK 초기화 guard let apiKey = Bundle.main.object(forInfoDictionaryKey: "KAKAO_APP_KEY") as? String else { @@ -21,6 +23,7 @@ struct AustinApp: App { var body: some Scene { WindowGroup { ContentView() + .environment(\.managedObjectContext, persistentController.container.viewContext) } } } diff --git a/Austin/Austin/Sources/ContentView.swift b/Austin/Austin/Sources/ContentView.swift index c6f1e00..f025a58 100644 --- a/Austin/Austin/Sources/ContentView.swift +++ b/Austin/Austin/Sources/ContentView.swift @@ -15,6 +15,11 @@ struct ContentView: View { Image(systemName: "1.square.fill") Text("Login") } + CoreDataView() + .tabItem { + Image(systemName: "2.square.fill") + Text("Core Data") + } } } } diff --git a/Austin/Austin/Sources/CoreData/Model/TestModel.swift b/Austin/Austin/Sources/CoreData/Model/TestModel.swift new file mode 100644 index 0000000..40308bc --- /dev/null +++ b/Austin/Austin/Sources/CoreData/Model/TestModel.swift @@ -0,0 +1,37 @@ +// +// TestModel.swift +// Austin +// +// Created by Seungui Moon on 10/9/23. +// + +import CoreData +import SwiftUI + +struct PersistenceController { + static let shared = PersistenceController() + + let container: NSPersistentContainer + + init(inMemory: Bool = false) { + // 여기 name은 .xcdatamodeld의 파일명과 같이야 한다. + container = NSPersistentContainer(name: "TestModel") + container.loadPersistentStores { _, error in + if let error = error as NSError? { + fatalError("Unresolved error \(error), \(error.userInfo)") + } + } + } + // context 변화에 대해 저장하는 method 생성 + func saveContext() { + let context = container.viewContext + if context.hasChanges { + do { + try context.save() + } catch { + let nserror = error as NSError + fatalError("Unresolved error \(nserror), \(nserror.userInfo)") + } + } + } +} diff --git a/Austin/Austin/Sources/CoreData/Model/TestModel.xcdatamodeld/TestModel.xcdatamodel/contents b/Austin/Austin/Sources/CoreData/Model/TestModel.xcdatamodeld/TestModel.xcdatamodel/contents new file mode 100644 index 0000000..922ad26 --- /dev/null +++ b/Austin/Austin/Sources/CoreData/Model/TestModel.xcdatamodeld/TestModel.xcdatamodel/contents @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Austin/Austin/Sources/CoreData/View/CoreDataView.swift b/Austin/Austin/Sources/CoreData/View/CoreDataView.swift new file mode 100644 index 0000000..2c99a15 --- /dev/null +++ b/Austin/Austin/Sources/CoreData/View/CoreDataView.swift @@ -0,0 +1,84 @@ +// +// CoreDataView.swift +// Austin +// +// Created by Seungui Moon on 10/9/23. +// + +import SwiftUI + +struct CoreDataView: View { + @Environment(\.managedObjectContext) private var managedObjectContext + @FetchRequest( + entity: Person.entity(), + sortDescriptors: [ + NSSortDescriptor(keyPath: \Person.age, ascending: true) + ] + ) private var people: FetchedResults + + var body: some View { + VStack { + Spacer() + .frame(height: 50) + VStack { + ForEach(people, id: \.self) { p in + HStack { + Text("\(p.name ?? "defaultName")") + Spacer() + Text("\(p.age)") + } + .font(.title) + .padding(.bottom, 20) + } + } + Spacer() + Button { + addPerson(name: "person\(people.count)", age: Int32(people.count + 20)) + } label: { + Text("add person") + .frame(width: 300, height: 50) + .background(.yellow) + } + Button { + deletePerson(at: IndexSet([people.count - 1])) + } label: { + Text("delete") + .frame(width: 300, height: 50) + .background(.orange) + } + Spacer() + .frame(height: 30) + } + .padding() + } + + // MARK: 이후 ViewModel로 분리 필요 + func saveContext() { + do { + try managedObjectContext.save() + } catch { + print("Error saving managed object context: \(error)") + } + } + + func addPerson(name: String, age: Int32) { + let newPerson = Person(context: managedObjectContext) + + newPerson.name = name + newPerson.age = age + + saveContext() + } + + func deletePerson(at offsets: IndexSet) { + offsets.forEach { index in + let person = self.people[index] + self.managedObjectContext.delete(person) + } + saveContext() + } +} + +#Preview { + CoreDataView() +}