Skip to content

Commit 26ab125

Browse files
author
Alexey Naumov
committed
Migrate to SVGView
1 parent a400582 commit 26ab125

File tree

16 files changed

+78
-525
lines changed

16 files changed

+78
-525
lines changed

CountriesSwiftUI.xcodeproj/project.pbxproj

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
522A537B2844DCAC002A3CE7 /* SVGView in Frameworks */ = {isa = PBXBuildFile; productRef = 522A537A2844DCAC002A3CE7 /* SVGView */; };
1011
52333A932654463B0034072B /* UIOpenURLContext_Init.m in Sources */ = {isa = PBXBuildFile; fileRef = 52333A922654463B0034072B /* UIOpenURLContext_Init.m */; };
1112
F60829712369CE0100DB292E /* RequestMocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = F60829702369CE0100DB292E /* RequestMocking.swift */; };
1213
F60829732369CE5300DB292E /* MockedResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F60829722369CE5300DB292E /* MockedResponse.swift */; };
@@ -44,8 +45,6 @@
4445
F661F2B6237734FA0014E142 /* AppEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F661F2B5237734FA0014E142 /* AppEnvironment.swift */; };
4546
F661F2B8237738CE0014E142 /* RootViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F661F2B7237738CE0014E142 /* RootViewModifier.swift */; };
4647
F661F2BC237757040014E142 /* ImageWebRepositoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F661F2BB237757040014E142 /* ImageWebRepositoryTests.swift */; };
47-
F661F2BF237761A80014E142 /* svg_convert_01.html in Resources */ = {isa = PBXBuildFile; fileRef = F661F2BD237761A80014E142 /* svg_convert_01.html */; };
48-
F661F2C0237761A80014E142 /* svg_convert_02.html in Resources */ = {isa = PBXBuildFile; fileRef = F661F2BE237761A80014E142 /* svg_convert_02.html */; };
4948
F661F2C5237772CE0014E142 /* ImagesInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F661F2C4237772CE0014E142 /* ImagesInteractorTests.swift */; };
5049
F661F2CA23777D440014E142 /* SVGImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F661F2C923777D440014E142 /* SVGImageViewTests.swift */; };
5150
F661F2CC23783E360014E142 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F661F2CB23783E360014E142 /* Helpers.swift */; };
@@ -145,8 +144,6 @@
145144
F661F2B5237734FA0014E142 /* AppEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppEnvironment.swift; sourceTree = "<group>"; };
146145
F661F2B7237738CE0014E142 /* RootViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewModifier.swift; sourceTree = "<group>"; };
147146
F661F2BB237757040014E142 /* ImageWebRepositoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageWebRepositoryTests.swift; sourceTree = "<group>"; };
148-
F661F2BD237761A80014E142 /* svg_convert_01.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = svg_convert_01.html; sourceTree = "<group>"; };
149-
F661F2BE237761A80014E142 /* svg_convert_02.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = svg_convert_02.html; sourceTree = "<group>"; };
150147
F661F2C4237772CE0014E142 /* ImagesInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagesInteractorTests.swift; sourceTree = "<group>"; };
151148
F661F2C923777D440014E142 /* SVGImageViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVGImageViewTests.swift; sourceTree = "<group>"; };
152149
F661F2CB23783E360014E142 /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
@@ -205,6 +202,7 @@
205202
isa = PBXFrameworksBuildPhase;
206203
buildActionMask = 2147483647;
207204
files = (
205+
522A537B2844DCAC002A3CE7 /* SVGView in Frameworks */,
208206
F6E7ACE223F5D1EC00AB48AB /* EnvironmentOverrides in Frameworks */,
209207
);
210208
runOnlyForDeploymentPostprocessing = 0;
@@ -251,8 +249,6 @@
251249
F60CC50E236C8963007E84B2 /* Resources */ = {
252250
isa = PBXGroup;
253251
children = (
254-
F661F2BD237761A80014E142 /* svg_convert_01.html */,
255-
F661F2BE237761A80014E142 /* svg_convert_02.html */,
256252
F67833D62369CCBD0065272F /* Info.plist */,
257253
);
258254
path = Resources;
@@ -519,6 +515,7 @@
519515
name = CountriesSwiftUI;
520516
packageProductDependencies = (
521517
F6E7ACE123F5D1EC00AB48AB /* EnvironmentOverrides */,
518+
522A537A2844DCAC002A3CE7 /* SVGView */,
522519
);
523520
productName = CountriesSwiftUI;
524521
productReference = F64495E12360D66400C9BB1F /* CountriesSwiftUI.app */;
@@ -580,6 +577,7 @@
580577
packageReferences = (
581578
F6B6212223B52AE600CD00C7 /* XCRemoteSwiftPackageReference "ViewInspector" */,
582579
F6E7ACE023F5D1EC00AB48AB /* XCRemoteSwiftPackageReference "EnvironmentOverrides" */,
580+
522A53792844DCAC002A3CE7 /* XCRemoteSwiftPackageReference "SVGView" */,
583581
);
584582
productRefGroup = F64495E22360D66400C9BB1F /* Products */;
585583
projectDirPath = "";
@@ -607,8 +605,6 @@
607605
isa = PBXResourcesBuildPhase;
608606
buildActionMask = 2147483647;
609607
files = (
610-
F661F2BF237761A80014E142 /* svg_convert_01.html in Resources */,
611-
F661F2C0237761A80014E142 /* svg_convert_02.html in Resources */,
612608
);
613609
runOnlyForDeploymentPostprocessing = 0;
614610
};
@@ -812,7 +808,7 @@
812808
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
813809
GCC_WARN_UNUSED_FUNCTION = YES;
814810
GCC_WARN_UNUSED_VARIABLE = YES;
815-
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
811+
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
816812
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
817813
MTL_FAST_MATH = YES;
818814
ONLY_ACTIVE_ARCH = YES;
@@ -868,7 +864,7 @@
868864
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
869865
GCC_WARN_UNUSED_FUNCTION = YES;
870866
GCC_WARN_UNUSED_VARIABLE = YES;
871-
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
867+
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
872868
MTL_ENABLE_DEBUG_INFO = NO;
873869
MTL_FAST_MATH = YES;
874870
SDKROOT = iphoneos;
@@ -890,10 +886,12 @@
890886
DEVELOPMENT_TEAM = "";
891887
ENABLE_PREVIEWS = YES;
892888
INFOPLIST_FILE = CountriesSwiftUI/Resources/Info.plist;
889+
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
893890
LD_RUNPATH_SEARCH_PATHS = (
894891
"$(inherited)",
895892
"@executable_path/Frameworks",
896893
);
894+
MACOSX_DEPLOYMENT_TARGET = 12.0;
897895
MARKETING_VERSION = 2.0;
898896
PRODUCT_BUNDLE_IDENTIFIER = com.countries.swiftui;
899897
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -915,10 +913,12 @@
915913
DEVELOPMENT_TEAM = "";
916914
ENABLE_PREVIEWS = YES;
917915
INFOPLIST_FILE = CountriesSwiftUI/Resources/Info.plist;
916+
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
918917
LD_RUNPATH_SEARCH_PATHS = (
919918
"$(inherited)",
920919
"@executable_path/Frameworks",
921920
);
921+
MACOSX_DEPLOYMENT_TARGET = 12.0;
922922
MARKETING_VERSION = 2.0;
923923
PRODUCT_BUNDLE_IDENTIFIER = com.countries.swiftui;
924924
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -942,6 +942,7 @@
942942
"@executable_path/Frameworks",
943943
"@loader_path/Frameworks",
944944
);
945+
MACOSX_DEPLOYMENT_TARGET = 12.0;
945946
PRODUCT_BUNDLE_IDENTIFIER = com.countries.swiftui.UnitTests;
946947
PRODUCT_NAME = "$(TARGET_NAME)";
947948
SWIFT_OBJC_BRIDGING_HEADER = "UnitTests/System/UnitTests-Bridging-Header.h";
@@ -966,6 +967,7 @@
966967
"@executable_path/Frameworks",
967968
"@loader_path/Frameworks",
968969
);
970+
MACOSX_DEPLOYMENT_TARGET = 12.0;
969971
PRODUCT_BUNDLE_IDENTIFIER = com.countries.swiftui.UnitTests;
970972
PRODUCT_NAME = "$(TARGET_NAME)";
971973
SWIFT_OBJC_BRIDGING_HEADER = "UnitTests/System/UnitTests-Bridging-Header.h";
@@ -1008,12 +1010,20 @@
10081010
/* End XCConfigurationList section */
10091011

10101012
/* Begin XCRemoteSwiftPackageReference section */
1013+
522A53792844DCAC002A3CE7 /* XCRemoteSwiftPackageReference "SVGView" */ = {
1014+
isa = XCRemoteSwiftPackageReference;
1015+
repositoryURL = "https://github.com/exyte/SVGView.git";
1016+
requirement = {
1017+
kind = upToNextMajorVersion;
1018+
minimumVersion = 1.0.0;
1019+
};
1020+
};
10111021
F6B6212223B52AE600CD00C7 /* XCRemoteSwiftPackageReference "ViewInspector" */ = {
10121022
isa = XCRemoteSwiftPackageReference;
10131023
repositoryURL = "https://github.com/nalexn/ViewInspector";
10141024
requirement = {
10151025
kind = exactVersion;
1016-
version = 0.9.0;
1026+
version = 0.9.1;
10171027
};
10181028
};
10191029
F6E7ACE023F5D1EC00AB48AB /* XCRemoteSwiftPackageReference "EnvironmentOverrides" */ = {
@@ -1027,6 +1037,11 @@
10271037
/* End XCRemoteSwiftPackageReference section */
10281038

10291039
/* Begin XCSwiftPackageProductDependency section */
1040+
522A537A2844DCAC002A3CE7 /* SVGView */ = {
1041+
isa = XCSwiftPackageProductDependency;
1042+
package = 522A53792844DCAC002A3CE7 /* XCRemoteSwiftPackageReference "SVGView" */;
1043+
productName = SVGView;
1044+
};
10301045
F6B6212323B52AE600CD00C7 /* ViewInspector */ = {
10311046
isa = XCSwiftPackageProductDependency;
10321047
package = F6B6212223B52AE600CD00C7 /* XCRemoteSwiftPackageReference "ViewInspector" */;

CountriesSwiftUI/Interactors/ImagesInteractor.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Foundation
1111
import SwiftUI
1212

1313
protocol ImagesInteractor {
14-
func load(image: LoadableSubject<UIImage>, url: URL?)
14+
func load(image: LoadableSubject<Data>, url: URL?)
1515
}
1616

1717
struct RealImagesInteractor: ImagesInteractor {
@@ -22,13 +22,13 @@ struct RealImagesInteractor: ImagesInteractor {
2222
self.webRepository = webRepository
2323
}
2424

25-
func load(image: LoadableSubject<UIImage>, url: URL?) {
25+
func load(image: LoadableSubject<Data>, url: URL?) {
2626
guard let url = url else {
2727
image.wrappedValue = .notRequested; return
2828
}
2929
let cancelBag = CancelBag()
3030
image.wrappedValue.setIsLoading(cancelBag: cancelBag)
31-
webRepository.load(imageURL: url, width: 300)
31+
webRepository.load(imageURL: url)
3232
.sinkToLoadable {
3333
image.wrappedValue = $0
3434
}
@@ -37,6 +37,6 @@ struct RealImagesInteractor: ImagesInteractor {
3737
}
3838

3939
struct StubImagesInteractor: ImagesInteractor {
40-
func load(image: LoadableSubject<UIImage>, url: URL?) {
40+
func load(image: LoadableSubject<Data>, url: URL?) {
4141
}
4242
}

CountriesSwiftUI/Repositories/ImageWebRepository.swift

Lines changed: 5 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Combine
1010
import UIKit
1111

1212
protocol ImageWebRepository: WebRepository {
13-
func load(imageURL: URL, width: Int) -> AnyPublisher<UIImage, Error>
13+
func load(imageURL: URL) -> AnyPublisher<Data, Error>
1414
}
1515

1616
struct RealImageWebRepository: ImageWebRepository {
@@ -24,155 +24,17 @@ struct RealImageWebRepository: ImageWebRepository {
2424
self.baseURL = baseURL
2525
}
2626

27-
func load(imageURL: URL, width: Int) -> AnyPublisher<UIImage, Error> {
28-
guard (imageURL.absoluteString as NSString).pathExtension.lowercased() == "svg" else {
29-
return download(rawImageURL: imageURL)
30-
.subscribe(on: bgQueue)
31-
.receive(on: DispatchQueue.main)
32-
.extractUnderlyingError()
33-
.eraseToAnyPublisher()
34-
}
35-
return Just<Void>.withErrorType(Error.self)
36-
.flatMap { self.importImage(originalURL: imageURL) }
37-
.flatMap { self.exportImage(imported: $0, width: width) }
38-
.flatMap { self.download(exported: $0) }
39-
.catch { self.removeCachedResponses(error: $0) }
27+
func load(imageURL: URL) -> AnyPublisher<Data, Error> {
28+
return download(rawImageURL: imageURL)
4029
.subscribe(on: bgQueue)
4130
.receive(on: DispatchQueue.main)
4231
.extractUnderlyingError()
4332
.eraseToAnyPublisher()
4433
}
4534

46-
private func importImage(originalURL: URL) -> AnyPublisher<ImageConversion.Import, Error> {
47-
guard let conversionURL = URL(string:
48-
baseURL + "/svg-to-png?url=" + originalURL.absoluteString) else {
49-
return Fail<ImageConversion.Import, Error>(error: APIError.invalidURL).eraseToAnyPublisher()
50-
}
51-
var urlRequest = URLRequest(url: conversionURL)
52-
urlRequest.httpMethod = "GET"
53-
return session.dataTaskPublisher(for: urlRequest)
54-
.tryMap { try ImageConversion.Import(data: $0.data, urlRequest: urlRequest) }
55-
.eraseToAnyPublisher()
56-
}
57-
58-
private func exportImage(imported: ImageConversion.Import,
59-
width: Int) -> AnyPublisher<ImageConversion.Export, Error> {
60-
guard let conversionURL = URL(string: imported.urlString + "?ajax=true") else {
61-
return Fail<ImageConversion.Export, Error>(
62-
error: APIError.imageProcessing([imported.urlRequest]))
63-
.eraseToAnyPublisher()
64-
}
65-
var urlRequest = URLRequest(url: conversionURL)
66-
urlRequest.httpMethod = "POST"
67-
let body: [String: Any] = [
68-
"file": (imported.urlString as NSString).lastPathComponent,
69-
"token": imported.ajaxToken,
70-
"width": width
71-
]
72-
let bodyString = body.map { $0.key + "=" + "\($0.value)" }.joined(separator: "&")
73-
urlRequest.httpBody = bodyString.data(using: .utf8)
74-
let urlRequests = [imported.urlRequest, urlRequest]
75-
return session.dataTaskPublisher(for: urlRequest)
76-
.tryMap { try ImageConversion.Export(data: $0.data, urlRequests: urlRequests) }
77-
.eraseToAnyPublisher()
78-
}
79-
80-
private func download(exported: ImageConversion.Export) -> AnyPublisher<UIImage, Error> {
81-
download(rawImageURL: exported.imageURL, requests: exported.urlRequests)
82-
}
83-
84-
private func download(rawImageURL: URL, requests: [URLRequest] = []) -> AnyPublisher<UIImage, Error> {
35+
private func download(rawImageURL: URL) -> AnyPublisher<Data, Error> {
8536
let urlRequest = URLRequest(url: rawImageURL)
8637
return session.dataTaskPublisher(for: urlRequest)
87-
.tryMap { (data, response) in
88-
guard let image = UIImage(data: data)
89-
else { throw APIError.imageProcessing(requests + [urlRequest]) }
90-
return image
91-
}
92-
.eraseToAnyPublisher()
93-
}
94-
95-
private func removeCachedResponses(error: Error) -> AnyPublisher<UIImage, Error> {
96-
if let apiError = error as? APIError,
97-
case let .imageProcessing(urlRequests) = apiError,
98-
let cache = session.configuration.urlCache {
99-
urlRequests.forEach(cache.removeCachedResponse)
100-
}
101-
return Fail(error: error).eraseToAnyPublisher()
102-
}
103-
}
104-
105-
private struct ImageConversion { }
106-
107-
extension ImageConversion {
108-
struct Import {
109-
110-
let urlString: String
111-
let ajaxToken: String
112-
let urlRequest: URLRequest
113-
114-
init(data: Data?, urlRequest: URLRequest) throws {
115-
guard let data = data, let string = String(data: data, encoding: .utf8),
116-
let elementWithURL = string.firstMatch(pattern: #"<form class="form ajax-form".*\.svg">"#),
117-
let conversionURL = elementWithURL.firstMatch(pattern: #"https.*\.svg"#),
118-
let ajaxTokenElement = string.firstMatch(pattern: #"name=\"file\"><input .*name=\"token\".*>"#),
119-
let dirtyToken = ajaxTokenElement.firstMatch(pattern: #"value="([a-z]|[0-9])*"#)
120-
else { throw APIError.imageProcessing([urlRequest]) }
121-
self.urlString = conversionURL
122-
self.ajaxToken = String(dirtyToken.suffix(from: dirtyToken.index(dirtyToken.startIndex, offsetBy: 7)))
123-
self.urlRequest = urlRequest
124-
}
125-
}
126-
}
127-
128-
extension ImageConversion {
129-
struct Export {
130-
131-
let imageURL: URL
132-
let urlRequests: [URLRequest]
133-
134-
init(data: Data?, urlRequests: [URLRequest]) throws {
135-
guard let data = data, let string = String(data: data, encoding: .utf8),
136-
let element = string.firstMatch(pattern: #"src=.*style="width"#),
137-
let imageURL = element.firstMatch(pattern: #"\/\/.*\.png"#),
138-
let url = URL(string: "https:" + imageURL)
139-
else { throw APIError.imageProcessing(urlRequests) }
140-
self.imageURL = url
141-
self.urlRequests = urlRequests
142-
}
143-
}
144-
}
145-
146-
private extension String {
147-
func firstMatch(pattern: String) -> String? {
148-
guard let regex = try? NSRegularExpression(pattern: pattern, options: []),
149-
let matchResult = regex.firstMatch(in: self, options: [], range: NSRange(location: 0, length: count)),
150-
let range = matchResult.ranges.first(where: { $0.location != NSNotFound })
151-
else { return nil }
152-
return (self as NSString).substring(with: range)
153-
}
154-
}
155-
156-
extension NSTextCheckingResult {
157-
struct Iterator: IteratorProtocol {
158-
typealias Element = NSRange
159-
160-
private var index: Int = 0
161-
private let collection: NSTextCheckingResult
162-
163-
init(collection: NSTextCheckingResult) {
164-
self.collection = collection
165-
}
166-
167-
mutating func next() -> NSRange? {
168-
defer { index += 1 }
169-
return index < collection.numberOfRanges ? collection.range(at: index) : nil
170-
}
171-
}
172-
}
173-
174-
extension NSTextCheckingResult {
175-
var ranges: IteratorSequence<NSTextCheckingResult.Iterator> {
176-
return .init(.init(collection: self))
38+
.requestData()
17739
}
17840
}

CountriesSwiftUI/System/PushNotificationsHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extension RealPushNotificationsHandler: UNUserNotificationCenterDelegate {
2929
willPresent notification: UNNotification,
3030
withCompletionHandler completionHandler:
3131
@escaping (UNNotificationPresentationOptions) -> Void) {
32-
completionHandler([.alert, .sound])
32+
completionHandler([.list, .banner, .sound])
3333
}
3434

3535
func userNotificationCenter(_ center: UNUserNotificationCenter,

0 commit comments

Comments
 (0)