├── Xenon Reader
├── Assets.xcassets
│ ├── Contents.json
│ ├── DefaultCover.imageset
│ │ ├── Placeholder.png
│ │ ├── Placeholder@2x.png
│ │ ├── Placeholder@3x.png
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── AccentColor.colorset
│ │ └── Contents.json
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── Xenon_Reader.entitlements
├── Helpers
│ ├── Realm
│ │ ├── RealmHelpers.swift
│ │ └── RealmObjects.swift
│ ├── View Helpers
│ │ ├── TOCFocusModifier.swift
│ │ ├── LibraryBookModifier.swift
│ │ ├── ToolbarModifier.swift
│ │ └── ViewHelpers.swift
│ ├── Helpers.swift
│ ├── LibraryHelpers.swift
│ ├── EPUBHelpers.swift
│ ├── ReadableHelpers.swift
│ └── FilesystemHelpers.swift
├── Views
│ ├── Testing
│ │ ├── TReaderView.swift
│ │ ├── FileListView.swift
│ │ └── VariablesView.swift
│ ├── Settings
│ │ ├── ReaderSettingsView.swift
│ │ ├── SettingsView.swift
│ │ ├── DebugSettingsView.swift
│ │ └── LibrarySettingsView.swift
│ ├── Library
│ │ ├── LibraryParentView.swift
│ │ ├── AuthorsView.swift
│ │ ├── PublishersView.swift
│ │ ├── LibraryView.swift
│ │ ├── Library View Types
│ │ │ ├── ListViewBook.swift
│ │ │ ├── ListView.swift
│ │ │ ├── GridView.swift
│ │ │ └── GridViewBook.swift
│ │ └── Sub-views
│ │ │ ├── SidebarView.swift
│ │ │ └── ReadableInformation.swift
│ ├── Sub-views
│ │ ├── DetailListRow.swift
│ │ ├── FileWebView.swift
│ │ └── NewCategorySheet.swift
│ └── Reader
│ │ ├── ReaderParentView.swift
│ │ ├── ReaderRenderView.swift
│ │ ├── Sub-views
│ │ ├── ReaderSidebarMetadata.swift
│ │ └── ReaderSidebarTOC.swift
│ │ └── ReaderSidebarView.swift
├── Extensions
│ ├── EPUBDocumentExtensions.swift
│ └── StringExtensions.swift
├── ContentView.swift
├── Info.plist
└── Xenon_ReaderApp.swift
├── Xenon Reader.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── hkamran.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── README.md
└── Plans.md
/Xenon Reader/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Xenon Reader/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Xenon Reader/Assets.xcassets/DefaultCover.imageset/Placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hkamran80/xenon-reader/HEAD/Xenon Reader/Assets.xcassets/DefaultCover.imageset/Placeholder.png
--------------------------------------------------------------------------------
/Xenon Reader/Assets.xcassets/DefaultCover.imageset/Placeholder@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hkamran80/xenon-reader/HEAD/Xenon Reader/Assets.xcassets/DefaultCover.imageset/Placeholder@2x.png
--------------------------------------------------------------------------------
/Xenon Reader/Assets.xcassets/DefaultCover.imageset/Placeholder@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hkamran80/xenon-reader/HEAD/Xenon Reader/Assets.xcassets/DefaultCover.imageset/Placeholder@3x.png
--------------------------------------------------------------------------------
/Xenon Reader.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Xenon Reader/Xenon_Reader.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Xenon Reader
2 | ### A new EPUB reader for macOS, built with the latest technologies
3 |
4 | 
5 | 
6 |
--------------------------------------------------------------------------------
/Xenon Reader.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/Realm/RealmHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RealmHelpers.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/19/21.
6 | //
7 |
8 | import Foundation
9 | //import RealmSwift
10 | //
11 | //func initializeRealm() -> Realm? {
12 | // do {
13 | // return try Realm()
14 | // } catch {
15 | // print("Error initalizing new Realm: \(error.localizedDescription)")
16 | // return nil
17 | // }
18 | //}
19 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Testing/TReaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TReaderView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/22/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct TReaderView: View {
11 | var body: some View {
12 | Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
13 | }
14 | }
15 |
16 | struct TReaderView_Previews: PreviewProvider {
17 | static var previews: some View {
18 | TReaderView()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Xenon Reader/Assets.xcassets/DefaultCover.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Placeholder.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "Placeholder@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "filename" : "Placeholder@3x.png",
15 | "idiom" : "universal",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "author" : "xcode",
21 | "version" : 1
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Xenon Reader/Extensions/EPUBDocumentExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EPUBDocumentExtensions.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import Foundation
9 | import EPUBKit
10 |
11 | extension EPUBDocument: Hashable, Equatable {
12 | public static func == (lhs: EPUBDocument, rhs: EPUBDocument) -> Bool {
13 | return lhs.title == rhs.title && lhs.author == rhs.author && lhs.publisher == rhs.publisher
14 | }
15 |
16 | public func hash(into hasher: inout Hasher) {
17 | hasher.combine(title)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/View Helpers/TOCFocusModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TOCFocusModifier.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 3/12/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct TOCFocusModifier: ViewModifier {
11 | let buttonText: String
12 | let activeChapter: String?
13 |
14 | func body(content: Content) -> some View {
15 | Group {
16 | if activeChapter != nil && activeChapter == buttonText {
17 | content
18 | .background(Color.accentColor)
19 | } else {
20 | content
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Settings/ReaderSettingsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReaderSettingsView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 3/9/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ReaderSettingsView: View {
11 | @AppStorage("fontSize") var fontSize = 12.0
12 |
13 | var body: some View {
14 | Slider(value: $fontSize, in: 8 ... 64) {
15 | Text("Font Size (\(fontSize, specifier: "%.0f") pts)")
16 | }
17 | }
18 | }
19 |
20 | #if DEBUG
21 | struct ReaderSettingsView_Previews: PreviewProvider {
22 | static var previews: some View {
23 | ReaderSettingsView()
24 | }
25 | }
26 | #endif
27 |
--------------------------------------------------------------------------------
/Xenon Reader/Extensions/StringExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringExtensions.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 3/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | extension String {
11 | func stripOutHtml() -> String? {
12 | do {
13 | guard let data = self.data(using: .unicode) else {
14 | return nil
15 | }
16 | let attributed = try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
17 | return attributed.string
18 | } catch {
19 | return nil
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/LibraryParentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LibraryParentView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/24/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LibraryParentView: View {
11 | @EnvironmentObject var xrShared: XRShared
12 |
13 | var body: some View {
14 | SidebarView()
15 | .environmentObject(self.xrShared)
16 |
17 | LibraryView(epubs: xrShared.epubs)
18 | .environmentObject(self.xrShared)
19 | }
20 | }
21 |
22 | #if DEBUG
23 | struct LibraryParentView_Previews: PreviewProvider {
24 | static var previews: some View {
25 | LibraryParentView()
26 | .environmentObject(XRShared())
27 | }
28 | }
29 | #endif
30 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Sub-views/DetailListRow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailListRow.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 3/11/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct DetailListRow: View {
11 | let name: String
12 | let detail: String
13 |
14 | var body: some View {
15 | VStack(alignment: .leading) {
16 | Text(name)
17 | .font(.subheadline)
18 |
19 | Text(detail)
20 | .font(.headline)
21 | .bold()
22 | }
23 | }
24 | }
25 |
26 | #if DEBUG
27 | struct DetailListRow_Previews: PreviewProvider {
28 | static var previews: some View {
29 | DetailListRow(name: "Example", detail: "Example Detail")
30 | }
31 | }
32 | #endif
33 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/Realm/RealmObjects.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RealmObjects.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/17/21.
6 | //
7 |
8 | import Foundation
9 | //import RealmSwift
10 | //
11 | //class Category: Object {
12 | // @objc dynamic var id: String = UUID().uuidString
13 | // @objc dynamic var name: String = ""
14 | // @objc dynamic var imageName: String = "tray.circle"
15 | // @objc dynamic var creationDate: Date = Date()
16 | // var readables: [String] = []
17 | //
18 | // override static func primaryKey() -> String? {
19 | // "id"
20 | // }
21 | //}
22 |
23 | struct ReadableCategory {
24 | var id: String
25 | var name: String
26 | var imageName: String
27 | var creationDate: Date
28 | var readables: [String] = []
29 | }
30 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/AuthorsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthorsView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct AuthorsView: View {
11 | @EnvironmentObject var xrShared: XRShared
12 |
13 | var body: some View {
14 | NavigationView {
15 | List(self.xrShared.authors.sorted { $0.name.lowercased() < $1.name.lowercased() }, id: \.id) { author in
16 | NavigationLink(destination: LibraryView(epubs: author.readables)) {
17 | Text(author.name)
18 | }
19 | }
20 | }
21 | }
22 | }
23 |
24 | #if DEBUG
25 | struct AuthorsView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | AuthorsView()
28 | }
29 | }
30 | #endif
31 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/PublishersView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PublishersView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/22/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct PublishersView: View {
11 | @EnvironmentObject var xrShared: XRShared
12 |
13 | var body: some View {
14 | NavigationView {
15 | List(self.xrShared.publishers.sorted { $0.name.lowercased() < $1.name.lowercased() }, id: \.id) { publisher in
16 | NavigationLink(destination: LibraryView(epubs: publisher.readables)) {
17 | Text(publisher.name)
18 | }
19 | }
20 | }
21 | }
22 | }
23 |
24 | struct PublishersView_Previews: PreviewProvider {
25 | static var previews: some View {
26 | PublishersView()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/LibraryView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LibraryView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/22/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct LibraryView: View {
12 | @AppStorage("libraryViewType") var viewType: LibraryViewTypes = .grid
13 | @EnvironmentObject var xrShared: XRShared
14 |
15 | let epubs: [EpubLoader]
16 | var body: some View {
17 | switch viewType {
18 | case .grid: GridView(epubs: epubs).environmentObject(self.xrShared)
19 | case .list: ListView(epubs: epubs).environmentObject(self.xrShared)
20 | }
21 | }
22 | }
23 |
24 | #if DEBUG
25 | struct LibraryView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | LibraryView(epubs: [])
28 | .environmentObject(XRShared())
29 | }
30 | }
31 | #endif
32 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/Library View Types/ListViewBook.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListViewBook.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/22/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct ListViewBook: View {
12 | let epub: EPUBDocument?
13 |
14 | var body: some View {
15 | VStack(alignment: .leading) {
16 | Text(epub?.title ?? "Unknown Title")
17 | .font(.headline)
18 | .bold()
19 |
20 | Text(epub?.author ?? "Unknown Author")
21 | .font(.subheadline)
22 | }
23 | .modifier(LibraryBookModifier(epub: epub))
24 | }
25 | }
26 |
27 | #if DEBUG
28 | struct ListViewBook_Previews: PreviewProvider {
29 | static var previews: some View {
30 | ListViewBook(epub: EPUBDocument(url: URL(string: "file:///Users/hkamran/Desktop/Desktop/Books/Xenon%20Library/You%20Are%20Enough.epub")!))
31 | }
32 | }
33 | #endif
34 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Settings/SettingsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SettingsView: View {
11 | private enum Tabs: Hashable {
12 | case library, reader
13 | }
14 |
15 | var body: some View {
16 | TabView {
17 | LibrarySettingsView()
18 | .tabItem {
19 | Label("Library", systemImage: "books.vertical")
20 | }
21 | .tag(Tabs.library)
22 |
23 | ReaderSettingsView()
24 | .tabItem {
25 | Label("Reader", systemImage: "book")
26 | }
27 | .tag(Tabs.reader)
28 | }
29 | .padding(20)
30 | .frame(minWidth: 350, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
31 | }
32 | }
33 |
34 | #if DEBUG
35 | struct SettingsView_Previews: PreviewProvider {
36 | static var previews: some View {
37 | SettingsView()
38 | }
39 | }
40 | #endif
41 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Reader/ReaderParentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReaderParentView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/24/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct ReaderParentView: View {
12 | @EnvironmentObject var xrShared: XRShared
13 |
14 | var body: some View {
15 | Group {
16 | if xrShared.activeReadable != nil {
17 | ReaderSidebarView()
18 | .environmentObject(self.xrShared)
19 |
20 | ReaderRenderView(activeReadable: xrShared.activeReadable, filename: xrShared.activeReadable?.epub?.manifest.items[(xrShared.activeReadable?.epub?.spine.items[xrShared.activeReadable?.spineItemIndex ?? 0].idref)!]?.path)
21 |
22 | } else {
23 | EmptyView()
24 | Text("No Active Readable")
25 | }
26 | }
27 | }
28 | }
29 |
30 | #if DEBUG
31 | struct ReaderParentView_Previews: PreviewProvider {
32 | static var previews: some View {
33 | ReaderParentView()
34 | .environmentObject(XRShared())
35 | }
36 | }
37 | #endif
38 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Testing/FileListView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FileListView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/19/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct FileListView: View {
12 | @AppStorage("libraryPath") var libraryPath = ""
13 | @AppStorage("libraryUrl") var libraryUrl = ""
14 | @EnvironmentObject var xrShared: XRShared
15 |
16 | @State var epub: EPUBDocument?
17 |
18 | var body: some View {
19 | VStack {
20 | Button(action: {
21 | self.xrShared.fileList = retrieveDirectoryList(libraryPath: libraryPath, showHidden: false, fileExtension: "epub") ?? []
22 | }) {
23 | Text("Load Library Items")
24 | }
25 |
26 | List(self.xrShared.fileList, id: \.self) { file in
27 | Text(file)
28 | }
29 | }
30 | .padding()
31 | }
32 | }
33 |
34 | #if DEBUG
35 | struct FileListView_Previews: PreviewProvider {
36 | static var previews: some View {
37 | FileListView()
38 | .environmentObject(XRShared())
39 | }
40 | }
41 | #endif
42 |
--------------------------------------------------------------------------------
/Xenon Reader/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Helpers.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/19/21.
6 | //
7 |
8 | import CryptoKit
9 | import EPUBKit
10 | import Foundation
11 | //import RealmSwift
12 |
13 | class XRShared: ObservableObject {
14 | @Published var epubs: [EpubLoader] = []
15 | @Published var authors: [Author] = []
16 | @Published var publishers: [Publisher] = []
17 | // TODO: Reset to empty array
18 | @Published var categories: [ReadableCategory] = [ReadableCategory(id: UUID().uuidString, name: "First Category", imageName: "tray.circle", creationDate: Date())]
19 |
20 | @Published var mainViewType: MainViewType = .library
21 | @Published var activeReadable: EpubLoader? = nil
22 |
23 | @Published var fileList: [String] = []
24 | // @Published var realmInstance: Realm? = initializeRealm()
25 |
26 | @Published var categoryCreationSheet: Bool = false
27 | }
28 |
29 | // TODO: Create extension to EPUBDocument which adds the hash as a parameter
30 | func sha256Hash(_ inputData: Data) -> String {
31 | let hashedData = SHA256.hash(data: inputData)
32 |
33 | return hashedData.compactMap { String(format: "%02x", $0) }.joined()
34 | }
35 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Reader/ReaderRenderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReaderRenderView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/24/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct ReaderRenderView: View {
12 | let activeReadable: EpubLoader?
13 | let filename: String?
14 |
15 | var body: some View {
16 | Group {
17 | if
18 | let fileUrl = getEpubPageUrl(epubFilename: activeReadable!.id, path: filename ?? ""),
19 | let directoryUrl = getEpubPageDirectoryUrl(epubId: activeReadable!.id, storageLocation: .applicationSupport)
20 | {
21 | FileWebView(fileURL: fileUrl, directoryURL: directoryUrl)
22 | } else {
23 | ProgressView()
24 | .progressViewStyle(CircularProgressViewStyle())
25 | }
26 | }
27 | }
28 | }
29 |
30 | #if DEBUG
31 | struct ReaderRenderView_Previews: PreviewProvider {
32 | static var previews: some View {
33 | ReaderRenderView(activeReadable: EpubLoader(withUrl: URL(string: "file:///Users/hkamran/Desktop/Desktop/Books/Xenon%20Library/You%20Are%20Enough.epub")!), filename: "")
34 | }
35 | }
36 | #endif
37 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/View Helpers/LibraryBookModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LibraryBookModifier.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/25/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct LibraryBookModifier: ViewModifier {
12 | @State private var informationSheet: Bool = false
13 | let epub: EPUBDocument?
14 |
15 | func body(content: Content) -> some View {
16 | content
17 | .contextMenu {
18 | Button(action: {}) {
19 | Label("Read \"\(returnPrefix(string: epub?.title ?? "Unknown Title", prefixLength: 16))\"", systemImage: "book")
20 | }
21 |
22 | Button(action: {
23 | informationSheet = true
24 | }) {
25 | Label("Information", systemImage: "info.circle")
26 | }
27 |
28 | Divider()
29 |
30 | Button(action: {}) {
31 | Label("Add to Category", systemImage: "tray")
32 | }
33 | }
34 | .sheet(isPresented: $informationSheet) {
35 | ReadableInformation(isPresented: $informationSheet, epub: epub)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Plans.md:
--------------------------------------------------------------------------------
1 | # Plans
2 |
3 | ## Onboarding
4 | - Walk through setup process
5 | - Select library path or create library folder in ~/Documents/Xenon Library
6 |
7 | ## Library
8 | - Search
9 | - Subtitle changes (readables in each section)
10 | - Example
11 | - Library: 100 readables
12 | - Authors: 100 readables
13 | - Selected author: 5 readables
14 | - Publishers: 100 readables
15 | - Selected publisher: 1 readable
16 | - Categories
17 | - Add Realm back when categories are added
18 |
19 | ## Reader
20 | - Pagination
21 | - Loading of external content
22 | - Stylesheets
23 | - Images
24 | - External link viewing (view in browser)
25 | - Bookmarks
26 | - Highlights (multicolor)
27 | - Notes (multicolor)
28 | - Search
29 |
30 | ## Settings
31 | - Library
32 | - Library folder
33 | - Library view
34 | - Library sort
35 | - Application theme (use macOS-style graphics)
36 | - Folder list
37 | - Reader
38 | - Font
39 | - Font size
40 | - Line spacing
41 | - Line width
42 | - Margin
43 | - Text alignment
44 | - Background color (macOS-style graphics, same visuals as app theme)
45 | - Set unzipped directory to ~/Library/Application Support/bundle.id
46 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/Library View Types/ListView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/22/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct ListView: View {
12 | @AppStorage("librarySortType") var librarySort: LibrarySortTypes = .title
13 | @EnvironmentObject var xrShared: XRShared
14 |
15 | let epubs: [EpubLoader]
16 | var body: some View {
17 | List {
18 | // TODO: Figure out why the spacing gets all out of whack when the sort type changes, then fixes itself if you switch the view type to grid then switch it back
19 | ForEach(sortLibraryList(epubList: epubs, sortType: librarySort), id: \.id) { epub in
20 | ListViewBook(epub: epub.epub)
21 | .onTapGesture(count: 2, perform: {
22 | self.xrShared.activeReadable = epub
23 | self.xrShared.mainViewType = .reader
24 | })
25 | }
26 | }
27 | }
28 | }
29 |
30 | #if DEBUG
31 | struct ListView_Previews: PreviewProvider {
32 | static var previews: some View {
33 | ListView(epubs: [])
34 | .environmentObject(XRShared())
35 | }
36 | }
37 | #endif
38 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Testing/VariablesView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VariablesView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/20/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct VariablesView: View {
11 | @AppStorage("libraryPath") var libraryPath = ""
12 | @AppStorage("libraryUrl") var libraryUrl = ""
13 | @AppStorage("libraryViewType") var viewType: LibraryViewTypes = .grid
14 | @AppStorage("librarySortType") var librarySort: LibrarySortTypes = .title
15 | @AppStorage("fontSize") var fontSize = 12.0
16 |
17 | var body: some View {
18 | List {
19 | Section(header: Text("Library")) {
20 | Text("Library Path: \(libraryPath)")
21 | Text("Library URL: \(libraryUrl)")
22 | Text("Library View Type: \(viewType == .grid ? "Grid" : "List")")
23 | Text("Library Sort Type: \(librarySort.rawValue)")
24 | }
25 |
26 | Section(header: Text("Reader")) {
27 | Text("Font Size: \(fontSize) pts")
28 | }
29 | }
30 | }
31 | }
32 |
33 | #if DEBUG
34 | struct VariablesView_Previews: PreviewProvider {
35 | static var previews: some View {
36 | VariablesView()
37 | }
38 | }
39 | #endif
40 |
--------------------------------------------------------------------------------
/Xenon Reader/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | // TODO: Add onboarding
12 | struct ContentView: View {
13 | @AppStorage("libraryPath") var libraryPath = ""
14 | @AppStorage("libraryUrl") var libraryUrl = ""
15 |
16 | @EnvironmentObject var xrShared: XRShared
17 |
18 | var body: some View {
19 | NavigationView {
20 | switch self.xrShared.mainViewType {
21 | case .library: LibraryParentView().environmentObject(xrShared)
22 | case .reader: ReaderParentView().environmentObject(xrShared)
23 | }
24 | }
25 | .navigationTitle(self.xrShared.mainViewType == .library ? "Xenon Reader" : (self.xrShared.activeReadable?.epub?.title ?? "Unknown Title"))
26 | .navigationSubtitle(generateSubtitle(xrShared: xrShared))
27 | .frame(minWidth: 350, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
28 | .modifier(ToolbarModifier(xrShared: self.xrShared))
29 | }
30 | }
31 |
32 | #if DEBUG
33 | struct ContentView_Previews: PreviewProvider {
34 | static var previews: some View {
35 | ContentView()
36 | }
37 | }
38 | #endif
39 |
--------------------------------------------------------------------------------
/Xenon Reader/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDocumentTypes
8 |
9 |
10 | CFBundleTypeRole
11 | None
12 | LSHandlerRank
13 | Default
14 |
15 |
16 | CFBundleExecutable
17 | $(EXECUTABLE_NAME)
18 | CFBundleIdentifier
19 | $(PRODUCT_BUNDLE_IDENTIFIER)
20 | CFBundleInfoDictionaryVersion
21 | 6.0
22 | CFBundleName
23 | $(PRODUCT_NAME)
24 | CFBundlePackageType
25 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
26 | CFBundleShortVersionString
27 | $(MARKETING_VERSION)
28 | CFBundleVersion
29 | 1
30 | LSApplicationCategoryType
31 | public.app-category.books
32 | LSMinimumSystemVersion
33 | $(MACOSX_DEPLOYMENT_TARGET)
34 | NSHumanReadableCopyright
35 | Created by H. Kamran
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Reader/Sub-views/ReaderSidebarMetadata.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReaderSidebarMetadata.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 3/11/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ReaderSidebarMetadata: View {
11 | @EnvironmentObject var xrShared: XRShared
12 |
13 | var body: some View {
14 | List {
15 | Section(header: Text("Details")) {
16 | DetailListRow(name: "Title", detail: xrShared.activeReadable?.epub?.title ?? "Unknown Title")
17 | DetailListRow(name: "Author", detail: xrShared.activeReadable?.epub?.author ?? "Unknown Author")
18 | DetailListRow(name: "Publisher", detail: xrShared.activeReadable?.epub?.publisher ?? "Unknown Publisher")
19 | }
20 |
21 | Section(header: Text("Counts")) {
22 | DetailListRow(name: "Chapter Count", detail: String(xrShared.activeReadable?.epub?.tableOfContents.subTable?.count ?? -1))
23 | DetailListRow(name: "Page Count", detail: String(xrShared.activeReadable?.epub?.spine.items.count ?? -1))
24 | }
25 | }
26 | }
27 | }
28 |
29 | #if DEBUG
30 | struct ReaderSidebarMetadata_Previews: PreviewProvider {
31 | static var previews: some View {
32 | ReaderSidebarMetadata()
33 | }
34 | }
35 | #endif
36 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/Library View Types/GridView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GridView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct GridView: View {
12 | @AppStorage("librarySortType") var librarySort: LibrarySortTypes = .title
13 | @EnvironmentObject var xrShared: XRShared
14 |
15 | let epubs: [EpubLoader]
16 |
17 | let columns = [
18 | GridItem(.adaptive(minimum: 150))
19 | ]
20 |
21 | var body: some View {
22 | ScrollView {
23 | LazyVGrid(columns: columns, alignment: .leading, spacing: 20) {
24 | ForEach(sortLibraryList(epubList: epubs, sortType: librarySort), id: \.id) { epub in
25 | GridViewBook(epub: epub.epub)
26 | .onTapGesture(count: 2, perform: {
27 | self.xrShared.activeReadable = epub
28 | self.xrShared.mainViewType = .reader
29 | })
30 | }
31 | }
32 | .padding(.horizontal)
33 | }
34 | }
35 | }
36 |
37 | #if DEBUG
38 | struct GridView_Previews: PreviewProvider {
39 | static var previews: some View {
40 | GridView(epubs: [])
41 | .environmentObject(XRShared())
42 | }
43 | }
44 | #endif
45 |
--------------------------------------------------------------------------------
/Xenon Reader/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.357",
9 | "green" : "0.094",
10 | "red" : "0.761"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "light"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.357",
27 | "green" : "0.094",
28 | "red" : "0.761"
29 | }
30 | },
31 | "idiom" : "universal"
32 | },
33 | {
34 | "appearances" : [
35 | {
36 | "appearance" : "luminosity",
37 | "value" : "dark"
38 | }
39 | ],
40 | "color" : {
41 | "color-space" : "srgb",
42 | "components" : {
43 | "alpha" : "1.000",
44 | "blue" : "0.573",
45 | "green" : "0.384",
46 | "red" : "0.941"
47 | }
48 | },
49 | "idiom" : "universal"
50 | }
51 | ],
52 | "info" : {
53 | "author" : "xcode",
54 | "version" : 1
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/LibraryHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LibraryHelpers.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/25/21.
6 | //
7 |
8 | import EPUBKit
9 | import Foundation
10 | import SwiftUI
11 |
12 | // MARK: Wrapper Struct
13 |
14 | struct LibraryLoader {
15 | @AppStorage("libraryPath") var libraryPath = ""
16 | @AppStorage("libraryUrl") var libraryUrl = ""
17 | let xrShared: XRShared
18 |
19 | func scanFiles() {
20 | self.xrShared.fileList = retrieveDirectoryList(libraryPath: self.libraryPath, showHidden: false, fileExtension: "epub") ?? []
21 | self.xrShared.epubs = loadLibraryItems(libraryUrl: self.libraryUrl, directoryList: self.xrShared.fileList)
22 | self.xrShared.authors = loadAuthors(readableList: self.xrShared.epubs)
23 | self.xrShared.publishers = loadPublishers(readableList: self.xrShared.epubs)
24 | }
25 | }
26 |
27 | // MARK: Functions
28 |
29 | // TODO: Make function asynchronous
30 | func loadLibraryItems(libraryUrl: String, directoryList: [String]) -> [EpubLoader] {
31 | var epubs: [EpubLoader] = []
32 |
33 | for filename in directoryList {
34 | let fileUrl = generateFileUrl(libraryUrl: libraryUrl, filename: filename.replacingOccurrences(of: ".epub", with: ""), fileExtension: "epub")!
35 | let epub = EpubLoader(withUrl: fileUrl)
36 |
37 | epubs.append(epub)
38 | }
39 |
40 | return epubs
41 | }
42 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Sub-views/FileWebView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FileWebView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/26/21.
6 | //
7 |
8 | import Cocoa
9 | import SwiftUI
10 | import WebKit
11 |
12 | struct FileWebView: NSViewRepresentable {
13 | let fileURL: URL
14 | let directoryURL: URL
15 |
16 | func makeNSView(context: Context) -> WKWebView {
17 | // TODO: Handle link elements in light mode (maybe a nice blue?)
18 | let css = ":root { color-scheme: light dark; } body { padding: 16px; font: -apple-system-body !important; font-size: 24px; } a { color: yellow }"
19 | let styleInjectionScript = "let style = document.createElement('style'); style.innerHTML = '\(css)'; document.head.appendChild(style);"
20 | let userScript = WKUserScript(source: styleInjectionScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
21 |
22 | let userContentController = WKUserContentController()
23 | userContentController.addUserScript(userScript)
24 |
25 | let configuration = WKWebViewConfiguration()
26 | configuration.userContentController = userContentController
27 |
28 | return WKWebView(frame: .zero, configuration: configuration)
29 | }
30 |
31 | func updateNSView(_ nsView: WKWebView, context: Context) {
32 | nsView.loadFileURL(fileURL, allowingReadAccessTo: directoryURL)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/Library View Types/GridViewBook.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GridViewBook.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct GridViewBook: View {
12 | let epub: EPUBDocument?
13 | @State private var informationSheet: Bool = false
14 |
15 | var body: some View {
16 | VStack(alignment: .center) {
17 | Image(nsImage: loadImage(epub?.cover) ?? NSImage())
18 | .resizable()
19 | .aspectRatio(contentMode: .fit)
20 | .frame(width: 150, height: 250)
21 |
22 | Group {
23 | Text(epub?.title ?? "Unknown Title")
24 | .font(.headline)
25 | .bold()
26 | .multilineTextAlignment(.center)
27 |
28 | Text(epub?.author ?? "Unknown Author")
29 | .font(.subheadline)
30 | .multilineTextAlignment(.center)
31 | }
32 | .frame(width: 150)
33 | }
34 | .modifier(LibraryBookModifier(epub: epub))
35 | }
36 | }
37 |
38 | #if DEBUG
39 | struct GridViewBook_Previews: PreviewProvider {
40 | static var previews: some View {
41 | GridViewBook(epub: EPUBDocument(url: URL(string: "file:///Users/hkamran/Desktop/Desktop/Books/Xenon%20Library/You%20Are%20Enough.epub")!))
42 | }
43 | }
44 | #endif
45 |
--------------------------------------------------------------------------------
/Xenon Reader.xcodeproj/xcuserdata/hkamran.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | GettingStarted (Playground) 1.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 3
13 |
14 | GettingStarted (Playground) 2.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 4
20 |
21 | GettingStarted (Playground) 3.xcscheme
22 |
23 | isShown
24 |
25 | orderHint
26 | 2
27 |
28 | GettingStarted (Playground) 4.xcscheme
29 |
30 | isShown
31 |
32 | orderHint
33 | 5
34 |
35 | GettingStarted (Playground) 5.xcscheme
36 |
37 | isShown
38 |
39 | orderHint
40 | 6
41 |
42 | GettingStarted (Playground).xcscheme
43 |
44 | isShown
45 |
46 | orderHint
47 | 1
48 |
49 | Xenon Reader.xcscheme_^#shared#^_
50 |
51 | orderHint
52 | 0
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Settings/DebugSettingsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DebugSettingsView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct DebugSettingsView: View {
11 | @AppStorage("fontSize") private var fontSize = 12.0
12 | @AppStorage("libraryPath") var libraryPath = ""
13 | @AppStorage("libraryUrl") var libraryUrl = ""
14 |
15 | var body: some View {
16 | Form {
17 | HStack {
18 | Text(libraryPath)
19 |
20 | Button("Select Folder") {
21 | let panel = NSOpenPanel()
22 | panel.allowsMultipleSelection = false
23 | panel.canChooseDirectories = true
24 | panel.canChooseFiles = false
25 |
26 | if panel.runModal() == .OK {
27 | let libraryPath = panel.url?.absoluteString ?? FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].absoluteString
28 |
29 | self.libraryPath = libraryPath.replacingOccurrences(of: "file://", with: "").removingPercentEncoding ?? ""
30 | self.libraryUrl = libraryPath
31 | }
32 | }
33 |
34 | Button(action: {
35 | libraryPath = ""
36 | }) {
37 | Image(systemName: "arrow.clockwise.circle")
38 | }
39 | }
40 | }
41 | }
42 | }
43 |
44 | #if DEBUG
45 | struct DebugSettingsView_Previews: PreviewProvider {
46 | static var previews: some View {
47 | DebugSettingsView()
48 | }
49 | }
50 | #endif
51 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Reader/ReaderSidebarView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReaderSidebarView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/24/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct ReaderSidebarView: View {
12 | @EnvironmentObject var xrShared: XRShared
13 | @State private var sidebarView: ReaderSidebarViews = .tableOfContents
14 |
15 | var body: some View {
16 | VStack(alignment: .leading) {
17 | Picker("", selection: $sidebarView) {
18 | Label("Table of Contents", systemImage: "list.bullet")
19 | .labelStyle(IconOnlyLabelStyle())
20 | .tag(ReaderSidebarViews.tableOfContents)
21 |
22 | Label("Metadata", systemImage: "info.circle")
23 | .labelStyle(IconOnlyLabelStyle())
24 | .tag(ReaderSidebarViews.metadata)
25 |
26 | Label("Markup", systemImage: "pencil.and.outline")
27 | .labelStyle(IconOnlyLabelStyle())
28 | .tag(ReaderSidebarViews.markup)
29 | }
30 | .pickerStyle(SegmentedPickerStyle())
31 | .padding(.horizontal, 8)
32 |
33 | // TODO: Figure out why switching sidebar views changes the web view
34 | switch sidebarView {
35 | case .tableOfContents: ReaderSidebarTOC().environmentObject(xrShared)
36 | case .metadata: ReaderSidebarMetadata().environmentObject(xrShared)
37 | default: ReaderSidebarTOC().environmentObject(xrShared)
38 | }
39 | }
40 | }
41 | }
42 |
43 | #if DEBUG
44 | struct ReaderSidebarView_Previews: PreviewProvider {
45 | static var previews: some View {
46 | ReaderSidebarView()
47 | .environmentObject(XRShared())
48 | }
49 | }
50 | #endif
51 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/EPUBHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EPUBHelpers.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import Cocoa
9 | import EPUBKit
10 | import Foundation
11 |
12 | // MARK: Wrapper Struct
13 |
14 | struct EpubLoader: Hashable {
15 | let id: String
16 | let epub: EPUBDocument?
17 | let storageLocation: StorageLocation = .applicationSupport
18 | var spineItemIndex: Int = 0
19 |
20 | init(withUrl fileUrl: URL) {
21 | // TODO: Figure out a way of handling readables with duplicate titles
22 | id = fileUrl.lastPathComponent.replacingOccurrences(of: ".epub", with: "").removingPercentEncoding!
23 | switch storageLocation {
24 | case .documents: epub = EPUBDocument(url: fileUrl, extractionDirectory: getAppDocumentsDirectory().appendingPathComponent(id))
25 | case .applicationSupport: epub = EPUBDocument(url: fileUrl, extractionDirectory: getAppSupportDirectory().appendingPathComponent(id))
26 | }
27 | }
28 | }
29 |
30 | // MARK: Functions
31 |
32 | func loadImage(_ imagePath: URL?) -> NSImage? {
33 | if let imagePathUrl = imagePath {
34 | do {
35 | let imageData = try Data(contentsOf: imagePathUrl)
36 | return NSImage(data: imageData)
37 | } catch {
38 | print(error.localizedDescription)
39 | return nil
40 | }
41 | } else {
42 | return NSImage(named: "DefaultCover")
43 | }
44 | }
45 |
46 | func getEpubPage(epubFilename: String, path: String) -> Data? {
47 | let pathUrl = getEpubPageUrl(epubFilename: epubFilename, path: path)
48 |
49 | do {
50 | return try Data(contentsOf: pathUrl)
51 | } catch {
52 | print("[P] epubFilename: \(epubFilename)")
53 | print("[P] path: \(path)")
54 | print("[ERROR] \(error.localizedDescription)")
55 |
56 | return nil
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Sub-views/NewCategorySheet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NewCategorySheet.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/22/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | let sfSymbolsMini: [String] = [
11 | "book",
12 | "book.circle",
13 | "books.vertical",
14 | "book.closed",
15 | "text.book.closed",
16 | "bookmark",
17 | "bookmark.circle",
18 | "folder",
19 | "folder.circle",
20 | "tray",
21 | "tray.circle"
22 | ]
23 |
24 | struct NewCategorySheet: View {
25 | @EnvironmentObject var xrShared: XRShared
26 | @Binding var isPresented: Bool
27 |
28 | @State var categoryName: String = ""
29 | @State var sfSymbol: String = "tray"
30 |
31 | var body: some View {
32 | Form {
33 | TextField("Category Name", text: $categoryName)
34 | .textFieldStyle(RoundedBorderTextFieldStyle())
35 | .padding([.trailing, .bottom])
36 |
37 | Picker("Icon", selection: $sfSymbol) {
38 | ForEach(sfSymbolsMini, id: \.self) { symbol in
39 | HStack {
40 | Image(systemName: symbol)
41 | Text(symbol)
42 | }
43 | }
44 | }
45 | .padding([.horizontal, .bottom])
46 |
47 | Button(action: {
48 | self.xrShared.categories.append(ReadableCategory(id: UUID().uuidString, name: categoryName, imageName: sfSymbol, creationDate: Date()))
49 | }) {
50 | Text("Add Category")
51 | }
52 | .disabled(categoryName == "" && sfSymbol == "")
53 | }
54 | .padding(.vertical)
55 | }
56 | }
57 |
58 | #if DEBUG
59 | struct NewCategorySheet_Previews: PreviewProvider {
60 | static var previews: some View {
61 | NewCategorySheet(isPresented: .constant(false))
62 | }
63 | }
64 | #endif
65 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Reader/Sub-views/ReaderSidebarTOC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReaderSidebarTOC.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 3/11/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ReaderSidebarTOC: View {
11 | @EnvironmentObject var xrShared: XRShared
12 |
13 | var body: some View {
14 | List {
15 | if let activeReadable = xrShared.activeReadable,
16 | let subTable = activeReadable.epub?.tableOfContents.subTable
17 | {
18 | ForEach(subTable, id: \.id) { item in
19 | Button(action: {
20 | let spineItems = activeReadable.epub?.spine.items
21 | let manifestItems = activeReadable.epub?.manifest.items.map { $0.1 }
22 | let itemNoPercent = item.item?.removingPercentEncoding
23 |
24 | if let spineIndex = spineItems?.firstIndex(where: { $0.idref == manifestItems?.first(where: { $0.path == itemNoPercent })?.id }) {
25 | xrShared.activeReadable?.spineItemIndex = spineIndex
26 | }
27 | }) {
28 | Text(item.label)
29 | }
30 | .buttonStyle(PlainButtonStyle())
31 | .frame(width: 180, height: 15, alignment: .leading)
32 | .padding(.leading, 4)
33 | .padding(.vertical, 2)
34 | .modifier(TOCFocusModifier(buttonText: item.label, activeChapter: getActiveChapterTitle(activeReadable: activeReadable)))
35 | }
36 | } else {
37 | Text("No TOC Available")
38 | }
39 | }
40 | .listStyle(SidebarListStyle())
41 | }
42 | }
43 |
44 | #if DEBUG
45 | struct ReaderSidebarTOC_Previews: PreviewProvider {
46 | static var previews: some View {
47 | ReaderSidebarTOC()
48 | }
49 | }
50 | #endif
51 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/Sub-views/SidebarView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SidebarView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/20/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SidebarView: View {
11 | @EnvironmentObject var xrShared: XRShared
12 | @State private var defaultItemActive: Bool = true
13 |
14 | var body: some View {
15 | List {
16 | Section(header: Text("Library")) {
17 | NavigationLink(destination: LibraryView(epubs: xrShared.epubs), isActive: $defaultItemActive) {
18 | Label("All Books", systemImage: "books.vertical")
19 | }
20 | NavigationLink(destination: AuthorsView()) {
21 | Label("Authors", systemImage: "person.3")
22 | }
23 | NavigationLink(destination: PublishersView()) {
24 | Label("Publishers", systemImage: "rectangle.stack.person.crop")
25 | }
26 | }
27 |
28 | // TODO: Add category support
29 | // Section(header: Text("Categories")) {
30 | // ForEach(self.xrShared.categories, id: \.id) { category in
31 | // NavigationLink(destination: Text("Planned: \(category.name)")) {
32 | // Label(category.name, systemImage: category.imageName)
33 | // }
34 | // }
35 | // }
36 |
37 | Spacer()
38 |
39 | Section(header: Text("Testing")) {
40 | NavigationLink(
41 | destination: FileListView()
42 | .environmentObject(self.xrShared)) {
43 | Label("File List View", systemImage: "doc")
44 | }
45 | NavigationLink(destination: VariablesView()) {
46 | Label("Variables", systemImage: "externaldrive.connected.to.line.below")
47 | }
48 | }
49 | }
50 | .listStyle(SidebarListStyle())
51 | }
52 | }
53 |
54 | #if DEBUG
55 | struct SidebarView_Previews: PreviewProvider {
56 | static var previews: some View {
57 | SidebarView()
58 | }
59 | }
60 | #endif
61 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/ReadableHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReadableHelpers.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/25/21.
6 | //
7 |
8 | import EPUBKit
9 | import Foundation
10 |
11 | // MARK: Structs
12 |
13 | struct Author: Identifiable, Hashable {
14 | let id = UUID()
15 | let name: String
16 | var readables: [EpubLoader] = []
17 | }
18 |
19 | struct Publisher: Identifiable, Hashable {
20 | let id = UUID()
21 | let name: String
22 | var readables: [EpubLoader] = []
23 | }
24 |
25 | // MARK: Loading Functions
26 |
27 | func loadAuthors(readableList: [EpubLoader]) -> [Author] {
28 | var authors: [Author] = []
29 |
30 | for readable in readableList {
31 | if let readableAuthor = readable.epub?.author {
32 | var authorStruct: Author?
33 |
34 | for author in authors {
35 | if author.name == readableAuthor {
36 | authorStruct = author
37 | }
38 | }
39 |
40 | if authorStruct == nil {
41 | authors.append(Author(name: readableAuthor, readables: [readable]))
42 | } else {
43 | let index = authors.firstIndex(of: authorStruct!) ?? -1
44 | authorStruct?.readables.append(readable)
45 | authors[index] = authorStruct!
46 | }
47 | }
48 | }
49 |
50 | return authors
51 | }
52 |
53 | func loadPublishers(readableList: [EpubLoader]) -> [Publisher] {
54 | var publishers: [Publisher] = []
55 |
56 | for readable in readableList {
57 | if let readablePublisher = readable.epub?.publisher {
58 | var publisherStruct: Publisher?
59 |
60 | for publisher in publishers {
61 | if publisher.name == readablePublisher {
62 | publisherStruct = publisher
63 | }
64 | }
65 |
66 | if publisherStruct == nil {
67 | publishers.append(Publisher(name: readablePublisher, readables: [readable]))
68 | } else {
69 | let index = publishers.firstIndex(of: publisherStruct!) ?? -1
70 | publisherStruct?.readables.append(readable)
71 | publishers[index] = publisherStruct!
72 | }
73 | }
74 | }
75 |
76 | return publishers
77 | }
78 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Library/Sub-views/ReadableInformation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReadableInformation.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/21/21.
6 | //
7 |
8 | import EPUBKit
9 | import SwiftUI
10 |
11 | struct ReadableInformation: View {
12 | @Binding var isPresented: Bool
13 |
14 | let epub: EPUBDocument?
15 |
16 | var body: some View {
17 | HStack(spacing: 20) {
18 | Image(nsImage: loadImage(epub?.cover) ?? NSImage())
19 | .resizable()
20 | .aspectRatio(contentMode: .fit)
21 | .frame(width: 150, height: 250)
22 |
23 | VStack(alignment: .leading, spacing: 5) {
24 | HStack(alignment: .center) {
25 | VStack(alignment: .leading) {
26 | Text(epub?.title ?? "Unknown Title")
27 | .font(.headline)
28 | .bold()
29 |
30 | Text(epub?.author ?? "Unknown Author")
31 | .font(.subheadline)
32 | }
33 |
34 | Spacer()
35 |
36 | Button(action: {
37 | withAnimation {
38 | isPresented = false
39 | }
40 | }) {
41 | Image(systemName: "xmark.circle")
42 | .resizable()
43 | .aspectRatio(contentMode: .fit)
44 | .frame(width: 20, height: 20)
45 | }
46 | .buttonStyle(PlainButtonStyle())
47 | .keyboardShortcut(.cancelAction)
48 | }
49 |
50 | Divider()
51 |
52 | List {
53 | if let publisher = epub?.publisher {
54 | DetailListRow(name: "Publisher", detail: publisher)
55 | }
56 |
57 | if let description = epub?.metadata.description {
58 | DetailListRow(name: "Description", detail: (description.stripOutHtml() ?? description).trimmingCharacters(in: .whitespacesAndNewlines))
59 | }
60 | }
61 | }
62 | }
63 | .padding(.all)
64 | .frame(minWidth: 750, maxWidth: 850, minHeight: 325, maxHeight: 400)
65 | }
66 | }
67 |
68 | #if DEBUG
69 | struct ReadableInformation_Previews: PreviewProvider {
70 | static var previews: some View {
71 | ReadableInformation(isPresented: .constant(true), epub: EPUBDocument(url: URL(string: "file:///Users/hkamran/Desktop/Desktop/Books/Xenon%20Library/SpySchoolBritishInvasion_StuartGibbs.epub")!))
72 | }
73 | }
74 | #endif
75 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/View Helpers/ToolbarModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ToolbarModifier.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ToolbarModifier: ViewModifier {
11 | @AppStorage("libraryViewType") var viewType: LibraryViewTypes = .grid
12 | @State var xrShared: XRShared
13 |
14 | func body(content: Content) -> some View {
15 | content
16 | .toolbar {
17 | ToolbarItemGroup(placement: .navigation) {
18 | if xrShared.mainViewType == .reader {
19 | Button(action: {
20 | self.xrShared.activeReadable = nil
21 | self.xrShared.mainViewType = .library
22 | }) {
23 | Image(systemName: "chevron.left")
24 | }
25 | }
26 |
27 | Button(action: toggleSidebar) {
28 | Image(systemName: "sidebar.left")
29 | .help("Toggle Sidebar")
30 | }
31 | }
32 |
33 | ToolbarItemGroup {
34 | if xrShared.mainViewType == .library {
35 | // MARK: Library Controls
36 |
37 | Picker("Library View", selection: $viewType) {
38 | Label("Grid", systemImage: "square.grid.3x2")
39 | .tag(LibraryViewTypes.grid)
40 | Label("List", systemImage: "tablecells")
41 | .tag(LibraryViewTypes.list)
42 | }
43 | .pickerStyle(SegmentedPickerStyle())
44 |
45 | // TODO: Add keyboard shortcut (slash) for searching
46 | Button(action: {
47 | print("Searching...")
48 | }) {
49 | Image(systemName: "magnifyingglass")
50 | }
51 | } else {
52 | // MARK: Reader Controls
53 |
54 | Button(action: {
55 | if xrShared.activeReadable != nil, xrShared.activeReadable!.spineItemIndex - 1 >= 0 {
56 | xrShared.activeReadable?.spineItemIndex -= 1
57 | }
58 | }) {
59 | Label("Previous Page", systemImage: "chevron.left")
60 | }
61 | Button(action: {
62 | if xrShared.activeReadable != nil, xrShared.activeReadable!.spineItemIndex + 1 < (xrShared.activeReadable?.epub?.spine.items.count) ?? 0 {
63 | xrShared.activeReadable?.spineItemIndex += 1
64 | }
65 | }) {
66 | Label("Next Page", systemImage: "chevron.right")
67 | }
68 | }
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Xenon Reader/Xenon_ReaderApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Xenon_ReaderApp.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/16/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct Xenon_ReaderApp: App {
12 | @AppStorage("libraryViewType") var viewType: LibraryViewTypes = .grid
13 | @AppStorage("librarySortType") var librarySort: LibrarySortTypes = .title
14 | @AppStorage("libraryPath") var libraryPath = ""
15 | @AppStorage("libraryUrl") var libraryUrl = ""
16 | @StateObject var xrShared = XRShared()
17 |
18 | @State private var categoryCreationSheet: Bool = false
19 |
20 | var body: some Scene {
21 | WindowGroup {
22 | ContentView()
23 | .onAppear(perform: {
24 | createAppDocumentsDirectory()
25 | LibraryLoader(libraryPath: libraryPath, libraryUrl: libraryUrl, xrShared: self.xrShared).scanFiles()
26 | })
27 | .environmentObject(self.xrShared)
28 | }
29 | .commands {
30 | CommandGroup(before: .newItem) {
31 | // TODO: Add keyboard shortcut
32 | Button(action: {
33 | print("New category")
34 | }) {
35 | Text("New Category")
36 | }
37 | }
38 |
39 | CommandGroup(after: .newItem) {
40 | Divider()
41 |
42 | Button(action: LibraryLoader(libraryPath: libraryPath, libraryUrl: libraryUrl, xrShared: self.xrShared).scanFiles) {
43 | Text("Scan")
44 | }
45 | .keyboardShortcut("R", modifiers: .command)
46 | }
47 |
48 | CommandGroup(before: .sidebar) {
49 | Picker("Library View", selection: $viewType) {
50 | Label("Grid", systemImage: "square.grid.3x2")
51 | .tag(LibraryViewTypes.grid)
52 | Label("List", systemImage: "tablecells")
53 | .tag(LibraryViewTypes.list)
54 | }
55 |
56 | Picker("Library Sort", selection: $librarySort) {
57 | Label("Title", systemImage: "textformat")
58 | .tag(LibrarySortTypes.title)
59 | Label("Title (Reversed)", systemImage: "textformat")
60 | .tag(LibrarySortTypes.titleReversed)
61 |
62 | Label("Author", systemImage: "person.3")
63 | .tag(LibrarySortTypes.author)
64 | Label("Author (Reversed)", systemImage: "person.3")
65 | .tag(LibrarySortTypes.authorReversed)
66 |
67 | Label("Publisher", systemImage: "rectangle.stack.person.crop")
68 | .tag(LibrarySortTypes.publisher)
69 | Label("Publisher (Reversed)", systemImage: "rectangle.stack.person.crop")
70 | .tag(LibrarySortTypes.publisherReversed)
71 |
72 | Label("Date Added", systemImage: "calendar.badge.plus")
73 | .tag(LibrarySortTypes.dateAdded)
74 | Label("Last Viewed", systemImage: "eyeglasses")
75 | .tag(LibrarySortTypes.lastViewed)
76 | }
77 | }
78 |
79 | SidebarCommands()
80 | }
81 |
82 | #if os(macOS)
83 | Settings {
84 | SettingsView()
85 | }
86 | #endif
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Xenon Reader/Views/Settings/LibrarySettingsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LibrarySettingsView.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 3/9/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LibrarySettingsView: View {
11 | @AppStorage("libraryPath") var libraryPath = ""
12 | @AppStorage("libraryUrl") var libraryUrl = ""
13 | @AppStorage("libraryViewType") var viewType: LibraryViewTypes = .grid
14 | @AppStorage("librarySortType") var librarySort: LibrarySortTypes = .title
15 |
16 | var body: some View {
17 | Form {
18 | Section {
19 | HStack {
20 | Text("Library Folder")
21 | .bold()
22 |
23 | Text(libraryPath)
24 |
25 | Button("Select Folder") {
26 | let panel = NSOpenPanel()
27 |
28 | panel.allowsMultipleSelection = false
29 | panel.canChooseDirectories = true
30 | panel.canChooseFiles = false
31 |
32 | panel.title = "Xenon Reader Library Path"
33 | panel.message = "Where your EPUBs are stored"
34 | panel.prompt = "Select"
35 |
36 | if panel.runModal() == .OK {
37 | let libraryPath = panel.url?.absoluteString ?? FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].absoluteString
38 |
39 | self.libraryPath = libraryPath.replacingOccurrences(of: "file://", with: "").removingPercentEncoding ?? ""
40 | self.libraryUrl = libraryPath
41 | }
42 | }
43 | }
44 | }
45 |
46 | Section {
47 | Picker("Library View", selection: $viewType) {
48 | Label("Grid", systemImage: "square.grid.3x2")
49 | .tag(LibraryViewTypes.grid)
50 | Label("List", systemImage: "tablecells")
51 | .tag(LibraryViewTypes.list)
52 | }
53 | .pickerStyle(SegmentedPickerStyle())
54 | }
55 |
56 | Section {
57 | Picker("Library Sort", selection: $librarySort) {
58 | Label("Title", systemImage: "textformat")
59 | .tag(LibrarySortTypes.title)
60 | Label("Title (Reversed)", systemImage: "textformat")
61 | .tag(LibrarySortTypes.titleReversed)
62 |
63 | Label("Author", systemImage: "person.3")
64 | .tag(LibrarySortTypes.author)
65 | Label("Author (Reversed)", systemImage: "person.3")
66 | .tag(LibrarySortTypes.authorReversed)
67 |
68 | Label("Publisher", systemImage: "rectangle.stack.person.crop")
69 | .tag(LibrarySortTypes.publisher)
70 | Label("Publisher (Reversed)", systemImage: "rectangle.stack.person.crop")
71 | .tag(LibrarySortTypes.publisherReversed)
72 |
73 | Label("Date Added", systemImage: "calendar.badge.plus")
74 | .tag(LibrarySortTypes.dateAdded)
75 | Label("Last Viewed", systemImage: "eyeglasses")
76 | .tag(LibrarySortTypes.lastViewed)
77 | }
78 | }
79 | }
80 | .padding(20)
81 | }
82 | }
83 |
84 | #if DEBUG
85 | struct LibrarySettingsView_Previews: PreviewProvider {
86 | static var previews: some View {
87 | LibrarySettingsView()
88 | }
89 | }
90 | #endif
91 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/View Helpers/ViewHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewHelpers.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/20/21.
6 | //
7 |
8 | import Cocoa
9 | import EPUBKit
10 | import Foundation
11 |
12 | // MARK: Enums
13 |
14 | enum LibraryViewTypes: String, CaseIterable, Identifiable {
15 | case grid
16 | case list
17 |
18 | var id: String { self.rawValue }
19 | }
20 |
21 | enum MainViewType: String, CaseIterable, Identifiable {
22 | case library
23 | case reader
24 |
25 | var id: String { self.rawValue }
26 | }
27 |
28 | enum LibrarySortTypes: String, CaseIterable, Identifiable {
29 | case title
30 | case titleReversed
31 | case author
32 | case authorReversed
33 | case dateAdded
34 | case lastViewed
35 | case publisher
36 | case publisherReversed
37 |
38 | var id: String { self.rawValue }
39 | }
40 |
41 | enum ReaderSidebarViews: String, CaseIterable, Identifiable {
42 | case tableOfContents
43 | case metadata
44 | case markup
45 |
46 | var id: String { self.rawValue }
47 | }
48 |
49 | // MARK: Functions
50 |
51 | // MARK: Generation Functions
52 |
53 | func generateReadableCount(count: Int) -> String {
54 | return count == 1 ? "\(count) Readable" : "\(count) Readables"
55 | }
56 |
57 | func generateSubtitle(xrShared: XRShared) -> String {
58 | var subtitle: String = ""
59 |
60 | switch xrShared.mainViewType {
61 | case .library: subtitle = generateReadableCount(count: xrShared.epubs.count)
62 | case .reader:
63 | if let activeReadable = xrShared.activeReadable, let activeChapter = getActiveChapterTitle(activeReadable: activeReadable) {
64 | subtitle = activeChapter
65 | }
66 | }
67 |
68 | return subtitle
69 | }
70 |
71 | // MARK: Generic Functions
72 |
73 | func toggleSidebar() {
74 | NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
75 | }
76 |
77 | func returnPrefix(string: String, prefixLength: Int) -> String {
78 | let prefixedString = String(string.prefix(prefixLength))
79 | return (prefixedString.count < string.count) ? prefixedString.appending("...") : prefixedString
80 | }
81 |
82 | func sortLibraryList(epubList: [EpubLoader], sortType: LibrarySortTypes) -> [EpubLoader] {
83 | var returnList: [EpubLoader]
84 |
85 | switch sortType {
86 | case .title: returnList = epubList.sorted { $0.epub?.title?.lowercased() ?? "no title" < $1.epub?.title?.lowercased() ?? "no title" }
87 | case .titleReversed: returnList = epubList.sorted { $1.epub?.title?.lowercased() ?? "no title" < $0.epub?.title?.lowercased() ?? "no title" }
88 | case .author: returnList = epubList.sorted { $0.epub?.author?.lowercased() ?? "no title" < $1.epub?.author?.lowercased() ?? "no title" }
89 | case .authorReversed: returnList = epubList.sorted { $1.epub?.author?.lowercased() ?? "no title" < $0.epub?.author?.lowercased() ?? "no title" }
90 | case .publisher: returnList = epubList.sorted { $0.epub?.publisher?.lowercased() ?? "no title" < $1.epub?.publisher?.lowercased() ?? "no title" }
91 | case .publisherReversed: returnList = epubList.sorted { $1.epub?.publisher?.lowercased() ?? "no title" < $0.epub?.publisher?.lowercased() ?? "no title" }
92 | default: returnList = epubList
93 | }
94 |
95 | return returnList
96 | }
97 |
98 | // MARK: Generation Helpers
99 |
100 | func getActiveChapterTitle(activeReadable: EpubLoader) -> String? {
101 | let spineItems = activeReadable.epub?.spine.items
102 | let manifestItems = activeReadable.epub?.manifest.items.map { $0.1 }
103 | let tocItems = activeReadable.epub?.tableOfContents.subTable
104 |
105 | if let tocItem = tocItems?.first(where: { $0.item == manifestItems?.first(where: { $0.id == spineItems?[activeReadable.spineItemIndex].idref })?.path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) }) {
106 | return tocItem.label
107 | } else {
108 | return nil
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Xenon Reader/Helpers/FilesystemHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FilesystemHelpers.swift
3 | // Xenon Reader
4 | //
5 | // Created by H. Kamran on 2/25/21.
6 | //
7 |
8 | import AEXML
9 | import EPUBKit
10 | import Foundation
11 |
12 | // MARK: Directory List Functions
13 |
14 | // TODO: Be able to search recursively
15 | func retrieveDirectoryList(libraryPath: String, showHidden: Bool = false, fileExtension: String = "") -> [String]? {
16 | if libraryPath != "" {
17 | let fm = FileManager.default
18 |
19 | do {
20 | let items = try fm.contentsOfDirectory(atPath: libraryPath)
21 | let filteredItems = showHidden ? items : items.filter { !$0.hasPrefix(".") }
22 |
23 | return fileExtension != "" ? filteredItems.filter { $0.hasSuffix(fileExtension) } : filteredItems
24 | } catch {
25 | print(error.localizedDescription)
26 | return nil
27 | }
28 | } else {
29 | print("Invalid library path")
30 | return nil
31 | }
32 | }
33 |
34 | // MARK: Creation/Generation Functions
35 |
36 | func absolutePath(libraryPath: String, filename: String) -> String {
37 | if libraryPath.last == "/" {
38 | return libraryPath + filename
39 | } else {
40 | return libraryPath + "/" + filename
41 | }
42 | }
43 |
44 | func createAppDocumentsDirectory() {
45 | let documentsDirectory: URL = getDocumentsDirectory()!
46 | let appDirectory: URL = documentsDirectory.appendingPathComponent("Xenon Reader", isDirectory: true)
47 |
48 | let fm = FileManager.default
49 |
50 | do {
51 | try fm.createDirectory(atPath: appDirectory.path, withIntermediateDirectories: true, attributes: nil)
52 | } catch {
53 | print("An error occurred when creating the application directory: \(error.localizedDescription)")
54 | }
55 | }
56 |
57 | func generateFileUrl(libraryUrl: String, filename: String, fileExtension: String) -> URL? {
58 | return URL(fileURLWithPath: filename, relativeTo: URL(string: libraryUrl)).appendingPathExtension(fileExtension)
59 | }
60 |
61 | // MARK: Fetch Functions
62 |
63 | func getDocumentsDirectory() -> URL? {
64 | return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
65 | }
66 |
67 | func getApplicationSupportDirectory() -> URL? {
68 | return FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
69 | }
70 |
71 | func getAppDocumentsDirectory() -> URL {
72 | return getDocumentsDirectory()?.appendingPathComponent("Xenon Reader", isDirectory: true) ?? getDocumentsDirectory()!
73 | }
74 |
75 | func getAppSupportDirectory() -> URL {
76 | return getApplicationSupportDirectory()!.appendingPathComponent(Bundle.main.bundleIdentifier ?? "com.hkamran.XenonReader")
77 | }
78 |
79 | func getEpubDirectory(epubFilename: String, storageLocation: StorageLocation = StorageLocation.applicationSupport) -> URL? {
80 | let storagePath = storageLocation == .documents ? getAppDocumentsDirectory() : getAppSupportDirectory()
81 | return storagePath.appendingPathComponent(epubFilename.replacingOccurrences(of: ".epub", with: "").removingPercentEncoding!, isDirectory: true)
82 | }
83 |
84 | func getEpubPageUrl(epubFilename: String, path: String, storageLocation: StorageLocation = StorageLocation.applicationSupport) -> URL {
85 | let epubDirectory = getEpubPageDirectoryUrl(epubId: epubFilename, storageLocation: storageLocation)
86 | let pathComponents = path.removingPercentEncoding!.components(separatedBy: ".")
87 |
88 | return URL(fileURLWithPath: pathComponents[0], relativeTo: epubDirectory).appendingPathExtension(pathComponents[1])
89 | }
90 |
91 | //func getEpubPageDirectoryUrl(epubFilename: String, path: String, storageLocation: StorageLocation = StorageLocation.applicationSupport) -> URL {
92 | // let epubDirectory = getEpubDirectory(epubFilename: epubFilename)
93 | // let pathComponents = path.removingPercentEncoding!.components(separatedBy: ".")
94 | // var pathFileComponents = pathComponents[0].components(separatedBy: "/")
95 | //
96 | // pathFileComponents.removeLast()
97 | //
98 | // return URL(fileURLWithPath: pathFileComponents.joined(separator: "/"), isDirectory: true, relativeTo: epubDirectory)
99 | //}
100 |
101 | func getEpubPageDirectoryUrl(epubId: String, storageLocation: StorageLocation) -> URL {
102 | let epubDirectory = getEpubDirectory(epubFilename: epubId, storageLocation: storageLocation)
103 | let epubContainerXML = URL(fileURLWithPath: "container.xml", relativeTo: epubDirectory?.appendingPathComponent("META-INF", isDirectory: true))
104 |
105 | do {
106 | let data = try Data(contentsOf: epubContainerXML)
107 | let xml = try AEXMLDocument(xml: data)
108 |
109 | let pathComponent = xml.root["rootfiles"]["rootfile"].attributes["full-path"]?.components(separatedBy: "/").first
110 |
111 | if pathComponent?.contains(".") == true {
112 | return epubDirectory!
113 | } else {
114 | return (epubDirectory?.appendingPathComponent(pathComponent!, isDirectory: true))!
115 | }
116 | } catch {
117 | print("[ERROR] getEpubPageDirectoryUrl2: \(error.localizedDescription)")
118 |
119 | return epubDirectory!
120 | }
121 | }
122 |
123 | // MARK: Fetch Function Helpers
124 |
125 | enum StorageLocation {
126 | case documents
127 | case applicationSupport
128 | }
129 |
--------------------------------------------------------------------------------
/Xenon Reader.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1200346725E3A45D00F467F1 /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1200346625E3A45D00F467F1 /* LibraryView.swift */; };
11 | 1200347425E3C2CE00F467F1 /* NewCategorySheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1200347325E3C2CE00F467F1 /* NewCategorySheet.swift */; };
12 | 1203E25225DCBCE0001D999E /* ToolbarModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1203E25125DCBCE0001D999E /* ToolbarModifier.swift */; };
13 | 1203E26625DCE0EB001D999E /* EPUBDocumentExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1203E26525DCE0EB001D999E /* EPUBDocumentExtensions.swift */; };
14 | 1220F94625E63FAC0077BEC8 /* LibraryParentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220F94525E63FAC0077BEC8 /* LibraryParentView.swift */; };
15 | 1220F94925E640120077BEC8 /* ReaderParentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220F94825E640120077BEC8 /* ReaderParentView.swift */; };
16 | 1220F95525E641A70077BEC8 /* ReaderSidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220F95425E641A70077BEC8 /* ReaderSidebarView.swift */; };
17 | 1220F95B25E650C10077BEC8 /* ReaderRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220F95A25E650C10077BEC8 /* ReaderRenderView.swift */; };
18 | 122529D325E8ACB5006184DB /* ReadableHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122529D225E8ACB5006184DB /* ReadableHelpers.swift */; };
19 | 122529D625E8ACF8006184DB /* LibraryHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122529D525E8ACF8006184DB /* LibraryHelpers.swift */; };
20 | 122529DE25E8B64B006184DB /* LibraryBookModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122529DD25E8B64B006184DB /* LibraryBookModifier.swift */; };
21 | 1239425525DBCAAB00B408DB /* Xenon_ReaderApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1239425425DBCAAB00B408DB /* Xenon_ReaderApp.swift */; };
22 | 1239425725DBCAAB00B408DB /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1239425625DBCAAB00B408DB /* ContentView.swift */; };
23 | 1239425925DBCAAD00B408DB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1239425825DBCAAD00B408DB /* Assets.xcassets */; };
24 | 1239425C25DBCAAD00B408DB /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1239425B25DBCAAD00B408DB /* Preview Assets.xcassets */; };
25 | 1239426825DBCD2900B408DB /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1239426725DBCD2900B408DB /* SettingsView.swift */; };
26 | 1239427525DBD75600B408DB /* EPUBHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1239427425DBD75600B408DB /* EPUBHelpers.swift */; };
27 | 1239427C25DBDAA700B408DB /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1239427B25DBDAA700B408DB /* DebugSettingsView.swift */; };
28 | 1252059D25DDB01600E6E464 /* RealmObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1252059C25DDB01600E6E464 /* RealmObjects.swift */; };
29 | 128336C225EA210000EEAEBE /* EPUBKit in Frameworks */ = {isa = PBXBuildFile; productRef = 128336C125EA210000EEAEBE /* EPUBKit */; };
30 | 12A8A54025DCE7BE003147E2 /* AuthorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A8A53F25DCE7BE003147E2 /* AuthorsView.swift */; };
31 | 12B4DEEA25DC7C8400B5908F /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B4DEE925DC7C8400B5908F /* GridView.swift */; };
32 | 12B4DEED25DC7D1800B5908F /* GridViewBook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B4DEEC25DC7D1800B5908F /* GridViewBook.swift */; };
33 | 12DD222525E9962400373A7B /* FileWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12DD222425E9962400373A7B /* FileWebView.swift */; };
34 | 12ECB5C825FB346200BBD6E3 /* ReaderSidebarTOC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12ECB5C725FB346200BBD6E3 /* ReaderSidebarTOC.swift */; };
35 | 12ECB5CD25FB36C100BBD6E3 /* ReaderSidebarMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12ECB5CC25FB36C100BBD6E3 /* ReaderSidebarMetadata.swift */; };
36 | 12ECB5D025FB374B00BBD6E3 /* DetailListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12ECB5CF25FB374B00BBD6E3 /* DetailListRow.swift */; };
37 | 12ECB5D325FBD6EB00BBD6E3 /* TOCFocusModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12ECB5D225FBD6EB00BBD6E3 /* TOCFocusModifier.swift */; };
38 | 12ECB5D625FBDCD600BBD6E3 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12ECB5D525FBDCD600BBD6E3 /* StringExtensions.swift */; };
39 | 12EDE4D725E31656005110E2 /* ReadableInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12EDE4D625E31656005110E2 /* ReadableInformation.swift */; };
40 | 12F22C4425E43704001BB4AE /* PublishersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F22C4325E43704001BB4AE /* PublishersView.swift */; };
41 | 12F22C4725E43852001BB4AE /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F22C4625E43852001BB4AE /* ListView.swift */; };
42 | 12F22C4A25E43907001BB4AE /* ListViewBook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F22C4925E43907001BB4AE /* ListViewBook.swift */; };
43 | 12F4FC3625F87299005ACFB5 /* ReaderSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F4FC3525F87299005ACFB5 /* ReaderSettingsView.swift */; };
44 | 12F4FC3925F872B2005ACFB5 /* LibrarySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F4FC3825F872B2005ACFB5 /* LibrarySettingsView.swift */; };
45 | 12F5355125E0BD9500D11F49 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F5355025E0BD9500D11F49 /* Helpers.swift */; };
46 | 12F5355825E0BEC500D11F49 /* FileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F5355725E0BEC500D11F49 /* FileListView.swift */; };
47 | 12F5355C25E0EB5500D11F49 /* RealmHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F5355B25E0EB5500D11F49 /* RealmHelpers.swift */; };
48 | 12F5356425E1FEF500D11F49 /* VariablesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F5356325E1FEF500D11F49 /* VariablesView.swift */; };
49 | 12F5356725E20C5B00D11F49 /* ViewHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F5356625E20C5B00D11F49 /* ViewHelpers.swift */; };
50 | 12F5356B25E223AC00D11F49 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F5356A25E223AC00D11F49 /* SidebarView.swift */; };
51 | 12F6F7C525E8637600FAB635 /* FilesystemHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F6F7C425E8637600FAB635 /* FilesystemHelpers.swift */; };
52 | /* End PBXBuildFile section */
53 |
54 | /* Begin PBXFileReference section */
55 | 1200346625E3A45D00F467F1 /* LibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryView.swift; sourceTree = ""; };
56 | 1200347325E3C2CE00F467F1 /* NewCategorySheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCategorySheet.swift; sourceTree = ""; };
57 | 1203E25125DCBCE0001D999E /* ToolbarModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarModifier.swift; sourceTree = ""; };
58 | 1203E26125DCDD86001D999E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
59 | 1203E26525DCE0EB001D999E /* EPUBDocumentExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPUBDocumentExtensions.swift; sourceTree = ""; };
60 | 1220F94525E63FAC0077BEC8 /* LibraryParentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryParentView.swift; sourceTree = ""; };
61 | 1220F94825E640120077BEC8 /* ReaderParentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderParentView.swift; sourceTree = ""; };
62 | 1220F95425E641A70077BEC8 /* ReaderSidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSidebarView.swift; sourceTree = ""; };
63 | 1220F95A25E650C10077BEC8 /* ReaderRenderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderRenderView.swift; sourceTree = ""; };
64 | 122529D225E8ACB5006184DB /* ReadableHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadableHelpers.swift; sourceTree = ""; };
65 | 122529D525E8ACF8006184DB /* LibraryHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryHelpers.swift; sourceTree = ""; };
66 | 122529DD25E8B64B006184DB /* LibraryBookModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryBookModifier.swift; sourceTree = ""; };
67 | 1239425125DBCAAB00B408DB /* Xenon Reader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Xenon Reader.app"; sourceTree = BUILT_PRODUCTS_DIR; };
68 | 1239425425DBCAAB00B408DB /* Xenon_ReaderApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xenon_ReaderApp.swift; sourceTree = ""; };
69 | 1239425625DBCAAB00B408DB /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
70 | 1239425825DBCAAD00B408DB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
71 | 1239425B25DBCAAD00B408DB /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
72 | 1239425D25DBCAAD00B408DB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
73 | 1239425E25DBCAAD00B408DB /* Xenon_Reader.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Xenon_Reader.entitlements; sourceTree = ""; };
74 | 1239426725DBCD2900B408DB /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
75 | 1239427425DBD75600B408DB /* EPUBHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPUBHelpers.swift; sourceTree = ""; };
76 | 1239427B25DBDAA700B408DB /* DebugSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugSettingsView.swift; sourceTree = ""; };
77 | 1252059C25DDB01600E6E464 /* RealmObjects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmObjects.swift; sourceTree = ""; };
78 | 12A8A53F25DCE7BE003147E2 /* AuthorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorsView.swift; sourceTree = ""; };
79 | 12B4DEE925DC7C8400B5908F /* GridView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridView.swift; sourceTree = ""; };
80 | 12B4DEEC25DC7D1800B5908F /* GridViewBook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridViewBook.swift; sourceTree = ""; };
81 | 12DD222425E9962400373A7B /* FileWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileWebView.swift; sourceTree = ""; };
82 | 12ECB5C725FB346200BBD6E3 /* ReaderSidebarTOC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSidebarTOC.swift; sourceTree = ""; };
83 | 12ECB5CC25FB36C100BBD6E3 /* ReaderSidebarMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSidebarMetadata.swift; sourceTree = ""; };
84 | 12ECB5CF25FB374B00BBD6E3 /* DetailListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailListRow.swift; sourceTree = ""; };
85 | 12ECB5D225FBD6EB00BBD6E3 /* TOCFocusModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TOCFocusModifier.swift; sourceTree = ""; };
86 | 12ECB5D525FBDCD600BBD6E3 /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; };
87 | 12EDE4D625E31656005110E2 /* ReadableInformation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadableInformation.swift; sourceTree = ""; };
88 | 12F22C4325E43704001BB4AE /* PublishersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishersView.swift; sourceTree = ""; };
89 | 12F22C4625E43852001BB4AE /* ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListView.swift; sourceTree = ""; };
90 | 12F22C4925E43907001BB4AE /* ListViewBook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewBook.swift; sourceTree = ""; };
91 | 12F4FC3325F815B0005ACFB5 /* Plans.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Plans.md; sourceTree = SOURCE_ROOT; };
92 | 12F4FC3525F87299005ACFB5 /* ReaderSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSettingsView.swift; sourceTree = ""; };
93 | 12F4FC3825F872B2005ACFB5 /* LibrarySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySettingsView.swift; sourceTree = ""; };
94 | 12F5355025E0BD9500D11F49 /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Helpers.swift; path = "Xenon Reader/Helpers/Helpers.swift"; sourceTree = SOURCE_ROOT; };
95 | 12F5355725E0BEC500D11F49 /* FileListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileListView.swift; sourceTree = ""; };
96 | 12F5355B25E0EB5500D11F49 /* RealmHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmHelpers.swift; sourceTree = ""; };
97 | 12F5356325E1FEF500D11F49 /* VariablesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariablesView.swift; sourceTree = ""; };
98 | 12F5356625E20C5B00D11F49 /* ViewHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewHelpers.swift; sourceTree = ""; };
99 | 12F5356A25E223AC00D11F49 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = ""; };
100 | 12F6F7C425E8637600FAB635 /* FilesystemHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesystemHelpers.swift; sourceTree = ""; };
101 | /* End PBXFileReference section */
102 |
103 | /* Begin PBXFrameworksBuildPhase section */
104 | 1239424E25DBCAAB00B408DB /* Frameworks */ = {
105 | isa = PBXFrameworksBuildPhase;
106 | buildActionMask = 2147483647;
107 | files = (
108 | 128336C225EA210000EEAEBE /* EPUBKit in Frameworks */,
109 | );
110 | runOnlyForDeploymentPostprocessing = 0;
111 | };
112 | /* End PBXFrameworksBuildPhase section */
113 |
114 | /* Begin PBXGroup section */
115 | 1200346925E3A70F00F467F1 /* Sub-views */ = {
116 | isa = PBXGroup;
117 | children = (
118 | 12ECB5CF25FB374B00BBD6E3 /* DetailListRow.swift */,
119 | 1200347325E3C2CE00F467F1 /* NewCategorySheet.swift */,
120 | 12DD222425E9962400373A7B /* FileWebView.swift */,
121 | );
122 | path = "Sub-views";
123 | sourceTree = "";
124 | };
125 | 1200346D25E3A73600F467F1 /* Testing */ = {
126 | isa = PBXGroup;
127 | children = (
128 | 12F5356325E1FEF500D11F49 /* VariablesView.swift */,
129 | 12F5355725E0BEC500D11F49 /* FileListView.swift */,
130 | );
131 | path = Testing;
132 | sourceTree = "";
133 | };
134 | 1203E25425DCBD9F001D999E /* View Helpers */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 12ECB5D225FBD6EB00BBD6E3 /* TOCFocusModifier.swift */,
138 | 122529DD25E8B64B006184DB /* LibraryBookModifier.swift */,
139 | 1203E25125DCBCE0001D999E /* ToolbarModifier.swift */,
140 | 12F5356625E20C5B00D11F49 /* ViewHelpers.swift */,
141 | );
142 | path = "View Helpers";
143 | sourceTree = "";
144 | };
145 | 1203E25525DCBDA9001D999E /* Helpers */ = {
146 | isa = PBXGroup;
147 | children = (
148 | 12F5355025E0BD9500D11F49 /* Helpers.swift */,
149 | 122529D525E8ACF8006184DB /* LibraryHelpers.swift */,
150 | 122529D225E8ACB5006184DB /* ReadableHelpers.swift */,
151 | 1239427425DBD75600B408DB /* EPUBHelpers.swift */,
152 | 12F6F7C425E8637600FAB635 /* FilesystemHelpers.swift */,
153 | 1252059F25DDB01A00E6E464 /* Realm */,
154 | 1203E25425DCBD9F001D999E /* View Helpers */,
155 | );
156 | path = Helpers;
157 | sourceTree = "";
158 | };
159 | 1203E26825DCE18E001D999E /* Extensions */ = {
160 | isa = PBXGroup;
161 | children = (
162 | 1203E26525DCE0EB001D999E /* EPUBDocumentExtensions.swift */,
163 | 12ECB5D525FBDCD600BBD6E3 /* StringExtensions.swift */,
164 | );
165 | path = Extensions;
166 | sourceTree = "";
167 | };
168 | 1220F94B25E640B60077BEC8 /* Library View Types */ = {
169 | isa = PBXGroup;
170 | children = (
171 | 12B4DEE925DC7C8400B5908F /* GridView.swift */,
172 | 12F22C4625E43852001BB4AE /* ListView.swift */,
173 | 12F22C4925E43907001BB4AE /* ListViewBook.swift */,
174 | 12B4DEEC25DC7D1800B5908F /* GridViewBook.swift */,
175 | );
176 | path = "Library View Types";
177 | sourceTree = "";
178 | };
179 | 1220F94D25E6412E0077BEC8 /* Library */ = {
180 | isa = PBXGroup;
181 | children = (
182 | 1220F94525E63FAC0077BEC8 /* LibraryParentView.swift */,
183 | 1200346625E3A45D00F467F1 /* LibraryView.swift */,
184 | 12A8A53F25DCE7BE003147E2 /* AuthorsView.swift */,
185 | 12F22C4325E43704001BB4AE /* PublishersView.swift */,
186 | 1220F94B25E640B60077BEC8 /* Library View Types */,
187 | 1220F95125E6414C0077BEC8 /* Sub-views */,
188 | );
189 | path = Library;
190 | sourceTree = "";
191 | };
192 | 1220F94F25E641360077BEC8 /* Reader */ = {
193 | isa = PBXGroup;
194 | children = (
195 | 1220F94825E640120077BEC8 /* ReaderParentView.swift */,
196 | 1220F95A25E650C10077BEC8 /* ReaderRenderView.swift */,
197 | 1220F95425E641A70077BEC8 /* ReaderSidebarView.swift */,
198 | 12ECB5CA25FB346500BBD6E3 /* Sub-views */,
199 | );
200 | path = Reader;
201 | sourceTree = "";
202 | };
203 | 1220F95125E6414C0077BEC8 /* Sub-views */ = {
204 | isa = PBXGroup;
205 | children = (
206 | 12EDE4D625E31656005110E2 /* ReadableInformation.swift */,
207 | 12F5356A25E223AC00D11F49 /* SidebarView.swift */,
208 | );
209 | path = "Sub-views";
210 | sourceTree = "";
211 | };
212 | 1239424825DBCAAB00B408DB = {
213 | isa = PBXGroup;
214 | children = (
215 | 12F4FC3325F815B0005ACFB5 /* Plans.md */,
216 | 1239425325DBCAAB00B408DB /* Xenon Reader */,
217 | 1239425225DBCAAB00B408DB /* Products */,
218 | );
219 | sourceTree = "";
220 | };
221 | 1239425225DBCAAB00B408DB /* Products */ = {
222 | isa = PBXGroup;
223 | children = (
224 | 1239425125DBCAAB00B408DB /* Xenon Reader.app */,
225 | );
226 | name = Products;
227 | sourceTree = "";
228 | };
229 | 1239425325DBCAAB00B408DB /* Xenon Reader */ = {
230 | isa = PBXGroup;
231 | children = (
232 | 1239425425DBCAAB00B408DB /* Xenon_ReaderApp.swift */,
233 | 1239425625DBCAAB00B408DB /* ContentView.swift */,
234 | 1203E26125DCDD86001D999E /* README.md */,
235 | 12F5355525E0BEA600D11F49 /* Views */,
236 | 1203E25525DCBDA9001D999E /* Helpers */,
237 | 1203E26825DCE18E001D999E /* Extensions */,
238 | 1239425D25DBCAAD00B408DB /* Info.plist */,
239 | 1239425825DBCAAD00B408DB /* Assets.xcassets */,
240 | 1239425E25DBCAAD00B408DB /* Xenon_Reader.entitlements */,
241 | 1239425A25DBCAAD00B408DB /* Preview Content */,
242 | );
243 | path = "Xenon Reader";
244 | sourceTree = "";
245 | };
246 | 1239425A25DBCAAD00B408DB /* Preview Content */ = {
247 | isa = PBXGroup;
248 | children = (
249 | 1239425B25DBCAAD00B408DB /* Preview Assets.xcassets */,
250 | );
251 | path = "Preview Content";
252 | sourceTree = "";
253 | };
254 | 1252059F25DDB01A00E6E464 /* Realm */ = {
255 | isa = PBXGroup;
256 | children = (
257 | 12F5355B25E0EB5500D11F49 /* RealmHelpers.swift */,
258 | 1252059C25DDB01600E6E464 /* RealmObjects.swift */,
259 | );
260 | path = Realm;
261 | sourceTree = "";
262 | };
263 | 12B4DEE325DC7A2000B5908F /* Settings */ = {
264 | isa = PBXGroup;
265 | children = (
266 | 1239426725DBCD2900B408DB /* SettingsView.swift */,
267 | 12F4FC3825F872B2005ACFB5 /* LibrarySettingsView.swift */,
268 | 12F4FC3525F87299005ACFB5 /* ReaderSettingsView.swift */,
269 | 1239427B25DBDAA700B408DB /* DebugSettingsView.swift */,
270 | );
271 | path = Settings;
272 | sourceTree = "";
273 | };
274 | 12ECB5CA25FB346500BBD6E3 /* Sub-views */ = {
275 | isa = PBXGroup;
276 | children = (
277 | 12ECB5CC25FB36C100BBD6E3 /* ReaderSidebarMetadata.swift */,
278 | 12ECB5C725FB346200BBD6E3 /* ReaderSidebarTOC.swift */,
279 | );
280 | path = "Sub-views";
281 | sourceTree = "";
282 | };
283 | 12F5355525E0BEA600D11F49 /* Views */ = {
284 | isa = PBXGroup;
285 | children = (
286 | 1220F94F25E641360077BEC8 /* Reader */,
287 | 1220F94D25E6412E0077BEC8 /* Library */,
288 | 1200346D25E3A73600F467F1 /* Testing */,
289 | 1200346925E3A70F00F467F1 /* Sub-views */,
290 | 12B4DEE325DC7A2000B5908F /* Settings */,
291 | );
292 | path = Views;
293 | sourceTree = "";
294 | };
295 | /* End PBXGroup section */
296 |
297 | /* Begin PBXNativeTarget section */
298 | 1239425025DBCAAB00B408DB /* Xenon Reader */ = {
299 | isa = PBXNativeTarget;
300 | buildConfigurationList = 1239426125DBCAAD00B408DB /* Build configuration list for PBXNativeTarget "Xenon Reader" */;
301 | buildPhases = (
302 | 1239424D25DBCAAB00B408DB /* Sources */,
303 | 1239424E25DBCAAB00B408DB /* Frameworks */,
304 | 1239424F25DBCAAB00B408DB /* Resources */,
305 | );
306 | buildRules = (
307 | );
308 | dependencies = (
309 | );
310 | name = "Xenon Reader";
311 | packageProductDependencies = (
312 | 128336C125EA210000EEAEBE /* EPUBKit */,
313 | );
314 | productName = "Xenon Reader";
315 | productReference = 1239425125DBCAAB00B408DB /* Xenon Reader.app */;
316 | productType = "com.apple.product-type.application";
317 | };
318 | /* End PBXNativeTarget section */
319 |
320 | /* Begin PBXProject section */
321 | 1239424925DBCAAB00B408DB /* Project object */ = {
322 | isa = PBXProject;
323 | attributes = {
324 | LastSwiftUpdateCheck = 1240;
325 | LastUpgradeCheck = 1240;
326 | TargetAttributes = {
327 | 1239425025DBCAAB00B408DB = {
328 | CreatedOnToolsVersion = 12.4;
329 | };
330 | };
331 | };
332 | buildConfigurationList = 1239424C25DBCAAB00B408DB /* Build configuration list for PBXProject "Xenon Reader" */;
333 | compatibilityVersion = "Xcode 9.3";
334 | developmentRegion = en;
335 | hasScannedForEncodings = 0;
336 | knownRegions = (
337 | en,
338 | Base,
339 | );
340 | mainGroup = 1239424825DBCAAB00B408DB;
341 | packageReferences = (
342 | 128336C025EA210000EEAEBE /* XCRemoteSwiftPackageReference "EPUBKit" */,
343 | );
344 | productRefGroup = 1239425225DBCAAB00B408DB /* Products */;
345 | projectDirPath = "";
346 | projectRoot = "";
347 | targets = (
348 | 1239425025DBCAAB00B408DB /* Xenon Reader */,
349 | );
350 | };
351 | /* End PBXProject section */
352 |
353 | /* Begin PBXResourcesBuildPhase section */
354 | 1239424F25DBCAAB00B408DB /* Resources */ = {
355 | isa = PBXResourcesBuildPhase;
356 | buildActionMask = 2147483647;
357 | files = (
358 | 1239425C25DBCAAD00B408DB /* Preview Assets.xcassets in Resources */,
359 | 1239425925DBCAAD00B408DB /* Assets.xcassets in Resources */,
360 | );
361 | runOnlyForDeploymentPostprocessing = 0;
362 | };
363 | /* End PBXResourcesBuildPhase section */
364 |
365 | /* Begin PBXSourcesBuildPhase section */
366 | 1239424D25DBCAAB00B408DB /* Sources */ = {
367 | isa = PBXSourcesBuildPhase;
368 | buildActionMask = 2147483647;
369 | files = (
370 | 1239426825DBCD2900B408DB /* SettingsView.swift in Sources */,
371 | 1200346725E3A45D00F467F1 /* LibraryView.swift in Sources */,
372 | 12DD222525E9962400373A7B /* FileWebView.swift in Sources */,
373 | 12ECB5D025FB374B00BBD6E3 /* DetailListRow.swift in Sources */,
374 | 12B4DEED25DC7D1800B5908F /* GridViewBook.swift in Sources */,
375 | 12F5355825E0BEC500D11F49 /* FileListView.swift in Sources */,
376 | 12F4FC3925F872B2005ACFB5 /* LibrarySettingsView.swift in Sources */,
377 | 122529D325E8ACB5006184DB /* ReadableHelpers.swift in Sources */,
378 | 1239427525DBD75600B408DB /* EPUBHelpers.swift in Sources */,
379 | 12EDE4D725E31656005110E2 /* ReadableInformation.swift in Sources */,
380 | 12ECB5C825FB346200BBD6E3 /* ReaderSidebarTOC.swift in Sources */,
381 | 12A8A54025DCE7BE003147E2 /* AuthorsView.swift in Sources */,
382 | 122529DE25E8B64B006184DB /* LibraryBookModifier.swift in Sources */,
383 | 12F6F7C525E8637600FAB635 /* FilesystemHelpers.swift in Sources */,
384 | 12F22C4425E43704001BB4AE /* PublishersView.swift in Sources */,
385 | 122529D625E8ACF8006184DB /* LibraryHelpers.swift in Sources */,
386 | 1220F94925E640120077BEC8 /* ReaderParentView.swift in Sources */,
387 | 12ECB5CD25FB36C100BBD6E3 /* ReaderSidebarMetadata.swift in Sources */,
388 | 1200347425E3C2CE00F467F1 /* NewCategorySheet.swift in Sources */,
389 | 12B4DEEA25DC7C8400B5908F /* GridView.swift in Sources */,
390 | 12F22C4725E43852001BB4AE /* ListView.swift in Sources */,
391 | 1203E26625DCE0EB001D999E /* EPUBDocumentExtensions.swift in Sources */,
392 | 1220F94625E63FAC0077BEC8 /* LibraryParentView.swift in Sources */,
393 | 12F5356425E1FEF500D11F49 /* VariablesView.swift in Sources */,
394 | 12ECB5D625FBDCD600BBD6E3 /* StringExtensions.swift in Sources */,
395 | 12F5356725E20C5B00D11F49 /* ViewHelpers.swift in Sources */,
396 | 1252059D25DDB01600E6E464 /* RealmObjects.swift in Sources */,
397 | 12F5355125E0BD9500D11F49 /* Helpers.swift in Sources */,
398 | 1220F95525E641A70077BEC8 /* ReaderSidebarView.swift in Sources */,
399 | 1239425725DBCAAB00B408DB /* ContentView.swift in Sources */,
400 | 1239425525DBCAAB00B408DB /* Xenon_ReaderApp.swift in Sources */,
401 | 12F5355C25E0EB5500D11F49 /* RealmHelpers.swift in Sources */,
402 | 12F5356B25E223AC00D11F49 /* SidebarView.swift in Sources */,
403 | 1203E25225DCBCE0001D999E /* ToolbarModifier.swift in Sources */,
404 | 12F22C4A25E43907001BB4AE /* ListViewBook.swift in Sources */,
405 | 12ECB5D325FBD6EB00BBD6E3 /* TOCFocusModifier.swift in Sources */,
406 | 1239427C25DBDAA700B408DB /* DebugSettingsView.swift in Sources */,
407 | 12F4FC3625F87299005ACFB5 /* ReaderSettingsView.swift in Sources */,
408 | 1220F95B25E650C10077BEC8 /* ReaderRenderView.swift in Sources */,
409 | );
410 | runOnlyForDeploymentPostprocessing = 0;
411 | };
412 | /* End PBXSourcesBuildPhase section */
413 |
414 | /* Begin XCBuildConfiguration section */
415 | 1239425F25DBCAAD00B408DB /* Debug */ = {
416 | isa = XCBuildConfiguration;
417 | buildSettings = {
418 | ALWAYS_SEARCH_USER_PATHS = NO;
419 | CLANG_ANALYZER_NONNULL = YES;
420 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
421 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
422 | CLANG_CXX_LIBRARY = "libc++";
423 | CLANG_ENABLE_MODULES = YES;
424 | CLANG_ENABLE_OBJC_ARC = YES;
425 | CLANG_ENABLE_OBJC_WEAK = YES;
426 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
427 | CLANG_WARN_BOOL_CONVERSION = YES;
428 | CLANG_WARN_COMMA = YES;
429 | CLANG_WARN_CONSTANT_CONVERSION = YES;
430 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
431 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
432 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
433 | CLANG_WARN_EMPTY_BODY = YES;
434 | CLANG_WARN_ENUM_CONVERSION = YES;
435 | CLANG_WARN_INFINITE_RECURSION = YES;
436 | CLANG_WARN_INT_CONVERSION = YES;
437 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
438 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
439 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
440 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
441 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
442 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
443 | CLANG_WARN_STRICT_PROTOTYPES = YES;
444 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
445 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
446 | CLANG_WARN_UNREACHABLE_CODE = YES;
447 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
448 | COPY_PHASE_STRIP = NO;
449 | DEBUG_INFORMATION_FORMAT = dwarf;
450 | ENABLE_STRICT_OBJC_MSGSEND = YES;
451 | ENABLE_TESTABILITY = YES;
452 | GCC_C_LANGUAGE_STANDARD = gnu11;
453 | GCC_DYNAMIC_NO_PIC = NO;
454 | GCC_NO_COMMON_BLOCKS = YES;
455 | GCC_OPTIMIZATION_LEVEL = 0;
456 | GCC_PREPROCESSOR_DEFINITIONS = (
457 | "DEBUG=1",
458 | "$(inherited)",
459 | );
460 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
461 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
462 | GCC_WARN_UNDECLARED_SELECTOR = YES;
463 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
464 | GCC_WARN_UNUSED_FUNCTION = YES;
465 | GCC_WARN_UNUSED_VARIABLE = YES;
466 | MACOSX_DEPLOYMENT_TARGET = 11.0;
467 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
468 | MTL_FAST_MATH = YES;
469 | ONLY_ACTIVE_ARCH = YES;
470 | SDKROOT = macosx;
471 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
472 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
473 | };
474 | name = Debug;
475 | };
476 | 1239426025DBCAAD00B408DB /* Release */ = {
477 | isa = XCBuildConfiguration;
478 | buildSettings = {
479 | ALWAYS_SEARCH_USER_PATHS = NO;
480 | CLANG_ANALYZER_NONNULL = YES;
481 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
482 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
483 | CLANG_CXX_LIBRARY = "libc++";
484 | CLANG_ENABLE_MODULES = YES;
485 | CLANG_ENABLE_OBJC_ARC = YES;
486 | CLANG_ENABLE_OBJC_WEAK = YES;
487 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
488 | CLANG_WARN_BOOL_CONVERSION = YES;
489 | CLANG_WARN_COMMA = YES;
490 | CLANG_WARN_CONSTANT_CONVERSION = YES;
491 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
492 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
493 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
494 | CLANG_WARN_EMPTY_BODY = YES;
495 | CLANG_WARN_ENUM_CONVERSION = YES;
496 | CLANG_WARN_INFINITE_RECURSION = YES;
497 | CLANG_WARN_INT_CONVERSION = YES;
498 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
499 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
500 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
501 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
502 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
503 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
504 | CLANG_WARN_STRICT_PROTOTYPES = YES;
505 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
506 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
507 | CLANG_WARN_UNREACHABLE_CODE = YES;
508 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
509 | COPY_PHASE_STRIP = NO;
510 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
511 | ENABLE_NS_ASSERTIONS = NO;
512 | ENABLE_STRICT_OBJC_MSGSEND = YES;
513 | GCC_C_LANGUAGE_STANDARD = gnu11;
514 | GCC_NO_COMMON_BLOCKS = YES;
515 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
516 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
517 | GCC_WARN_UNDECLARED_SELECTOR = YES;
518 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
519 | GCC_WARN_UNUSED_FUNCTION = YES;
520 | GCC_WARN_UNUSED_VARIABLE = YES;
521 | MACOSX_DEPLOYMENT_TARGET = 11.0;
522 | MTL_ENABLE_DEBUG_INFO = NO;
523 | MTL_FAST_MATH = YES;
524 | SDKROOT = macosx;
525 | SWIFT_COMPILATION_MODE = wholemodule;
526 | SWIFT_OPTIMIZATION_LEVEL = "-O";
527 | };
528 | name = Release;
529 | };
530 | 1239426225DBCAAD00B408DB /* Debug */ = {
531 | isa = XCBuildConfiguration;
532 | buildSettings = {
533 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
534 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
535 | CODE_SIGN_ENTITLEMENTS = "Xenon Reader/Xenon_Reader.entitlements";
536 | CODE_SIGN_IDENTITY = "-";
537 | CODE_SIGN_STYLE = Automatic;
538 | COMBINE_HIDPI_IMAGES = YES;
539 | DEVELOPMENT_ASSET_PATHS = "\"Xenon Reader/Preview Content\"";
540 | DEVELOPMENT_TEAM = "";
541 | ENABLE_PREVIEWS = YES;
542 | INFOPLIST_FILE = "Xenon Reader/Info.plist";
543 | LD_RUNPATH_SEARCH_PATHS = (
544 | "$(inherited)",
545 | "@executable_path/../Frameworks",
546 | );
547 | MACOSX_DEPLOYMENT_TARGET = 11.0;
548 | MARKETING_VERSION = 0.1;
549 | PRODUCT_BUNDLE_IDENTIFIER = com.hkamran.XenonReader;
550 | PRODUCT_NAME = "$(TARGET_NAME)";
551 | PROVISIONING_PROFILE_SPECIFIER = "";
552 | SWIFT_VERSION = 5.0;
553 | };
554 | name = Debug;
555 | };
556 | 1239426325DBCAAD00B408DB /* Release */ = {
557 | isa = XCBuildConfiguration;
558 | buildSettings = {
559 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
560 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
561 | CODE_SIGN_ENTITLEMENTS = "Xenon Reader/Xenon_Reader.entitlements";
562 | CODE_SIGN_IDENTITY = "-";
563 | CODE_SIGN_STYLE = Automatic;
564 | COMBINE_HIDPI_IMAGES = YES;
565 | DEVELOPMENT_ASSET_PATHS = "\"Xenon Reader/Preview Content\"";
566 | DEVELOPMENT_TEAM = "";
567 | ENABLE_PREVIEWS = YES;
568 | INFOPLIST_FILE = "Xenon Reader/Info.plist";
569 | LD_RUNPATH_SEARCH_PATHS = (
570 | "$(inherited)",
571 | "@executable_path/../Frameworks",
572 | );
573 | MACOSX_DEPLOYMENT_TARGET = 11.0;
574 | MARKETING_VERSION = 0.1;
575 | PRODUCT_BUNDLE_IDENTIFIER = com.hkamran.XenonReader;
576 | PRODUCT_NAME = "$(TARGET_NAME)";
577 | PROVISIONING_PROFILE_SPECIFIER = "";
578 | SWIFT_VERSION = 5.0;
579 | };
580 | name = Release;
581 | };
582 | /* End XCBuildConfiguration section */
583 |
584 | /* Begin XCConfigurationList section */
585 | 1239424C25DBCAAB00B408DB /* Build configuration list for PBXProject "Xenon Reader" */ = {
586 | isa = XCConfigurationList;
587 | buildConfigurations = (
588 | 1239425F25DBCAAD00B408DB /* Debug */,
589 | 1239426025DBCAAD00B408DB /* Release */,
590 | );
591 | defaultConfigurationIsVisible = 0;
592 | defaultConfigurationName = Release;
593 | };
594 | 1239426125DBCAAD00B408DB /* Build configuration list for PBXNativeTarget "Xenon Reader" */ = {
595 | isa = XCConfigurationList;
596 | buildConfigurations = (
597 | 1239426225DBCAAD00B408DB /* Debug */,
598 | 1239426325DBCAAD00B408DB /* Release */,
599 | );
600 | defaultConfigurationIsVisible = 0;
601 | defaultConfigurationName = Release;
602 | };
603 | /* End XCConfigurationList section */
604 |
605 | /* Begin XCRemoteSwiftPackageReference section */
606 | 128336C025EA210000EEAEBE /* XCRemoteSwiftPackageReference "EPUBKit" */ = {
607 | isa = XCRemoteSwiftPackageReference;
608 | repositoryURL = "https://github.com/hkamran80/EPUBKit";
609 | requirement = {
610 | branch = master;
611 | kind = branch;
612 | };
613 | };
614 | /* End XCRemoteSwiftPackageReference section */
615 |
616 | /* Begin XCSwiftPackageProductDependency section */
617 | 128336C125EA210000EEAEBE /* EPUBKit */ = {
618 | isa = XCSwiftPackageProductDependency;
619 | package = 128336C025EA210000EEAEBE /* XCRemoteSwiftPackageReference "EPUBKit" */;
620 | productName = EPUBKit;
621 | };
622 | /* End XCSwiftPackageProductDependency section */
623 | };
624 | rootObject = 1239424925DBCAAB00B408DB /* Project object */;
625 | }
626 |
--------------------------------------------------------------------------------