From 9e04d9dabe1d487e469de2ad11c18b6112f18784 Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Mon, 29 Aug 2022 17:45:31 +0900 Subject: [PATCH 01/10] Reflect review - Delete duplicate - Change logic, some naming --- .../Calculator.xcodeproj/project.pbxproj | 8 +- .../Extensions/Numeric+Extension.swift | 5 +- .../Extensions/String+Extension.swift | 22 +-- .../Calculator/Model/Calculator.swift | 174 ++++++++++++------ .../Calculator/Style/ButtonStyle.swift | 44 ++--- .../Calculator/View/CalculatorView.swift | 7 +- .../{EachButton.swift => PadButton.swift} | 9 +- .../Calculator/Calculator/View/PadView.swift | 2 +- .../Calculator/View/ScreenView.swift | 27 ++- .../ViewModel/CalculatorManager.swift | 116 ++---------- 10 files changed, 201 insertions(+), 213 deletions(-) rename week4/Calculator/Calculator/View/{EachButton.swift => PadButton.swift} (65%) diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj index e618279..3fccc2c 100644 --- a/week4/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ A67D5A3228AB573F00624B25 /* CalculatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A3128AB573F00624B25 /* CalculatorManager.swift */; }; A67D5A3428AB68B200624B25 /* PadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A3328AB68B200624B25 /* PadView.swift */; }; A67D5A3A28ADC86000624B25 /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A3928ADC86000624B25 /* ScreenView.swift */; }; - A67D5A3C28ADCA6C00624B25 /* EachButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A3B28ADCA6C00624B25 /* EachButton.swift */; }; + A67D5A3C28ADCA6C00624B25 /* PadButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A3B28ADCA6C00624B25 /* PadButton.swift */; }; A67D5A3E28ADD28D00624B25 /* ButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A3D28ADD28D00624B25 /* ButtonStyle.swift */; }; A67D5A5628B7024800624B25 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A5528B7024800624B25 /* String+Extension.swift */; }; A67D5A5928B74B3200624B25 /* Numeric+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A5828B74B3200624B25 /* Numeric+Extension.swift */; }; @@ -32,7 +32,7 @@ A67D5A3128AB573F00624B25 /* CalculatorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorManager.swift; sourceTree = ""; }; A67D5A3328AB68B200624B25 /* PadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadView.swift; sourceTree = ""; }; A67D5A3928ADC86000624B25 /* ScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenView.swift; sourceTree = ""; }; - A67D5A3B28ADCA6C00624B25 /* EachButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EachButton.swift; sourceTree = ""; }; + A67D5A3B28ADCA6C00624B25 /* PadButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadButton.swift; sourceTree = ""; }; A67D5A3D28ADD28D00624B25 /* ButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStyle.swift; sourceTree = ""; }; A67D5A5528B7024800624B25 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; A67D5A5828B74B3200624B25 /* Numeric+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Numeric+Extension.swift"; sourceTree = ""; }; @@ -103,7 +103,7 @@ A67D5A2028AB566600624B25 /* CalculatorView.swift */, A67D5A3328AB68B200624B25 /* PadView.swift */, A67D5A3928ADC86000624B25 /* ScreenView.swift */, - A67D5A3B28ADCA6C00624B25 /* EachButton.swift */, + A67D5A3B28ADCA6C00624B25 /* PadButton.swift */, ); path = View; sourceTree = ""; @@ -206,7 +206,7 @@ files = ( A67D5A5628B7024800624B25 /* String+Extension.swift in Sources */, A67D5A5928B74B3200624B25 /* Numeric+Extension.swift in Sources */, - A67D5A3C28ADCA6C00624B25 /* EachButton.swift in Sources */, + A67D5A3C28ADCA6C00624B25 /* PadButton.swift in Sources */, A67D5A3E28ADD28D00624B25 /* ButtonStyle.swift in Sources */, A67D5A3428AB68B200624B25 /* PadView.swift in Sources */, A67D5A2128AB566600624B25 /* CalculatorView.swift in Sources */, diff --git a/week4/Calculator/Calculator/Extensions/Numeric+Extension.swift b/week4/Calculator/Calculator/Extensions/Numeric+Extension.swift index 2a3d52e..c5ce6f7 100644 --- a/week4/Calculator/Calculator/Extensions/Numeric+Extension.swift +++ b/week4/Calculator/Calculator/Extensions/Numeric+Extension.swift @@ -9,6 +9,9 @@ import Foundation extension Numeric { var exponentialNotation: String { - return Formatter.exponentialNotation.string(for: self) ?? "" + guard let exponentialNotationString = Formatter.exponentialNotation.string(for: self) else { + return "오류" + } + return exponentialNotationString } } diff --git a/week4/Calculator/Calculator/Extensions/String+Extension.swift b/week4/Calculator/Calculator/Extensions/String+Extension.swift index d40c973..9581101 100644 --- a/week4/Calculator/Calculator/Extensions/String+Extension.swift +++ b/week4/Calculator/Calculator/Extensions/String+Extension.swift @@ -8,21 +8,17 @@ import Foundation extension String { - var insertComma: String { + var decimalFormat: String { let numberFormatter = NumberFormatter(); numberFormatter.numberStyle = .decimal - if self.contains(".") { - let numberArray = self.components(separatedBy: ".") - let numberString = numberArray.first ?? "0" - guard let doubleValue = Double(numberString) else { - return self - } - return (numberFormatter.string(from: NSNumber(value: doubleValue)) ?? numberString) + ".\(numberArray[numberArray.index(numberArray.startIndex, offsetBy: 1)])" - } else { - guard let doubleValue = Double(self) else { - return self - } - return numberFormatter.string(from: NSNumber(value: doubleValue)) ?? self + let decimalFractionComponents = components(separatedBy: ".") + guard let decimalString = decimalFractionComponents.first, + let decimal = Int(decimalString), + var decimalFormat = numberFormatter.string(from: NSNumber(value: decimal)) + else { return "오류"} + if 1 < decimalFractionComponents.count, let fractionString = decimalFractionComponents.last { + decimalFormat += ".\(fractionString)" } + return decimalFormat } } diff --git a/week4/Calculator/Calculator/Model/Calculator.swift b/week4/Calculator/Calculator/Model/Calculator.swift index e4311f5..8dfcfbb 100644 --- a/week4/Calculator/Calculator/Model/Calculator.swift +++ b/week4/Calculator/Calculator/Model/Calculator.swift @@ -9,18 +9,14 @@ import Foundation struct Calculator { - // MARK: Alias(es) - - typealias BinaryOperator = CalculatorManager.BinaryOperator - typealias Digit = CalculatorManager.Digit - // MARK: Propery(ies) private(set) var displayValue = "0" private(set) var isAllClear = true private var calculationResult: Decimal? = 0 private var newValue: Decimal? = nil - private var operation: BinaryOperator? = .add + private var `operator`: BinaryOperator? = .add + private var isPreOperatorEqual = false private var isCalculationResultIsNil: Bool { calculationResult == nil } @@ -41,36 +37,38 @@ struct Calculator { } } - mutating func setOperation(_ newOperation: BinaryOperator) { - guard let operation = operation, let newValue = newValue else { - self.operation = newOperation - return + mutating func setOperator(_ newOperator: BinaryOperator) { + if isPreOperatorEqual { + `operator` = nil + newValue = nil + isPreOperatorEqual = false } - calculationResult = calculate(operation, newValue) - guard let calculationResult = calculationResult else { - displayValue = "오류" - return - } - displayValue = String(describing: calculationResult) + proveAndCalculate(newOperator: newOperator) self.newValue = nil - self.operation = newOperation + self.`operator` = newOperator } mutating func equal() { - guard let operation = operation, let newValue = newValue else { + proveAndCalculate(newOperator: nil) + } + + mutating func proveAndCalculate(newOperator: BinaryOperator?) { + let isEqual = newOperator == nil + guard let `operator` = `operator`, let newValue = newValue else { return } - calculationResult = calculate(operation, newValue) + if isEqual { + isPreOperatorEqual = true + } + calculationResult = calculate(`operator`, newValue) guard let calculationResult = calculationResult else { displayValue = "오류" self.newValue = nil return } displayValue = String(describing: calculationResult) - self.newValue = nil - self.operation = nil } - + mutating func dot() { if displayValue.contains(".") || isCalculationResultIsNil { return @@ -81,36 +79,32 @@ struct Calculator { } mutating func percent() { - guard let calculationResult = calculationResult else { - return - } - if let newValue = newValue { - displayValue = String(describing: newValue * 0.01) - self.newValue = Decimal(string: displayValue) - } else { - displayValue = String(describing: calculationResult * 0.01) - self.calculationResult = Decimal(string: displayValue) - } + percentOrToggleDisplayNumber(isToggle: false) + } + + mutating func toggleDisplayNumber() { + percentOrToggleDisplayNumber(isToggle: true) } - mutating func toggle() { + private mutating func percentOrToggleDisplayNumber(isToggle: Bool) { guard let calculationResult = calculationResult else { return } - if let newValue = newValue { - displayValue = String(describing: -newValue) + let operand = isToggle ? -1 : 0.01 + if let newValue = newValue, !isPreOperatorEqual { + displayValue = String(describing: newValue * Decimal(operand)) self.newValue = Decimal(string: displayValue) } else { - displayValue = String(describing: -calculationResult) + displayValue = String(describing: calculationResult * Decimal(operand)) self.calculationResult = Decimal(string: displayValue) } } mutating func allClear() { - displayValue = "0" - newValue = nil + clear() + isPreOperatorEqual = false calculationResult = 0 - operation = .add + `operator` = .add } mutating func clear() { @@ -120,22 +114,21 @@ struct Calculator { } mutating func undoWhenDragged() { - if newValue == nil { - if displayValue.count > 1 { - displayValue.removeLast() - calculationResult = Decimal(string: displayValue) - } else if displayValue.count == 1 { - displayValue = "0" - calculationResult = Decimal(string: displayValue) - } + let isNewValueNil = newValue == nil + if displayValue.count == 1 { + displayValue = "0" + assignValueWhenUndo(isNewValueNil) } else { - if displayValue.count > 1 { - displayValue.removeLast() - newValue = Decimal(string: displayValue) - } else if displayValue.count == 1 { - displayValue = "0" - newValue = Decimal(string: displayValue) - } + displayValue.removeLast() + assignValueWhenUndo(isNewValueNil) + } + } + + private mutating func assignValueWhenUndo(_ newValueIsNil: Bool) { + if newValueIsNil { + calculationResult = Decimal(string: displayValue) + } else { + newValue = Decimal(string: displayValue) } } @@ -150,7 +143,7 @@ struct Calculator { return calculationResult - newValue case .divide: if newValue == 0 { - return nil + return nil } return calculationResult / newValue case .multiply: @@ -158,3 +151,74 @@ struct Calculator { } } } + +extension Calculator { + enum Button: Hashable { + case digit(_ digit: Digit) + case binaryOperator(_ binaryOperator: BinaryOperator) + case equal + case dot + case percent + case toggle + case allClear + case clear + + var appearance: String { + switch self { + case .digit(let digit): + return digit.appearance + case .binaryOperator(let binaryOperator): + return binaryOperator.appearance + case .equal: + return "=" + case .dot: + return "." + case .percent: + return "%" + case .toggle: + return "+/-" + case .allClear: + return "AC" + case .clear: + return "C" + } + } + } + + enum BinaryOperator { + case add + case substarct + case divide + case multiply + + var appearance: String { + switch self { + case .add: + return "+" + case .substarct: + return "-" + case .divide: + return "/" + case .multiply: + return "*" + } + } + } + + enum Digit: Int { + case zero + case one + case two + case three + case four + case five + case six + case seven + case eight + case nine + + var appearance: String { + return String(describing: self.rawValue) + } + } +} diff --git a/week4/Calculator/Calculator/Style/ButtonStyle.swift b/week4/Calculator/Calculator/Style/ButtonStyle.swift index 5e5dbee..4c76d21 100644 --- a/week4/Calculator/Calculator/Style/ButtonStyle.swift +++ b/week4/Calculator/Calculator/Style/ButtonStyle.swift @@ -7,36 +7,24 @@ import SwiftUI struct CalculateButtonStyle: ButtonStyle { - let buttonColor: (Color, Color) + let buttonColor: (background: Color, foreground: Color) let isZero: Bool - + func makeBody(configuration: Self.Configuration) -> some View { - if isZero { - configuration.label - .frame(maxWidth:UIScreen.main.bounds.size.width / 2, maxHeight: UIScreen.main.bounds.size.height / 12, alignment: .leading) - .font(.title) - .padding() - .background(buttonColor.0) - .foregroundColor(buttonColor.1) - .overlay { - if configuration.isPressed { - Color(white: 1.0, opacity: 0.3) - } - } - .clipShape(Capsule()) - } else { - configuration.label - .frame(maxWidth:UIScreen.main.bounds.size.width / 6.5, maxHeight: UIScreen.main.bounds.size.height / 12, alignment: .center) - .font(.title) - .padding() - .background(buttonColor.0) - .foregroundColor(buttonColor.1) - .overlay { - if configuration.isPressed { - Color(white: 1.0, opacity: 0.3) - } + configuration.label + .frame( + maxWidth:UIScreen.main.bounds.size.width / (isZero ? 2.0 : 6.5), + maxHeight: UIScreen.main.bounds.size.height / 12, + alignment: isZero ? .leading : .center) + .font(.title) + .padding() + .background(buttonColor.background) + .foregroundColor(buttonColor.foreground) + .overlay { + if configuration.isPressed { + Color(white: 1.0, opacity: 0.3) } - .clipShape(Circle()) - } + } + .clipShape(Capsule()) } } diff --git a/week4/Calculator/Calculator/View/CalculatorView.swift b/week4/Calculator/Calculator/View/CalculatorView.swift index 895118d..90dc1e0 100644 --- a/week4/Calculator/Calculator/View/CalculatorView.swift +++ b/week4/Calculator/Calculator/View/CalculatorView.swift @@ -14,13 +14,10 @@ struct CalculatorView: View { VStack { Spacer() ScreenView() + .border(.red) PadView() + .border(.blue) } - .contentShape(Rectangle()) - .gesture(DragGesture(minimumDistance: 10, coordinateSpace: .local) - .onEnded({ _ in - calculatorManager.undoWhenSwiped() - })) .background(.black) } } diff --git a/week4/Calculator/Calculator/View/EachButton.swift b/week4/Calculator/Calculator/View/PadButton.swift similarity index 65% rename from week4/Calculator/Calculator/View/EachButton.swift rename to week4/Calculator/Calculator/View/PadButton.swift index 5d20c76..f95c973 100644 --- a/week4/Calculator/Calculator/View/EachButton.swift +++ b/week4/Calculator/Calculator/View/PadButton.swift @@ -7,7 +7,7 @@ import SwiftUI -struct EachButton: View { +struct PadButton: View { @EnvironmentObject var calculatorManager: CalculatorManager let button: CalculatorManager.Button @@ -15,7 +15,12 @@ struct EachButton: View { ZStack { Button(button.appearance) { calculatorManager.touchButton(button) - } .buttonStyle(CalculateButtonStyle(buttonColor: calculatorManager.buttonColor(button), isZero: button.appearance == "0")) + } .buttonStyle( + CalculateButtonStyle( + buttonColor: calculatorManager.buttonColor(button), + isZero: button.appearance == "0" + ) + ) } } } diff --git a/week4/Calculator/Calculator/View/PadView.swift b/week4/Calculator/Calculator/View/PadView.swift index 5baae41..68bb22d 100644 --- a/week4/Calculator/Calculator/View/PadView.swift +++ b/week4/Calculator/Calculator/View/PadView.swift @@ -15,7 +15,7 @@ struct PadView: View { ForEach(calculatorManager.pad, id: \.self) { row in HStack { ForEach(row, id: \.self) { button in - EachButton(button: button) + PadButton(button: button) } } } diff --git a/week4/Calculator/Calculator/View/ScreenView.swift b/week4/Calculator/Calculator/View/ScreenView.swift index 4e9d65c..eb8d440 100644 --- a/week4/Calculator/Calculator/View/ScreenView.swift +++ b/week4/Calculator/Calculator/View/ScreenView.swift @@ -10,17 +10,38 @@ import SwiftUI struct ScreenView: View { @EnvironmentObject var calculatorManager: CalculatorManager + private func swipe() -> some Gesture { + DragGesture(minimumDistance: 10, coordinateSpace: .local) + .onEnded { drag in + if 0 < drag.translation.width { + calculatorManager.undoWhenSwiped() + } + } + } + var body: some View { HStack { Spacer() Text(calculatorManager.displayValue) - .lineLimit(1) + .lineLimit(DrawConstants.lineLimit) .foregroundColor(.white) .frame(alignment: .bottomTrailing) - .font(.system(size: 80)) - .minimumScaleFactor(0.5) + .font(.system(size: DrawConstants.fontSize)) + .minimumScaleFactor(DrawConstants.minimumScaleFactor) .padding() } + .contentShape(Rectangle()) + .gesture(swipe()) + } +} + +// MARK: - Constant(s) + +extension ScreenView { + private enum DrawConstants { + static let lineLimit: Int = 1 + static let minimumScaleFactor: CGFloat = 0.5 + static let fontSize: CGFloat = 80 } } diff --git a/week4/Calculator/Calculator/ViewModel/CalculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CalculatorManager.swift index ba3093b..0f8d1c2 100644 --- a/week4/Calculator/Calculator/ViewModel/CalculatorManager.swift +++ b/week4/Calculator/Calculator/ViewModel/CalculatorManager.swift @@ -5,23 +5,20 @@ // Created by ohhyeongseok on 2022/08/16. // -import Foundation import SwiftUI class CalculatorManager: ObservableObject { + // MARK: Alia(es) + + typealias Button = Calculator.Button + // MARK: Property(ies) @Published private var calculator = Calculator() let maxNumberDisplayNormalNotation: Decimal = 999999999 var displayValue: String { - guard let decimalTypeDisplayValue = Decimal(string: calculator.displayValue) else { - return "오류" - } - if decimalTypeDisplayValue > maxNumberDisplayNormalNotation { - return decimalTypeDisplayValue.exponentialNotation - } - return calculator.displayValue.insertComma + return calculator.displayValue } var pad: [[Button]] { var buttonLayout: [[Button]] = [ @@ -40,7 +37,14 @@ class CalculatorManager: ObservableObject { // MARK: Method(s) func buttonColor(_ button: Button) -> (Color, Color) { - return (button.backgroundColor, button.foregorundColor) + switch button { + case .digit, .dot: + return (Color(UIColor.darkGray), .white) + case .binaryOperator, .equal: + return (.orange, .white) + default: + return (Color(UIColor.lightGray), .black) + } } func undoWhenSwiped() { @@ -52,7 +56,7 @@ class CalculatorManager: ObservableObject { case .digit(let digit): calculator.setDigit(digit) case .binaryOperator(let binaryOperator): - calculator.setOperation(binaryOperator) + calculator.setOperator(binaryOperator) case .equal: calculator.equal() case .dot: @@ -60,7 +64,7 @@ class CalculatorManager: ObservableObject { case .percent: calculator.percent() case .toggle: - calculator.toggle() + calculator.toggleDisplayNumber() case .allClear: calculator.allClear() case .clear: @@ -69,93 +73,3 @@ class CalculatorManager: ObservableObject { } } -// MARK: Button - -extension CalculatorManager { - enum Button: Hashable { - case digit(_ digit: Digit) - case binaryOperator(_ binaryOperator: BinaryOperator) - case equal - case dot - case percent - case toggle - case allClear - case clear - - var appearance: String { - switch self { - case .digit(let digit): - return digit.appearance - case .binaryOperator(let binaryOperator): - return binaryOperator.appearance - case .equal: - return "=" - case .dot: - return "." - case .percent: - return "%" - case .toggle: - return "+/-" - case .allClear: - return "AC" - case .clear: - return "C" - } - } - var backgroundColor: Color { - switch self { - case .digit, .dot: - return Color(UIColor.darkGray) - case .binaryOperator, .equal: - return .orange - default: - return Color(UIColor.lightGray) - } - } - var foregorundColor: Color { - switch self { - case .digit, .dot, .binaryOperator, .equal: - return .white - default: - return .black - } - } - } - - enum BinaryOperator { - case add - case substarct - case divide - case multiply - - var appearance: String { - switch self { - case .add: - return "+" - case .substarct: - return "-" - case .divide: - return "/" - case .multiply: - return "*" - } - } - } - - enum Digit: Int { - case zero - case one - case two - case three - case four - case five - case six - case seven - case eight - case nine - - var appearance: String { - return String(describing: self.rawValue) - } - } -} From 1b8972cc80efa7b75b528274b37029ef5657fd42 Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Thu, 1 Sep 2022 11:34:18 +0900 Subject: [PATCH 02/10] =?UTF-8?q?0.0000~=20=EA=B3=84=EC=86=8D=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EB=90=98=EB=8A=94=20bug=20block=20swap=EC=9D=84=20cal?= =?UTF-8?q?culatorView=EC=97=90=EC=84=9C=20=EB=8F=99=EC=9E=91=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=ED=95=B4=EC=84=9C=20ScreenView=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EC=B5=9C=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Calculator/Model/Calculator.swift | 13 +++++++------ .../Calculator/View/CalculatorView.swift | 18 ++++++++++++++---- .../Calculator/Calculator/View/PadButton.swift | 11 ++++++----- week4/Calculator/Calculator/View/PadView.swift | 5 +++-- .../Calculator/View/ScreenView.swift | 17 +++-------------- .../ViewModel/CalculatorManager.swift | 13 +++++++++---- 6 files changed, 42 insertions(+), 35 deletions(-) diff --git a/week4/Calculator/Calculator/Model/Calculator.swift b/week4/Calculator/Calculator/Model/Calculator.swift index 8dfcfbb..cc66288 100644 --- a/week4/Calculator/Calculator/Model/Calculator.swift +++ b/week4/Calculator/Calculator/Model/Calculator.swift @@ -29,7 +29,8 @@ struct Calculator { self.newValue = Decimal(string: displayValue) isAllClear = false } else { - if String(describing: self.newValue ?? 0).count >= 9 { + let maxLengthOfInputDigit = displayValue.contains(".") ? 10 : 9 + if displayValue.count >= maxLengthOfInputDigit { return } displayValue = displayValue.appending(String(describing: newValue.rawValue)) @@ -52,7 +53,7 @@ struct Calculator { proveAndCalculate(newOperator: nil) } - mutating func proveAndCalculate(newOperator: BinaryOperator?) { + private mutating func proveAndCalculate(newOperator: BinaryOperator?) { let isEqual = newOperator == nil guard let `operator` = `operator`, let newValue = newValue else { return @@ -79,14 +80,14 @@ struct Calculator { } mutating func percent() { - percentOrToggleDisplayNumber(isToggle: false) + percentOrToggleSignOfDisplayNumber(isToggle: false) } - mutating func toggleDisplayNumber() { - percentOrToggleDisplayNumber(isToggle: true) + mutating func toggleSignOfDisplayNumber() { + percentOrToggleSignOfDisplayNumber(isToggle: true) } - private mutating func percentOrToggleDisplayNumber(isToggle: Bool) { + private mutating func percentOrToggleSignOfDisplayNumber(isToggle: Bool) { guard let calculationResult = calculationResult else { return } diff --git a/week4/Calculator/Calculator/View/CalculatorView.swift b/week4/Calculator/Calculator/View/CalculatorView.swift index 90dc1e0..6b4b28a 100644 --- a/week4/Calculator/Calculator/View/CalculatorView.swift +++ b/week4/Calculator/Calculator/View/CalculatorView.swift @@ -9,14 +9,23 @@ import SwiftUI struct CalculatorView: View { @EnvironmentObject var calculatorManager: CalculatorManager - + + private func swipe() -> some Gesture { + DragGesture(minimumDistance: 10, coordinateSpace: .local) + .onEnded { drag in + if 0 < drag.translation.width { + calculatorManager.undoWhenSwiped() + } + } + } + var body: some View { VStack { Spacer() - ScreenView() - .border(.red) + ScreenView(displayValue: calculatorManager.displayValue) + .contentShape(Rectangle()) + .gesture(swipe()) PadView() - .border(.blue) } .background(.black) } @@ -25,5 +34,6 @@ struct CalculatorView: View { struct CalculatorView_Previews: PreviewProvider { static var previews: some View { CalculatorView() + .environmentObject(CalculatorManager()) } } diff --git a/week4/Calculator/Calculator/View/PadButton.swift b/week4/Calculator/Calculator/View/PadButton.swift index f95c973..162a18e 100644 --- a/week4/Calculator/Calculator/View/PadButton.swift +++ b/week4/Calculator/Calculator/View/PadButton.swift @@ -25,8 +25,9 @@ struct PadButton: View { } } -//struct Button_Previews: PreviewProvider { -// static var previews: some View { -// Button() -// } -//} +struct Button_Previews: PreviewProvider { + static var previews: some View { + PadButton(button: CalculatorManager.Button.allClear) + .environmentObject(CalculatorManager()) + } +} diff --git a/week4/Calculator/Calculator/View/PadView.swift b/week4/Calculator/Calculator/View/PadView.swift index 68bb22d..a02d3af 100644 --- a/week4/Calculator/Calculator/View/PadView.swift +++ b/week4/Calculator/Calculator/View/PadView.swift @@ -8,11 +8,11 @@ import SwiftUI struct PadView: View { - @EnvironmentObject var calculatorManager: CalculatorManager + @EnvironmentObject var calculator: CalculatorManager var body: some View { VStack { - ForEach(calculatorManager.pad, id: \.self) { row in + ForEach(calculator.pad, id: \.self) { row in HStack { ForEach(row, id: \.self) { button in PadButton(button: button) @@ -26,5 +26,6 @@ struct PadView: View { struct PadView_Previews: PreviewProvider { static var previews: some View { PadView() + .environmentObject(CalculatorManager()) } } diff --git a/week4/Calculator/Calculator/View/ScreenView.swift b/week4/Calculator/Calculator/View/ScreenView.swift index eb8d440..8f88d8f 100644 --- a/week4/Calculator/Calculator/View/ScreenView.swift +++ b/week4/Calculator/Calculator/View/ScreenView.swift @@ -8,21 +8,12 @@ import SwiftUI struct ScreenView: View { - @EnvironmentObject var calculatorManager: CalculatorManager - - private func swipe() -> some Gesture { - DragGesture(minimumDistance: 10, coordinateSpace: .local) - .onEnded { drag in - if 0 < drag.translation.width { - calculatorManager.undoWhenSwiped() - } - } - } + let displayValue: String var body: some View { HStack { Spacer() - Text(calculatorManager.displayValue) + Text(displayValue) .lineLimit(DrawConstants.lineLimit) .foregroundColor(.white) .frame(alignment: .bottomTrailing) @@ -30,8 +21,6 @@ struct ScreenView: View { .minimumScaleFactor(DrawConstants.minimumScaleFactor) .padding() } - .contentShape(Rectangle()) - .gesture(swipe()) } } @@ -47,6 +36,6 @@ extension ScreenView { struct ScreenView_Previews: PreviewProvider { static var previews: some View { - ScreenView() + ScreenView(displayValue: "aa") } } diff --git a/week4/Calculator/Calculator/ViewModel/CalculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CalculatorManager.swift index 0f8d1c2..08ddd01 100644 --- a/week4/Calculator/Calculator/ViewModel/CalculatorManager.swift +++ b/week4/Calculator/Calculator/ViewModel/CalculatorManager.swift @@ -16,9 +16,15 @@ class CalculatorManager: ObservableObject { // MARK: Property(ies) @Published private var calculator = Calculator() - let maxNumberDisplayNormalNotation: Decimal = 999999999 var displayValue: String { - return calculator.displayValue + guard let decimalTypeDisplayValue = Decimal(string: calculator.displayValue) else { + return "오류" + } + let maxLengthOfDisplayValue = String(describing: calculator.displayValue).contains(".") ? 10 : 9 + if calculator.displayValue.count > maxLengthOfDisplayValue { + return decimalTypeDisplayValue.exponentialNotation + } + return calculator.displayValue.decimalFormat } var pad: [[Button]] { var buttonLayout: [[Button]] = [ @@ -64,7 +70,7 @@ class CalculatorManager: ObservableObject { case .percent: calculator.percent() case .toggle: - calculator.toggleDisplayNumber() + calculator.toggleSignOfDisplayNumber() case .allClear: calculator.allClear() case .clear: @@ -72,4 +78,3 @@ class CalculatorManager: ObservableObject { } } } - From 6114815f451045bd882c4a67e90a6a6d4b6fccb7 Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Fri, 2 Sep 2022 20:41:55 +0900 Subject: [PATCH 03/10] =?UTF-8?q?NaverOpenAPI=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20Network=ED=95=99=EC=8A=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project.pbxproj | 412 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++++ .../Assets.xcassets/Contents.json | 6 + .../AttributedString+Extension.swift | 20 + .../NaverOpenAPITest/Model/BoxOffice.swift | 41 ++ .../NaverOpenAPITest/Model/Encyclopedia.swift | 34 ++ .../NaverOpenAPITest/Model/NewsDesk.swift | 36 ++ .../NaverOpenAPITest/MyProtocol.swift | 15 + .../NaverOpenAPITestApp.swift | 18 + .../Preview Assets.xcassets/Contents.json | 6 + .../NaverOpenAPITest/View/ChoiceView.swift | 26 ++ .../NaverOpenAPITest/View/DocumentList.swift | 49 +++ .../NaverOpenAPITest/View/MovieList.swift | 50 +++ .../NaverOpenAPITest/View/NewsList.swift | 57 +++ .../NaverOpenAPITest/ViewModel/Network.swift | 97 +++++ 18 files changed, 991 insertions(+) create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/Contents.json create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Extension/AttributedString+Extension.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/MyProtocol.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Network.swift diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e0db932 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj @@ -0,0 +1,412 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + A67D5A7428C0537D00624B25 /* NaverOpenAPITestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A7328C0537D00624B25 /* NaverOpenAPITestApp.swift */; }; + A67D5A7828C0537F00624B25 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A67D5A7728C0537F00624B25 /* Assets.xcassets */; }; + A67D5A7B28C0537F00624B25 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A67D5A7A28C0537F00624B25 /* Preview Assets.xcassets */; }; + A67D5A8228C053EC00624B25 /* BoxOffice.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8128C053EC00624B25 /* BoxOffice.swift */; }; + A67D5A8828C0551700624B25 /* MovieList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8728C0551700624B25 /* MovieList.swift */; }; + A67D5A8C28C1B74000624B25 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8B28C1B74000624B25 /* Network.swift */; }; + A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */; }; + A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */; }; + A67D5A9328C1DBCD00624B25 /* AttributedString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9228C1DBCD00624B25 /* AttributedString+Extension.swift */; }; + A67D5A9828C1E7D700624B25 /* MyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9728C1E7D700624B25 /* MyProtocol.swift */; }; + A67D5A9C28C1F51500624B25 /* ChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9B28C1F51500624B25 /* ChoiceView.swift */; }; + A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9D28C1F53100624B25 /* DocumentList.swift */; }; + A67D5AA028C1F54900624B25 /* NewsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9F28C1F54900624B25 /* NewsList.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + A67D5A7028C0537D00624B25 /* NaverOpenAPITest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NaverOpenAPITest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A67D5A7328C0537D00624B25 /* NaverOpenAPITestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NaverOpenAPITestApp.swift; sourceTree = ""; }; + A67D5A7728C0537F00624B25 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + A67D5A7A28C0537F00624B25 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + A67D5A8128C053EC00624B25 /* BoxOffice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOffice.swift; sourceTree = ""; }; + A67D5A8728C0551700624B25 /* MovieList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieList.swift; sourceTree = ""; }; + A67D5A8B28C1B74000624B25 /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; + A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsDesk.swift; sourceTree = ""; }; + A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encyclopedia.swift; sourceTree = ""; }; + A67D5A9228C1DBCD00624B25 /* AttributedString+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Extension.swift"; sourceTree = ""; }; + A67D5A9728C1E7D700624B25 /* MyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProtocol.swift; sourceTree = ""; }; + A67D5A9B28C1F51500624B25 /* ChoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceView.swift; sourceTree = ""; }; + A67D5A9D28C1F53100624B25 /* DocumentList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentList.swift; sourceTree = ""; }; + A67D5A9F28C1F54900624B25 /* NewsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsList.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A67D5A6D28C0537D00624B25 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A67D5A6728C0537D00624B25 = { + isa = PBXGroup; + children = ( + A67D5A7228C0537D00624B25 /* NaverOpenAPITest */, + A67D5A7128C0537D00624B25 /* Products */, + ); + sourceTree = ""; + }; + A67D5A7128C0537D00624B25 /* Products */ = { + isa = PBXGroup; + children = ( + A67D5A7028C0537D00624B25 /* NaverOpenAPITest.app */, + ); + name = Products; + sourceTree = ""; + }; + A67D5A7228C0537D00624B25 /* NaverOpenAPITest */ = { + isa = PBXGroup; + children = ( + A67D5A9728C1E7D700624B25 /* MyProtocol.swift */, + A67D5A7328C0537D00624B25 /* NaverOpenAPITestApp.swift */, + A67D5A9428C1DBEC00624B25 /* Extension */, + A67D5A7728C0537F00624B25 /* Assets.xcassets */, + A67D5A7928C0537F00624B25 /* Preview Content */, + A67D5A8F28C1CD3800624B25 /* Model */, + A67D5A9A28C1F00D00624B25 /* View */, + A67D5A9928C1F00600624B25 /* ViewModel */, + ); + path = NaverOpenAPITest; + sourceTree = ""; + }; + A67D5A7928C0537F00624B25 /* Preview Content */ = { + isa = PBXGroup; + children = ( + A67D5A7A28C0537F00624B25 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + A67D5A8F28C1CD3800624B25 /* Model */ = { + isa = PBXGroup; + children = ( + A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */, + A67D5A8128C053EC00624B25 /* BoxOffice.swift */, + A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */, + ); + path = Model; + sourceTree = ""; + }; + A67D5A9428C1DBEC00624B25 /* Extension */ = { + isa = PBXGroup; + children = ( + A67D5A9228C1DBCD00624B25 /* AttributedString+Extension.swift */, + ); + path = Extension; + sourceTree = ""; + }; + A67D5A9928C1F00600624B25 /* ViewModel */ = { + isa = PBXGroup; + children = ( + A67D5A8B28C1B74000624B25 /* Network.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + A67D5A9A28C1F00D00624B25 /* View */ = { + isa = PBXGroup; + children = ( + A67D5A9B28C1F51500624B25 /* ChoiceView.swift */, + A67D5A8728C0551700624B25 /* MovieList.swift */, + A67D5A9D28C1F53100624B25 /* DocumentList.swift */, + A67D5A9F28C1F54900624B25 /* NewsList.swift */, + ); + path = View; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A67D5A6F28C0537D00624B25 /* NaverOpenAPITest */ = { + isa = PBXNativeTarget; + buildConfigurationList = A67D5A7E28C0537F00624B25 /* Build configuration list for PBXNativeTarget "NaverOpenAPITest" */; + buildPhases = ( + A67D5A6C28C0537D00624B25 /* Sources */, + A67D5A6D28C0537D00624B25 /* Frameworks */, + A67D5A6E28C0537D00624B25 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NaverOpenAPITest; + productName = NaverOpenAPITest; + productReference = A67D5A7028C0537D00624B25 /* NaverOpenAPITest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A67D5A6828C0537D00624B25 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + A67D5A6F28C0537D00624B25 = { + CreatedOnToolsVersion = 13.4.1; + }; + }; + }; + buildConfigurationList = A67D5A6B28C0537D00624B25 /* Build configuration list for PBXProject "NaverOpenAPITest" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = A67D5A6728C0537D00624B25; + productRefGroup = A67D5A7128C0537D00624B25 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A67D5A6F28C0537D00624B25 /* NaverOpenAPITest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A67D5A6E28C0537D00624B25 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A67D5A7B28C0537F00624B25 /* Preview Assets.xcassets in Resources */, + A67D5A7828C0537F00624B25 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A67D5A6C28C0537D00624B25 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A67D5A8C28C1B74000624B25 /* Network.swift in Sources */, + A67D5A9C28C1F51500624B25 /* ChoiceView.swift in Sources */, + A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */, + A67D5A7428C0537D00624B25 /* NaverOpenAPITestApp.swift in Sources */, + A67D5AA028C1F54900624B25 /* NewsList.swift in Sources */, + A67D5A9328C1DBCD00624B25 /* AttributedString+Extension.swift in Sources */, + A67D5A8828C0551700624B25 /* MovieList.swift in Sources */, + A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */, + A67D5A9828C1E7D700624B25 /* MyProtocol.swift in Sources */, + A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */, + A67D5A8228C053EC00624B25 /* BoxOffice.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A67D5A7C28C0537F00624B25 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + A67D5A7D28C0537F00624B25 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + A67D5A7F28C0537F00624B25 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"NaverOpenAPITest/Preview Content\""; + DEVELOPMENT_TEAM = 3N2D6D7SCN; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ohs.NaverOpenAPITest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + A67D5A8028C0537F00624B25 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"NaverOpenAPITest/Preview Content\""; + DEVELOPMENT_TEAM = 3N2D6D7SCN; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ohs.NaverOpenAPITest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A67D5A6B28C0537D00624B25 /* Build configuration list for PBXProject "NaverOpenAPITest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A67D5A7C28C0537F00624B25 /* Debug */, + A67D5A7D28C0537F00624B25 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A67D5A7E28C0537F00624B25 /* Build configuration list for PBXNativeTarget "NaverOpenAPITest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A67D5A7F28C0537F00624B25 /* Debug */, + A67D5A8028C0537F00624B25 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A67D5A6828C0537D00624B25 /* Project object */; +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/AccentColor.colorset/Contents.json b/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/AppIcon.appiconset/Contents.json b/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/Contents.json b/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Extension/AttributedString+Extension.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Extension/AttributedString+Extension.swift new file mode 100644 index 0000000..f5d35cb --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Extension/AttributedString+Extension.swift @@ -0,0 +1,20 @@ +// +// AttributedString+Extension.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/02. +// + +import Foundation + +extension AttributedString { + /// HTML형식의 문자열을 기본 문자열로 변환합니다. + /// - Parameter htmlString: HTML 형식의 문자열. + init?(htmlString: String) { + let option: [NSAttributedString.DocumentReadingOptionKey: NSAttributedString.DocumentType] = [.documentType: .html] + guard let htmlData = htmlString.data(using: .utf16), + let nsStr = try? NSAttributedString(data: htmlData, options: option, documentAttributes: nil) + else { return nil } + self.init(nsStr.string) + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift new file mode 100644 index 0000000..496b95b --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift @@ -0,0 +1,41 @@ +// +// BoxOffice.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/01. +// + +import Foundation + +struct BoxOffice: MyProtocol { + var searchKeyword = "" + var elements: [Any] = [] + var path = "/v1/search/movie.json" + init() {} +} + +extension BoxOffice { + + struct Movie: Codable, Identifiable { + let attributedTitle: AttributedString? + let image: String + let subtitle: AttributedString? + let pubDate: String + let director: String + let actor: String + let userRating: String + let id: Int + + init(_ movie: Response.Movie, id: Int) { + attributedTitle = AttributedString(htmlString: movie.title) + image = movie.image + subtitle = AttributedString(htmlString: movie.subtitle) + pubDate = movie.pubDate + director = movie.director + actor = movie.actor + userRating = movie.userRating + self.id = id + } + } +} + diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift new file mode 100644 index 0000000..a3d84c1 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift @@ -0,0 +1,34 @@ +// +// Department.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/02. +// + +import Foundation + +struct Encyclopedia: MyProtocol { + var searchKeyword = "" + var elements: [Any] = [] + var path = "/v1/search/encyc.json" + init() {} +} + +extension Encyclopedia { + + struct Document: Codable, Identifiable { + let attributedTitle: AttributedString? + let link: String + let description: String + let tumbnail: String + let id: Int + + init(_ document: Response.Document, id: Int) { + attributedTitle = AttributedString(htmlString: document.title) + link = document.link + description = document.description + tumbnail = document.thumbnail + self.id = id + } + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift new file mode 100644 index 0000000..9bc17b0 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift @@ -0,0 +1,36 @@ +// +// NewsDesk.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/02. +// + +import Foundation + +struct NewsDesk: MyProtocol { + var searchKeyword = "" + var elements: [Any] = [] + var path = "/v1/search/news.json" + init() {} +} + +extension NewsDesk{ + + struct News: Codable, Identifiable { + let attributedTitle: AttributedString? + let originallink: String + let link: String + let description: String + let pubDate: String + let id: Int + + init(_ news: Response.News, id: Int) { + attributedTitle = AttributedString(htmlString: news.title) + pubDate = news.pubDate + originallink = news.originallink + link = news.link + description = news.description + self.id = id + } + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/MyProtocol.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/MyProtocol.swift new file mode 100644 index 0000000..7f9023b --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/MyProtocol.swift @@ -0,0 +1,15 @@ +// +// MyProtocol.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/02. +// + +import Foundation + +protocol MyProtocol { + var path: String {get} + var searchKeyword: String {get} + init() + var elements: [Any] {get set} +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift new file mode 100644 index 0000000..336d796 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift @@ -0,0 +1,18 @@ +// +// NaverOpenAPITestApp.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/01. +// + +import SwiftUI + +@main +struct NaverOpenAPITestApp: App { + + var body: some Scene { + WindowGroup { + MovieList(viewModel: MovieFinder()) + } + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Preview Content/Preview Assets.xcassets/Contents.json b/week5/NaverOpenAPITest/NaverOpenAPITest/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift new file mode 100644 index 0000000..b6178b7 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift @@ -0,0 +1,26 @@ +// +// ChoiceView.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/02. +// + +import SwiftUI + +struct ChoiceView: View { + var body: some View { + NavigationView { + List { + NavigationLink("Search News", destination: NewsList()) + NavigationLink("Search Movie", destination: MovieList()) + NavigationLink("Search Document", destination: DocumentList()) + } + } + } +} + +struct ChoiceView_Previews: PreviewProvider { + static var previews: some View { + ChoiceView() + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift new file mode 100644 index 0000000..8a294bf --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift @@ -0,0 +1,49 @@ +// +// DocumentList.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/02. +// + +import SwiftUI + +struct DocumentList: View { + @ObservedObject var documentFinder: MovieFinder + + var body: some View { + ZStack { + List { + HStack { + TextField(text: $viewModel.model.searchKeyword, label: { + Text("검색어를 입력하세요.") }) + Button(action: {viewModel.fetchMovieList() }) { + Text("검색") + } + .buttonStyle(.bordered) + } + Section { + ForEach(viewModel.model.movies) { movie in + VStack { + Spacer() + Group { + if let attributedTitle = movie.attributedTitle { + Text(attributedTitle) + } else { + Text("알 수 없음") + } + } + .font(Font.system(size: 15)) + .minimumScaleFactor(0.5) + Spacer() + } + } + } + } + if viewModel.fetchingStatus == .fetching { + ProgressView() + .scaleEffect(1.5) + } + } + .foregroundColor(.black) + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift new file mode 100644 index 0000000..8e271cc --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift @@ -0,0 +1,50 @@ +// +// MovieList.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/01. +// + +import SwiftUI + +struct MovieList: View { + + @ObservedObject var viewModel: MovieFinder + + var body: some View { + ZStack { + List { + HStack { + TextField(text: $viewModel.model.searchKeyword, label: { + Text("검색어를 입력하세요.") }) + Button(action: {viewModel.fetchMovieList() }) { + Text("검색") + } + .buttonStyle(.bordered) + } + Section { + ForEach(viewModel.model.movies) { movie in + VStack { + Spacer() + Group { + if let attributedTitle = movie.attributedTitle { + Text(attributedTitle) + } else { + Text("알 수 없음") + } + } + .font(Font.system(size: 15)) + .minimumScaleFactor(0.5) + Spacer() + } + } + } + } + if viewModel.fetchingStatus == .fetching { + ProgressView() + .scaleEffect(1.5) + } + } + .foregroundColor(.black) + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift new file mode 100644 index 0000000..9435326 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift @@ -0,0 +1,57 @@ +// +// NewsList.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/02. +// + +import SwiftUI + +import SwiftUI + +struct NewsList: View { + @StateObject var viewModel = Network< + var body: some View { + ZStack { + List { + HStack { + TextField(text: $viewModel.model.searchKeyword, label: { + Text("검색어를 입력하세요.") }) + Button(action: {viewModel.fetchMovieList() }) { + Text("검색") + } + .buttonStyle(.bordered) + } + Section { + ForEach(viewModel.model.movies) { movie in + VStack { + Spacer() + Group { + if let attributedTitle = movie.attributedTitle { + Text(attributedTitle) + } else { + Text("알 수 없음") + } + } + .font(Font.system(size: 15)) + .minimumScaleFactor(0.5) + Spacer() + } + } + } + } + if viewModel.fetchingStatus == .fetching { + ProgressView() + .scaleEffect(1.5) + } + } + .foregroundColor(.black) + } +} + + +struct NewsList_Previews: PreviewProvider { + static var previews: some View { + NewsList() + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Network.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Network.swift new file mode 100644 index 0000000..2503fe6 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Network.swift @@ -0,0 +1,97 @@ +// +// Network.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/02. +// + +import Foundation + +fileprivate enum NaverOpenAPI { + static let clientID = "QDph9OaM58FywxjbaSDe" + static let clientSecret = "7BEzAGG02o" + static let scheme = "https" + static let host = "openapi.naver.com" +} + +class RequestAPI: ObservableObject { + + @Published var model = T() + @Published var fetchingStatus = FetchStatus.idle + + private var urlComponents = URLComponents() + + init() { + urlComponents.scheme = NaverOpenAPI.scheme + urlComponents.host = NaverOpenAPI.host + } + + func fetchDataList(wantToSearch: T) { + fetchingStatus = .fetching + urlComponents.path = wantToSearch.path + urlComponents.queryItems = [URLQueryItem(name: "query", value: wantToSearch.searchKeyword)] + guard let url = urlComponents.url + else { return } + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = "GET" + urlRequest.addValue(NaverOpenAPI.clientID, forHTTPHeaderField: "X-Naver-Client-Id") + urlRequest.addValue(NaverOpenAPI.clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + // 각기 다른 예외처리를 하는 것이 좋겠죠 ? + guard error == nil, + let httpURLResponse = response as? HTTPURLResponse, + (200 ... 299).contains(httpURLResponse.statusCode), + let data = data, + let parsedData = try? JSONDecoder().decode(Response.self, from: data) + else { return } + DispatchQueue.main.async { [weak self] in + self?.model.elements = parsedData.items.indices.map { + BoxOffice.Movie(parsedData.items[$0], id: parsedData.start + $0) + } + self?.fetchingStatus = .idle + } + } + task.resume() + fetchingStatus = .idle + } + enum FetchStatus { + case idle + case fetching + } + +} + +struct Response: Codable { + let lastBuildDate: String + let total: Int + let start: Int + let display: Int + let items: [Movie] + + struct Movie: Codable { + let title: String + let link: String + let image: String + let subtitle: String + let pubDate: String + let director: String + let actor: String + let userRating: String + } + + struct News: Codable { + let title: String + let originallink: String + let link: String + let description: String + let pubDate: String + } + + struct Document: Codable { + let title: String + let link: String + let description: String + let thumbnail: String + } +} + From 1b03827d427579df3ee82211082ee763948f411d Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Sat, 3 Sep 2022 10:10:13 +0900 Subject: [PATCH 04/10] Keep coding convention && Edit naming --- .../NaverOpenAPITest.xcodeproj/project.pbxproj | 16 ++++++++-------- .../NaverOpenAPITest/Model/BoxOffice.swift | 3 ++- .../NaverOpenAPITest/Model/Encyclopedia.swift | 2 +- .../NaverOpenAPITest/Model/NewsDesk.swift | 2 +- .../NaverOpenAPITest/NaverOpenAPITestApp.swift | 2 +- .../{MyProtocol.swift => Searchable.swift} | 2 +- .../NaverOpenAPITest/View/ChoiceView.swift | 1 + .../NaverOpenAPITest/View/DocumentList.swift | 6 +++--- .../NaverOpenAPITest/View/MovieList.swift | 5 ++--- .../NaverOpenAPITest/View/NewsList.swift | 9 +++------ .../ViewModel/{Network.swift => Finder.swift} | 6 +----- 11 files changed, 24 insertions(+), 30 deletions(-) rename week5/NaverOpenAPITest/NaverOpenAPITest/{MyProtocol.swift => Searchable.swift} (91%) rename week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/{Network.swift => Finder.swift} (97%) diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj index e0db932..b311ac4 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj +++ b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj @@ -12,11 +12,11 @@ A67D5A7B28C0537F00624B25 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A67D5A7A28C0537F00624B25 /* Preview Assets.xcassets */; }; A67D5A8228C053EC00624B25 /* BoxOffice.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8128C053EC00624B25 /* BoxOffice.swift */; }; A67D5A8828C0551700624B25 /* MovieList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8728C0551700624B25 /* MovieList.swift */; }; - A67D5A8C28C1B74000624B25 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8B28C1B74000624B25 /* Network.swift */; }; + A67D5A8C28C1B74000624B25 /* Finder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8B28C1B74000624B25 /* Finder.swift */; }; A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */; }; A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */; }; A67D5A9328C1DBCD00624B25 /* AttributedString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9228C1DBCD00624B25 /* AttributedString+Extension.swift */; }; - A67D5A9828C1E7D700624B25 /* MyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9728C1E7D700624B25 /* MyProtocol.swift */; }; + A67D5A9828C1E7D700624B25 /* Searchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9728C1E7D700624B25 /* Searchable.swift */; }; A67D5A9C28C1F51500624B25 /* ChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9B28C1F51500624B25 /* ChoiceView.swift */; }; A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9D28C1F53100624B25 /* DocumentList.swift */; }; A67D5AA028C1F54900624B25 /* NewsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9F28C1F54900624B25 /* NewsList.swift */; }; @@ -29,11 +29,11 @@ A67D5A7A28C0537F00624B25 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; A67D5A8128C053EC00624B25 /* BoxOffice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOffice.swift; sourceTree = ""; }; A67D5A8728C0551700624B25 /* MovieList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieList.swift; sourceTree = ""; }; - A67D5A8B28C1B74000624B25 /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; + A67D5A8B28C1B74000624B25 /* Finder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Finder.swift; sourceTree = ""; }; A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsDesk.swift; sourceTree = ""; }; A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encyclopedia.swift; sourceTree = ""; }; A67D5A9228C1DBCD00624B25 /* AttributedString+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Extension.swift"; sourceTree = ""; }; - A67D5A9728C1E7D700624B25 /* MyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProtocol.swift; sourceTree = ""; }; + A67D5A9728C1E7D700624B25 /* Searchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Searchable.swift; sourceTree = ""; }; A67D5A9B28C1F51500624B25 /* ChoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceView.swift; sourceTree = ""; }; A67D5A9D28C1F53100624B25 /* DocumentList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentList.swift; sourceTree = ""; }; A67D5A9F28C1F54900624B25 /* NewsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsList.swift; sourceTree = ""; }; @@ -69,7 +69,7 @@ A67D5A7228C0537D00624B25 /* NaverOpenAPITest */ = { isa = PBXGroup; children = ( - A67D5A9728C1E7D700624B25 /* MyProtocol.swift */, + A67D5A9728C1E7D700624B25 /* Searchable.swift */, A67D5A7328C0537D00624B25 /* NaverOpenAPITestApp.swift */, A67D5A9428C1DBEC00624B25 /* Extension */, A67D5A7728C0537F00624B25 /* Assets.xcassets */, @@ -110,7 +110,7 @@ A67D5A9928C1F00600624B25 /* ViewModel */ = { isa = PBXGroup; children = ( - A67D5A8B28C1B74000624B25 /* Network.swift */, + A67D5A8B28C1B74000624B25 /* Finder.swift */, ); path = ViewModel; sourceTree = ""; @@ -196,7 +196,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A67D5A8C28C1B74000624B25 /* Network.swift in Sources */, + A67D5A8C28C1B74000624B25 /* Finder.swift in Sources */, A67D5A9C28C1F51500624B25 /* ChoiceView.swift in Sources */, A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */, A67D5A7428C0537D00624B25 /* NaverOpenAPITestApp.swift in Sources */, @@ -204,7 +204,7 @@ A67D5A9328C1DBCD00624B25 /* AttributedString+Extension.swift in Sources */, A67D5A8828C0551700624B25 /* MovieList.swift in Sources */, A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */, - A67D5A9828C1E7D700624B25 /* MyProtocol.swift in Sources */, + A67D5A9828C1E7D700624B25 /* Searchable.swift in Sources */, A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */, A67D5A8228C053EC00624B25 /* BoxOffice.swift in Sources */, ); diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift index 496b95b..59e449b 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift @@ -7,10 +7,11 @@ import Foundation -struct BoxOffice: MyProtocol { +struct BoxOffice: Searchable { var searchKeyword = "" var elements: [Any] = [] var path = "/v1/search/movie.json" + init() {} } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift index a3d84c1..60ff22a 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift @@ -7,7 +7,7 @@ import Foundation -struct Encyclopedia: MyProtocol { +struct Encyclopedia: Searchable { var searchKeyword = "" var elements: [Any] = [] var path = "/v1/search/encyc.json" diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift index 9bc17b0..8c2a40e 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift @@ -7,7 +7,7 @@ import Foundation -struct NewsDesk: MyProtocol { +struct NewsDesk: Searchable { var searchKeyword = "" var elements: [Any] = [] var path = "/v1/search/news.json" diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift index 336d796..089d25b 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift @@ -12,7 +12,7 @@ struct NaverOpenAPITestApp: App { var body: some Scene { WindowGroup { - MovieList(viewModel: MovieFinder()) + ChoiceView() } } } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/MyProtocol.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift similarity index 91% rename from week5/NaverOpenAPITest/NaverOpenAPITest/MyProtocol.swift rename to week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift index 7f9023b..930dce8 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/MyProtocol.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift @@ -7,7 +7,7 @@ import Foundation -protocol MyProtocol { +protocol Searchable { var path: String {get} var searchKeyword: String {get} init() diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift index b6178b7..8aaae35 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift @@ -8,6 +8,7 @@ import SwiftUI struct ChoiceView: View { + var body: some View { NavigationView { List { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift index 8a294bf..8471514 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift @@ -8,7 +8,7 @@ import SwiftUI struct DocumentList: View { - @ObservedObject var documentFinder: MovieFinder + @ObservedObject var documentFinder: RequestAPI var body: some View { ZStack { @@ -22,7 +22,7 @@ struct DocumentList: View { .buttonStyle(.bordered) } Section { - ForEach(viewModel.model.movies) { movie in + ForEach(documentFinder.model.) { movie in VStack { Spacer() Group { @@ -39,7 +39,7 @@ struct DocumentList: View { } } } - if viewModel.fetchingStatus == .fetching { + if documentFinder.fetchingStatus == .fetching { ProgressView() .scaleEffect(1.5) } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift index 8e271cc..28970d3 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift @@ -8,8 +8,7 @@ import SwiftUI struct MovieList: View { - - @ObservedObject var viewModel: MovieFinder + @ObservedObject var movieFinder: RequestAPI var body: some View { ZStack { @@ -40,7 +39,7 @@ struct MovieList: View { } } } - if viewModel.fetchingStatus == .fetching { + if movieFinder.fetchingStatus == .fetching { ProgressView() .scaleEffect(1.5) } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift index 9435326..2f2dab6 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift @@ -7,10 +7,8 @@ import SwiftUI -import SwiftUI - struct NewsList: View { - @StateObject var viewModel = Network< + @ObservedObject var newsFinder: RequestAPI var body: some View { ZStack { List { @@ -23,7 +21,7 @@ struct NewsList: View { .buttonStyle(.bordered) } Section { - ForEach(viewModel.model.movies) { movie in + ForEach(newsFinder.model.el) { movie in VStack { Spacer() Group { @@ -40,7 +38,7 @@ struct NewsList: View { } } } - if viewModel.fetchingStatus == .fetching { + if newsFinder.fetchingStatus == .fetching { ProgressView() .scaleEffect(1.5) } @@ -49,7 +47,6 @@ struct NewsList: View { } } - struct NewsList_Previews: PreviewProvider { static var previews: some View { NewsList() diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Network.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift similarity index 97% rename from week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Network.swift rename to week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift index 2503fe6..09ad2ea 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Network.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift @@ -14,11 +14,9 @@ fileprivate enum NaverOpenAPI { static let host = "openapi.naver.com" } -class RequestAPI: ObservableObject { - +class RequestAPI: ObservableObject { @Published var model = T() @Published var fetchingStatus = FetchStatus.idle - private var urlComponents = URLComponents() init() { @@ -58,7 +56,6 @@ class RequestAPI: ObservableObject { case idle case fetching } - } struct Response: Codable { @@ -94,4 +91,3 @@ struct Response: Codable { let thumbnail: String } } - From b272cba686cbda2a9ec7a841e5859c9219f18f4e Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Sat, 3 Sep 2022 10:17:23 +0900 Subject: [PATCH 05/10] Change Naming --- .../NaverOpenAPITest/View/DocumentList.swift | 2 +- .../NaverOpenAPITest/View/MovieList.swift | 4 ++-- .../NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift | 4 ++-- .../NaverOpenAPITest/ViewModel/Finder.swift | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift index 8471514..6590470 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift @@ -8,7 +8,7 @@ import SwiftUI struct DocumentList: View { - @ObservedObject var documentFinder: RequestAPI + @ObservedObject var documentFinder: Finder var body: some View { ZStack { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift index 28970d3..e5c5574 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift @@ -8,7 +8,7 @@ import SwiftUI struct MovieList: View { - @ObservedObject var movieFinder: RequestAPI + @ObservedObject var movieFinder: Finder var body: some View { ZStack { @@ -22,7 +22,7 @@ struct MovieList: View { .buttonStyle(.bordered) } Section { - ForEach(viewModel.model.movies) { movie in + ForEach(movieFinder) { movie in VStack { Spacer() Group { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift index 2f2dab6..31b79e3 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift @@ -8,7 +8,7 @@ import SwiftUI struct NewsList: View { - @ObservedObject var newsFinder: RequestAPI + @ObservedObject var newsFinder: Finder var body: some View { ZStack { List { @@ -21,7 +21,7 @@ struct NewsList: View { .buttonStyle(.bordered) } Section { - ForEach(newsFinder.model.el) { movie in + ForEach(newsFinder.model.) { movie in VStack { Spacer() Group { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift index 09ad2ea..b82c05a 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift @@ -14,8 +14,8 @@ fileprivate enum NaverOpenAPI { static let host = "openapi.naver.com" } -class RequestAPI: ObservableObject { - @Published var model = T() +class Finder: ObservableObject { + @Published var model = Article() @Published var fetchingStatus = FetchStatus.idle private var urlComponents = URLComponents() @@ -24,7 +24,7 @@ class RequestAPI: ObservableObject { urlComponents.host = NaverOpenAPI.host } - func fetchDataList(wantToSearch: T) { + func fetchDataList(wantToSearch: Article) { fetchingStatus = .fetching urlComponents.path = wantToSearch.path urlComponents.queryItems = [URLQueryItem(name: "query", value: wantToSearch.searchKeyword)] @@ -35,7 +35,7 @@ class RequestAPI: ObservableObject { urlRequest.addValue(NaverOpenAPI.clientID, forHTTPHeaderField: "X-Naver-Client-Id") urlRequest.addValue(NaverOpenAPI.clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in - // 각기 다른 예외처리를 하는 것이 좋겠죠 ? + // 각기 다른 예외처리를 하는 것이 좋겠죠 ? --> 기능 완성하고 변경하겠습니다 ㅠ guard error == nil, let httpURLResponse = response as? HTTPURLResponse, (200 ... 299).contains(httpURLResponse.statusCode), From 7ec500f4ec3aa977e3b903000e1818c43178ff4f Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Sat, 3 Sep 2022 10:26:00 +0900 Subject: [PATCH 06/10] Keep coding convention --- .../NaverOpenAPITest.xcodeproj/project.pbxproj | 8 ++++---- ...butedString+Extension.swift => String+Extension.swift} | 0 .../NaverOpenAPITest/Model/Encyclopedia.swift | 1 + .../NaverOpenAPITest/Model/NewsDesk.swift | 1 + week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift | 3 ++- .../NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift | 3 ++- .../NaverOpenAPITest/ViewModel/Finder.swift | 1 + 7 files changed, 11 insertions(+), 6 deletions(-) rename week5/NaverOpenAPITest/NaverOpenAPITest/Extension/{AttributedString+Extension.swift => String+Extension.swift} (100%) diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj index b311ac4..22a3ea0 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj +++ b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ A67D5A8C28C1B74000624B25 /* Finder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8B28C1B74000624B25 /* Finder.swift */; }; A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */; }; A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */; }; - A67D5A9328C1DBCD00624B25 /* AttributedString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9228C1DBCD00624B25 /* AttributedString+Extension.swift */; }; + A67D5A9328C1DBCD00624B25 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9228C1DBCD00624B25 /* String+Extension.swift */; }; A67D5A9828C1E7D700624B25 /* Searchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9728C1E7D700624B25 /* Searchable.swift */; }; A67D5A9C28C1F51500624B25 /* ChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9B28C1F51500624B25 /* ChoiceView.swift */; }; A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9D28C1F53100624B25 /* DocumentList.swift */; }; @@ -32,7 +32,7 @@ A67D5A8B28C1B74000624B25 /* Finder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Finder.swift; sourceTree = ""; }; A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsDesk.swift; sourceTree = ""; }; A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encyclopedia.swift; sourceTree = ""; }; - A67D5A9228C1DBCD00624B25 /* AttributedString+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Extension.swift"; sourceTree = ""; }; + A67D5A9228C1DBCD00624B25 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; A67D5A9728C1E7D700624B25 /* Searchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Searchable.swift; sourceTree = ""; }; A67D5A9B28C1F51500624B25 /* ChoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceView.swift; sourceTree = ""; }; A67D5A9D28C1F53100624B25 /* DocumentList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentList.swift; sourceTree = ""; }; @@ -102,7 +102,7 @@ A67D5A9428C1DBEC00624B25 /* Extension */ = { isa = PBXGroup; children = ( - A67D5A9228C1DBCD00624B25 /* AttributedString+Extension.swift */, + A67D5A9228C1DBCD00624B25 /* String+Extension.swift */, ); path = Extension; sourceTree = ""; @@ -201,7 +201,7 @@ A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */, A67D5A7428C0537D00624B25 /* NaverOpenAPITestApp.swift in Sources */, A67D5AA028C1F54900624B25 /* NewsList.swift in Sources */, - A67D5A9328C1DBCD00624B25 /* AttributedString+Extension.swift in Sources */, + A67D5A9328C1DBCD00624B25 /* String+Extension.swift in Sources */, A67D5A8828C0551700624B25 /* MovieList.swift in Sources */, A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */, A67D5A9828C1E7D700624B25 /* Searchable.swift in Sources */, diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Extension/AttributedString+Extension.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Extension/String+Extension.swift similarity index 100% rename from week5/NaverOpenAPITest/NaverOpenAPITest/Extension/AttributedString+Extension.swift rename to week5/NaverOpenAPITest/NaverOpenAPITest/Extension/String+Extension.swift diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift index 60ff22a..8a21ec6 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift @@ -11,6 +11,7 @@ struct Encyclopedia: Searchable { var searchKeyword = "" var elements: [Any] = [] var path = "/v1/search/encyc.json" + init() {} } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift index 8c2a40e..1f5d79f 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift @@ -11,6 +11,7 @@ struct NewsDesk: Searchable { var searchKeyword = "" var elements: [Any] = [] var path = "/v1/search/news.json" + init() {} } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift index 930dce8..3d95c32 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift @@ -10,6 +10,7 @@ import Foundation protocol Searchable { var path: String {get} var searchKeyword: String {get} - init() var elements: [Any] {get set} + + init() } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift index 31b79e3..0891bb6 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift @@ -9,6 +9,7 @@ import SwiftUI struct NewsList: View { @ObservedObject var newsFinder: Finder + var body: some View { ZStack { List { @@ -21,7 +22,7 @@ struct NewsList: View { .buttonStyle(.bordered) } Section { - ForEach(newsFinder.model.) { movie in + ForEach() { movie in VStack { Spacer() Group { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift index b82c05a..90f2d78 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift @@ -52,6 +52,7 @@ class Finder: ObservableObject { task.resume() fetchingStatus = .idle } + enum FetchStatus { case idle case fetching From 8ed199b9b04ebb4d15d3b00ae647a5b26d26e55a Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Mon, 5 Sep 2022 17:53:23 +0900 Subject: [PATCH 07/10] =?UTF-8?q?Make=20NetworkManager=20Finder=EC=9D=98?= =?UTF-8?q?=20fetch=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A5=BC=20=EB=AA=A8?= =?UTF-8?q?=EB=93=A0=20=ED=83=80=EC=9E=85(news,=20movie,=20document)?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=20=ED=95=A0=20=EC=88=98?= =?UTF-8?q?=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EB=A7=8C=EB=93=A4=EA=B3=A0=20?= =?UTF-8?q?=EC=8B=B6=EC=A7=80=EB=A7=8C=20=EC=95=84=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=98=EC=A7=80=20=EB=AA=BB=ED=95=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project.pbxproj | 24 +++- .../NaverOpenAPITest/Model/BoxOffice.swift | 6 +- .../NaverOpenAPITest/Model/Encyclopedia.swift | 6 +- .../NaverOpenAPITest/Model/NewsDesk.swift | 6 +- .../NaverOpenAPITestApp.swift | 2 +- .../Network/NetworkManager.swift | 60 ++++++++++ .../NaverOpenAPITest/Searchable.swift | 16 --- .../NaverOpenAPITest/View/ChoiceView.swift | 40 +++---- .../NaverOpenAPITest/View/DocumentList.swift | 94 ++++++++-------- .../NaverOpenAPITest/View/MovieList.swift | 8 +- .../NaverOpenAPITest/View/NewsList.swift | 104 +++++++++--------- .../NaverOpenAPITest/ViewModel/Finder.swift | 45 ++------ 12 files changed, 219 insertions(+), 192 deletions(-) create mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift delete mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj index 22a3ea0..138f06a 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj +++ b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj @@ -16,10 +16,10 @@ A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */; }; A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */; }; A67D5A9328C1DBCD00624B25 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9228C1DBCD00624B25 /* String+Extension.swift */; }; - A67D5A9828C1E7D700624B25 /* Searchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9728C1E7D700624B25 /* Searchable.swift */; }; A67D5A9C28C1F51500624B25 /* ChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9B28C1F51500624B25 /* ChoiceView.swift */; }; A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9D28C1F53100624B25 /* DocumentList.swift */; }; A67D5AA028C1F54900624B25 /* NewsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9F28C1F54900624B25 /* NewsList.swift */; }; + A67D5AA428C58D6F00624B25 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5AA328C58D6F00624B25 /* NetworkManager.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -33,10 +33,10 @@ A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsDesk.swift; sourceTree = ""; }; A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encyclopedia.swift; sourceTree = ""; }; A67D5A9228C1DBCD00624B25 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; - A67D5A9728C1E7D700624B25 /* Searchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Searchable.swift; sourceTree = ""; }; A67D5A9B28C1F51500624B25 /* ChoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceView.swift; sourceTree = ""; }; A67D5A9D28C1F53100624B25 /* DocumentList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentList.swift; sourceTree = ""; }; A67D5A9F28C1F54900624B25 /* NewsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsList.swift; sourceTree = ""; }; + A67D5AA328C58D6F00624B25 /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -69,8 +69,9 @@ A67D5A7228C0537D00624B25 /* NaverOpenAPITest */ = { isa = PBXGroup; children = ( - A67D5A9728C1E7D700624B25 /* Searchable.swift */, + A67D5AA228C58D5100624B25 /* Network */, A67D5A7328C0537D00624B25 /* NaverOpenAPITestApp.swift */, + A67D5AA128C4528800624B25 /* Protocol */, A67D5A9428C1DBEC00624B25 /* Extension */, A67D5A7728C0537F00624B25 /* Assets.xcassets */, A67D5A7928C0537F00624B25 /* Preview Content */, @@ -126,6 +127,21 @@ path = View; sourceTree = ""; }; + A67D5AA128C4528800624B25 /* Protocol */ = { + isa = PBXGroup; + children = ( + ); + path = Protocol; + sourceTree = ""; + }; + A67D5AA228C58D5100624B25 /* Network */ = { + isa = PBXGroup; + children = ( + A67D5AA328C58D6F00624B25 /* NetworkManager.swift */, + ); + path = Network; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -200,11 +216,11 @@ A67D5A9C28C1F51500624B25 /* ChoiceView.swift in Sources */, A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */, A67D5A7428C0537D00624B25 /* NaverOpenAPITestApp.swift in Sources */, + A67D5AA428C58D6F00624B25 /* NetworkManager.swift in Sources */, A67D5AA028C1F54900624B25 /* NewsList.swift in Sources */, A67D5A9328C1DBCD00624B25 /* String+Extension.swift in Sources */, A67D5A8828C0551700624B25 /* MovieList.swift in Sources */, A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */, - A67D5A9828C1E7D700624B25 /* Searchable.swift in Sources */, A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */, A67D5A8228C053EC00624B25 /* BoxOffice.swift in Sources */, ); diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift index 59e449b..f9b132f 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift @@ -7,12 +7,10 @@ import Foundation -struct BoxOffice: Searchable { +struct BoxOffice { var searchKeyword = "" - var elements: [Any] = [] + var movies: [Movie] = [] var path = "/v1/search/movie.json" - - init() {} } extension BoxOffice { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift index 8a21ec6..9587f44 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift @@ -7,12 +7,10 @@ import Foundation -struct Encyclopedia: Searchable { +struct Encyclopedia { var searchKeyword = "" - var elements: [Any] = [] + var documents: [Document] = [] var path = "/v1/search/encyc.json" - - init() {} } extension Encyclopedia { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift index 1f5d79f..f9d406f 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift @@ -7,12 +7,10 @@ import Foundation -struct NewsDesk: Searchable { +struct NewsDesk { var searchKeyword = "" - var elements: [Any] = [] + var news: [News] = [] var path = "/v1/search/news.json" - - init() {} } extension NewsDesk{ diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift index 089d25b..d8ad985 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift @@ -12,7 +12,7 @@ struct NaverOpenAPITestApp: App { var body: some Scene { WindowGroup { - ChoiceView() + MovieList(movieFinder: Finder()) } } } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift new file mode 100644 index 0000000..bb3e419 --- /dev/null +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift @@ -0,0 +1,60 @@ +// +// NetworkManager.swift +// NaverOpenAPITest +// +// Created by ohhyeongseok on 2022/09/05. +// + +import Foundation + +fileprivate enum NaverOpenAPI { + static let clientID = "QDph9OaM58FywxjbaSDe" + static let clientSecret = "7BEzAGG02o" + static let scheme = "https" + static let host = "openapi.naver.com" +} + +class NetworkManager { + + private func fillUrlComponents(path: String, query: String) -> URLComponents { + var urlComponents = URLComponents() + urlComponents.scheme = NaverOpenAPI.scheme + urlComponents.host = NaverOpenAPI.host + urlComponents.path = path + urlComponents.queryItems = [URLQueryItem(name: "query", value: query)] + return urlComponents + } + + private func fillUrlReuqest(url: URL, httpMethod: String) -> URLRequest { + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = httpMethod + urlRequest.addValue(NaverOpenAPI.clientID, forHTTPHeaderField: "X-Naver-Client-Id") + urlRequest.addValue(NaverOpenAPI.clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") + return urlRequest + } + + func fetchDataList(path: String, query: String, fetchDataInBackground: @escaping (_ parsedData: Response) -> Void) { + let urlComponents = fillUrlComponents(path: path, query: query) + guard let url = urlComponents.url else { + return + } + let urlRequest = fillUrlReuqest(url: url, httpMethod: "GET") + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + if error != nil { + print("에러가 occur!") + return + } + guard let response = response as? HTTPURLResponse, + (200...299).contains(response.statusCode) else { + print ("server error!!") + return + } + guard let data = data, let parsedData = try? JSONDecoder().decode(Response.self, from: data) else { + print("data error!") + return + } + fetchDataInBackground(parsedData) + } + task.resume() + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift deleted file mode 100644 index 3d95c32..0000000 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Searchable.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// MyProtocol.swift -// NaverOpenAPITest -// -// Created by ohhyeongseok on 2022/09/02. -// - -import Foundation - -protocol Searchable { - var path: String {get} - var searchKeyword: String {get} - var elements: [Any] {get set} - - init() -} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift index 8aaae35..f2fe726 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift @@ -5,23 +5,23 @@ // Created by ohhyeongseok on 2022/09/02. // -import SwiftUI - -struct ChoiceView: View { - - var body: some View { - NavigationView { - List { - NavigationLink("Search News", destination: NewsList()) - NavigationLink("Search Movie", destination: MovieList()) - NavigationLink("Search Document", destination: DocumentList()) - } - } - } -} - -struct ChoiceView_Previews: PreviewProvider { - static var previews: some View { - ChoiceView() - } -} +//import SwiftUI +// +//struct ChoiceView: View { +// +// var body: some View { +// NavigationView { +// List { +// NavigationLink("Search News", destination: NewsList()) +// NavigationLink("Search Movie", destination: MovieList()) +// NavigationLink("Search Document", destination: DocumentList()) +// } +// } +// } +//} +// +//struct ChoiceView_Previews: PreviewProvider { +// static var previews: some View { +// ChoiceView() +// } +//} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift index 6590470..4001886 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift @@ -1,49 +1,49 @@ +//// +//// DocumentList.swift +//// NaverOpenAPITest +//// +//// Created by ohhyeongseok on 2022/09/02. +//// // -// DocumentList.swift -// NaverOpenAPITest +//import SwiftUI // -// Created by ohhyeongseok on 2022/09/02. -// - -import SwiftUI - -struct DocumentList: View { - @ObservedObject var documentFinder: Finder - - var body: some View { - ZStack { - List { - HStack { - TextField(text: $viewModel.model.searchKeyword, label: { - Text("검색어를 입력하세요.") }) - Button(action: {viewModel.fetchMovieList() }) { - Text("검색") - } - .buttonStyle(.bordered) - } - Section { - ForEach(documentFinder.model.) { movie in - VStack { - Spacer() - Group { - if let attributedTitle = movie.attributedTitle { - Text(attributedTitle) - } else { - Text("알 수 없음") - } - } - .font(Font.system(size: 15)) - .minimumScaleFactor(0.5) - Spacer() - } - } - } - } - if documentFinder.fetchingStatus == .fetching { - ProgressView() - .scaleEffect(1.5) - } - } - .foregroundColor(.black) - } -} +//struct DocumentList: View { +// @ObservedObject var documentFinder: Finder +// +// var body: some View { +// ZStack { +// List { +// HStack { +// TextField(text: $viewModel.model.searchKeyword, label: { +// Text("검색어를 입력하세요.") }) +// Button(action: {viewModel.fetchMovieList() }) { +// Text("검색") +// } +// .buttonStyle(.bordered) +// } +// Section { +// ForEach(documentFinder.model.) { movie in +// VStack { +// Spacer() +// Group { +// if let attributedTitle = movie.attributedTitle { +// Text(attributedTitle) +// } else { +// Text("알 수 없음") +// } +// } +// .font(Font.system(size: 15)) +// .minimumScaleFactor(0.5) +// Spacer() +// } +// } +// } +// } +// if documentFinder.fetchingStatus == .fetching { +// ProgressView() +// .scaleEffect(1.5) +// } +// } +// .foregroundColor(.black) +// } +//} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift index e5c5574..fe71b40 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift @@ -8,21 +8,21 @@ import SwiftUI struct MovieList: View { - @ObservedObject var movieFinder: Finder + @ObservedObject var movieFinder: Finder var body: some View { ZStack { List { HStack { - TextField(text: $viewModel.model.searchKeyword, label: { + TextField(text: $movieFinder.movieModel.searchKeyword, label: { Text("검색어를 입력하세요.") }) - Button(action: {viewModel.fetchMovieList() }) { + Button(action: {movieFinder.fetch() }) { Text("검색") } .buttonStyle(.bordered) } Section { - ForEach(movieFinder) { movie in + ForEach(movieFinder.movieModel.movies) { movie in VStack { Spacer() Group { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift index 0891bb6..1a43f6a 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift @@ -1,55 +1,55 @@ +//// +//// NewsList.swift +//// NaverOpenAPITest +//// +//// Created by ohhyeongseok on 2022/09/02. +//// // -// NewsList.swift -// NaverOpenAPITest +//import SwiftUI // -// Created by ohhyeongseok on 2022/09/02. +//struct NewsList: View { +// @ObservedObject var newsFinder: Finder +// +// var body: some View { +// ZStack { +// List { +// HStack { +// TextField(text: $viewModel.model.searchKeyword, label: { +// Text("검색어를 입력하세요.") }) +// Button(action: {viewModel.fetchMovieList() }) { +// Text("검색") +// } +// .buttonStyle(.bordered) +// } +// Section { +// ForEach() { movie in +// VStack { +// Spacer() +// Group { +// if let attributedTitle = movie.attributedTitle { +// Text(attributedTitle) +// } else { +// Text("알 수 없음") +// } +// } +// .font(Font.system(size: 15)) +// .minimumScaleFactor(0.5) +// Spacer() +// } +// } +// } +// } +// if newsFinder.fetchingStatus == .fetching { +// ProgressView() +// .scaleEffect(1.5) +// } +// } +// .foregroundColor(.black) +// } +//} // - -import SwiftUI - -struct NewsList: View { - @ObservedObject var newsFinder: Finder - - var body: some View { - ZStack { - List { - HStack { - TextField(text: $viewModel.model.searchKeyword, label: { - Text("검색어를 입력하세요.") }) - Button(action: {viewModel.fetchMovieList() }) { - Text("검색") - } - .buttonStyle(.bordered) - } - Section { - ForEach() { movie in - VStack { - Spacer() - Group { - if let attributedTitle = movie.attributedTitle { - Text(attributedTitle) - } else { - Text("알 수 없음") - } - } - .font(Font.system(size: 15)) - .minimumScaleFactor(0.5) - Spacer() - } - } - } - } - if newsFinder.fetchingStatus == .fetching { - ProgressView() - .scaleEffect(1.5) - } - } - .foregroundColor(.black) - } -} - -struct NewsList_Previews: PreviewProvider { - static var previews: some View { - NewsList() - } -} +//struct NewsList_Previews: PreviewProvider { +// static var previews: some View { +// NewsList() +// } +//} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift index 90f2d78..b08f4d1 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift @@ -7,50 +7,23 @@ import Foundation -fileprivate enum NaverOpenAPI { - static let clientID = "QDph9OaM58FywxjbaSDe" - static let clientSecret = "7BEzAGG02o" - static let scheme = "https" - static let host = "openapi.naver.com" -} - -class Finder: ObservableObject { - @Published var model = Article() +class Finder: ObservableObject { + @Published var newsModei = NewsDesk() + @Published var movieModel = BoxOffice() + @Published var documentModel = Encyclopedia() @Published var fetchingStatus = FetchStatus.idle - private var urlComponents = URLComponents() - - init() { - urlComponents.scheme = NaverOpenAPI.scheme - urlComponents.host = NaverOpenAPI.host - } - - func fetchDataList(wantToSearch: Article) { + var networkManager = NetworkManager() + + func fetch() { fetchingStatus = .fetching - urlComponents.path = wantToSearch.path - urlComponents.queryItems = [URLQueryItem(name: "query", value: wantToSearch.searchKeyword)] - guard let url = urlComponents.url - else { return } - var urlRequest = URLRequest(url: url) - urlRequest.httpMethod = "GET" - urlRequest.addValue(NaverOpenAPI.clientID, forHTTPHeaderField: "X-Naver-Client-Id") - urlRequest.addValue(NaverOpenAPI.clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") - let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in - // 각기 다른 예외처리를 하는 것이 좋겠죠 ? --> 기능 완성하고 변경하겠습니다 ㅠ - guard error == nil, - let httpURLResponse = response as? HTTPURLResponse, - (200 ... 299).contains(httpURLResponse.statusCode), - let data = data, - let parsedData = try? JSONDecoder().decode(Response.self, from: data) - else { return } + networkManager.fetchDataList(path: movieModel.path, query: movieModel.searchKeyword) { parsedData in DispatchQueue.main.async { [weak self] in - self?.model.elements = parsedData.items.indices.map { + self?.movieModel.movies = parsedData.items.indices.map { BoxOffice.Movie(parsedData.items[$0], id: parsedData.start + $0) } self?.fetchingStatus = .idle } } - task.resume() - fetchingStatus = .idle } enum FetchStatus { From 22ecceb45532626af7ebf95dd004951d4fb09153 Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Mon, 12 Sep 2022 11:47:24 +0900 Subject: [PATCH 08/10] =?UTF-8?q?Search=20News/=20Document=20//=20?= =?UTF-8?q?=EC=A0=9C=EB=84=A4=EB=A6=AD=EC=8A=A4=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=ED=95=B4=EC=84=9C=20=ED=95=A8=EC=88=98=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EB=A1=9C=20=EB=AA=A8=EB=93=A0=20=ED=83=80=EC=9E=85=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A4=EB=A0=A4=ED=96=88=EC=A7=80=EB=A7=8C=20?= =?UTF-8?q?=EC=95=84=EC=A7=81=20=EA=B5=AC=ED=98=84=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EB=AA=BB=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project.pbxproj | 16 --- .../NaverOpenAPITest/Model/BoxOffice.swift | 40 -------- .../NaverOpenAPITest/Model/Encyclopedia.swift | 2 +- .../NaverOpenAPITest/Model/NewsDesk.swift | 3 +- .../NaverOpenAPITestApp.swift | 3 +- .../Network/NetworkManager.swift | 38 ++++++- .../NaverOpenAPITest/View/ChoiceView.swift | 39 ++++---- .../NaverOpenAPITest/View/DocumentList.swift | 94 +++++++++--------- .../NaverOpenAPITest/View/MovieList.swift | 49 --------- .../NaverOpenAPITest/View/NewsList.swift | 99 +++++++++---------- .../NaverOpenAPITest/ViewModel/Finder.swift | 51 ++++++---- 11 files changed, 183 insertions(+), 251 deletions(-) delete mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift delete mode 100644 week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj index 138f06a..efa5701 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj +++ b/week5/NaverOpenAPITest/NaverOpenAPITest.xcodeproj/project.pbxproj @@ -10,8 +10,6 @@ A67D5A7428C0537D00624B25 /* NaverOpenAPITestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A7328C0537D00624B25 /* NaverOpenAPITestApp.swift */; }; A67D5A7828C0537F00624B25 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A67D5A7728C0537F00624B25 /* Assets.xcassets */; }; A67D5A7B28C0537F00624B25 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A67D5A7A28C0537F00624B25 /* Preview Assets.xcassets */; }; - A67D5A8228C053EC00624B25 /* BoxOffice.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8128C053EC00624B25 /* BoxOffice.swift */; }; - A67D5A8828C0551700624B25 /* MovieList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8728C0551700624B25 /* MovieList.swift */; }; A67D5A8C28C1B74000624B25 /* Finder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8B28C1B74000624B25 /* Finder.swift */; }; A67D5A8E28C1CD2E00624B25 /* NewsDesk.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */; }; A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */; }; @@ -27,8 +25,6 @@ A67D5A7328C0537D00624B25 /* NaverOpenAPITestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NaverOpenAPITestApp.swift; sourceTree = ""; }; A67D5A7728C0537F00624B25 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A67D5A7A28C0537F00624B25 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - A67D5A8128C053EC00624B25 /* BoxOffice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOffice.swift; sourceTree = ""; }; - A67D5A8728C0551700624B25 /* MovieList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieList.swift; sourceTree = ""; }; A67D5A8B28C1B74000624B25 /* Finder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Finder.swift; sourceTree = ""; }; A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsDesk.swift; sourceTree = ""; }; A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encyclopedia.swift; sourceTree = ""; }; @@ -71,7 +67,6 @@ children = ( A67D5AA228C58D5100624B25 /* Network */, A67D5A7328C0537D00624B25 /* NaverOpenAPITestApp.swift */, - A67D5AA128C4528800624B25 /* Protocol */, A67D5A9428C1DBEC00624B25 /* Extension */, A67D5A7728C0537F00624B25 /* Assets.xcassets */, A67D5A7928C0537F00624B25 /* Preview Content */, @@ -94,7 +89,6 @@ isa = PBXGroup; children = ( A67D5A8D28C1CD2E00624B25 /* NewsDesk.swift */, - A67D5A8128C053EC00624B25 /* BoxOffice.swift */, A67D5A9028C1CD6D00624B25 /* Encyclopedia.swift */, ); path = Model; @@ -120,20 +114,12 @@ isa = PBXGroup; children = ( A67D5A9B28C1F51500624B25 /* ChoiceView.swift */, - A67D5A8728C0551700624B25 /* MovieList.swift */, A67D5A9D28C1F53100624B25 /* DocumentList.swift */, A67D5A9F28C1F54900624B25 /* NewsList.swift */, ); path = View; sourceTree = ""; }; - A67D5AA128C4528800624B25 /* Protocol */ = { - isa = PBXGroup; - children = ( - ); - path = Protocol; - sourceTree = ""; - }; A67D5AA228C58D5100624B25 /* Network */ = { isa = PBXGroup; children = ( @@ -219,10 +205,8 @@ A67D5AA428C58D6F00624B25 /* NetworkManager.swift in Sources */, A67D5AA028C1F54900624B25 /* NewsList.swift in Sources */, A67D5A9328C1DBCD00624B25 /* String+Extension.swift in Sources */, - A67D5A8828C0551700624B25 /* MovieList.swift in Sources */, A67D5A9E28C1F53100624B25 /* DocumentList.swift in Sources */, A67D5A9128C1CD6D00624B25 /* Encyclopedia.swift in Sources */, - A67D5A8228C053EC00624B25 /* BoxOffice.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift deleted file mode 100644 index f9b132f..0000000 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/BoxOffice.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// BoxOffice.swift -// NaverOpenAPITest -// -// Created by ohhyeongseok on 2022/09/01. -// - -import Foundation - -struct BoxOffice { - var searchKeyword = "" - var movies: [Movie] = [] - var path = "/v1/search/movie.json" -} - -extension BoxOffice { - - struct Movie: Codable, Identifiable { - let attributedTitle: AttributedString? - let image: String - let subtitle: AttributedString? - let pubDate: String - let director: String - let actor: String - let userRating: String - let id: Int - - init(_ movie: Response.Movie, id: Int) { - attributedTitle = AttributedString(htmlString: movie.title) - image = movie.image - subtitle = AttributedString(htmlString: movie.subtitle) - pubDate = movie.pubDate - director = movie.director - actor = movie.actor - userRating = movie.userRating - self.id = id - } - } -} - diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift index 9587f44..f9e8c2f 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/Encyclopedia.swift @@ -22,7 +22,7 @@ extension Encyclopedia { let tumbnail: String let id: Int - init(_ document: Response.Document, id: Int) { + init(_ document: DocumentResponse.Document, id: Int) { attributedTitle = AttributedString(htmlString: document.title) link = document.link description = document.description diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift index f9d406f..9d17a71 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift @@ -23,7 +23,7 @@ extension NewsDesk{ let pubDate: String let id: Int - init(_ news: Response.News, id: Int) { + init(_ news: NewsResponse.News, id: Int) { attributedTitle = AttributedString(htmlString: news.title) pubDate = news.pubDate originallink = news.originallink @@ -33,3 +33,4 @@ extension NewsDesk{ } } } + diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift index d8ad985..decc563 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/NaverOpenAPITestApp.swift @@ -12,7 +12,8 @@ struct NaverOpenAPITestApp: App { var body: some Scene { WindowGroup { - MovieList(movieFinder: Finder()) + ChoiceView() } } } + diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift index bb3e419..e77e2e3 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift @@ -33,12 +33,12 @@ class NetworkManager { return urlRequest } - func fetchDataList(path: String, query: String, fetchDataInBackground: @escaping (_ parsedData: Response) -> Void) { + func fetchNewsList(path: String, query: String, fetchDataInBackground: @escaping (_ parsedData: NewsResponse) -> Void) { let urlComponents = fillUrlComponents(path: path, query: query) guard let url = urlComponents.url else { return } - let urlRequest = fillUrlReuqest(url: url, httpMethod: "GET") + let urlRequest = fillUrlReuqest(url: url, httpMethod: HttpMethod.get.rawValue) let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in if error != nil { print("에러가 occur!") @@ -49,7 +49,7 @@ class NetworkManager { print ("server error!!") return } - guard let data = data, let parsedData = try? JSONDecoder().decode(Response.self, from: data) else { + guard let data = data, let parsedData = try? JSONDecoder().decode(NewsResponse.self, from: data) else { print("data error!") return } @@ -57,4 +57,36 @@ class NetworkManager { } task.resume() } + + func fetchDocumentList(path: String, query: String, fetchDataInBackground: @escaping (_ parsedData: DocumentResponse) -> Void) { + let urlComponents = fillUrlComponents(path: path, query: query) + guard let url = urlComponents.url else { + return + } + let urlRequest = fillUrlReuqest(url: url, httpMethod: HttpMethod.get.rawValue) + let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in + if error != nil { + print("에러가 occur!") + return + } + guard let response = response as? HTTPURLResponse, + (200...299).contains(response.statusCode) else { + print ("server error!!") + return + } + guard let data = data, let parsedData = try? JSONDecoder().decode(DocumentResponse.self, from: data) else { + print("data error!") + return + } + fetchDataInBackground(parsedData) + } + task.resume() + } +} + +enum HttpMethod: String { + case get = "GET" + case post = "POST" + case put = "PUT" + case delete = "DELETE" } diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift index f2fe726..10870c9 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/ChoiceView.swift @@ -5,23 +5,22 @@ // Created by ohhyeongseok on 2022/09/02. // -//import SwiftUI -// -//struct ChoiceView: View { -// -// var body: some View { -// NavigationView { -// List { -// NavigationLink("Search News", destination: NewsList()) -// NavigationLink("Search Movie", destination: MovieList()) -// NavigationLink("Search Document", destination: DocumentList()) -// } -// } -// } -//} -// -//struct ChoiceView_Previews: PreviewProvider { -// static var previews: some View { -// ChoiceView() -// } -//} +import SwiftUI + +struct ChoiceView: View { + @StateObject var finder = Finder() + var body: some View { + NavigationView { + List { + NavigationLink("Search News", destination: NewsList(newsFinder: finder)) + NavigationLink("Search Document", destination: DocumentList(documentFinder: finder)) + } + } + } +} + +struct ChoiceView_Previews: PreviewProvider { + static var previews: some View { + ChoiceView() + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift index 4001886..74755e5 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift @@ -1,49 +1,49 @@ -//// -//// DocumentList.swift -//// NaverOpenAPITest -//// -//// Created by ohhyeongseok on 2022/09/02. -//// // -//import SwiftUI +// DocumentList.swift +// NaverOpenAPITest // -//struct DocumentList: View { -// @ObservedObject var documentFinder: Finder -// -// var body: some View { -// ZStack { -// List { -// HStack { -// TextField(text: $viewModel.model.searchKeyword, label: { -// Text("검색어를 입력하세요.") }) -// Button(action: {viewModel.fetchMovieList() }) { -// Text("검색") -// } -// .buttonStyle(.bordered) -// } -// Section { -// ForEach(documentFinder.model.) { movie in -// VStack { -// Spacer() -// Group { -// if let attributedTitle = movie.attributedTitle { -// Text(attributedTitle) -// } else { -// Text("알 수 없음") -// } -// } -// .font(Font.system(size: 15)) -// .minimumScaleFactor(0.5) -// Spacer() -// } -// } -// } -// } -// if documentFinder.fetchingStatus == .fetching { -// ProgressView() -// .scaleEffect(1.5) -// } -// } -// .foregroundColor(.black) -// } -//} +// Created by ohhyeongseok on 2022/09/02. +// + +import SwiftUI + +struct DocumentList: View { + @ObservedObject var documentFinder: Finder + + var body: some View { + ZStack { + List { + HStack { + TextField(text: $documentFinder.documentModel.searchKeyword, label: { + Text("검색어를 입력하세요.") }) + Button(action: {documentFinder.fetchDocument() }) { + Text("검색") + } + .buttonStyle(.bordered) + } + Section { + ForEach(documentFinder.documentModel.documents) { movie in + VStack { + Spacer() + Group { + if let attributedTitle = movie.attributedTitle { + Text(attributedTitle) + } else { + Text("알 수 없음") + } + } + .font(Font.system(size: 15)) + .minimumScaleFactor(0.5) + Spacer() + } + } + } + } + if documentFinder.fetchingStatus == .fetching { + ProgressView() + .scaleEffect(1.5) + } + } + .foregroundColor(.black) + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift deleted file mode 100644 index fe71b40..0000000 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/MovieList.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// MovieList.swift -// NaverOpenAPITest -// -// Created by ohhyeongseok on 2022/09/01. -// - -import SwiftUI - -struct MovieList: View { - @ObservedObject var movieFinder: Finder - - var body: some View { - ZStack { - List { - HStack { - TextField(text: $movieFinder.movieModel.searchKeyword, label: { - Text("검색어를 입력하세요.") }) - Button(action: {movieFinder.fetch() }) { - Text("검색") - } - .buttonStyle(.bordered) - } - Section { - ForEach(movieFinder.movieModel.movies) { movie in - VStack { - Spacer() - Group { - if let attributedTitle = movie.attributedTitle { - Text(attributedTitle) - } else { - Text("알 수 없음") - } - } - .font(Font.system(size: 15)) - .minimumScaleFactor(0.5) - Spacer() - } - } - } - } - if movieFinder.fetchingStatus == .fetching { - ProgressView() - .scaleEffect(1.5) - } - } - .foregroundColor(.black) - } -} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift index 1a43f6a..3e0ee65 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift @@ -1,55 +1,50 @@ -//// -//// NewsList.swift -//// NaverOpenAPITest -//// -//// Created by ohhyeongseok on 2022/09/02. -//// // -//import SwiftUI +// NewsList.swift +// NaverOpenAPITest // -//struct NewsList: View { -// @ObservedObject var newsFinder: Finder -// -// var body: some View { -// ZStack { -// List { -// HStack { -// TextField(text: $viewModel.model.searchKeyword, label: { -// Text("검색어를 입력하세요.") }) -// Button(action: {viewModel.fetchMovieList() }) { -// Text("검색") -// } -// .buttonStyle(.bordered) -// } -// Section { -// ForEach() { movie in -// VStack { -// Spacer() -// Group { -// if let attributedTitle = movie.attributedTitle { -// Text(attributedTitle) -// } else { -// Text("알 수 없음") -// } -// } -// .font(Font.system(size: 15)) -// .minimumScaleFactor(0.5) -// Spacer() -// } -// } -// } -// } -// if newsFinder.fetchingStatus == .fetching { -// ProgressView() -// .scaleEffect(1.5) -// } -// } -// .foregroundColor(.black) -// } -//} +// Created by ohhyeongseok on 2022/09/02. // -//struct NewsList_Previews: PreviewProvider { -// static var previews: some View { -// NewsList() -// } -//} + + +import SwiftUI + +struct NewsList: View { + @ObservedObject var newsFinder: Finder + + var body: some View { + ZStack { + List { + HStack { + TextField(text: $newsFinder.newsModel.searchKeyword, label: { + Text("검색어를 입력하세요.") }) + Button(action: {newsFinder.fetchNews() }) { + Text("검색") + } + .buttonStyle(.bordered) + } + Section { + ForEach(newsFinder.newsModel.news) { movie in + VStack { + Spacer() + Group { + if let attributedTitle = movie.attributedTitle { + Text(attributedTitle) + } else { + Text("알 수 없음") + } + } + .font(Font.system(size: 15)) + .minimumScaleFactor(0.5) + Spacer() + } + } + } + } + if newsFinder.fetchingStatus == .fetching { + ProgressView() + .scaleEffect(1.5) + } + } + .foregroundColor(.black) + } +} diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift index b08f4d1..8a3eb44 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift @@ -8,18 +8,30 @@ import Foundation class Finder: ObservableObject { - @Published var newsModei = NewsDesk() - @Published var movieModel = BoxOffice() + @Published var newsModel = NewsDesk() @Published var documentModel = Encyclopedia() @Published var fetchingStatus = FetchStatus.idle - var networkManager = NetworkManager() - - func fetch() { + let networkManager = NetworkManager() + + func fetchNews() { fetchingStatus = .fetching - networkManager.fetchDataList(path: movieModel.path, query: movieModel.searchKeyword) { parsedData in + networkManager.fetchNewsList(path: newsModel.path, query: newsModel.searchKeyword) { parsedData in DispatchQueue.main.async { [weak self] in - self?.movieModel.movies = parsedData.items.indices.map { - BoxOffice.Movie(parsedData.items[$0], id: parsedData.start + $0) + self?.newsModel.news = parsedData.items.indices.map { + NewsDesk.News(parsedData.items[$0], id: parsedData.start + $0) + } + self?.fetchingStatus = .idle + } + } + } + + + func fetchDocument() { + fetchingStatus = .fetching + networkManager.fetchDocumentList(path: documentModel.path, query: documentModel.searchKeyword) { parsedData in + DispatchQueue.main.async { [weak self] in + self?.documentModel.documents = parsedData.items.indices.map { + Encyclopedia.Document(parsedData.items[$0], id: parsedData.start + $0) } self?.fetchingStatus = .idle } @@ -32,23 +44,12 @@ class Finder: ObservableObject { } } -struct Response: Codable { +struct NewsResponse: Codable { let lastBuildDate: String let total: Int let start: Int let display: Int - let items: [Movie] - - struct Movie: Codable { - let title: String - let link: String - let image: String - let subtitle: String - let pubDate: String - let director: String - let actor: String - let userRating: String - } + let items: [News] struct News: Codable { let title: String @@ -57,6 +58,14 @@ struct Response: Codable { let description: String let pubDate: String } +} + +struct DocumentResponse: Codable { + let lastBuildDate: String + let total: Int + let start: Int + let display: Int + let items: [Document] struct Document: Codable { let title: String From 09d412b7ce60ae973760a525abecd2cdd8e33fec Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Mon, 12 Sep 2022 15:01:34 +0900 Subject: [PATCH 09/10] Change naming, Keep coding convention --- .../NaverOpenAPITest/Model/NewsDesk.swift | 5 ++--- .../NaverOpenAPITest/Network/NetworkManager.swift | 6 ------ .../NaverOpenAPITest/View/DocumentList.swift | 4 ++-- .../NaverOpenAPITest/View/NewsList.swift | 4 ++-- .../NaverOpenAPITest/ViewModel/Finder.swift | 13 ++++++------- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift index 9d17a71..a755675 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Model/NewsDesk.swift @@ -17,7 +17,7 @@ extension NewsDesk{ struct News: Codable, Identifiable { let attributedTitle: AttributedString? - let originallink: String + let originalLink: String let link: String let description: String let pubDate: String @@ -26,11 +26,10 @@ extension NewsDesk{ init(_ news: NewsResponse.News, id: Int) { attributedTitle = AttributedString(htmlString: news.title) pubDate = news.pubDate - originallink = news.originallink + originalLink = news.originallink link = news.link description = news.description self.id = id } } } - diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift index e77e2e3..7186a4e 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/Network/NetworkManager.swift @@ -41,16 +41,13 @@ class NetworkManager { let urlRequest = fillUrlReuqest(url: url, httpMethod: HttpMethod.get.rawValue) let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in if error != nil { - print("에러가 occur!") return } guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else { - print ("server error!!") return } guard let data = data, let parsedData = try? JSONDecoder().decode(NewsResponse.self, from: data) else { - print("data error!") return } fetchDataInBackground(parsedData) @@ -66,16 +63,13 @@ class NetworkManager { let urlRequest = fillUrlReuqest(url: url, httpMethod: HttpMethod.get.rawValue) let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in if error != nil { - print("에러가 occur!") return } guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else { - print ("server error!!") return } guard let data = data, let parsedData = try? JSONDecoder().decode(DocumentResponse.self, from: data) else { - print("data error!") return } fetchDataInBackground(parsedData) diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift index 74755e5..474d835 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/DocumentList.swift @@ -14,7 +14,7 @@ struct DocumentList: View { ZStack { List { HStack { - TextField(text: $documentFinder.documentModel.searchKeyword, label: { + TextField(text: $documentFinder.encyclopedia.searchKeyword, label: { Text("검색어를 입력하세요.") }) Button(action: {documentFinder.fetchDocument() }) { Text("검색") @@ -22,7 +22,7 @@ struct DocumentList: View { .buttonStyle(.bordered) } Section { - ForEach(documentFinder.documentModel.documents) { movie in + ForEach(documentFinder.encyclopedia.documents) { movie in VStack { Spacer() Group { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift index 3e0ee65..b3ec923 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/View/NewsList.swift @@ -15,7 +15,7 @@ struct NewsList: View { ZStack { List { HStack { - TextField(text: $newsFinder.newsModel.searchKeyword, label: { + TextField(text: $newsFinder.newsDesk.searchKeyword, label: { Text("검색어를 입력하세요.") }) Button(action: {newsFinder.fetchNews() }) { Text("검색") @@ -23,7 +23,7 @@ struct NewsList: View { .buttonStyle(.bordered) } Section { - ForEach(newsFinder.newsModel.news) { movie in + ForEach(newsFinder.newsDesk.news) { movie in VStack { Spacer() Group { diff --git a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift index 8a3eb44..1d0f95d 100644 --- a/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift +++ b/week5/NaverOpenAPITest/NaverOpenAPITest/ViewModel/Finder.swift @@ -8,16 +8,16 @@ import Foundation class Finder: ObservableObject { - @Published var newsModel = NewsDesk() - @Published var documentModel = Encyclopedia() + @Published var newsDesk = NewsDesk() + @Published var encyclopedia = Encyclopedia() @Published var fetchingStatus = FetchStatus.idle let networkManager = NetworkManager() func fetchNews() { fetchingStatus = .fetching - networkManager.fetchNewsList(path: newsModel.path, query: newsModel.searchKeyword) { parsedData in + networkManager.fetchNewsList(path: newsDesk.path, query: newsDesk.searchKeyword) { parsedData in DispatchQueue.main.async { [weak self] in - self?.newsModel.news = parsedData.items.indices.map { + self?.newsDesk.news = parsedData.items.indices.map { NewsDesk.News(parsedData.items[$0], id: parsedData.start + $0) } self?.fetchingStatus = .idle @@ -25,12 +25,11 @@ class Finder: ObservableObject { } } - func fetchDocument() { fetchingStatus = .fetching - networkManager.fetchDocumentList(path: documentModel.path, query: documentModel.searchKeyword) { parsedData in + networkManager.fetchDocumentList(path: encyclopedia.path, query: encyclopedia.searchKeyword) { parsedData in DispatchQueue.main.async { [weak self] in - self?.documentModel.documents = parsedData.items.indices.map { + self?.encyclopedia.documents = parsedData.items.indices.map { Encyclopedia.Document(parsedData.items[$0], id: parsedData.start + $0) } self?.fetchingStatus = .idle From 78f819b176c9350ad64e8fd32495b7d92d85e282 Mon Sep 17 00:00:00 2001 From: HyeongSeok Date: Tue, 11 Oct 2022 17:23:33 +0900 Subject: [PATCH 10/10] Apply animation // matchedGeometryEffect error Refactor --- .../Memorize.xcodeproj/project.pbxproj | 24 +-- week2/Memorize/Memorize/Extensions.swift | 22 +++ .../Memorize/Model/MemorizeGame.swift | 16 +- .../Memorize/Memorize/View/CardListView.swift | 15 -- week2/Memorize/Memorize/View/CardView.swift | 35 ++--- week2/Memorize/Memorize/View/Cardify.swift | 48 ++++++ .../Memorize/View/MemorizeGameView.swift | 138 ++++++++++++++++-- .../Memorize/View/NewGameButton.swift | 17 --- week2/Memorize/Memorize/View/ScoreView.swift | 15 -- .../Memorize/View/ThemeNameView.swift | 15 -- .../ViewModel/MemorizeGameDealer.swift | 4 + 11 files changed, 238 insertions(+), 111 deletions(-) create mode 100644 week2/Memorize/Memorize/Extensions.swift delete mode 100644 week2/Memorize/Memorize/View/CardListView.swift create mode 100644 week2/Memorize/Memorize/View/Cardify.swift delete mode 100644 week2/Memorize/Memorize/View/NewGameButton.swift delete mode 100644 week2/Memorize/Memorize/View/ScoreView.swift delete mode 100644 week2/Memorize/Memorize/View/ThemeNameView.swift diff --git a/week2/Memorize/Memorize.xcodeproj/project.pbxproj b/week2/Memorize/Memorize.xcodeproj/project.pbxproj index 9b226b4..4f77fbd 100644 --- a/week2/Memorize/Memorize.xcodeproj/project.pbxproj +++ b/week2/Memorize/Memorize.xcodeproj/project.pbxproj @@ -7,22 +7,21 @@ objects = { /* Begin PBXBuildFile section */ + A690699028F5413B0070676A /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A690698F28F5413B0070676A /* Extensions.swift */; }; A69F17AF288E87F600AF88CC /* MemorizeApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17AE288E87F600AF88CC /* MemorizeApp.swift */; }; A69F17B1288E87F600AF88CC /* MemorizeGameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17B0288E87F600AF88CC /* MemorizeGameView.swift */; }; A69F17B3288E87F700AF88CC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A69F17B2288E87F700AF88CC /* Assets.xcassets */; }; A69F17B6288E87F700AF88CC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A69F17B5288E87F700AF88CC /* Preview Assets.xcassets */; }; A69F17C5288F77E100AF88CC /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17C4288F77E100AF88CC /* Theme.swift */; }; A69F17CB288F8BC400AF88CC /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17CA288F8BC400AF88CC /* CardView.swift */; }; - A69F17CF288F9B6400AF88CC /* CardListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17CE288F9B6400AF88CC /* CardListView.swift */; }; A69F17D72890CBC700AF88CC /* MemorizeGameDealer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17D62890CBC700AF88CC /* MemorizeGameDealer.swift */; }; - A69F17E52898B11C00AF88CC /* ThemeNameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17E42898B11C00AF88CC /* ThemeNameView.swift */; }; - A69F17E72898B26C00AF88CC /* NewGameButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17E62898B26C00AF88CC /* NewGameButton.swift */; }; A69F17E92898C1CD00AF88CC /* MemorizeGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17E82898C1CD00AF88CC /* MemorizeGame.swift */; }; - A69F17F1289A2D7A00AF88CC /* ScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17F0289A2D7A00AF88CC /* ScoreView.swift */; }; A69F17F7289B5A9300AF88CC /* SuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A69F17F6289B5A9300AF88CC /* SuccessView.swift */; }; + A6FD786128E40041009A2677 /* Cardify.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6FD786028E40041009A2677 /* Cardify.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + A690698F28F5413B0070676A /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; A69F17AB288E87F600AF88CC /* Memorize.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memorize.app; sourceTree = BUILT_PRODUCTS_DIR; }; A69F17AE288E87F600AF88CC /* MemorizeApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemorizeApp.swift; sourceTree = ""; }; A69F17B0288E87F600AF88CC /* MemorizeGameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemorizeGameView.swift; sourceTree = ""; }; @@ -30,13 +29,10 @@ A69F17B5288E87F700AF88CC /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; A69F17C4288F77E100AF88CC /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; A69F17CA288F8BC400AF88CC /* CardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = ""; }; - A69F17CE288F9B6400AF88CC /* CardListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardListView.swift; sourceTree = ""; }; A69F17D62890CBC700AF88CC /* MemorizeGameDealer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemorizeGameDealer.swift; sourceTree = ""; }; - A69F17E42898B11C00AF88CC /* ThemeNameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeNameView.swift; sourceTree = ""; }; - A69F17E62898B26C00AF88CC /* NewGameButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewGameButton.swift; sourceTree = ""; }; A69F17E82898C1CD00AF88CC /* MemorizeGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemorizeGame.swift; sourceTree = ""; }; - A69F17F0289A2D7A00AF88CC /* ScoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScoreView.swift; sourceTree = ""; }; A69F17F6289B5A9300AF88CC /* SuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessView.swift; sourceTree = ""; }; + A6FD786028E40041009A2677 /* Cardify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cardify.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,6 +71,7 @@ A69F17C3288F77AC00AF88CC /* Model */, A69F17F8289FD3E600AF88CC /* View */, A69F17F9289FD43A00AF88CC /* ViewModel */, + A690698F28F5413B0070676A /* Extensions.swift */, ); path = Memorize; sourceTree = ""; @@ -101,11 +98,8 @@ children = ( A69F17B0288E87F600AF88CC /* MemorizeGameView.swift */, A69F17CA288F8BC400AF88CC /* CardView.swift */, - A69F17CE288F9B6400AF88CC /* CardListView.swift */, - A69F17E42898B11C00AF88CC /* ThemeNameView.swift */, - A69F17E62898B26C00AF88CC /* NewGameButton.swift */, - A69F17F0289A2D7A00AF88CC /* ScoreView.swift */, A69F17F6289B5A9300AF88CC /* SuccessView.swift */, + A6FD786028E40041009A2677 /* Cardify.swift */, ); path = View; sourceTree = ""; @@ -189,14 +183,12 @@ buildActionMask = 2147483647; files = ( A69F17E92898C1CD00AF88CC /* MemorizeGame.swift in Sources */, + A690699028F5413B0070676A /* Extensions.swift in Sources */, A69F17C5288F77E100AF88CC /* Theme.swift in Sources */, A69F17B1288E87F600AF88CC /* MemorizeGameView.swift in Sources */, A69F17F7289B5A9300AF88CC /* SuccessView.swift in Sources */, - A69F17E72898B26C00AF88CC /* NewGameButton.swift in Sources */, - A69F17E52898B11C00AF88CC /* ThemeNameView.swift in Sources */, + A6FD786128E40041009A2677 /* Cardify.swift in Sources */, A69F17AF288E87F600AF88CC /* MemorizeApp.swift in Sources */, - A69F17F1289A2D7A00AF88CC /* ScoreView.swift in Sources */, - A69F17CF288F9B6400AF88CC /* CardListView.swift in Sources */, A69F17CB288F8BC400AF88CC /* CardView.swift in Sources */, A69F17D72890CBC700AF88CC /* MemorizeGameDealer.swift in Sources */, ); diff --git a/week2/Memorize/Memorize/Extensions.swift b/week2/Memorize/Memorize/Extensions.swift new file mode 100644 index 0000000..a206cb3 --- /dev/null +++ b/week2/Memorize/Memorize/Extensions.swift @@ -0,0 +1,22 @@ +// +// Extensions.swift +// Memorize +// +// Created by ohhyeongseok on 2022/10/11. +// + +import Foundation + +extension Array { + + var oneAndOnly: Element? { + count == 1 ? first : nil + } +} + +extension Array where Element: Identifiable { + + func index(matching element: Element) -> Index? { + firstIndex(where: { $0.id == element.id }) + } +} diff --git a/week2/Memorize/Memorize/Model/MemorizeGame.swift b/week2/Memorize/Memorize/Model/MemorizeGame.swift index 9b4cf80..876e907 100644 --- a/week2/Memorize/Memorize/Model/MemorizeGame.swift +++ b/week2/Memorize/Memorize/Model/MemorizeGame.swift @@ -1,6 +1,9 @@ import Foundation struct MemorizeGame { + + // MARK: Property(ies) + private(set) var cards: [Card] = [] private(set) var score = 0 private var currentOpenedCards: [Card] = [] @@ -8,10 +11,14 @@ struct MemorizeGame { cards.filter({ $0.isMatched }).count / 2 == cards.count / 2 } + // MARK: Initializer(s) + init(numberOfPairsOfCards: Int, createContent: (Int) -> CardContent) { cards = makeCards(numberOfPairsOfCards: numberOfPairsOfCards, createContent: createContent) } + // MARK: Method(s) + func makeCards(numberOfPairsOfCards: Int, createContent: (Int) -> CardContent) -> [Card] { var cards: [Card] = [] for pairIndex in 0 ..< numberOfPairsOfCards { @@ -61,8 +68,7 @@ struct MemorizeGame { cards[chosenIndex].isMatched = true } } - } - else { + } else { currentOpenedCards.forEach { opendCard in if opendCard.isFaceUpAtLeastOnce == true { score -= 1 @@ -74,8 +80,12 @@ struct MemorizeGame { } } + mutating func shuffle() { + cards.shuffle() + } + struct Card: Identifiable { - var isFaceUp = false + var isFaceUp = true var isMatched = false var isFaceUpAtLeastOnce = false let content: CardContent diff --git a/week2/Memorize/Memorize/View/CardListView.swift b/week2/Memorize/Memorize/View/CardListView.swift deleted file mode 100644 index 50525fd..0000000 --- a/week2/Memorize/Memorize/View/CardListView.swift +++ /dev/null @@ -1,15 +0,0 @@ -import SwiftUI - -struct CardListView: View { - @EnvironmentObject var memorizeGameDealer: MemorizeGameDealer - - var body: some View { - ScrollView { - LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))]) { - ForEach(memorizeGameDealer.cards) { card in - CardView(card: card).aspectRatio(2 / 3, contentMode: .fit) - } - } - } - } -} diff --git a/week2/Memorize/Memorize/View/CardView.swift b/week2/Memorize/Memorize/View/CardView.swift index b334049..6498352 100644 --- a/week2/Memorize/Memorize/View/CardView.swift +++ b/week2/Memorize/Memorize/View/CardView.swift @@ -5,28 +5,23 @@ struct CardView: View { let card: MemorizeGame.Card var body: some View { - ZStack { - let shape = RoundedRectangle(cornerRadius: 20) - if card.isFaceUp { - shape.fill().foregroundColor(.white) - shape.strokeBorder(lineWidth: 5) - Text(card.content).font(.largeTitle) - } else if card.isMatched { - shape.fill(.white) - } else { - shape.fill().foregroundColor(memorizeGameDealer.themeColor) - } - } - .onTapGesture { - if !card.isMatched { - memorizeGameDealer.choose(card: card) - } - } + Text(card.content) + .font(.largeTitle) + .rotationEffect(Angle(degrees: card.isMatched ? 360 : 0)) + .animation(matchedRotateAnimation, value: card.isMatched) + .cardify(isFaceUp: card.isFaceUp, isMatched: card.isMatched) + } + + private var matchedRotateAnimation: Animation { + Animation + .linear(duration: AnimationConstants.matchedRotateDuration) + .repeatCount(AnimationConstants.matchedRotateCount, autoreverses: true) } } -struct CardView_Previews: PreviewProvider { - static var previews: some View { - CardView(card: MemorizeGame.Card(content: "aa", id: 1)) +extension CardView { + private enum AnimationConstants { + static let matchedRotateDuration = 5.0 + static let matchedRotateCount = 5 } } diff --git a/week2/Memorize/Memorize/View/Cardify.swift b/week2/Memorize/Memorize/View/Cardify.swift new file mode 100644 index 0000000..c40f059 --- /dev/null +++ b/week2/Memorize/Memorize/View/Cardify.swift @@ -0,0 +1,48 @@ +// +// Cardify.swift +// Memorize +// +// Created by ohhyeongseok on 2022/09/28. + +import SwiftUI + +struct Cardify: ViewModifier, Animatable { + @EnvironmentObject var memorizeGameDealer: MemorizeGameDealer + var isMatched: Bool + var rotation: Double + var animatableData: Double { + get { rotation } + set { rotation = newValue } + } + + init(isFaceUp: Bool, isMatched: Bool) { + rotation = isFaceUp ? 0 : 180 + self.isMatched = isMatched + } + + func body(content: Content) -> some View { + ZStack { + let shape = RoundedRectangle(cornerRadius: 20) + if rotation < 90 { + shape.fill().foregroundColor(.white) + shape.strokeBorder(lineWidth: 5) + } else if isMatched{ + shape.fill(.white) + } else { + shape.fill().foregroundColor(memorizeGameDealer.themeColor) + } + content.opacity(rotation < 90 ? 1 : 0) + } + .rotation3DEffect(Angle(degrees: rotation), axis: (0, 1, 0)) + + } +} + +extension View { + + func cardify(isFaceUp: Bool, isMatched: Bool) -> some View { + self.modifier(Cardify(isFaceUp: isFaceUp, isMatched: isMatched)) + } + +} + diff --git a/week2/Memorize/Memorize/View/MemorizeGameView.swift b/week2/Memorize/Memorize/View/MemorizeGameView.swift index 6da224e..95f2dbb 100644 --- a/week2/Memorize/Memorize/View/MemorizeGameView.swift +++ b/week2/Memorize/Memorize/View/MemorizeGameView.swift @@ -1,37 +1,155 @@ +// +// Cardify.swift +// Memorize +// +// Created by ohhyeongseok on 2022/09/28. + import SwiftUI struct MemorizeGameView: View { @EnvironmentObject var memorizeGameDealer: MemorizeGameDealer + @Namespace private var dealingNamespace + @State var dealt = Set() var body: some View { if memorizeGameDealer.isFinished { VStack { SuccessView(score: memorizeGameDealer.score) Spacer() - NewGameButton() + newGameButton } .foregroundColor(memorizeGameDealer.themeColor) } else { VStack { - ScrollView { + VStack { HStack { - ThemeNameView(currentThemeName: memorizeGameDealer.currentThemeName) - ScoreView(score: memorizeGameDealer.score) - .frame(width: 150, alignment: .trailing) + themeName + Spacer() + score } - CardListView() + cardList Spacer() } .padding() - NewGameButton() + HStack { + newGameButton + deckBody + shuffleButton + } } .foregroundColor(memorizeGameDealer.themeColor) } } + + // MARK: SubView(s) + + var newGameButton: some View { + Button(Texts.newGame) { + withAnimation(Animation.easeInOut(duration: AnimationConstants.dealDuration)) { + dealt.removeAll() + memorizeGameDealer.newGame() + } + } + .padding(DrawingConstants.padding) + } + + var score: some View { + Text("Score:\(memorizeGameDealer.score)").font(.title) + .padding(DrawingConstants.padding) + } + + var themeName: some View { + Text(memorizeGameDealer.currentThemeName).font(.largeTitle) + .padding(DrawingConstants.padding) + } + + var shuffleButton: some View { + Button(Texts.shuffle) { + withAnimation(Animation.easeInOut(duration: AnimationConstants.dealDuration)) { + memorizeGameDealer.shuffle() + } + } + .padding(DrawingConstants.padding) + } + + var cardList: some View { + ScrollView { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))]) { + ForEach(memorizeGameDealer.cards) { card in + CardView(card: card).aspectRatio(DrawingConstants.cardAspectRatio, contentMode: .fit) + .matchedGeometryEffect(id: card.id, in: dealingNamespace) + .transition(.asymmetric(insertion: .slide, removal: .slide)) + .zIndex(zIndex(of: card)) + .onTapGesture { + withAnimation { + memorizeGameDealer.choose(card: card) + } + } + } + } + } + } + + var deckBody: some View { + ZStack { + ForEach(memorizeGameDealer.cards.filter(isUndealt)) { card in + CardView(card: card) + .matchedGeometryEffect(id: card.id, in: dealingNamespace) + .transition(.asymmetric(insertion: .scale, removal: .scale)) + .zIndex(zIndex(of: card)) + } + } + .frame(width: DrawingConstants.undealtWidth, height: DrawingConstants.undealtHeight) + .onTapGesture { + for card in memorizeGameDealer.cards { + withAnimation(dealAnimation(for: card)) { + deal(card) + } + } + } + } + + // MARK: Method(s) + + private func deal(_ card: MemorizeGame.Card) { + dealt.insert(card.id) + } + + private func isUndealt(_ card: MemorizeGame.Card) -> Bool { + dealt.contains(card.id) == false + } + + private func dealAnimation(for card: MemorizeGame.Card) -> Animation { + var delay = 0.0 + if let index = memorizeGameDealer.cards.index(matching: card) { + delay = Double(index) * (AnimationConstants.totalDealDuration / Double(memorizeGameDealer.cards.count)) + } + return Animation.easeInOut(duration: AnimationConstants.dealDuration).delay(delay) + } + + private func zIndex(of card: MemorizeGame.Card) -> Double { + -Double(memorizeGameDealer.cards.index(matching: card) ?? 0) + } } -struct MemorizeGameView_Previews: PreviewProvider { - static var previews: some View { - MemorizeGameView() +extension MemorizeGameView { + + private enum Texts { + static let newGame = "New Game" + static let shuffle = "Shuffle" + } + + private enum DrawingConstants { + static let cardAspectRatio: CGFloat = 2 / 3 + static let padding: CGFloat = 30 + + static let undealtHeight: CGFloat = 90 + static let undealtWidth = undealtHeight * cardAspectRatio + } + + private enum AnimationConstants { + static let dealDuration = 0.5 + static let totalDealDuration = 2.0 } } + diff --git a/week2/Memorize/Memorize/View/NewGameButton.swift b/week2/Memorize/Memorize/View/NewGameButton.swift deleted file mode 100644 index 907762a..0000000 --- a/week2/Memorize/Memorize/View/NewGameButton.swift +++ /dev/null @@ -1,17 +0,0 @@ -import SwiftUI - -struct NewGameButton: View { - @EnvironmentObject var memorizeGameDealer: MemorizeGameDealer - - var body: some View { - Button("New Game") { - memorizeGameDealer.newGame() - } - } -} - -struct NewStartButton_Previews: PreviewProvider { - static var previews: some View { - NewGameButton() - } -} diff --git a/week2/Memorize/Memorize/View/ScoreView.swift b/week2/Memorize/Memorize/View/ScoreView.swift deleted file mode 100644 index c6e28be..0000000 --- a/week2/Memorize/Memorize/View/ScoreView.swift +++ /dev/null @@ -1,15 +0,0 @@ -import SwiftUI - -struct ScoreView: View { - let score: Int - - var body: some View { - Text("Score:\(score)").font(.title) - } -} - -struct ScoreView_Previews: PreviewProvider { - static var previews: some View { - ScoreView(score: 1) - } -} diff --git a/week2/Memorize/Memorize/View/ThemeNameView.swift b/week2/Memorize/Memorize/View/ThemeNameView.swift deleted file mode 100644 index f4e95a5..0000000 --- a/week2/Memorize/Memorize/View/ThemeNameView.swift +++ /dev/null @@ -1,15 +0,0 @@ -import SwiftUI - -struct ThemeNameView: View { - let currentThemeName: String - - var body: some View { - Text(currentThemeName).font(.largeTitle) - } -} - -struct ThemeNameView_Previews: PreviewProvider { - static var previews: some View { - ThemeNameView(currentThemeName: "") - } -} diff --git a/week2/Memorize/Memorize/ViewModel/MemorizeGameDealer.swift b/week2/Memorize/Memorize/ViewModel/MemorizeGameDealer.swift index e8a8844..13236bc 100644 --- a/week2/Memorize/Memorize/ViewModel/MemorizeGameDealer.swift +++ b/week2/Memorize/Memorize/ViewModel/MemorizeGameDealer.swift @@ -65,4 +65,8 @@ class MemorizeGameDealer: ObservableObject { func choose(card: MemorizeGame.Card) { memorizeGameManager.choose(card: card) } + + func shuffle() { + memorizeGameManager.shuffle() + } }