Skip to content

Commit 3b69b94

Browse files
authored
Merge pull request #21 from tryboxx/SNLB-20/feature/widgets
[SNLB-20] Create macOS widgets
2 parents b24fdf6 + 94b2698 commit 3b69b94

File tree

23 files changed

+883
-5
lines changed

23 files changed

+883
-5
lines changed

SnippetsLibrary.xcodeproj/project.pbxproj

+272-1
Large diffs are not rendered by default.

SnippetsLibrary.xcodeproj/xcuserdata/krzysztoflowiec.xcuserdatad/xcschemes/xcschememanagement.plist

+8-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
<key>isShown</key>
1010
<false/>
1111
<key>orderHint</key>
12-
<integer>3</integer>
12+
<integer>24</integer>
1313
</dict>
1414
<key>Promises (Playground) 2.xcscheme</key>
1515
<dict>
1616
<key>isShown</key>
1717
<false/>
1818
<key>orderHint</key>
19-
<integer>4</integer>
19+
<integer>25</integer>
2020
</dict>
2121
<key>Promises (Playground) 3.xcscheme</key>
2222
<dict>
@@ -44,13 +44,18 @@
4444
<key>isShown</key>
4545
<false/>
4646
<key>orderHint</key>
47-
<integer>2</integer>
47+
<integer>23</integer>
4848
</dict>
4949
<key>SnippetsLibrary.xcscheme_^#shared#^_</key>
5050
<dict>
5151
<key>orderHint</key>
5252
<integer>0</integer>
5353
</dict>
54+
<key>SnippetsLibraryWidgetExtension.xcscheme_^#shared#^_</key>
55+
<dict>
56+
<key>orderHint</key>
57+
<integer>1</integer>
58+
</dict>
5459
</dict>
5560
<key>SuppressBuildableAutocreation</key>
5661
<dict>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"colors" : [
3+
{
4+
"color" : {
5+
"color-space" : "display-p3",
6+
"components" : {
7+
"alpha" : "1.000",
8+
"blue" : "0.971",
9+
"green" : "0.962",
10+
"red" : "0.966"
11+
}
12+
},
13+
"idiom" : "universal"
14+
},
15+
{
16+
"appearances" : [
17+
{
18+
"appearance" : "luminosity",
19+
"value" : "dark"
20+
}
21+
],
22+
"color" : {
23+
"color-space" : "display-p3",
24+
"components" : {
25+
"alpha" : "1.000",
26+
"blue" : "0.102",
27+
"green" : "0.102",
28+
"red" : "0.102"
29+
}
30+
},
31+
"idiom" : "universal"
32+
}
33+
],
34+
"info" : {
35+
"author" : "xcode",
36+
"version" : 1
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "icSnippetFileBlank.png",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Loading

SnippetsLibrary/Services/UserDefaults/UserDefaultsService.swift

+45-1
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ import Combine
1111
protocol UserDefaultsService {
1212
func saveRecentSnippet(_ snippet: Snippet)
1313
func fetchRecentSnippets() -> AnyPublisher<[Snippet], Never>
14+
15+
func fetchRecentSnippetsFromAppGroup() -> [Snippet]
1416
}
1517

1618
final class UserDefaultsServiceImpl: UserDefaultsService {
1719

1820
private enum Constants {
1921
static let recentSnippetKey = "RecentSnippet"
22+
static let appGroupName = "group.com.cphlowiec.SnippetsLibrary"
2023
}
2124

2225
// MARK: - Stored Properties
@@ -25,13 +28,21 @@ final class UserDefaultsServiceImpl: UserDefaultsService {
2528

2629
// MARK: - Methods
2730

31+
// MARK: - Recent Snippets -
32+
2833
internal func saveRecentSnippet(_ snippet: Snippet) {
2934
let snippet = SnippetPlist(from: snippet)
3035
let snippetDictonary = snippet.convertedToDictonary()
36+
let snippetKey = Constants.recentSnippetKey + " \(snippet.id)"
3137

3238
userDefaults.set(
3339
snippetDictonary,
34-
forKey: Constants.recentSnippetKey + " \(snippet.id)"
40+
forKey: snippetKey
41+
)
42+
43+
saveSnippetDictonaryIntoAppGroup(
44+
snippetDictonary,
45+
key: snippetKey
3546
)
3647
}
3748

@@ -56,4 +67,37 @@ final class UserDefaultsServiceImpl: UserDefaultsService {
5667
.eraseToAnyPublisher()
5768
}
5869

70+
// MARK: - App Group -
71+
72+
internal func fetchRecentSnippetsFromAppGroup() -> [Snippet] {
73+
guard let userDefaults = UserDefaults(suiteName: Constants.appGroupName) else { return [] }
74+
75+
let keys = userDefaults.dictionaryRepresentation().keys.filter { $0.contains(Constants.recentSnippetKey) }
76+
var snippets = [Snippet]()
77+
78+
for key in keys {
79+
guard
80+
let snippetDictonary = userDefaults.object(forKey: key),
81+
let data = try? JSONSerialization.data(withJSONObject: snippetDictonary, options: []),
82+
let snippet = try? JSONDecoder().decode(SnippetPlist.self, from: data)
83+
else { continue }
84+
85+
snippets.append(Snippet(from: snippet))
86+
}
87+
88+
return snippets
89+
}
90+
91+
private func saveSnippetDictonaryIntoAppGroup(
92+
_ snippetDictonary: [String: Any],
93+
key: String
94+
) {
95+
guard let userDefaults = UserDefaults(suiteName: Constants.appGroupName) else { return }
96+
97+
userDefaults.set(
98+
snippetDictonary,
99+
forKey: key
100+
)
101+
}
102+
59103
}

SnippetsLibrary/SnippetsLibrary.entitlements

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
<dict>
55
<key>com.apple.security.app-sandbox</key>
66
<true/>
7+
<key>com.apple.security.application-groups</key>
8+
<array>
9+
<string>group.com.cphlowiec.SnippetsLibrary</string>
10+
</array>
711
<key>com.apple.security.files.user-selected.read-write</key>
812
<true/>
913
<key>com.apple.security.network.client</key>

SnippetsLibrary/SnippetsLibraryDebug.entitlements

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
<dict>
55
<key>com.apple.security.app-sandbox</key>
66
<true/>
7+
<key>com.apple.security.application-groups</key>
8+
<array>
9+
<string>group.com.cphlowiec.SnippetsLibrary</string>
10+
</array>
711
<key>com.apple.security.files.user-selected.read-write</key>
812
<true/>
913
<key>com.apple.security.network.client</key>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// UserDefaultsServiceError.swift
3+
// SnippetsLibrary
4+
//
5+
// Created by Krzysztof Łowiec on 02/10/2021.
6+
//
7+
8+
import Foundation
9+
10+
enum UserDefaultsServiceError: Error {
11+
case unableToGetData
12+
}

SnippetsLibrary/Views/AppView.swift

+26
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import SwiftUI
99

1010
struct AppView: View {
1111

12+
private enum Constants {
13+
static let statusWindowClassName = "NSStatusBarWindow"
14+
static let delay = DispatchTime.now() + 0.01
15+
}
16+
1217
// MARK: - Stored Properties
1318

1419
@Environment(\.scenePhase) var scenePhase
@@ -29,6 +34,9 @@ struct AppView: View {
2934
shouldShowNetworkAlert.toggle()
3035
}
3136
}
37+
.onOpenURL {
38+
openSnippet(fromURL: $0)
39+
}
3240
.makeDisplayed(
3341
with: $shouldShowNetworkAlert,
3442
imageName: "network",
@@ -86,6 +94,24 @@ struct AppView: View {
8694
}
8795
}
8896

97+
private func openSnippet(fromURL url: URL) {
98+
closeWindowsIfVisible()
99+
let snippetId = url.absoluteString.replacingOccurrences(of: "widget://", with: "")
100+
101+
DispatchQueue.main.asyncAfter(deadline: Constants.delay) {
102+
NSApplication.shared.windows.first?.orderFront(nil)
103+
activeAppView = .snippetsLibrary(snippetId)
104+
}
105+
}
106+
107+
private func closeWindowsIfVisible() {
108+
for window in NSApplication.shared.windows where window.className != Constants.statusWindowClassName {
109+
DispatchQueue.main.async {
110+
window.close()
111+
}
112+
}
113+
}
114+
89115
}
90116

91117

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}

SnippetsLibraryWidget/Info.plist

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>NSExtension</key>
6+
<dict>
7+
<key>NSExtensionPointIdentifier</key>
8+
<string>com.apple.widgetkit-extension</string>
9+
</dict>
10+
</dict>
11+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// WidgetEntry.swift
3+
// SnippetsLibrary
4+
//
5+
// Created by Krzysztof Łowiec on 03/10/2021.
6+
//
7+
8+
import WidgetKit
9+
10+
struct WidgetEntry: TimelineEntry {
11+
12+
var date: Date = Date()
13+
let snippet: Snippet
14+
let snippets: [Snippet]
15+
16+
}

0 commit comments

Comments
 (0)