Skip to content
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
95 changes: 95 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## DS_Store
*.DS_Store

## User settings
xcuserdata/
xcuserdata/*
*.xcuserstate

## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout

## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3

## Obj-C/Swift specific
*.hmap

## App packaging
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm

.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build/

# Accio dependency management
Dependencies/
.accio/

# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/
12 changes: 12 additions & 0 deletions Git_ Exercise.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

/* Begin PBXBuildFile section */
2768CA08294F453B00990B98 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2768CA07294F453B00990B98 /* main.swift */; };
3B7DEA03295003AA002C7E4D /* input data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B7DEA02295003AA002C7E4D /* input data.swift */; };
3B7DEA05295004C8002C7E4D /* student class.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B7DEA04295004C8002C7E4D /* student class.swift */; };
3B7DEA0729500574002C7E4D /* store data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B7DEA0629500574002C7E4D /* store data.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand All @@ -25,6 +28,9 @@
/* Begin PBXFileReference section */
2768CA04294F453B00990B98 /* Git_ Exercise */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Git_ Exercise"; sourceTree = BUILT_PRODUCTS_DIR; };
2768CA07294F453B00990B98 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
3B7DEA02295003AA002C7E4D /* input data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "input data.swift"; sourceTree = "<group>"; };
3B7DEA04295004C8002C7E4D /* student class.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "student class.swift"; sourceTree = "<group>"; };
3B7DEA0629500574002C7E4D /* store data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "store data.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -58,6 +64,9 @@
isa = PBXGroup;
children = (
2768CA07294F453B00990B98 /* main.swift */,
3B7DEA02295003AA002C7E4D /* input data.swift */,
3B7DEA04295004C8002C7E4D /* student class.swift */,
3B7DEA0629500574002C7E4D /* store data.swift */,
);
path = "Git_ Exercise";
sourceTree = "<group>";
Expand Down Expand Up @@ -121,6 +130,9 @@
buildActionMask = 2147483647;
files = (
2768CA08294F453B00990B98 /* main.swift in Sources */,
3B7DEA03295003AA002C7E4D /* input data.swift in Sources */,
3B7DEA0729500574002C7E4D /* store data.swift in Sources */,
3B7DEA05295004C8002C7E4D /* student class.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
81 changes: 81 additions & 0 deletions Git_ Exercise/input data.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// input data.swift
// Git_ Exercise
//
// Created by J.E on 2022/12/19.
//

import Foundation

///**'메뉴선택'용 μž…λ ₯κ°’**을 μ €μž₯ν•˜λŠ” λ³€μˆ˜
///- main의 repeat-while문의 반볡쑰건
///- `X`λ₯Ό μž…λ ₯ν•˜λ©΄ 반볡문 μ’…λ£Œ
var inputMenu = String()

///**'κΈ°λŠ₯μ‹€ν–‰'용 μž…λ ₯κ°’(λ“€)**을 μ €μž₯ν•˜λŠ” λ³€μˆ˜
///- main의 switch-caseλ¬Έμ—μ„œ 호좜
///- 쀑볡·가μž₯자리 곡백은 μžλ™μœΌλ‘œ 처리, μœ νš¨ν•œ 단어 μˆ˜μ— 따라 λ³€μˆ˜μ— μ €μž₯
var inputData = String()


//MARK: - readLine()κ°’ μ €μž₯
var (name, subject, grade) = (String(), String(), String())
func getInputs(_ s: String = (readLine() ?? "")) {
let input = s.components(separatedBy: .whitespaces).compactMap { try? checkInput(str: $0).get() }
switch input.count {
case 1: (name, subject, grade) = (input[0], "", "")
case 2: (name, subject, grade) = (input[0], input[1], "")
case 3: (name, subject, grade) = (input[0], input[1], input[2])
default: print("μž…λ ₯이 잘λͺ»λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ ν™•μΈν•΄μ£Όμ„Έμš”.")
}
}


//MARK: - ν—ˆμš©λœ μž…λ ₯값인지 검사

///**μž…λ ₯κ°’ μ—λŸ¬ μ’…λ₯˜** λͺ©λ‘
///- __notMenu__: `inputMenu`에 선택지 6μ’… μ™Έμ˜ 값이 λ“€μ–΄μ˜¬ 경우
///- __notAlphanumeric__: `inputMenu` λ˜λŠ” `inputData`에 μ˜λ¬ΈΒ·μˆ«μžκ°€ μ•„λ‹Œ 자λͺ¨κ°€ ν¬ν•¨λœ 경우
enum InputError: Error {
case notMenu(text: String = "λ­”κ°€ μž…λ ₯이 잘λͺ»λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 1~5 μ‚¬μ΄μ˜ 숫자 ν˜Ήμ€ Xλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”.")
case notAlphanumeric(text: String = "μž…λ ₯이 잘λͺ»λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ ν™•μΈν•΄μ£Όμ„Έμš”.")
}

///**μž…λ ₯κ°’ μ—λŸ¬ νŒλ³„** ν•¨μˆ˜
///- `checkInput(str:)`: **κΈ°λŠ₯μ‹€ν–‰** λ‹¨κ³„μ—μ„œ ν™œμš©
///- `checkInput(chr:)`: **κΈ°λŠ₯선택** λ‹¨κ³„μ—μ„œ ν™œμš©
func checkInput(str: String) -> Result<String?, InputError> {
var isValid = Bool()
for chr in str {
isValid = chr.isCased || chr.isNumber || chr.isWhitespace || chr == "+"
}
guard isValid else { return .failure(InputError.notAlphanumeric()) }
return .success(isValid ? str : nil)
}
///**μž…λ ₯κ°’ μ—λŸ¬ νŒλ³„** ν•¨μˆ˜
///- `checkInput(str:)`: **κΈ°λŠ₯μ‹€ν–‰** λ‹¨κ³„μ—μ„œ ν™œμš©
///- `checkInput(chr:)`: **κΈ°λŠ₯선택** λ‹¨κ³„μ—μ„œ ν™œμš©
func checkInput(chr: String) -> Result<String, InputError> {
guard chr.count == 1 && ["1", "2", "3", "4", "5", "X"].contains(chr) else { return .failure(InputError.notMenu()) }
return .success(chr)
}


///**μž…λ ₯κ°’ μ—λŸ¬ 처리** ν•¨μˆ˜ - κΈ°λŠ₯ 선택 μ‹œ
///+ μ—λŸ¬κ°€ μ•„λ‹ˆλ©΄ true, μ—λŸ¬μ— ν•΄λ‹Ήν•˜λ©΄ falseλ₯Ό λ°˜ν™˜
///+ μš©λ„μ— 따라(guardλ¬Έ 쑰건 or 문ꡬ좜λ ₯) λ‹€μš©λ„λ‘œ μ‚¬μš©
func getInput(_ checkedResult: Result<String, InputError>) -> Bool {
switch checkedResult {
case .success: return true //return try! checkedResult.get()
case .failure(let error): print("\(error)".components(separatedBy: "\"")[1]); return false
}
}
///**μž…λ ₯κ°’ μ—λŸ¬ 처리** ν•¨μˆ˜ - κΈ°λŠ₯ μ‹€ν–‰ μ‹œ
///+ μ—λŸ¬κ°€ μ•„λ‹ˆλ©΄ true, μ—λŸ¬μ— ν•΄λ‹Ήν•˜λ©΄ falseλ₯Ό λ°˜ν™˜
///+ μš©λ„μ— 따라(guardλ¬Έ 쑰건 or 문ꡬ좜λ ₯) λ‹€μš©λ„λ‘œ μ‚¬μš©
func getInput(_ checkedResult: Result<String?, InputError>) -> Bool {
switch checkedResult {
case .success: return true
case .failure(let error): print("\(error)".components(separatedBy: "\"")[1]); return false
}
}
66 changes: 65 additions & 1 deletion Git_ Exercise/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,69 @@

import Foundation

print("Hello, World!")
//MARK: - μ§€λ‚œ 싀행기둝 뢈러였기
readWholeData()

repeat {

//MARK: - μ›ν•˜λŠ” κΈ°λŠ₯ μ„ νƒν•˜κΈ°
print("""
μ›ν•˜λŠ” κΈ°λŠ₯을 μž…λ ₯ν•΄μ£Όμ„Έμš”
1: 학생좔가, 2: ν•™μƒμ‚­μ œ, 3: 성적좔가(λ³€κ²½), 4: μ„±μ μ‚­μ œ, 5: 평점보기, X: μ’…λ£Œ
""")
inputMenu = readLine() ?? ""
let _ = checkInput(chr: inputMenu)

//MARK: - μ„ νƒν•œ κΈ°λŠ₯ μ‹€ν–‰ν•˜κΈ°
switch inputMenu {
case "1":
print("μΆ”κ°€ν•  ν•™μƒμ˜ 이름을 μž…λ ₯ν•΄μ£Όμ„Έμš”")
inputData = readLine() ?? ""
guard getInput(checkInput(str: inputData)) else { print(); continue }
getInputs(inputData)
let _ = Student(name: name)
case "2":
print("μ‚­μ œν•  ν•™μƒμ˜ 이름을 μž…λ ₯ν•΄μ£Όμ„Έμš”")
inputData = readLine() ?? ""
guard getInput(checkInput(str: inputData)) else { print(); continue }
getInputs(inputData)
print(Student.students.removeValue(forKey: name) == nil ? "\(name) 학생을 μ°Ύμ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€." : "")
case "3":
print("""
성적을 μΆ”κ°€ν•  ν•™μƒμ˜ 이름, κ³Όλͺ© 이름, 성적(A+, A0, F λ“±)을 λ„μ–΄μ“°κΈ°λ‘œ κ΅¬λΆ„ν•˜μ—¬ μ°¨λ‘€λ‘œ μž‘μ„±ν•΄μ£Όμ„Έμš”.
μž…λ ₯예) Mickey Swift A+
λ§Œμ•½μ— ν•™μƒμ˜ 성적 쀑 ν•΄λ‹Ή κ³Όλͺ©μ΄ μ‘΄μž¬ν•˜λ©΄ κΈ°μ‘΄ μ μˆ˜κ°€ κ°±μ‹ λ©λ‹ˆλ‹€.
""")
inputData = readLine() ?? ""
guard getInput(checkInput(str: inputData)) else { print(); continue }
getInputs(inputData)
guard let student = Student.students[name] else { print("\(name) 학생을 μ°Ύμ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€."); break }
student.grades.updateValue(grade, forKey: subject)
case "4":
print("""
성적을 μ‚­μ œν•  ν•™μƒμ˜ 이름, κ³Όλͺ© 이름을 λ„μ–΄μ“°κΈ°λ‘œ κ΅¬λΆ„ν•˜μ—¬ μ°¨λ‘€λ‘œ μž‘μ„±ν•΄μ£Όμ„Έμš”.
μž…λ ₯예) Mickey Swift
""")
inputData = readLine() ?? ""
guard getInput(checkInput(str: inputData)) else { print(); continue }
getInputs(inputData)
guard let student = Student.students[name] else { print("\(name) 학생을 μ°Ύμ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€."); break }
student.grades.removeValue(forKey: subject)
case "5":
print("평점을 μ•Œκ³ μ‹Άμ€ ν•™μƒμ˜ 이름을 μž…λ ₯ν•΄μ£Όμ„Έμš”")
inputData = readLine() ?? ""
guard getInput(checkInput(str: inputData)) else { print(); continue }
getInputs(inputData)
guard let student = Student.students[name] else { print("\(name) 학생을 μ°Ύμ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€."); break }
student.grades.sorted(by: { $0.key.count < $1.key.count }).forEach { print("\($0.key): \($0.value)") }
print("평점 :", "\(student.overallScore)".replacingOccurrences(of: ".0", with: "").prefix(4))
case "X":
//MARK: - μ§€λ‚œ 싀행기둝 μ €μž₯ν•˜κΈ°
saveWholeData()
print("ν”„λ‘œκ·Έλž¨μ„ μ’…λ£Œν•©λ‹ˆλ‹€...")
default:
print("λ­”κ°€ μž…λ ₯이 잘λͺ»λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 1~5 μ‚¬μ΄μ˜ 숫자 ν˜Ήμ€ Xλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”.")
}
print()

} while inputMenu != "X"
28 changes: 28 additions & 0 deletions Git_ Exercise/store data.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// store data.swift
// Git_ Exercise
//
// Created by J.E on 2022/12/19.
//

import Foundation

//MARK: - 학생객체λ₯Ό λ°±κ·ΈλΌμš΄λ“œμ— μ €μž₯ν•˜λŠ” 방법

///이번 싀행데이터 μ €μž₯ν•˜κΈ°
func saveWholeData() {
let path = "/Users/<username>/Desktop/α„‰α…’α„Šα…‘α†¨/tastycode_SeSAC_1st/MyCreditManager_cdenen20/MyCreditManager_cdenen20/Info.plist"
let backupDictionary = NSMutableDictionary(contentsOfFile: path) ?? [:]
let encoder = PropertyListEncoder()
let encodedData = try! encoder.encode(Student.students)
try! encodedData.write(to: .init(fileURLWithPath: path))
}

///μ§€λ‚œ 싀행데이터 뢈러였기
func readWholeData() {
let path = "/Users/<username>/Desktop/α„‰α…’α„Šα…‘α†¨/tastycode_SeSAC_1st/MyCreditManager_cdenen20/MyCreditManager_cdenen20/Info.plist"
let backupDictionary = try! Data(contentsOf: URL(fileURLWithPath: path))
let decoder = PropertyListDecoder()
let data = try! decoder.decode([String: Student].self, from: backupDictionary)
Student.students = data
}
52 changes: 52 additions & 0 deletions Git_ Exercise/student class.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// student class.swift
// Git_ Exercise
//
// Created by J.E on 2022/12/19.
//

import Foundation

///학생객체λ₯Ό μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•˜λŠ” 클래슀
///- `students`: 이름(key)에 λŒ€μ‘ν•˜λŠ” 학생객체(value)λ₯Ό μ €μž₯
///- `grades`: κ³Όλͺ©(key)에 λŒ€μ‘ν•˜λŠ” 성적(value)을 μ €μž₯
class Student: Codable {

static var students = [String: Student]()
let name: String
var grades = [String: String]() {
didSet {
switch grades.count - oldValue.count {
case 1: print("\(name) ν•™μƒμ˜ \(subject) κ³Όλͺ©μ΄ \(grade)둜 μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
case 0 where grade != "": print("\(name) ν•™μƒμ˜ \(subject) κ³Όλͺ©μ΄ \(grade)둜 λ³€κ²½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
case 0 where grade == "": print("\(name) ν•™μƒμ˜ \(subject) κ³Όλͺ©μ˜ 성적을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
default: print("\(name) ν•™μƒμ˜ \(subject) κ³Όλͺ©μ˜ 성적이 μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
}
}
}

///평점 계산
var overallScore: Double {
let gradeList = ["F", "D0", "D+", "C0", "C+", "B0", "B+", "A0", "A+"]
let scoreList = [0, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5]
guard !grades.isEmpty else { return 0 }
return grades.values.map { scoreList[gradeList.firstIndex(of: $0) ?? 0] }.reduce(0, +) / Double(grades.count)
}

///학생 μΆ”κ°€
init?(name: String) {
if Student.students.keys.contains(name) {
print("\(name)은(λŠ”) 이미 μ‘΄μž¬ν•˜λŠ” ν•™μƒμž…λ‹ˆλ‹€. μΆ”κ°€ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.")
return nil
}
self.name = name
Self.students.updateValue(self, forKey: name)
print("\(name) 학생을 μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.")
}

///학생 μ‚­μ œ
deinit {
print("\(name) 학생을 μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€.")
}

}