Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

AI 챗봇 앱 [STEP 3] Jin, Harry #48

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 95 additions & 41 deletions ChatBot/ChatBot.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion ChatBot/ChatBot/App/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }

guard let windowScene = (scene as? UIWindowScene) else { return }

window = UIWindow(windowScene: windowScene)
window?.rootViewController = ChatRoomViewController()
window?.makeKeyAndVisible()
}

func sceneDidDisconnect(_ scene: UIScene) { }
Expand Down
38 changes: 38 additions & 0 deletions ChatBot/ChatBot/Assets.xcassets/NormalPink.colorset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.541",
"green" : "0.475",
"red" : "0.918"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.541",
"green" : "0.475",
"red" : "0.918"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.714",
"green" : "0.925",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.714",
"green" : "0.925",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
9 changes: 6 additions & 3 deletions ChatBot/ChatBot/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--ChatGPT View Controller-->
<!--Chat Room View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ChatGPTViewController" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="BYZ-38-t0r" customClass="ChatRoomViewController" customModule="ChatBot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="131" y="-27"/>
</scene>
</scenes>
<resources>
Expand Down
32 changes: 0 additions & 32 deletions ChatBot/ChatBot/Controller/ChatGPTViewController.swift

This file was deleted.

11 changes: 11 additions & 0 deletions ChatBot/ChatBot/Domain/Entity/ChatRoomModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

enum Section {
case chat
}

struct ChatRoomModel: Hashable {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ChatRoomModel 은 좋지않은 이름입니다. 모든게 다 model아닌가요 ?

let id: UUID = UUID()
let content: String
let role: Role
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import Foundation
import Combine

protocol Mappable {
func mapChatGPTContent(with messages: [Message]) -> AnyPublisher<String, Error>
func mapChatGPTContent(with messages: [Message]) -> AnyPublisher<[ChatRoomModel], Error>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ final class Mapper: Mappable {
self.dataDecoder = dataDecoder
}

func mapChatGPTContent(with messages: [Message]) -> AnyPublisher<String, Error> {
func mapChatGPTContent(with messages: [Message]) -> AnyPublisher<[ChatRoomModel], Error> {
return dataDecoder.decodedChatGPTCompletionData(with: messages, type: GPTResponseDTO.self)
.receive(on: RunLoop.main)
.map { result in
return result.choices[0].message.content
print(result)
return [ChatRoomModel(content: result.choices[0].message.content, role: result.choices[0].message.role)]
}
.eraseToAnyPublisher()
}
Expand Down
2 changes: 0 additions & 2 deletions ChatBot/ChatBot/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
Expand Down
80 changes: 80 additions & 0 deletions ChatBot/ChatBot/Presentation/ChatRoom/Cell/ChatBubbleView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import UIKit

final class ChatBubbleView: UIView {

let bubbleLayer = CAShapeLayer()

let chatLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()

var outcoming = Role.user
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

적절한 이름인가요 ?


override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}

private func commonInit() -> Void {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. layout을 잡는 코드같은데 왜 이름이 init인가요 ?
  2. -> Void의 용도는 무엇인가요 ?

layer.addSublayer(bubbleLayer)
addSubview(chatLabel)

NSLayoutConstraint.activate([
chatLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 12.0),
chatLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12.0),
chatLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 12.0),
chatLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -12.0),
])
}

override func layoutSubviews() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

무엇을 해주는 메서드인가요 ?

super.layoutSubviews()

let width = bounds.size.width
let height = bounds.size.height

let bezierPath = UIBezierPath()

if outcoming == .user {
bezierPath.move(to: CGPoint(x: width - 22, y: height))
bezierPath.addLine(to: CGPoint(x: 17, y: height))
bezierPath.addCurve(to: CGPoint(x: 0, y: height - 17), controlPoint1: CGPoint(x: 7.61, y: height), controlPoint2: CGPoint(x: 0, y: height - 7.61))
bezierPath.addLine(to: CGPoint(x: 0, y: 17))
bezierPath.addCurve(to: CGPoint(x: 17, y: 0), controlPoint1: CGPoint(x: 0, y: 7.61), controlPoint2: CGPoint(x: 7.61, y: 0))
bezierPath.addLine(to: CGPoint(x: width - 21, y: 0))
bezierPath.addCurve(to: CGPoint(x: width - 4, y: 17), controlPoint1: CGPoint(x: width - 11.61, y: 0), controlPoint2: CGPoint(x: width - 4, y: 7.61))
bezierPath.addLine(to: CGPoint(x: width - 4, y: height - 11))
bezierPath.addCurve(to: CGPoint(x: width, y: height), controlPoint1: CGPoint(x: width - 4, y: height - 1), controlPoint2: CGPoint(x: width, y: height))
bezierPath.addLine(to: CGPoint(x: width + 0.05, y: height - 0.01))
bezierPath.addCurve(to: CGPoint(x: width - 11.04, y: height - 4.04), controlPoint1: CGPoint(x: width - 4.07, y: height + 0.43), controlPoint2: CGPoint(x: width - 8.16, y: height - 1.06))
bezierPath.addCurve(to: CGPoint(x: width - 22, y: height), controlPoint1: CGPoint(x: width - 16, y: height), controlPoint2: CGPoint(x: width - 19, y: height))
bezierPath.close()
} else {
bezierPath.move(to: CGPoint(x: 22, y: height))
bezierPath.addLine(to: CGPoint(x: width - 17, y: height))
bezierPath.addCurve(to: CGPoint(x: width, y: height - 17), controlPoint1: CGPoint(x: width - 7.61, y: height), controlPoint2: CGPoint(x: width, y: height - 7.61))
bezierPath.addLine(to: CGPoint(x: width, y: 17))
bezierPath.addCurve(to: CGPoint(x: width - 17, y: 0), controlPoint1: CGPoint(x: width, y: 7.61), controlPoint2: CGPoint(x: width - 7.61, y: 0))
bezierPath.addLine(to: CGPoint(x: 21, y: 0))
bezierPath.addCurve(to: CGPoint(x: 4, y: 17), controlPoint1: CGPoint(x: 11.61, y: 0), controlPoint2: CGPoint(x: 4, y: 7.61))
bezierPath.addLine(to: CGPoint(x: 4, y: height - 11))
bezierPath.addCurve(to: CGPoint(x: 0, y: height), controlPoint1: CGPoint(x: 4, y: height - 1), controlPoint2: CGPoint(x: 0, y: height))
bezierPath.addLine(to: CGPoint(x: -0.05, y: height - 0.01))
bezierPath.addCurve(to: CGPoint(x: 11.04, y: height - 4.04), controlPoint1: CGPoint(x: 4.07, y: height + 0.43), controlPoint2: CGPoint(x: 8.16, y: height - 1.06))
bezierPath.addCurve(to: CGPoint(x: 22, y: height), controlPoint1: CGPoint(x: 16, y: height), controlPoint2: CGPoint(x: 19, y: height))
bezierPath.close()
}

bubbleLayer.fillColor = (outcoming == .user) ? UIColor(named: "SunshineYellow")?.cgColor : UIColor(white: 0.90, alpha: 1.0).cgColor

bubbleLayer.path = bezierPath.cgPath
}
}
51 changes: 51 additions & 0 deletions ChatBot/ChatBot/Presentation/ChatRoom/Cell/ChatRoomCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import UIKit

final class ChatRoomCell: UICollectionViewListCell {
static let identifier = "ChatRoomCell"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른방법은 없을까요?


var viewModel: ChatRoomCellViewModel! {
didSet { setupViewModel() }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 곳의 컨벤션과는 다른 모습이네요.

}

var bubbleView = ChatBubbleView()
var leadingOrTrailingConstraint = NSLayoutConstraint()

override init(frame: CGRect) {
super.init(frame: .zero)
commonInit()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func prepareForReuse() {
super.prepareForReuse()
commonInit()
}

private func commonInit() {
bubbleView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(bubbleView)

NSLayoutConstraint.activate([
bubbleView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
bubbleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12.0),
bubbleView.widthAnchor.constraint(lessThanOrEqualTo: contentView.widthAnchor, multiplier: 0.66)
])
}

private func setupViewModel() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setupViewModel
뷰모델을 셋업하는게 맞나요 ?


bubbleView.chatLabel.text = viewModel.contentMessage
bubbleView.outcoming = viewModel.contentRole

leadingOrTrailingConstraint.isActive = false
if viewModel.contentRole == .user {
leadingOrTrailingConstraint = bubbleView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12.0)
} else {
leadingOrTrailingConstraint = bubbleView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12.0)
}
leadingOrTrailingConstraint.isActive = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

layoutIfNeeded 는 언제 사용될까요 ? 한번 공부해보시면 좋을 것 같습니다.

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation
import Combine

final class ChatRoomCellViewModel {
@Published var contentMessage: String = ""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@published는 왜 사용하신걸까요 ?
용도는 무엇이고, 어떤 부가 동작이 있어야할까요 ?

@Published var contentRole: Role = .user

private let message: ChatRoomModel

init(message: ChatRoomModel) {
self.message = message

setupBinding()
}

private func setupBinding() {
contentMessage = message.content
contentRole = message.role
}
}
Loading