Skip to content
Merged
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
17 changes: 17 additions & 0 deletions ONMIR.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
46EE698D2DC10341001736D6 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 46EE698C2DC10341001736D6 /* SnapKit */; };
46EE6A232DC196D9001736D6 /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 46EE6A222DC196D9001736D6 /* Nuke */; };
D0FD53152DB4DDC400F2593B /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = D0FD53142DB4DDC400F2593B /* .gitignore */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -72,6 +73,7 @@
buildActionMask = 2147483647;
files = (
46EE698D2DC10341001736D6 /* SnapKit in Frameworks */,
46EE6A232DC196D9001736D6 /* Nuke in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -134,6 +136,7 @@
name = ONMIR;
packageProductDependencies = (
46EE698C2DC10341001736D6 /* SnapKit */,
46EE6A222DC196D9001736D6 /* Nuke */,
);
productName = ONMIR;
productReference = D011D3032DB4DAAB00412C5C /* ONMIR.app */;
Expand Down Expand Up @@ -219,6 +222,7 @@
minimizedProjectReferenceProxies = 1;
packageReferences = (
46EE698B2DC10341001736D6 /* XCRemoteSwiftPackageReference "SnapKit" */,
46EE6A212DC196D9001736D6 /* XCRemoteSwiftPackageReference "Nuke" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = D011D3042DB4DAAB00412C5C /* Products */;
Expand Down Expand Up @@ -586,6 +590,14 @@
minimumVersion = 5.7.1;
};
};
46EE6A212DC196D9001736D6 /* XCRemoteSwiftPackageReference "Nuke" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kean/Nuke.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 12.8.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand All @@ -594,6 +606,11 @@
package = 46EE698B2DC10341001736D6 /* XCRemoteSwiftPackageReference "SnapKit" */;
productName = SnapKit;
};
46EE6A222DC196D9001736D6 /* Nuke */ = {
isa = XCSwiftPackageProductDependency;
package = 46EE6A212DC196D9001736D6 /* XCRemoteSwiftPackageReference "Nuke" */;
productName = Nuke;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = D011D2FB2DB4DAAB00412C5C /* Project object */;
Expand Down

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

102 changes: 102 additions & 0 deletions ONMIR.xcodeproj/xcshareddata/xcschemes/ONMIR.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D011D3022DB4DAAB00412C5C"
BuildableName = "ONMIR.app"
BlueprintName = "ONMIR"
ReferencedContainer = "container:ONMIR.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D011D3182DB4DAAD00412C5C"
BuildableName = "ONMIRTests.xctest"
BlueprintName = "ONMIRTests"
ReferencedContainer = "container:ONMIR.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D011D3222DB4DAAD00412C5C"
BuildableName = "ONMIRUITests.xctest"
BlueprintName = "ONMIRUITests"
ReferencedContainer = "container:ONMIR.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D011D3022DB4DAAB00412C5C"
BuildableName = "ONMIR.app"
BlueprintName = "ONMIR"
ReferencedContainer = "container:ONMIR.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D011D3022DB4DAAB00412C5C"
BuildableName = "ONMIR.app"
BlueprintName = "ONMIR"
ReferencedContainer = "container:ONMIR.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
44 changes: 44 additions & 0 deletions ONMIR/Feature/NewBook/BookSearchRepresentation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Foundation

public struct BookSearchRepresentation: Sendable, Hashable {
public let id: String
public let title: String
public let subtitle: String?
public let authors: [String]?
public let publisher: String?
public let publishedDate: String?
public let description: String?
public let thumbnailURL: URL?

init(from bookItem: GoogleBooksClient.BookSearchResponse.BookItem) {
self.id = bookItem.id
self.title = bookItem.volumeInfo.title
self.subtitle = bookItem.volumeInfo.subtitle
self.authors = bookItem.volumeInfo.authors
self.publisher = bookItem.volumeInfo.publisher
self.publishedDate = bookItem.volumeInfo.publishedDate
self.description = bookItem.volumeInfo.description

if let thumbnailURLString = bookItem.volumeInfo.imageLinks?.thumbnail {
let secureURL = thumbnailURLString.replacingOccurrences(
of: "http://",
with: "https://"
)
self.thumbnailURL = URL(string: secureURL)
} else {
self.thumbnailURL = nil
}
}

public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}

public static func == (lhs: BookSearchRepresentation, rhs: BookSearchRepresentation) -> Bool {
return lhs.id == rhs.id && lhs.title == rhs.title
&& lhs.subtitle == rhs.subtitle && lhs.authors == rhs.authors
&& lhs.publisher == rhs.publisher
&& lhs.publishedDate == rhs.publishedDate
&& lhs.description == rhs.description
}
}
103 changes: 103 additions & 0 deletions ONMIR/Feature/NewBook/Components/SearchResultBookCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Nuke
import SnapKit
import UIKit

extension NewBookViewController {
final class BookCell: UICollectionViewCell {
private let coverImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.layer.cornerRadius = 5
imageView.clipsToBounds = true
return imageView
}()

private let titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 13, weight: .bold)
label.numberOfLines = 2
label.minimumScaleFactor = 0.75
label.adjustsFontSizeToFitWidth = true
label.textColor = .label
return label
}()

private let authorLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12)
label.textColor = .tertiaryLabel
label.numberOfLines = 2
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.75
return label
}()

private var imageDownloadTask: Task<Void, Never>?

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

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

override func prepareForReuse() {
super.prepareForReuse()
imageDownloadTask?.cancel()
imageDownloadTask = nil
coverImageView.image = nil
titleLabel.text = nil
authorLabel.text = nil
}

private func setupUI() {
self.backgroundColor = .clear
contentView.backgroundColor = .clear

contentView.addSubview(coverImageView)
contentView.addSubview(titleLabel)
contentView.addSubview(authorLabel)

coverImageView.snp.makeConstraints { make in
make.leading.equalToSuperview().inset(32)
make.verticalEdges.equalToSuperview().inset(8)
make.height.equalTo(100)
make.width.equalTo(coverImageView.snp.height).multipliedBy(0.75)
}

titleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().inset(8)
make.leading.equalTo(coverImageView.snp.trailing).offset(10)
make.trailing.equalToSuperview().inset(12)
}

authorLabel.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(0)
make.leading.equalTo(coverImageView.snp.trailing).offset(10)
make.trailing.equalToSuperview().inset(32)
make.bottom.lessThanOrEqualToSuperview()
}
}

func configure(with book: BookSearchRepresentation) {
titleLabel.text = book.title
authorLabel.text = book.authors?.joined(separator: ", ")

if let thumbnailURL = book.thumbnailURL {
self.imageDownloadTask = Task {
do {
let image = try await ImagePipeline.shared.image(for: thumbnailURL)

await MainActor.run {
self.coverImageView.image = image
}
} catch {
Logger.error(error)
}
}
}
}
}
}
Loading