Skip to content

Commit 3ebb345

Browse files
committed
Update snippets writing
1 parent ed95de6 commit 3ebb345

File tree

6 files changed

+201
-103
lines changed

6 files changed

+201
-103
lines changed

SnippetsLibrary.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
B8CE1CB826FF965D004AD5D5 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CE1CB726FF965D004AD5D5 /* NetworkService.swift */; };
115115
B8CFD8B42700D6A7002A66FF /* SnippetsUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CFD8B32700D6A7002A66FF /* SnippetsUploadView.swift */; };
116116
B8CFD8B72700D717002A66FF /* SnippetsUploadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CFD8B62700D717002A66FF /* SnippetsUploadViewModel.swift */; };
117+
B8D5F1EF2718895E00D8C3C4 /* SnippetWriteType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D5F1EE2718895E00D8C3C4 /* SnippetWriteType.swift */; };
117118
B8EB5AD526F005CC00BE3EF6 /* CustomCodeTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8EB5AD426F005CC00BE3EF6 /* CustomCodeTheme.swift */; };
118119
B8EB5AD926F0061A00BE3EF6 /* SnippetFileCardViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8EB5AD826F0061A00BE3EF6 /* SnippetFileCardViewState.swift */; };
119120
B8EB5ADB26F0076C00BE3EF6 /* PlistCodingKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8EB5ADA26F0076C00BE3EF6 /* PlistCodingKeys.swift */; };
@@ -269,6 +270,7 @@
269270
B8CE1CB726FF965D004AD5D5 /* NetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = "<group>"; };
270271
B8CFD8B32700D6A7002A66FF /* SnippetsUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnippetsUploadView.swift; sourceTree = "<group>"; };
271272
B8CFD8B62700D717002A66FF /* SnippetsUploadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnippetsUploadViewModel.swift; sourceTree = "<group>"; };
273+
B8D5F1EE2718895E00D8C3C4 /* SnippetWriteType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnippetWriteType.swift; sourceTree = "<group>"; };
272274
B8EB5AD426F005CC00BE3EF6 /* CustomCodeTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCodeTheme.swift; sourceTree = "<group>"; };
273275
B8EB5AD826F0061A00BE3EF6 /* SnippetFileCardViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnippetFileCardViewState.swift; sourceTree = "<group>"; };
274276
B8EB5ADA26F0076C00BE3EF6 /* PlistCodingKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlistCodingKeys.swift; sourceTree = "<group>"; };
@@ -731,6 +733,7 @@
731733
B81B087A2702468C00E59F86 /* FileStatusCardType.swift */,
732734
B8546F37270771020043A99F /* SnippetCategory.swift */,
733735
B832886C27160F17006BD465 /* SnippetsFetchingType.swift */,
736+
B8D5F1EE2718895E00D8C3C4 /* SnippetWriteType.swift */,
734737
);
735738
path = Enums;
736739
sourceTree = "<group>";
@@ -1089,6 +1092,7 @@
10891092
B8CB01072716613F00D3A6DD /* String+Extensions.swift in Sources */,
10901093
B8546F3C27077C310043A99F /* SnippetsLibraryListViewModel.swift in Sources */,
10911094
B8CE1CAB26FD4193004AD5D5 /* DisabledCommandGroupButton.swift in Sources */,
1095+
B8D5F1EF2718895E00D8C3C4 /* SnippetWriteType.swift in Sources */,
10921096
B85D1A9D26FA8EA50053FF3C /* SnippetsLibraryListSectionView.swift in Sources */,
10931097
B82561E626E8C5490040A67E /* Mocks.swift in Sources */,
10941098
);

SnippetsLibrary/Modules/SnippetsUpload/SnippetsUploadViewModel.swift

+27-56
Original file line numberDiff line numberDiff line change
@@ -10,83 +10,54 @@ import Combine
1010

1111
final class SnippetsUploadViewModel: ObservableObject {
1212

13+
private enum Constants {
14+
static let midValue: CGFloat = 0.5
15+
static let fullValue: CGFloat = 1.0
16+
}
17+
1318
// MARK: - Stored Properties
1419

1520
private(set) var snippets: [Snippet]
1621

17-
@Published internal var progressValue: CGFloat = 0.0
22+
@Published internal var progressValue: CGFloat = .zero
1823
@Published private(set) var shouldDismissView = false
1924
@Published internal var uploadingStatus: UploadingStatus = .initializing
2025

26+
private let snippetsParserService: SnippetsParserService
27+
2128
private var cancellables = Set<AnyCancellable>()
2229

2330
// MARK: - Initialization
2431

25-
init(snippets: [Snippet]) {
32+
init(
33+
snippets: [Snippet],
34+
snippetsParserService: SnippetsParserService = DIContainer.snippetsParserService
35+
) {
2636
self.snippets = snippets
37+
self.snippetsParserService = snippetsParserService
2738
}
2839

2940
// MARK: - Methods
3041

3142
internal func uploadSnippetsToXcode() {
32-
let fileManager = FileManager.default
33-
let encoder = PropertyListEncoder()
34-
encoder.outputFormat = .xml
35-
36-
let directoryURLs = fileManager.urls(
37-
for: .libraryDirectory,
38-
in: .userDomainMask
39-
)
40-
41-
guard
42-
let containerDirectoryURL = directoryURLs.first,
43-
let libraryDirectory = containerDirectoryURL.absoluteString.components(separatedBy: "Containers/").first,
44-
let xcodeUserSnippetsDirectory = xcodeUserSnippetsDirectoryURL(with: libraryDirectory)
45-
else {
46-
uploadingStatus = .error
47-
return
48-
}
49-
50-
for (index, snippet) in snippets.enumerated() {
51-
uploadingStatus = .uploading
52-
53-
DispatchQueue.main.async {
54-
self.progressValue = CGFloat(index + 1) / CGFloat(self.snippets.count)
55-
}
56-
do {
57-
let plistSnippet = SnippetPlist(from: snippet)
58-
let data = try encoder.encode(plistSnippet)
59-
let filePath = xcodeUserSnippetsDirectory.appendingPathComponent("\(snippet.id).codesnippet")
60-
try data.write(to: filePath, options: [])
61-
} catch {
62-
self.uploadingStatus = .error
43+
snippetsParserService.writeToPath(
44+
type: .uploadToXcode,
45+
snippets: snippets
46+
) {
47+
self.uploadingStatus = .uploading
48+
self.progressValue = Constants.midValue
49+
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.midValue) {
50+
self.progressValue = Constants.fullValue
51+
self.finishUpload()
6352
}
64-
}
65-
66-
guard uploadingStatus != .error else { return }
67-
68-
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
69-
self.progressValue = 1.0
70-
self.uploadingStatus = .done
53+
} onError: {
54+
self.uploadingStatus = .error
7155
}
7256
}
7357

74-
private func xcodeUserSnippetsDirectoryURL(with libraryDirectory: String) -> URL? {
75-
let openPanel = NSOpenPanel()
76-
openPanel.message = "Confirm Xcode user snippets directory"
77-
openPanel.prompt = "Confirm"
78-
openPanel.allowedFileTypes = ["none"]
79-
openPanel.allowsOtherFileTypes = false
80-
openPanel.canChooseFiles = false
81-
openPanel.canChooseDirectories = true
82-
openPanel.directoryURL = URL(string: "\(libraryDirectory)Developer/Xcode/UserData/CodeSnippets")
83-
84-
let response = openPanel.runModal()
85-
if response != .OK {
86-
uploadingStatus = .error
87-
return nil
88-
} else {
89-
return openPanel.urls.first
58+
private func finishUpload() {
59+
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.fullValue) {
60+
self.uploadingStatus = .done
9061
}
9162
}
9263

SnippetsLibrary/Services/SnippetParser/SnippetsParserService.swift

+145-1
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@
55
// Created by Krzysztof Łowiec on 12/09/2021.
66
//
77

8-
import Foundation
8+
import SwiftUI
99
import Combine
1010

1111
protocol SnippetsParserService {
1212
func createSnippet(_ snippet: Snippet) -> AnyPublisher<SnippetPlist, SnippetsParserServiceError>
1313
func removeSnippet(_ snippet: Snippet) -> AnyPublisher<Void, SnippetsParserServiceError>
14+
func writeSnippetsToPath(
15+
type: SnippetWriteType,
16+
snippets: [Snippet],
17+
uploadingStatus: Binding<UploadingStatus>,
18+
progressValue: Binding<CGFloat>,
19+
completion: @escaping () -> Void,
20+
onError: () -> Void
21+
)
1422
}
1523

1624
final class SnippetsParserServiceImpl: SnippetsParserService {
@@ -93,4 +101,140 @@ final class SnippetsParserServiceImpl: SnippetsParserService {
93101
.eraseToAnyPublisher()
94102
}
95103

104+
// MARK: - Writing
105+
106+
func writeSnippetsToPath(
107+
type: SnippetWriteType,
108+
snippets: [Snippet],
109+
uploadingStatus: Binding<UploadingStatus>,
110+
progressValue: Binding<CGFloat>,
111+
completion: @escaping () -> Void,
112+
onError: () -> Void
113+
) {
114+
switch type {
115+
case .download:
116+
write(
117+
sourceDirectory: .downloadsDirectory,
118+
componentsSeparator: "Library/",
119+
snippets: snippets,
120+
uploadingStatus: uploadingStatus,
121+
progressValue: progressValue,
122+
message: "Where do you want to save the snippet?",
123+
directoryPathSuffix: "Downloads",
124+
completion: completion,
125+
onError: onError
126+
)
127+
case .uploadToXcode:
128+
write(
129+
sourceDirectory: .libraryDirectory,
130+
componentsSeparator: "Containers/",
131+
snippets: snippets,
132+
uploadingStatus: uploadingStatus,
133+
progressValue: progressValue,
134+
message: "Confirm Xcode user snippets directory",
135+
directoryPathSuffix: "Developer/Xcode/UserData/CodeSnippets",
136+
completion: completion,
137+
onError: onError
138+
)
139+
}
140+
}
141+
142+
private func write(
143+
sourceDirectory: FileManager.SearchPathDirectory,
144+
componentsSeparator: String,
145+
snippets: [Snippet],
146+
uploadingStatus: Binding<UploadingStatus>? = nil,
147+
progressValue: Binding<CGFloat>? = nil,
148+
message: String,
149+
directoryPathSuffix: String,
150+
completion: (() -> Void)? = nil,
151+
onError: () -> Void
152+
) {
153+
let fileManager = FileManager.default
154+
let encoder = PropertyListEncoder()
155+
encoder.outputFormat = .xml
156+
157+
let directoryURLs = fileManager.urls(
158+
for: sourceDirectory,
159+
in: .userDomainMask
160+
)
161+
162+
guard
163+
let containerDirectoryURL = directoryURLs.first,
164+
let sourceDirectory = containerDirectoryURL.absoluteString.components(separatedBy: componentsSeparator).first,
165+
let userSourceDirectory = snippetSourceDirectoryURL(
166+
withDirectory: sourceDirectory,
167+
message: message,
168+
directoryPathSuffix: directoryPathSuffix,
169+
uploadingStatus: uploadingStatus
170+
)
171+
else {
172+
uploadingStatus?.wrappedValue = .error
173+
return
174+
}
175+
176+
for (index, snippet) in snippets.enumerated() {
177+
DispatchQueue.main.async {
178+
uploadingStatus?.wrappedValue = .uploading
179+
progressValue?.wrappedValue = (CGFloat(index + 1) / CGFloat(snippets.count))
180+
}
181+
do {
182+
let plistSnippet = SnippetPlist(from: snippet)
183+
let data = try encoder.encode(plistSnippet)
184+
let filePath = userSourceDirectory.appendingPathComponent("\(snippet.id).codesnippet")
185+
try data.write(to: filePath, options: [])
186+
} catch {
187+
onError()
188+
}
189+
}
190+
191+
completion?()
192+
}
193+
194+
private func snippetSourceDirectoryURL(
195+
withDirectory userDirectory: String,
196+
message: String,
197+
directoryPathSuffix: String,
198+
uploadingStatus: Binding<UploadingStatus>? = nil
199+
) -> URL? {
200+
let openPanel = NSOpenPanel()
201+
openPanel.message = message
202+
openPanel.prompt = "Confirm"
203+
openPanel.allowedFileTypes = ["none"]
204+
openPanel.allowsOtherFileTypes = false
205+
openPanel.canChooseFiles = false
206+
openPanel.canChooseDirectories = true
207+
openPanel.directoryURL = URL(string: "\(userDirectory)\(directoryPathSuffix)")
208+
209+
let response = openPanel.runModal()
210+
if response != .OK {
211+
uploadingStatus?.wrappedValue = .error
212+
return nil
213+
} else {
214+
return openPanel.urls.first
215+
}
216+
}
217+
218+
}
219+
220+
extension SnippetsParserService {
221+
222+
func writeToPath(
223+
type: SnippetWriteType,
224+
snippets: [Snippet],
225+
uploadingStatus: Binding<UploadingStatus>? = nil,
226+
progressValue: Binding<CGFloat>? = nil,
227+
completion: (() -> Void)? = nil,
228+
onError: () -> Void
229+
) {
230+
writeSnippetsToPath(
231+
type: type,
232+
snippets: snippets,
233+
uploadingStatus: uploadingStatus ?? .constant(.initializing),
234+
progressValue: progressValue ?? .constant(.zero),
235+
completion: completion ?? {},
236+
onError: onError
237+
)
238+
}
239+
96240
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// SnippetWriteType.swift
3+
// SnippetsLibrary
4+
//
5+
// Created by Krzysztof Łowiec on 14/10/2021.
6+
//
7+
8+
import Foundation
9+
10+
enum SnippetWriteType {
11+
case download
12+
case uploadToXcode
13+
}

SnippetsLibrary/Views/SnippetFileCard/SnippetFileCardView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ struct SnippetFileCardView: View {
137137
.frame(height: Constants.bottomBarHeight)
138138
.background(
139139
RoundedRectangle(cornerRadius: Constants.bottomBarCornerRadius)
140-
.foregroundColor(Color("darkGrey"))
140+
.foregroundColor(Color("skeletonLight"))
141141
)
142142
.padding(.vertical, Layout.smallPadding)
143143
}

0 commit comments

Comments
 (0)