Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat]음악 검색 앱 구현 #23

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
380 changes: 380 additions & 0 deletions hyotaek/Week6/MusicSearchApp/MusicSearchApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
36 changes: 36 additions & 0 deletions hyotaek/Week6/MusicSearchApp/MusicSearchApp/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// AppDelegate.swift
// MusicSearchApp
//
// Created by HyoTaek on 5/21/24.
//

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {



func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

// MARK: UISceneSession Lifecycle

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}


}

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<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="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="QCR-SN-pWI">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="collection view cell content view" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Search Music List View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController storyboardIdentifier="SearchMusicListViewController" id="BYZ-38-t0r" customClass="SearchMusicListViewController" customModule="MusicSearchApp" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="SpJ-2I-mXE">
<rect key="frame" x="16" y="103" width="361" height="715"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<collectionViewFlowLayout key="collectionViewLayout" automaticEstimatedItemSize="YES" minimumLineSpacing="10" minimumInteritemSpacing="10" id="ofS-My-yL2">
<size key="itemSize" width="361" height="112"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="musicListCell" id="Uor-ad-W7u">
<rect key="frame" x="0.0" y="0.0" width="361" height="112"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<collectionViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="Gdb-6t-pLX">
<rect key="frame" x="0.0" y="0.0" width="361" height="112"/>
<autoresizingMask key="autoresizingMask"/>
</collectionViewCellContentView>
<size key="customSize" width="361" height="112"/>
</collectionViewCell>
</cells>
</collectionView>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="SpJ-2I-mXE" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="0Dc-3I-txO"/>
<constraint firstItem="SpJ-2I-mXE" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="jTd-9p-IbG"/>
<constraint firstItem="SpJ-2I-mXE" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="t39-hD-sUo" userLabel="Collection View.leading = Safe Area.leading"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="SpJ-2I-mXE" secondAttribute="trailing" constant="16" id="vnZ-cm-vNK" userLabel="Safe Area.trailing = Collection View.trailing"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="a2O-L3-rUO"/>
<connections>
<outlet property="collectionView" destination="SpJ-2I-mXE" id="ee8-4O-aPh"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="773.28244274809163" y="-29.577464788732396"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="WxM-mc-Lii">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="QCR-SN-pWI" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="ONl-oL-kmC">
<rect key="frame" x="0.0" y="59" width="393" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="wh4-f8-e1r"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="wYM-4e-SQj" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-153.43511450381678" y="-29.577464788732396"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
25 changes: 25 additions & 0 deletions hyotaek/Week6/MusicSearchApp/MusicSearchApp/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
</dict>
</plist>
13 changes: 13 additions & 0 deletions hyotaek/Week6/MusicSearchApp/MusicSearchApp/MusicInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Foundation

struct SearchResult: Codable {
let results: [MusicInfo]
}

struct MusicInfo: Codable {
let artistName: String
let collectionName: String?
let trackName: String
let artworkUrl100: String
let releaseDate: String
}
59 changes: 59 additions & 0 deletions hyotaek/Week6/MusicSearchApp/MusicSearchApp/MusicListCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import UIKit

final class MusicListCell: UICollectionViewCell {
@IBOutlet weak var trackName: UILabel!
@IBOutlet weak var artistName: UILabel!
@IBOutlet weak var artworkUrl100: UIImageView!
@IBOutlet weak var releaseDate: UILabel!
@IBOutlet weak var collectionName: UILabel!

override func awakeFromNib() {
super.awakeFromNib()
}

func configure(with musicInfo: MusicInfo) {
trackName.text = musicInfo.trackName
artistName.text = musicInfo.artistName
collectionName.text = musicInfo.collectionName ?? "nil"
releaseDate.text = formatReleaseDate(musicInfo.releaseDate)
setImage(urlString: musicInfo.artworkUrl100)
}

private func formatReleaseDate(_ dateStr: String) -> String {
let inputFormatter = ISO8601DateFormatter()
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = "yyyy-MM-dd"

if let date = inputFormatter.date(from: dateStr) {
return outputFormatter.string(from: date)
}
return dateStr
}

func setImage(urlString: String) {
guard let url = URL(string: urlString) else {
artworkUrl100.image = UIImage(named: "placeholder")
return
}
Task {
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self.artworkUrl100.image = image
}
}
} catch {
DispatchQueue.main.async {
self.artworkUrl100.image = UIImage(named: "placeholder")
}
}
}
}
}

extension MusicListCell {
static var identifier: String {
return String(describing: self)
}
}
Loading