diff --git a/.gitmodules b/.gitmodules index 5e518e043..ed5613d14 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "ios-tenprintcover"] - path = ios-tenprintcover - url = https://github.com/ThePalaceProject/ios-tenprintcover.git [submodule "mobile-bookmark-spec"] path = mobile-bookmark-spec url = https://github.com/ThePalaceProject/mobile-bookmark-spec.git diff --git a/Palace.xcconfig b/Palace.xcconfig index c0d274009..c070d3b2f 100644 --- a/Palace.xcconfig +++ b/Palace.xcconfig @@ -1,4 +1,4 @@ -HEADER_SEARCH_PATHS = $(inherited) "$(SDKROOT)/usr/include/libxml2" "$(SRCROOT)/readium-sdk/Platform/Apple/RDServices" "$(SRCROOT)/readium-sdk/Platform/Apple/include" "$(SRCROOT)/ios-tenprintcover/TenPrintCover" "$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor" "$(SRCROOT)/adept-ios" "$(SRCROOT)/adobe-content-filter/public" "$(SRCROOT)/adobe-rmsdk/dp/public" "$(SRCROOT)/adobe-rmsdk/xml/uft/public" +HEADER_SEARCH_PATHS = $(inherited) "$(SDKROOT)/usr/include/libxml2" "$(SRCROOT)/readium-sdk/Platform/Apple/RDServices" "$(SRCROOT)/readium-sdk/Platform/Apple/include" "$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor" "$(SRCROOT)/adept-ios" "$(SRCROOT)/adobe-content-filter/public" "$(SRCROOT)/adobe-rmsdk/dp/public" "$(SRCROOT)/adobe-rmsdk/xml/uft/public" WARNING_CFLAGS = -Wall -Wextra -Weverything -Wno-objc-missing-property-synthesis -Wno-c11-extensions -Wno-documentation -Wno-c++98-compat-pedantic -Wno-c++98-compat diff --git a/Palace.xcodeproj/project.pbxproj b/Palace.xcodeproj/project.pbxproj index 4069c8f0b..ac3472d96 100644 --- a/Palace.xcodeproj/project.pbxproj +++ b/Palace.xcodeproj/project.pbxproj @@ -49,7 +49,6 @@ 1111973C19880E8D0014462F /* TPPLinearView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1111973B19880E8D0014462F /* TPPLinearView.m */; }; 1111973F1988226F0014462F /* TPPBookDetailDownloadFailedView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1111973E1988226F0014462F /* TPPBookDetailDownloadFailedView.m */; }; 11119742198827850014462F /* TPPBookDetailDownloadingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 11119741198827850014462F /* TPPBookDetailDownloadingView.m */; }; - 1112A8431A3249B4002B8CC1 /* libTenPrintCover.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1112A81F1A322C53002B8CC1 /* libTenPrintCover.a */; }; 1114A6A1195884CB007507A2 /* TPPCatalogUngroupedFeed.m in Sources */ = {isa = PBXBuildFile; fileRef = 1114A6A0195884CB007507A2 /* TPPCatalogUngroupedFeed.m */; }; 111559ED19B8FA590003BE94 /* TPPXML.m in Sources */ = {isa = PBXBuildFile; fileRef = 111559EC19B8FA590003BE94 /* TPPXML.m */; }; 1120749319D20BF9008203A4 /* TPPBookCellCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1120749219D20BF9008203A4 /* TPPBookCellCollectionViewController.m */; }; @@ -576,7 +575,6 @@ 3312643B2A287F53006BA87D /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FCD2601B7BA79200BFEDD9 /* CoreLocation.framework */; }; 3312643C2A287F53006BA87D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A823D814192BABA400B55DE2 /* UIKit.framework */; }; 3312643D2A287F53006BA87D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A823D810192BABA400B55DE2 /* Foundation.framework */; }; - 3312643F2A287F53006BA87D /* libTenPrintCover.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1112A81F1A322C53002B8CC1 /* libTenPrintCover.a */; }; 331264402A287F53006BA87D /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 119503E81993F919009FB788 /* libz.dylib */; }; 331264432A287F53006BA87D /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 119503E61993F914009FB788 /* libxml2.dylib */; }; 331264442A287F53006BA87D /* Minizip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2198690C267E29B00041369E /* Minizip.xcframework */; }; @@ -979,7 +977,6 @@ 73EB0B3F25821DF4006BC997 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FCD2601B7BA79200BFEDD9 /* CoreLocation.framework */; }; 73EB0B4025821DF4006BC997 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A823D814192BABA400B55DE2 /* UIKit.framework */; }; 73EB0B4125821DF4006BC997 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A823D810192BABA400B55DE2 /* Foundation.framework */; }; - 73EB0B4225821DF4006BC997 /* libTenPrintCover.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1112A81F1A322C53002B8CC1 /* libTenPrintCover.a */; }; 73EB0B4525821DF4006BC997 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 119503E81993F919009FB788 /* libz.dylib */; }; 73EB0B4625821DF4006BC997 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 119503E61993F914009FB788 /* libxml2.dylib */; }; 73EB0B5925821DF4006BC997 /* OFL.txt in Resources */ = {isa = PBXBuildFile; fileRef = 84B7A3431B84E8FE00584FB2 /* OFL.txt */; }; @@ -1030,6 +1027,7 @@ 7566BEA12EA7C612009EEF9C /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7566BEA02EA7C604009EEF9C /* ToastView.swift */; }; 7566BEA22EA7C612009EEF9C /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7566BEA02EA7C604009EEF9C /* ToastView.swift */; }; 7566BEA32EA7C612009EEF9C /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7566BEA02EA7C604009EEF9C /* ToastView.swift */; }; + 7577C6FF30939308009C595A /* TPPBookCoverRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7577C6FE30939308009C595A /* TPPBookCoverRegistryTests.swift */; }; 7582FFD02DB9188A0057AC71 /* BookSelectionButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7582FFCF2DB918880057AC71 /* BookSelectionButtonsView.m */; }; 7582FFD12DB9188A0057AC71 /* BookSelectionButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7582FFCF2DB918880057AC71 /* BookSelectionButtonsView.m */; }; 7582FFD22DB9188A0057AC71 /* BookSelectionButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7582FFCF2DB918880057AC71 /* BookSelectionButtonsView.m */; }; @@ -1411,13 +1409,6 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 1112A81E1A322C53002B8CC1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 1112A8191A322C53002B8CC1 /* TenPrintCover.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CCAC4FAE195B3DD700D775F5; - remoteInfo = TenPrintCover; - }; 220AB4E72BB16DE100435D59 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A823D805192BABA400B55DE2 /* Project object */; @@ -1425,13 +1416,6 @@ remoteGlobalIDString = 331262D32A287F53006BA87D; remoteInfo = Ekirjasto; }; - 59D9CC1B25FFDE1B00C89B81 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 1112A8191A322C53002B8CC1 /* TenPrintCover.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 59D9CBF525FFDBC500C89B81; - remoteInfo = TenPrintCoverTests; - }; 5A569A2B1B8351C6003B5B61 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5A569A261B8351C6003B5B61 /* ADEPT.xcodeproj */; @@ -1588,7 +1572,6 @@ 1111973E1988226F0014462F /* TPPBookDetailDownloadFailedView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPPBookDetailDownloadFailedView.m; sourceTree = ""; }; 11119740198827850014462F /* TPPBookDetailDownloadingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPPBookDetailDownloadingView.h; sourceTree = ""; }; 11119741198827850014462F /* TPPBookDetailDownloadingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPPBookDetailDownloadingView.m; sourceTree = ""; }; - 1112A8191A322C53002B8CC1 /* TenPrintCover.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TenPrintCover.xcodeproj; path = "ios-tenprintcover/TenPrintCover.xcodeproj"; sourceTree = ""; }; 1114A69F195884CB007507A2 /* TPPCatalogUngroupedFeed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPPCatalogUngroupedFeed.h; sourceTree = ""; }; 1114A6A0195884CB007507A2 /* TPPCatalogUngroupedFeed.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPPCatalogUngroupedFeed.m; sourceTree = ""; }; 1114A6AC1958B215007507A2 /* TPPLOG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = TPPLOG.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; @@ -1946,6 +1929,7 @@ 7557C4332E8AF69E00181B79 /* UserNotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationService.swift; sourceTree = ""; }; 7566BE9C2EA7C5F8009EEF9C /* ToastService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastService.swift; sourceTree = ""; }; 7566BEA02EA7C604009EEF9C /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = ""; }; + 7577C6FE30939308009C595A /* TPPBookCoverRegistryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TPPBookCoverRegistryTests.swift; sourceTree = ""; }; 7582FFCE2DB918630057AC71 /* BookSelectionButtonsView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BookSelectionButtonsView.h; sourceTree = ""; }; 7582FFCF2DB918880057AC71 /* BookSelectionButtonsView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BookSelectionButtonsView.m; sourceTree = ""; }; 7582FFD32DBADF240057AC71 /* BookSelectionButtonsState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BookSelectionButtonsState.h; sourceTree = ""; }; @@ -2244,7 +2228,6 @@ 3312643B2A287F53006BA87D /* CoreLocation.framework in Frameworks */, 3312643C2A287F53006BA87D /* UIKit.framework in Frameworks */, 3312643D2A287F53006BA87D /* Foundation.framework in Frameworks */, - 3312643F2A287F53006BA87D /* libTenPrintCover.a in Frameworks */, 33372CF92AC548FB0001BE2F /* FirebaseCrashlytics in Frameworks */, 331264402A287F53006BA87D /* libz.dylib in Frameworks */, 331264432A287F53006BA87D /* libxml2.dylib in Frameworks */, @@ -2286,7 +2269,6 @@ 73EB0B4025821DF4006BC997 /* UIKit.framework in Frameworks */, E78494992A8E99CF0059B4B0 /* PalaceAudiobookToolkit.framework in Frameworks */, 73EB0B4125821DF4006BC997 /* Foundation.framework in Frameworks */, - 73EB0B4225821DF4006BC997 /* libTenPrintCover.a in Frameworks */, 73EB0B4525821DF4006BC997 /* libz.dylib in Frameworks */, 73EB0B4625821DF4006BC997 /* libxml2.dylib in Frameworks */, 2198695B267E2A440041369E /* Minizip.xcframework in Frameworks */, @@ -2323,7 +2305,6 @@ E7498A8A2A0E7F460037DD93 /* FirebaseDynamicLinks in Frameworks */, A823D811192BABA400B55DE2 /* Foundation.framework in Frameworks */, E7CA1C4E29381068009CC6FD /* SQLite in Frameworks */, - 1112A8431A3249B4002B8CC1 /* libTenPrintCover.a in Frameworks */, 21986922267E29D70041369E /* PureLayout.xcframework in Frameworks */, E795A7822A744F0A00314EC8 /* ULID in Frameworks */, 119503E91993F919009FB788 /* libz.dylib in Frameworks */, @@ -2339,15 +2320,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 1112A81A1A322C53002B8CC1 /* Products */ = { - isa = PBXGroup; - children = ( - 1112A81F1A322C53002B8CC1 /* libTenPrintCover.a */, - 59D9CC1C25FFDE1B00C89B81 /* TenPrintCoverTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; 1183F34D194F775400DC322F /* OPDS */ = { isa = PBXGroup; children = ( @@ -3555,7 +3527,6 @@ E5C1F52927990929005FC6B2 /* OverdriveProcessor.xcodeproj */, E585662026979AC100A5FBD5 /* NYPLAEToolkit.xcodeproj */, 5A569A261B8351C6003B5B61 /* ADEPT.xcodeproj */, - 1112A8191A322C53002B8CC1 /* TenPrintCover.xcodeproj */, A823D816192BABA400B55DE2 /* Palace */, A823D82F192BABA400B55DE2 /* PalaceTests */, A823D80E192BABA400B55DE2 /* Products */, @@ -3716,6 +3687,7 @@ E5F8A57C28A48B8300A229AE /* BookPreviewTests.swift */, E534510429FA238500D88BC6 /* AudiobookmarkTests.swift */, E5024A8E2A0EB9D6006BF653 /* AudiobookBookmarkBusinessLogicTests.swift */, + 7577C6FE30939308009C595A /* TPPBookCoverRegistryTests.swift */, ); path = PalaceTests; sourceTree = ""; @@ -4229,10 +4201,6 @@ ProductGroup = E7F9BAB92A8E6811001697DE /* Products */; ProjectRef = E553424F293FB23F004FF34A /* PalaceAudiobookToolkit.xcodeproj */; }, - { - ProductGroup = 1112A81A1A322C53002B8CC1 /* Products */; - ProjectRef = 1112A8191A322C53002B8CC1 /* TenPrintCover.xcodeproj */; - }, ); projectRoot = ""; targets = ( @@ -4245,20 +4213,6 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 1112A81F1A322C53002B8CC1 /* libTenPrintCover.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libTenPrintCover.a; - remoteRef = 1112A81E1A322C53002B8CC1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 59D9CC1C25FFDE1B00C89B81 /* TenPrintCoverTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = TenPrintCoverTests.xctest; - remoteRef = 59D9CC1B25FFDE1B00C89B81 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 5A569A2C1B8351C6003B5B61 /* libADEPT.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -4641,6 +4595,7 @@ 735771B12537691800067CEA /* TPPDRMAuthorizingMock.swift in Sources */, E5F8A57D28A48B8300A229AE /* BookPreviewTests.swift in Sources */, 730EF263260955EF008E1DC3 /* TPPBookmarkSpecTests.swift in Sources */, + 7577C6FF30939308009C595A /* TPPBookCoverRegistryTests.swift in Sources */, B51C1DFE22860563003B49A5 /* OPDS2CatalogsFeedTests.swift in Sources */, E5A0CEA42AA9855200C304EC /* TPPBookMock.swift in Sources */, 73A794BD2548E36100C59CC1 /* TPPBookCreationTests.swift in Sources */, @@ -6142,7 +6097,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -6454,7 +6408,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -6766,7 +6719,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -7078,7 +7030,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -7380,7 +7331,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -7681,7 +7631,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -7982,7 +7931,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -8283,7 +8231,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -8347,7 +8294,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", @@ -8410,7 +8356,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(SRCROOT)/ios-tenprintcover/TenPrintCover\"", "\"$(SRCROOT)/ios-audiobook-overdrive/OverdriveProcessor\"", "\"$(SRCROOT)/adept-ios\"", "\"$(SRCROOT)/adobe-content-filter/public\"", diff --git a/Palace.xcodeproj/project.xcworkspace/xcshareddata/Simplified.xcscmblueprint b/Palace.xcodeproj/project.xcworkspace/xcshareddata/Simplified.xcscmblueprint index d2ecca07b..3fc7ac3a5 100644 --- a/Palace.xcodeproj/project.xcworkspace/xcshareddata/Simplified.xcscmblueprint +++ b/Palace.xcodeproj/project.xcworkspace/xcshareddata/Simplified.xcscmblueprint @@ -7,7 +7,6 @@ "AD4E54206183D886987A2A3031947817F2F202B6" : 0, "306FC097E980A949FBFF7EC996125BA9F20AEF86" : 0, "CA647A59556D3849BD27CCFE9423C80ECF6C0F48" : 0, - "E375BEBE2A272874C7869C33EC6726F091440FD9" : 0, "FD8A948335A628DCE5C35F1A302BBE858DD773F0" : 0, "7EA574B7954BB140EB4FDBD252E584FCDFCE648C" : 0, "06CFE79786291A98E71F9F1158D5AAE92B0D88ED" : 0 @@ -17,7 +16,6 @@ "AD4E54206183D886987A2A3031947817F2F202B6" : "Simplified-iOS\/readium-shared-js\/", "306FC097E980A949FBFF7EC996125BA9F20AEF86" : "Simplified-iOS\/", "CA647A59556D3849BD27CCFE9423C80ECF6C0F48" : "Simplified-iOS\/adobe-content-filter\/", - "E375BEBE2A272874C7869C33EC6726F091440FD9" : "Simplified-iOS\/tenprintcover-ios\/", "FD8A948335A628DCE5C35F1A302BBE858DD773F0" : "Simplified-iOS\/preloaded-content\/", "7EA574B7954BB140EB4FDBD252E584FCDFCE648C" : "Simplified-iOS\/readium-sdk\/", "06CFE79786291A98E71F9F1158D5AAE92B0D88ED" : "Simplified-iOS\/adept-ios\/" @@ -51,15 +49,10 @@ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "CA647A59556D3849BD27CCFE9423C80ECF6C0F48" }, - { - "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:NYPL\/tenprintcover-ios.git", - "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", - "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E375BEBE2A272874C7869C33EC6726F091440FD9" - }, { "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:NYPL-Simplified\/preloaded-content.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "FD8A948335A628DCE5C35F1A302BBE858DD773F0" } ] -} \ No newline at end of file +} diff --git a/Palace.xcodeproj/xcshareddata/xcschemes/Palace.xcscheme b/Palace.xcodeproj/xcshareddata/xcschemes/Palace.xcscheme index 0d7185c3b..227cc5679 100644 --- a/Palace.xcodeproj/xcshareddata/xcschemes/Palace.xcscheme +++ b/Palace.xcodeproj/xcshareddata/xcschemes/Palace.xcscheme @@ -59,19 +59,6 @@ ReferencedContainer = "container:Palace.xcodeproj"> - - - - - - Void) { - guard let imagePath = pinnedThumbnailImageUrlOfBookIdentifier(book.identifier)?.path else { - handler(nil) + /// - book: The book whose thumbnail image should be loaded, TPPBook object + /// - handler: Called with the cached, downloaded, or placeholder image + func thumbnailImageForBook( + _ book: TPPBook, + handler: @escaping (_ image: UIImage?) -> Void + ) { + + guard + let imagePath = pinnedThumbnailImageUrlOfBookIdentifier(book.identifier)? + .path + else { + handler(self.getPlaceholderBookCoverImage()) return } - let isPinned = TPPBookRegistry.shared.book(forIdentifier: book.identifier) != nil + + // a pinned book is saved in the user's local book registry. + let isPinned = + TPPBookRegistry.shared.book(forIdentifier: book.identifier) != nil + if isPinned { + if let image = UIImage(contentsOfFile: imagePath) { handler(image) } else if let thumbnailUrl = book.imageThumbnailURL, - let fileUrl = self.pinnedThumbnailImageUrlOfBookIdentifier(book.identifier) { - self.getBookCoverImage(url: thumbnailUrl, fileUrl: fileUrl, handler: handler, forBook: book) + let fileUrl = self.pinnedThumbnailImageUrlOfBookIdentifier( + book.identifier + ) + { + self.getBookCoverImage( + url: thumbnailUrl, + fileUrl: fileUrl, + handler: handler + ) + } else { + handler(self.getPlaceholderBookCoverImage()) } + } else { + if let thumbnailUrl = book.imageThumbnailURL { - self.getBookCoverImage(url: thumbnailUrl, fileUrl: nil, handler: handler, forBook: book) + self.getBookCoverImage( + url: thumbnailUrl, + fileUrl: nil, + handler: handler + ) } else { - handler(self.generateBookCoverImage(book)) + handler(self.getPlaceholderBookCoverImage()) } + } - + } - + /// Downloads cover image for the provided book. /// - Parameters: /// - book: `TPPBook` object. /// - handler: completion handler. - func coverImageForBook(_ book: TPPBook, handler: @escaping (_ image: UIImage?) -> Void) { + func coverImageForBook( + _ book: TPPBook, + handler: @escaping (_ image: UIImage?) -> Void + ) { + thumbnailImageForBook(book, handler: handler) + guard let imageUrl = book.imageURL else { return } - urlSession.dataTask(with: URLRequest(url: imageUrl)) { imageData, response, error in + + urlSession.dataTask(with: URLRequest(url: imageUrl)) { + imageData, + response, + error in if let imageData = imageData, let image = UIImage(data: imageData) { DispatchQueue.main.async { handler(image) } } }.resume() + } - - /// Downloads or creates book covers for the provided set of books. + + /// Downloads thumbnail images for the provided set of books. + /// Uses the placeholder image when a thumbnail is unavailable. /// - Parameters: - /// - books: A set of `TPPBook` objects. - /// - handler: completion handler. `handler()` is called once, after all covers are downloaded. - func thumbnailImagesForBooks(_ books: Set, handler: @escaping (_ bookIdentifiersToImages: [String: UIImage]) -> Void) { + /// - books: The books whose thumbnail images should be loaded, a set of `TPPBook` objects + /// - handler: Called once with an image for each book, completion handler + func thumbnailImagesForBooks( + _ books: Set, + handler: @escaping (_ bookIdentifiersToImages: [String: UIImage]) -> Void + ) { + + // Complete immediately if there are no books to load. + guard !books.isEmpty else { + handler([:]) + return + } + + // Collect one image for each requested book. + // Note: thread safety should be checked, + // URLSession callbacks may update this dictionary concurrently var result: [String: UIImage] = [:] { didSet { + // Notify the caller after all requested images are available. if _books.count == result.keys.count { DispatchQueue.main.async { handler(result) @@ -69,9 +122,10 @@ class TPPBookCoverRegistry { } } } - - var _books : [TPPBook] = [] - + + var _books: [TPPBook] = [] + + // Keep only one book for each identifier. for b in books { if !_books.contains(where: { book in book.identifier == b.identifier @@ -80,88 +134,124 @@ class TPPBookCoverRegistry { } } - + // Load a thumbnail or use the placeholder when no URL is available. _books.forEach { book in + guard let thumbnailUrl = book.imageThumbnailURL else { DispatchQueue.main.async { - result[book.identifier] = self.generateBookCoverImage(book) + result[book.identifier] = self.getPlaceholderBookCoverImage() } return } - urlSession.dataTask(with: URLRequest(url: thumbnailUrl)) { imageData, response, error in + + // Download the thumbnail asynchronously. + urlSession.dataTask(with: URLRequest(url: thumbnailUrl)) { + imageData, + response, + error in if let imageData = imageData, let image = UIImage(data: imageData) { + // Store the downloaded image. result[book.identifier] = image } else { + // Use the placeholder when the download or image decoding fails. DispatchQueue.main.async { - result[book.identifier] = self.generateBookCoverImage(book) + result[book.identifier] = self.getPlaceholderBookCoverImage() } - + } }.resume() - + } + } - - /// Immediately returns the cached thumbnail if available, else nil. Generated images are not returned. + + /// Immediately returns a thumbnail from the URL cache or nil. + /// Placeholder images are not returned. /// - Parameter book: book /// - Returns: cover image, if one is available. func cachedThumbnailImageForBook(_ book: TPPBook) -> UIImage? { + guard let thumbnailUrl = book.imageThumbnailURL, - let cachedData = urlSession.configuration.urlCache?.cachedResponse(for: URLRequest(url: thumbnailUrl))?.data + let cachedData = urlSession.configuration.urlCache?.cachedResponse( + for: URLRequest(url: thumbnailUrl) + )?.data else { return nil } + return UIImage(data: cachedData) } - + /// Saves cover image of the book. /// - Parameter book: `TPPBook` object. func pinThumbnailImageForBook(_ book: TPPBook) { + guard let thumbnailUrl = book.imageThumbnailURL, - let fileUrl = pinnedThumbnailImageUrlOfBookIdentifier(book.identifier) + let fileUrl = pinnedThumbnailImageUrlOfBookIdentifier(book.identifier) else { return } + try? Data().write(to: fileUrl, options: .atomic) - urlSession.dataTask(with: URLRequest(url: thumbnailUrl)) { imageData, response, error in + + urlSession.dataTask(with: URLRequest(url: thumbnailUrl)) { + imageData, + response, + error in if let imageData = imageData { do { try imageData.write(to: fileUrl, options: .atomic) } catch { - Log.error(#file, "Error saving thumbnail file: \(error.localizedDescription)") + Log.error( + #file, + "Error saving thumbnail file: \(error.localizedDescription)" + ) } } }.resume() + } - + /// Deletes cover image of a book with `bookIdentifier`. /// - Parameter bookIdentifier: book identifier. func removePinnedThumbnailImageForBookIdentifier(_ bookIdentifier: String) { - guard let fileUrl = pinnedThumbnailImageUrlOfBookIdentifier(bookIdentifier) else { + + guard let fileUrl = pinnedThumbnailImageUrlOfBookIdentifier(bookIdentifier) + else { return } + do { try FileManager.default.removeItem(at: fileUrl) } catch { - Log.error(#file, "Error removeing thumbnail file: \(error.localizedDescription)") + Log.error( + #file, + "Error removeing thumbnail file: \(error.localizedDescription)" + ) } + } - + /// Deletes book cover directory func removeAllPinnedThumbnailImages() { + do { if let url = pinnedThumbnailImageDirectoryURL { try FileManager.default.removeItem(at: url) } } catch { - Log.error(#file, "Error removing thumbnail directory: \(error.localizedDescription)") + Log.error( + #file, + "Error removing thumbnail directory: \(error.localizedDescription)" + ) } + } - + private lazy var urlSession: URLSession = { - let diskCacheInMegabytes = 16; - let memoryCacheInMegabytes = 2; + let diskCacheInMegabytes = 16 + let memoryCacheInMegabytes = 2 let configuration = URLSessionConfiguration.default configuration.httpCookieStorage = nil @@ -172,77 +262,103 @@ class TPPBookCoverRegistry { configuration.urlCache?.diskCapacity = 1024 * 1024 * diskCacheInMegabytes configuration.urlCache?.memoryCapacity = 1024 * 1024 * memoryCacheInMegabytes configuration.urlCredentialStorage = nil - + return URLSession(configuration: configuration) }() - - /// Directory URL for pinned images + + /// Returns the directory for locally saved thumbnail images. + /// Creates the directory when it does not exist. + /// This property performs file system I/O and may return nil. private var pinnedThumbnailImageDirectoryURL: URL? { - guard let accountsDirectoryUrl = TPPBookContentMetadataFilesHelper - .currentAccountDirectory() else { + + // resolve the directory for the currently active user account. + guard let accountsDirectoryUrl = + TPPBookContentMetadataFilesHelper.currentAccountDirectory() + else { Log.error(#file, "currentAccountDirectory is nil") return nil } + do { - let pinnedThumbnailsFolderUrl = accountsDirectoryUrl.appendingPathComponent("pinned-thumbnail-images") - if !FileManager.default.fileExists(atPath: pinnedThumbnailsFolderUrl.path) { - try FileManager.default.createDirectory(at: pinnedThumbnailsFolderUrl, withIntermediateDirectories: true) + // store thumbnails in the current user's local on-device account data, + // the folder name is "pinned-thumbnail-images" + let pinnedThumbnailsFolderUrl = + accountsDirectoryUrl.appendingPathComponent("pinned-thumbnail-images") + + // create the thumbnail directory on first use + if !FileManager.default.fileExists(atPath: pinnedThumbnailsFolderUrl.path) + { + try FileManager.default.createDirectory( + at: pinnedThumbnailsFolderUrl, + withIntermediateDirectories: true + ) } + return pinnedThumbnailsFolderUrl } catch { - Log.error(#file, "Failed to create pinned thumbnail images folder.") + Log.error( + #file, + "Failed to create pinned thumbnail images folder: \(error.localizedDescription)" + ) } + return nil } - + /// URL for pinned thumbnail image /// - Parameter bookIdentifier: book identifier /// - Returns: cover image file URL - private func pinnedThumbnailImageUrlOfBookIdentifier(_ bookIdentifier: String) -> URL? { - pinnedThumbnailImageDirectoryURL?.appendingPathComponent(bookIdentifier.sha256()) + private func pinnedThumbnailImageUrlOfBookIdentifier(_ bookIdentifier: String) -> URL? + { + + pinnedThumbnailImageDirectoryURL?.appendingPathComponent( + bookIdentifier.sha256() + ) + } - + /// Downloads book cover image. /// - Parameters: - /// - url: cover image URL. + /// - url: URL for the books cover image /// - fileUrl: file URL for storing the cover image. /// - handler: completion handler. - /// - book: `TPPBook` object. - private func getBookCoverImage(url: URL, fileUrl: URL?, handler: @escaping (_ image: UIImage?) -> (), forBook book: TPPBook) { - urlSession.dataTask(with: URLRequest(url: url)) { imageData, response, error in + private func getBookCoverImage( + url: URL, + fileUrl: URL?, + handler: @escaping (_ image: UIImage?) -> Void + ) { + + urlSession.dataTask(with: URLRequest(url: url)) { + imageData, + response, + error in if let imageData = imageData, let image = UIImage(data: imageData) { DispatchQueue.main.async { handler(image) + if let fileUrl = fileUrl { do { try imageData.write(to: fileUrl, options: .atomic) } catch { - // log error + Log.error( + #file, + "Error saving thumbnail file: \(error.localizedDescription)" + ) } } + } } else { DispatchQueue.main.async { - handler(self.generateBookCoverImage(book)) + handler(self.getPlaceholderBookCoverImage()) } } }.resume() + } - - /// Generates TenPrintCover cover for books. - /// - Parameter book: `TPPBook` object. - /// - Returns: cover image - private func generateBookCoverImage(_ book: TPPBook) -> UIImage? { - var image: UIImage? - let width: CGFloat = 80 - let height: CGFloat = 120 - let coverView = NYPLTenPrintCoverView(frame: CGRect(x: 0, y: 0, width: width, height: height), withTitle: book.title, withAuthor: book.authors, withScale: 0.4) - UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), true, 0) - if let context = UIGraphicsGetCurrentContext() { - coverView?.layer.render(in: context) - image = UIGraphicsGetImageFromCurrentImageContext() - } - UIGraphicsEndImageContext() - return image + + /// Returns the placeholder image used when a book has no available cover. + private func getPlaceholderBookCoverImage() -> UIImage? { + ImageProviders.MyBooksView.bookPlaceholder } } diff --git a/Palace/Utilities/SwiftUI/ImageProvider.swift b/Palace/Utilities/SwiftUI/ImageProvider.swift index 5a8f79477..7c01ebaf0 100644 --- a/Palace/Utilities/SwiftUI/ImageProvider.swift +++ b/Palace/Utilities/SwiftUI/ImageProvider.swift @@ -18,7 +18,7 @@ struct ImageProviders { } struct MyBooksView { - static let bookPlaceholder = UIImage(systemName: "book.closed.fill") + static let bookPlaceholder = UIImage(named: "NoCover") static let audiobookBadge = Image("AudiobookBadge") static let unreadBadge = Image("Unread") static let clock = Image("Clock") diff --git a/Palace/software-licenses.html b/Palace/software-licenses.html index 724383421..13d07e2c7 100644 --- a/Palace/software-licenses.html +++ b/Palace/software-licenses.html @@ -66,10 +66,5 @@

Readium

Modules: readium-sdk, readium-shared-js, r2-shared-swift, r2-streamer-swift, r2-lcp-swift, r2-navigator-swift.

This code is distributed under the terms and conditions of the BSD 3-Clause License.

-

TenPrintCover

-

Copyright (c) 2014 Mauricio Giraldo ( - https://github.com/ThePalaceProject/ios-tenprintcover)

-

This code is distributed under the terms and conditions of the MIT license.

- diff --git a/PalaceTests/TPPBookCoverRegistryTests.swift b/PalaceTests/TPPBookCoverRegistryTests.swift new file mode 100644 index 000000000..f555ff683 --- /dev/null +++ b/PalaceTests/TPPBookCoverRegistryTests.swift @@ -0,0 +1,51 @@ +// +// TPPBookCoverRegistryTests.swift +// PalaceTests +// +// Tests for book cover fallback behavior. +// + +import XCTest +@testable import Palace + +final class TPPBookCoverRegistryTests: XCTestCase { + func testThumbnailImageForBookWithoutThumbnailUsesPlaceholder() { + let book = TPPBook( + acquisitions: [], + authors: nil, + bookAccessibility: nil, + categoryStrings: [], + distributor: nil, + identifier: UUID().uuidString, + imageURL: nil, + imageThumbnailURL: nil, + published: nil, + publisher: nil, + subtitle: nil, + summary: nil, + title: "Book without a cover", + updated: Date(), + annotationsURL: nil, + analyticsURL: nil, + alternateURL: nil, + relatedWorksURL: nil, + previewLink: nil, + seriesURL: nil, + revokeURL: nil, + reportURL: nil, + timeTrackingURL: nil, + contributors: nil, + bookDuration: nil, + language: nil + ) + let expectedPlaceholder = ImageProviders.MyBooksView.bookPlaceholder + let expectation = expectation(description: "Placeholder image is returned") + + TPPBookCoverRegistry().thumbnailImageForBook(book) { image in + XCTAssertEqual(image?.pngData(), expectedPlaceholder?.pngData()) + expectation.fulfill() + } + + wait(for: [expectation], timeout: 1.0) + } +} diff --git a/ios-tenprintcover b/ios-tenprintcover deleted file mode 160000 index 3eba1fb24..000000000 --- a/ios-tenprintcover +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3eba1fb24806f2da32654d63290bb99c0edd2c80