Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions PhotoTag/PhotoTag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
6AD3ED6725519FE900F8EF61 /* UseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD3ED6625519FE900F8EF61 /* UseCase.swift */; };
6AD3ED6C2551A09900F8EF61 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD3ED6B2551A09900F8EF61 /* HTTPMethod.swift */; };
6AD3ED712551A0B400F8EF61 /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD3ED702551A0B400F8EF61 /* NetworkError.swift */; };
6AD8DAB225BDB63D003205C0 /* TagSuggestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD8DAB125BDB63D003205C0 /* TagSuggestion.swift */; };
6AE1245E256830A300291388 /* UISwipeGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE1245D256830A300291388 /* UISwipeGestureRecognizer.swift */; };
6AEB5545259CA8220044699D /* AlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AEB5544259CA8220044699D /* AlertView.swift */; };
6AEB554A259CB2640044699D /* PhotoNoteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AEB5549259CB2640044699D /* PhotoNoteViewModel.swift */; };
Expand Down Expand Up @@ -252,6 +253,7 @@
6AD3ED6625519FE900F8EF61 /* UseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCase.swift; sourceTree = "<group>"; };
6AD3ED6B2551A09900F8EF61 /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = "<group>"; };
6AD3ED702551A0B400F8EF61 /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = "<group>"; };
6AD8DAB125BDB63D003205C0 /* TagSuggestion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TagSuggestion.swift; sourceTree = "<group>"; };
6AE1245D256830A300291388 /* UISwipeGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISwipeGestureRecognizer.swift; sourceTree = "<group>"; };
6AEB5544259CA8220044699D /* AlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertView.swift; sourceTree = "<group>"; };
6AEB5549259CB2640044699D /* PhotoNoteViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoNoteViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -314,6 +316,7 @@
children = (
6A0A216725AAC158009D56DE /* NoteNetworkingManager.swift */,
6AC3ED1D25B821EA0032FF29 /* PhotoNote.swift */,
6AD8DAB125BDB63D003205C0 /* TagSuggestion.swift */,
);
path = Model;
sourceTree = "<group>";
Expand Down Expand Up @@ -918,6 +921,7 @@
6AAE725E2555988B00CF7F9F /* TagCategoryView.swift in Sources */,
6A57CAEF255ED33600E42348 /* UsesAutoLayout.swift in Sources */,
6A63BA2C255C2F210096A0F7 /* TagManagementTableViewDelegate.swift in Sources */,
6AD8DAB225BDB63D003205C0 /* TagSuggestion.swift in Sources */,
6A5544C925599CDF003F3864 /* UIColor.swift in Sources */,
6A5544E42559B3CA003F3864 /* ScrollableContentViewWithHeader.swift in Sources */,
6AEC28AE259223CC00D0634A /* URLComponents.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ struct AppViewControllersFactory {
return SelectPhotoViewController(coordinator: coordinator)
}

func writePhotoNoteViewController(coordinator: PhotoNoteCoordinator, with text: String) -> UIViewController {
let noteViewController = NoteViewController(coordinator: coordinator, with: text)
func writePhotoNoteViewController(coordinator: PhotoNoteCoordinator, with text: String, and photos: [NoteImage]) -> UIViewController {
let noteViewController = NoteViewController(coordinator: coordinator, with: text, and: photos)
return noteViewController
}

Expand Down
4 changes: 2 additions & 2 deletions PhotoTag/PhotoTag/Coordinator/PhotoNoteCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ final class PhotoNoteCoordinator: ChildCoordinator {
navigationController.pushViewController(selectPhotoViewController, animated: true)
}

func navigateToWritePhotoNote(with text: String) {
let writePhotoNoteViewController = appViewControllerFactory.writePhotoNoteViewController(coordinator: self, with: text)
func navigateToWritePhotoNote(with text: String, photos: [NoteImage]) {
let writePhotoNoteViewController = appViewControllerFactory.writePhotoNoteViewController(coordinator: self, with: text, and: photos)
navigationController.pushViewController(writePhotoNoteViewController, animated: true)
}

Expand Down
2 changes: 2 additions & 0 deletions PhotoTag/PhotoTag/Network/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct Endpoint: RequestProviding {
case createNote
case fetchPhotoNoteList
case fetchPhotoNote
case tagSuggestion

var description: String {
switch self {
Expand All @@ -34,6 +35,7 @@ struct Endpoint: RequestProviding {
case .createNote: return "/notes"
case .fetchPhotoNoteList: return "/tags"
case .fetchPhotoNote: return "/notes/"
case .tagSuggestion: return "/suggestion"
}
}
}
Expand Down
13 changes: 12 additions & 1 deletion PhotoTag/PhotoTag/Network/UseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct UseCase {
.eraseToAnyPublisher()
}

// send file
// send file (send file and return HTTPURLResponse)
func request(_ network: NetworkConnectable = NetworkManager.shared,
request: URLRequest) -> AnyPublisher<HTTPURLResponse, NetworkError> {
return network
Expand All @@ -75,6 +75,17 @@ struct UseCase {
.eraseToAnyPublisher()
}

// tag suggestion (send file and return two string arrays)
func request(_ network: NetworkConnectable = NetworkManager.shared,
urlRequest: URLRequest) -> AnyPublisher<TagSuggestion, Error> {
return network
.session
.dataTaskPublisher(for: urlRequest)
.map { $0.data }
.decode(type: TagSuggestion.self, decoder: decoder)
.eraseToAnyPublisher()
}

func request<D: Decodable>(_ network: NetworkConnectable = NetworkManager.shared,
type: D.Type, endpoint: RequestProviding,
method: HTTPMethod) -> AnyPublisher<D, Error> {
Expand Down
30 changes: 30 additions & 0 deletions PhotoTag/PhotoTag/Photo Note/Model/NoteNetworkingManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,36 @@ final class NoteNetworkingManager {
}))
}

// MARK: - fetch tag recommendation
func fetchTagRecommendation(images: [NoteImage],
completion: @escaping(TagSuggestion) -> Void) {

let boundary = generateBoundaryString()
guard let endpoint = Endpoint(path: .tagSuggestion).url else { return }
var request = URLRequest(urlWithToken: endpoint, method: .get)
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

let httpBody = NSMutableData()

// photo Image Data
for image in images {
guard let imageData = image.jpegData(compressionQuality: 0.1) else { return }
httpBody.append(convertFileData(fieldName: "photo", fileName: "\(Date().millisecondsSince1970)_photo.jpg", mimeType: "multipart/form-data", fileData: imageData, using: boundary))
}
httpBody.appendString("--\(boundary)--") // add final boundary with the two trailing dashes
request.httpBody = httpBody as Data

// request
UseCase.shared
.request(urlRequest: request)
.receive(subscriber: Subscribers.Sink(receiveCompletion: { [weak self] in
guard case let .failure(error) = $0 else { return }
debugPrint(error.localizedDescription)
}, receiveValue: { data in
completion(data)
}))
}

}

extension NoteNetworkingManager {
Expand Down
12 changes: 12 additions & 0 deletions PhotoTag/PhotoTag/Photo Note/Model/TagSuggestion.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// TagSuggestion.swift
// Pods
//
// Created by Keunna Lee on 2021/01/24.
//

import Foundation

struct TagSuggestion: Codable {
let tagsEn, tagsKr: [String]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ import UIKit
final class NoteViewController: UIViewController {

@IBOutlet weak var noteTextView: UITextView!
private let noteNetworkingManager = NoteNetworkingManager()
weak var coordinator: PhotoNoteCoordinator?
static let contentTextKey = "contentText"
private var contentText: NoteText = ""
private var existingText = ""
private var photos: [NoteImage]

init(coordinator: PhotoNoteCoordinator, with text: NoteText) {
init(coordinator: PhotoNoteCoordinator, with text: NoteText, and photos: [NoteImage]) {
self.coordinator = coordinator
self.existingText = text
self.photos = photos
super.init(nibName: nil, bundle: nil)
}

Expand All @@ -26,11 +29,47 @@ final class NoteViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupView()
showTagRecommendation()
}


@objc func tagTapped(_ sender: UIButton) {
guard let buttonTitle = sender.title(for: .normal) else { return }
DispatchQueue.main.async {
self.contentText += buttonTitle
self.noteTextView.text += buttonTitle
}
}

private func showTagRecommendation() {

fetchTagRecommendation { tagSuggestions in
for tag in tagSuggestions {
let tagButton = self.tagButton(title: "#\(tag)")
self.tagStackView.addArrangedSubview(tagButton)
}
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
scrollView.contentSize = CGSize(width: self.tagStackView.frame.width, height: self.tagStackView.frame.height)
scrollView.addSubview(self.tagStackView)
scrollView.sizeToFit()
self.noteTextView.inputAccessoryView = scrollView
}

}

private func fetchTagRecommendation( completionHandler: @escaping ([TagName]) -> Void) {
noteNetworkingManager.fetchTagRecommendation(images: photos) { tagSuggestion in
var tagSuggestions: [TagName] = []
tagSuggestions.append(contentsOf: tagSuggestion.tagsEn)
tagSuggestions.append(contentsOf: tagSuggestion.tagsKr)
completionHandler(tagSuggestions)
}
}

private func setupView() {
noteTextView.text = contentText
noteTextView.text = existingText
noteTextView.becomeFirstResponder()
noteTextView.keyboardAppearance = .dark
setupNoteTextView()
}

Expand Down Expand Up @@ -74,4 +113,37 @@ extension NoteViewController: UITextViewDelegate {
let changedText = currentText.replacingCharacters(in: stringRange, with: text)
return changedText.count <= maximumCount
}

func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return true
}
}

extension NoteViewController {
private var tagStackView: UIStackView {
let stackView = UIStackView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
stackView.axis = .horizontal
stackView.sizeToFit()
stackView.alignment = .fill
stackView.distribution = .equalSpacing
stackView.spacing = 5
stackView.backgroundColor = .lightGray
stackView.contentMode = .scaleToFill
stackView.clipsToBounds = false
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}

private func tagButton(title: String) -> UIButton {
let button = UIButton()
button.backgroundColor = .lightGray
button.backgroundColor = .white
button.setTitleColor(.black, for: .normal)
button.setTitle("#\(title)", for: .normal)
button.frame = CGRect(x: 0, y: 0, width: button.intrinsicContentSize.width + 18, height: 50)
button.addTarget(self, action: #selector(self.tagTapped), for: .touchUpInside)
self.tagStackView.addArrangedSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class PhotoNoteViewController: UIViewController {
@IBOutlet weak var imageHorizontalScrollView: UIScrollView!
private var noteState: NoteState
private var noteContentText: NoteText = ""
private var tagNames: [TagName] = []
private let noteNetworkManager = NoteNetworkingManager()

init(coordinator: PhotoNoteCoordinator,
Expand Down Expand Up @@ -158,7 +159,7 @@ class PhotoNoteViewController: UIViewController {
}

@objc private func presentNoteWritingScene() {
coordinator?.navigateToWritePhotoNote(with: noteContentText)
coordinator?.navigateToWritePhotoNote(with: noteContentText, photos: viewModel.selectedImages.value)
}

@objc func saveNoteText(_ notification: Notification) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
Expand Down Expand Up @@ -117,14 +117,14 @@
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Wxs-Mv-r2c">
<rect key="frame" x="10" y="0.0" width="394" height="235"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="M4O-iK-5Lv" userLabel="date label">
<rect key="frame" x="0.0" y="0.0" width="394" height="20.5"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="M4O-iK-5Lv" userLabel="date label">
<rect key="frame" x="0.0" y="0.0" width="394" height="50"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="ddu-1g-Zcf" userLabel="note text view">
<rect key="frame" x="0.0" y="20.5" width="394" height="214.5"/>
<rect key="frame" x="0.0" y="50" width="394" height="185"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ class PhotoNoteListViewModel {
noteNetworkingManager.fetchNoteList(tagIds: selectedTags) { photoList in
guard let allPhotoList = photoList else { return }
self.photoNoteList.value = allPhotoList
self.firstSelectedTagText.value = self.photoNoteList.value[0].tags[0]
self.secondSelectedTagText.value = self.photoNoteList.value[1].tags[0]
self.thirdSelectedTagText.value = self.photoNoteList.value[2].tags[0]
completionHandler(allPhotoList)
}
}
Expand Down
2 changes: 1 addition & 1 deletion PhotoTag/PhotoTag/Tag/Model/TagNetworkingManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class TagNetworkingManager {
}))
}

func updateHashtagActivatedState(of tagId: Int, with data: HastagState) {
func updateHashtagActivatedState(of tagId: TagID, with data: HastagState) {
UseCase.shared.request(data: data,
endpoint: Endpoint.hashtagPatch(path: .patchHashtags, tagId: tagId),
method: .patch)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ final class TagCategoryViewController: UIViewController {
fetchTags()
configure()
viewAppeared = true
activateButton()
}

// MARK: - Functions
Expand Down
1 change: 1 addition & 0 deletions PhotoTag/PhotoTag/Utility/Typealias.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Foundation
import UIKit.UIImage

// tag
typealias TagName = String
typealias TagID = Int
typealias TagImage = UIImage

Expand Down