Skip to content

Commit

Permalink
Merge pull request #10 from PLREQ/feat/Data
Browse files Browse the repository at this point in the history
Feat[#6, #7] : CoreDataManager 추가, SearchingPlayListView와 Swipe Cell 구현
  • Loading branch information
Juhwa-Lee1023 authored Jun 13, 2023
2 parents af3542b + 5dd437b commit cb76cd7
Show file tree
Hide file tree
Showing 18 changed files with 650 additions and 72 deletions.
Binary file modified .DS_Store
Binary file not shown.
53 changes: 47 additions & 6 deletions Muzip.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@
objects = {

/* Begin PBXBuildFile section */
A81DC8602A344DF600C451ED /* PlayListCellActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81DC85F2A344DF600C451ED /* PlayListCellActionView.swift */; };
A81DC8622A34727800C451ED /* Struct+.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81DC8612A34727800C451ED /* Struct+.swift */; };
A888B9452A18D2B400E89416 /* MusicDB+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = A888B9412A18D2B300E89416 /* MusicDB+CoreDataClass.swift */; };
A888B9462A18D2B400E89416 /* MusicDB+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = A888B9422A18D2B300E89416 /* MusicDB+CoreDataProperties.swift */; };
A888B9472A18D2B400E89416 /* PlayListDB+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = A888B9432A18D2B300E89416 /* PlayListDB+CoreDataClass.swift */; };
A888B9482A18D2B400E89416 /* PlayListDB+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = A888B9442A18D2B300E89416 /* PlayListDB+CoreDataProperties.swift */; };
A888B9762A2A6BB300E89416 /* PlayListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A888B9752A2A6BB300E89416 /* PlayListView.swift */; };
A888B9782A2A701E00E89416 /* PlayListCellContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A888B9772A2A701E00E89416 /* PlayListCellContentView.swift */; };
D53658C12A2490C900B484F9 /* CircleBeatAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53658C02A2490C900B484F9 /* CircleBeatAnimationView.swift */; };
D53658C32A2497C100B484F9 /* bottomSearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53658C22A2497C100B484F9 /* bottomSearchBar.swift */; };
D53658C72A24F4F800B484F9 /* WaveAnimaion.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53658C62A24F4F800B484F9 /* WaveAnimaion.swift */; };
D57E78CC2A17680A00F35DFC /* MuzipApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57E78CB2A17680A00F35DFC /* MuzipApp.swift */; };
D57E78CE2A17680A00F35DFC /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57E78CD2A17680A00F35DFC /* MainView.swift */; };
D57E78D02A17680B00F35DFC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D57E78CF2A17680B00F35DFC /* Assets.xcassets */; };
D57E78D32A17680B00F35DFC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D57E78D22A17680B00F35DFC /* Preview Assets.xcassets */; };
D57E78D52A17680B00F35DFC /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57E78D42A17680B00F35DFC /* Persistence.swift */; };
D57E78D52A17680B00F35DFC /* MuzipDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57E78D42A17680B00F35DFC /* MuzipDataManager.swift */; };
D57E78D82A17680B00F35DFC /* Muzip.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D57E78D62A17680B00F35DFC /* Muzip.xcdatamodeld */; };
D57E78E32A17680B00F35DFC /* MuzipTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57E78E22A17680B00F35DFC /* MuzipTests.swift */; };
D57E78ED2A17680B00F35DFC /* MuzipUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57E78EC2A17680B00F35DFC /* MuzipUITests.swift */; };
Expand Down Expand Up @@ -50,6 +58,14 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
A81DC85F2A344DF600C451ED /* PlayListCellActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayListCellActionView.swift; sourceTree = "<group>"; };
A81DC8612A34727800C451ED /* Struct+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Struct+.swift"; sourceTree = "<group>"; };
A888B9412A18D2B300E89416 /* MusicDB+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MusicDB+CoreDataClass.swift"; sourceTree = "<group>"; };
A888B9422A18D2B300E89416 /* MusicDB+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MusicDB+CoreDataProperties.swift"; sourceTree = "<group>"; };
A888B9432A18D2B300E89416 /* PlayListDB+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PlayListDB+CoreDataClass.swift"; sourceTree = "<group>"; };
A888B9442A18D2B300E89416 /* PlayListDB+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PlayListDB+CoreDataProperties.swift"; sourceTree = "<group>"; };
A888B9752A2A6BB300E89416 /* PlayListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayListView.swift; sourceTree = "<group>"; };
A888B9772A2A701E00E89416 /* PlayListCellContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayListCellContentView.swift; sourceTree = "<group>"; };
D53658C02A2490C900B484F9 /* CircleBeatAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleBeatAnimationView.swift; sourceTree = "<group>"; };
D53658C22A2497C100B484F9 /* bottomSearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = bottomSearchBar.swift; sourceTree = "<group>"; };
D53658C62A24F4F800B484F9 /* WaveAnimaion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveAnimaion.swift; sourceTree = "<group>"; };
Expand All @@ -58,7 +74,7 @@
D57E78CD2A17680A00F35DFC /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
D57E78CF2A17680B00F35DFC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D57E78D22A17680B00F35DFC /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
D57E78D42A17680B00F35DFC /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
D57E78D42A17680B00F35DFC /* MuzipDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuzipDataManager.swift; sourceTree = "<group>"; };
D57E78D72A17680B00F35DFC /* Muzip.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Muzip.xcdatamodel; sourceTree = "<group>"; };
D57E78D92A17680B00F35DFC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D57E78DE2A17680B00F35DFC /* MuzipTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MuzipTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -104,6 +120,15 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
A81DC8672A36B47B00C451ED /* PlayListCell */ = {
isa = PBXGroup;
children = (
A888B9772A2A701E00E89416 /* PlayListCellContentView.swift */,
A81DC85F2A344DF600C451ED /* PlayListCellActionView.swift */,
);
path = PlayListCell;
sourceTree = "<group>";
};
D57E78BF2A17680A00F35DFC = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -170,8 +195,13 @@
D57E78FB2A17687C00F35DFC /* Model */ = {
isa = PBXGroup;
children = (
D57E78D42A17680B00F35DFC /* Persistence.swift */,
A888B9412A18D2B300E89416 /* MusicDB+CoreDataClass.swift */,
A888B9422A18D2B300E89416 /* MusicDB+CoreDataProperties.swift */,
A888B9432A18D2B300E89416 /* PlayListDB+CoreDataClass.swift */,
A888B9442A18D2B300E89416 /* PlayListDB+CoreDataProperties.swift */,
D57E78D62A17680B00F35DFC /* Muzip.xcdatamodeld */,
D57E78D42A17680B00F35DFC /* MuzipDataManager.swift */,
A81DC8612A34727800C451ED /* Struct+.swift */,
);
path = Model;
sourceTree = "<group>";
Expand Down Expand Up @@ -232,6 +262,8 @@
D57E79022A1768D800F35DFC /* ReusableUI */ = {
isa = PBXGroup;
children = (
A81DC8672A36B47B00C451ED /* PlayListCell */,
A888B9752A2A6BB300E89416 /* PlayListView.swift */,
D57E79032A17692C00F35DFC /* ReusableTemp.swift */,
D57E79172A17776A00F35DFC /* BubbleShape.swift */,
D53658C02A2490C900B484F9 /* CircleBeatAnimationView.swift */,
Expand Down Expand Up @@ -373,22 +405,31 @@
files = (
D57E79062A17693A00F35DFC /* ViewModelTemp.swift in Sources */,
D53658C12A2490C900B484F9 /* CircleBeatAnimationView.swift in Sources */,
D57E78D52A17680B00F35DFC /* Persistence.swift in Sources */,
D57E78D52A17680B00F35DFC /* MuzipDataManager.swift in Sources */,
D57E78D52A17680B00F35DFC /* MuzipDataManager.swift in Sources */,
D57E78CE2A17680A00F35DFC /* MainView.swift in Sources */,
D53658C72A24F4F800B484F9 /* WaveAnimaion.swift in Sources */,
D57E78CC2A17680A00F35DFC /* MuzipApp.swift in Sources */,
A888B9762A2A6BB300E89416 /* PlayListView.swift in Sources */,
D57E78D82A17680B00F35DFC /* Muzip.xcdatamodeld in Sources */,
D57E790C2A17695700F35DFC /* NetworkTemp.swift in Sources */,
D57E79122A1769D100F35DFC /* ViewExtension.swift in Sources */,
D57E790A2A17694E00F35DFC /* RoundedCorner.swift in Sources */,
D53658C32A2497C100B484F9 /* bottomSearchBar.swift in Sources */,
A888B9462A18D2B400E89416 /* MusicDB+CoreDataProperties.swift in Sources */,
A81DC8602A344DF600C451ED /* PlayListCellActionView.swift in Sources */,
D57E79082A17694400F35DFC /* ExtensionTemp.swift in Sources */,
A888B9452A18D2B400E89416 /* MusicDB+CoreDataClass.swift in Sources */,
D57E79042A17692C00F35DFC /* ReusableTemp.swift in Sources */,
A81DC8622A34727800C451ED /* Struct+.swift in Sources */,
D57E79182A17776A00F35DFC /* BubbleShape.swift in Sources */,
D57E79102A1769A700F35DFC /* ImageExtension.swift in Sources */,
A888B9782A2A701E00E89416 /* PlayListCellContentView.swift in Sources */,
A888B9482A18D2B400E89416 /* PlayListDB+CoreDataProperties.swift in Sources */,
D57E790E2A17698F00F35DFC /* ColorExtension.swift in Sources */,
D57E79142A1769DB00F35DFC /* ArrayExtension.swift in Sources */,
D57E79162A17756700F35DFC /* NetworkMonitor.swift in Sources */,
A888B9472A18D2B400E89416 /* PlayListDB+CoreDataClass.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -547,7 +588,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Muzip/Preview Content\"";
DEVELOPMENT_TEAM = 5N5TS7Y4MA;
DEVELOPMENT_TEAM = 22A6RK2943;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
Expand Down Expand Up @@ -576,7 +617,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Muzip/Preview Content\"";
DEVELOPMENT_TEAM = 5N5TS7Y4MA;
DEVELOPMENT_TEAM = 22A6RK2943;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
Expand Down
21 changes: 21 additions & 0 deletions Muzip/Extensions/ArrayExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,24 @@ extension Array {
return indices ~= index ? self[index] : nil
}
}

// array를 담는 App Storage 사용을 위한 extension
extension Array: RawRepresentable where Element: Codable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode([Element].self, from: data)
else {
return nil
}
self = result
}

public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "[]"
}
return result
}
}
7 changes: 7 additions & 0 deletions Muzip/Extensions/ViewExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,10 @@ extension View {
}
}


// PlayList Cell에서 SwipeAction Button을 추가해주는 Extension입니다.
extension View {
func addButtonActions(trailingButton: [CellActionButton], onClick: @escaping (CellActionButton) -> Void) -> some View {
self.modifier(SwipeContainerCell(trailingButton: trailingButton, onClick: onClick))
}
}
15 changes: 15 additions & 0 deletions Muzip/Model/MusicDB+CoreDataClass.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// MusicDB+CoreDataClass.swift
// Muzip
//
// Created by Yeni Hwang on 2023/05/20.
//
//

import Foundation
import CoreData

@objc(Music)
public class MusicDB: NSManagedObject {

}
28 changes: 28 additions & 0 deletions Muzip/Model/MusicDB+CoreDataProperties.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// MusicDB+CoreDataProperties.swift
// Muzip
//
// Created by Yeni Hwang on 2023/05/20.
//
//

import Foundation
import CoreData


extension MusicDB {

@nonobjc public class func fetchRequest() -> NSFetchRequest<MusicDB> {
return NSFetchRequest<MusicDB>(entityName: "Music")
}

@NSManaged public var title: String?
@NSManaged public var artist: String?
@NSManaged public var musicImage: Data?
@NSManaged public var playList: PlayListDB?

}

extension MusicDB : Identifiable {

}
17 changes: 11 additions & 6 deletions Muzip/Model/Muzip.xcdatamodeld/Muzip.xcdatamodel/contents
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="true" userDefinedModelVersionIdentifier="">
<entity name="Item" representedClassName="Item" syncable="YES" codeGenerationType="class">
<attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21754" systemVersion="22E261" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Music" representedClassName=".MusicDB" syncable="YES">
<attribute name="artist" optional="YES" attributeType="String"/>
<attribute name="musicImage" optional="YES" attributeType="Binary"/>
<attribute name="title" optional="YES" attributeType="String"/>
<relationship name="playList" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PlayList" inverseName="music" inverseEntity="PlayList"/>
</entity>
<entity name="PlayList" representedClassName=".PlayListDB" syncable="YES">
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="title" optional="YES" attributeType="String"/>
<relationship name="music" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="Music" inverseName="playList" inverseEntity="Music"/>
</entity>
<elements>
<element name="Item" positionX="-63" positionY="-18" width="128" height="44"/>
</elements>
</model>
156 changes: 156 additions & 0 deletions Muzip/Model/MuzipDataManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//
// Persistence.swift
// Muzip
//
// Created by 이주화 on 2023/05/19.
//

import CoreData
import SwiftUI

class MuzipDataManager {

static let shared: MuzipDataManager = MuzipDataManager()

let container: NSPersistentContainer
var context: NSManagedObjectContext {
return container.viewContext
}

/// Model Name
let playListModelName: String = "PlayList"
let MusicModelName: String = "Music"

private init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "Muzip")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}

/// PlayList 불러오기
func fetch() -> [NSManagedObject] {
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: playListModelName)

let sort = NSSortDescriptor(key: "day", ascending: false)
fetchRequest.sortDescriptors = [sort]

do {
let result = try context.fetch(fetchRequest)
return result
} catch {
print("fetch fail")
return []
}
}

/// 플레이리스트 저장
func savePlayList(title: String, location: String, day: Date, latitude: Double, longtitude: Double, musics: [Music]) {
let playListObject = NSEntityDescription.insertNewObject(forEntityName: playListModelName, into: context)
playListObject.setValue(title, forKey: "title")
playListObject.setValue(day, forKey: "day")
playListObject.setValue(location, forKey: "location")
playListObject.setValue(latitude, forKey: "latitude")
playListObject.setValue(longtitude, forKey: "longtitude")

let group = DispatchGroup()
var errorOccurred = false

for music in musics {
if music.title != "" { // 음악 검색을 실패하면 저장되지 않는다.
let musicObject = NSEntityDescription.insertNewObject(forEntityName: MusicModelName, into: context) as! MusicDB
musicObject.title = music.title
musicObject.artist = music.artist

group.enter()
DispatchQueue.global().async {
if let data = try? Data(contentsOf: music.musicImageURL) {
if let image = UIImage(data: data) {
musicObject.musicImage = image.jpegData(compressionQuality: 1.0)
}
}
(playListObject as! PlayListDB).addMusicToPlayList(musicObject)
group.leave()
}
}
}

group.notify(queue: DispatchQueue.global()) {
do {
try self.context.save()
} catch {
print("failure Save")
}
}
}


/// playList 삭제
func deletePlayList(playListObject: NSManagedObject) -> Bool {
context.delete(playListObject)

return saveContext()
}

/// platList의 제목 변경
func updatePlayListTitle(playListObject: NSManagedObject, title: String) -> Bool {
playListObject.setValue(title, forKey: "title")

return saveContext()
}

/// playLisy에서 music들을 불러오기
func musicsFetch(playList: PlayListDB) -> [MusicDB] {
return playList.music?.array as! [MusicDB]
}

/// playList에 music들을 추가
func addMusicsToPlayList(playListObject: NSManagedObject, musics: [MusicData]) -> Bool {
for music in musics {
let musicObject = NSEntityDescription.insertNewObject(forEntityName: MusicModelName, into: context) as! MusicDB
musicObject.title = music.title
musicObject.artist = music.artist
musicObject.musicImage = music.musicImage

(playListObject as! PlayListDB).addMusicToPlayList(musicObject)
}
return saveContext()
}

/// playList에서 music 삭제
func deleteMusic(music: NSManagedObject){
context.delete(music)
saveContext()
}

/// playList 안에서 music의 순서를 변경
func changeMusicOrder(playListObject: [NSManagedObject], musics: [MusicData]) -> Bool {
for i in 0..<playListObject.count {
playListObject[i].setValue(musics[i].title, forKey: "title")
playListObject[i].setValue(musics[i].artist, forKey: "artist")
playListObject[i].setValue(musics[i].musicImage, forKey: "musicImage")
}

return saveContext()
}

/// 상태 저장
func saveContext() -> Bool{
if context.hasChanges{
do {
try context.save()
return true
} catch {
let nserror = error as NSError
fatalError("Unsolved error \(nserror), \(nserror.userInfo)")
}
}
return false
}
}
Loading

0 comments on commit cb76cd7

Please sign in to comment.