Here's a starter Swift solution framework for the Library Management System based on the requirements. This code provides a command-line interface for interacting with the library system:
import Foundation
// MARK: - Models
struct Book: Codable {
let isbn: String
var title: String
var author: String
var publicationYear: Int
var genre: String
var isAvailable: Bool = true
var dueDate: Date?
mutating func checkOut() {
isAvailable = false
dueDate = Calendar.current.date(byAdding: .day, value: 14, to: Date())
}
mutating func checkIn() {
isAvailable = true
dueDate = nil
}
}
struct Patron: Codable {
let id: String
var name: String
var contactInfo: String
var checkedOutBooks: [String] // ISBNs of checked out books
var fines: Double = 0
mutating func checkOutBook(_ isbn: String) {
checkedOutBooks.append(isbn)
}
mutating func returnBook(_ isbn: String) {
checkedOutBooks.removeAll { $0 == isbn }
}
mutating func addFine(_ amount: Double) {
fines += amount
}
mutating func payFine(_ amount: Double) {
fines = max(0, fines - amount)
}
}
class Library: Codable {
var books: [Book]
var patrons: [Patron]
init() {
books = []
patrons = []
}
func addBook(_ book: Book) {
books.append(book)
}
func removeBook(isbn: String) {
books.removeAll { $0.isbn == isbn }
}
func findBook(isbn: String) -> Book? {
return books.first { $0.isbn == isbn }
}
func updateBook(isbn: String, updatedBook: Book) {
if let index = books.firstIndex(where: { $0.isbn == isbn }) {
books[index] = updatedBook
}
}
func searchBooks(query: String) -> [Book] {
return books.filter { $0.title.lowercased().contains(query.lowercased()) ||
$0.author.lowercased().contains(query.lowercased()) ||
$0.isbn.lowercased().contains(query.lowercased()) }
}
func availableBooks() -> [Book] {
return books.filter { $0.isAvailable }
}
func addPatron(_ patron: Patron) {
patrons.append(patron)
}
func removePatron(id: String) {
patrons.removeAll { $0.id == id }
}
func findPatron(id: String) -> Patron? {
return patrons.first { $0.id == id }
}
func checkOutBook(isbn: String, patronId: String) throws {
guard var book = findBook(isbn: isbn), book.isAvailable else {
throw LibraryError.bookUnavailable
}
guard var patron = findPatron(id: patronId) else {
throw LibraryError.patronNotFound
}
guard patron.checkedOutBooks.count < 5 else {
throw LibraryError.checkoutLimitReached
}
book.checkOut()
patron.checkOutBook(isbn)
updateBook(isbn: isbn, updatedBook: book)
updatePatron(id: patronId, updatedPatron: patron)
}
func returnBook(isbn: String, patronId: String) throws {
guard var book = findBook(isbn: isbn), !book.isAvailable else {
throw LibraryError.bookNotCheckedOut
}
guard var patron = findPatron(id: patronId) else {
throw LibraryError.patronNotFound
}
book.checkIn()
patron.returnBook(isbn)
if let dueDate = book.dueDate, dueDate < Date() {
let daysLate = Calendar.current.dateComponents([.day], from: dueDate, to: Date()).day ?? 0
let fine = Double(daysLate) * 0.25
patron.addFine(fine)
}
updateBook(isbn: isbn, updatedBook: book)
updatePatron(id: patronId, updatedPatron: patron)
}
func updatePatron(id: String, updatedPatron: Patron) {
if let index = patrons.firstIndex(where: { $0.id == id }) {
patrons[index] = updatedPatron
}
}
func overdueBooks() -> [Book] {
let today = Date()
return books.filter { !$0.isAvailable && $0.dueDate ?? today < today }
}
func popularBooks(limit: Int = 10) -> [(Book, Int)] {
var bookCounts: [String: Int] = [:]
for patron in patrons {
for isbn in patron.checkedOutBooks {
bookCounts[isbn, default: 0] += 1
}
}
let sortedBooks = bookCounts.sorted { $0.value > $1.value }
return sortedBooks.prefix(limit).compactMap { isbn, count in
guard let book = findBook(isbn: isbn) else { return nil }
return (book, count)
}
}
func patronsWithFines() -> [Patron] {
return patrons.filter { $0.fines > 0 }
}
}
// MARK: - Error Handling
enum LibraryError: Error {
case bookUnavailable
case patronNotFound
case checkoutLimitReached
case bookNotCheckedOut
}
// MARK: - Data Persistence
func saveLibrary(_ library: Library, to filePath: String) {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601