├── .gitignore
├── LICENSE
├── README.md
├── To-do Bar.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── To-do Bar
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── 1024.png
│ │ ├── 128.png
│ │ ├── 16.png
│ │ ├── 256-1.png
│ │ ├── 256.png
│ │ ├── 32-1.png
│ │ ├── 32.png
│ │ ├── 512-1.png
│ │ ├── 512.png
│ │ ├── 64.png
│ │ └── Contents.json
│ ├── BarIcon.imageset
│ │ ├── 20.png
│ │ ├── 40.png
│ │ ├── 60.png
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ └── Main.storyboard
├── Info.plist
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
└── To_do_Bar.entitlements
├── To-do BarTests
├── Info.plist
└── To_do_BarTests.swift
├── To-do BarUITests
├── Info.plist
└── To_do_BarUITests.swift
└── img
├── To-do Bar.icns
├── To-do Bar.sketch
├── screen1.png
├── screen1_v1.png
├── screen2.png
├── screen2_v2.png
├── tutorial.gif
├── tutorial.mp4
├── tutorial_v1.gif
└── tutorial_v1.mp4
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # Created by https://www.gitignore.io/api/macos,swift
4 | # Edit at https://www.gitignore.io/?templates=macos,swift
5 |
6 | ### macOS ###
7 | # General
8 | .DS_Store
9 | .AppleDouble
10 | .LSOverride
11 |
12 | # Icon must end with two \r
13 | Icon
14 |
15 | # Thumbnails
16 | ._*
17 |
18 | # Files that might appear in the root of a volume
19 | .DocumentRevisions-V100
20 | .fseventsd
21 | .Spotlight-V100
22 | .TemporaryItems
23 | .Trashes
24 | .VolumeIcon.icns
25 | .com.apple.timemachine.donotpresent
26 |
27 | # Directories potentially created on remote AFP share
28 | .AppleDB
29 | .AppleDesktop
30 | Network Trash Folder
31 | Temporary Items
32 | .apdisk
33 |
34 | ### Swift ###
35 | # Xcode
36 | #
37 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
38 |
39 | ## Build generated
40 | build/
41 | DerivedData/
42 |
43 | ## Various settings
44 | *.pbxuser
45 | !default.pbxuser
46 | *.mode1v3
47 | !default.mode1v3
48 | *.mode2v3
49 | !default.mode2v3
50 | *.perspectivev3
51 | !default.perspectivev3
52 | xcuserdata/
53 |
54 | ## Other
55 | *.moved-aside
56 | *.xccheckout
57 | *.xcscmblueprint
58 |
59 | ## Obj-C/Swift specific
60 | *.hmap
61 | *.ipa
62 | *.dSYM.zip
63 | *.dSYM
64 |
65 | ## Playgrounds
66 | timeline.xctimeline
67 | playground.xcworkspace
68 |
69 | # Swift Package Manager
70 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
71 | # Packages/
72 | # Package.pins
73 | # Package.resolved
74 | .build/
75 |
76 | # CocoaPods
77 | # We recommend against adding the Pods directory to your .gitignore. However
78 | # you should judge for yourself, the pros and cons are mentioned at:
79 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
80 | # Pods/
81 | # Add this line if you want to avoid checking in source code from the Xcode workspace
82 | # *.xcworkspace
83 |
84 | # Carthage
85 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
86 | # Carthage/Checkouts
87 |
88 | Carthage/Build
89 |
90 | # fastlane
91 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
92 | # screenshots whenever they are needed.
93 | # For more information about the recommended setup visit:
94 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
95 |
96 | fastlane/report.xml
97 | fastlane/Preview.html
98 | fastlane/screenshots/**/*.png
99 | fastlane/test_output
100 |
101 | # Code Injection
102 | # After new code Injection tools there's a generated folder /iOSInjectionProject
103 | # https://github.com/johnno1962/injectionforxcode
104 |
105 | iOSInjectionProject/
106 |
107 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
108 | *.xcscmblueprint
109 | *.xccheckout
110 |
111 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
112 | build/
113 | DerivedData/
114 | *.moved-aside
115 | *.pbxuser
116 | !default.pbxuser
117 | *.mode1v3
118 | !default.mode1v3
119 | *.mode2v3
120 | !default.mode2v3
121 | *.perspectivev3
122 | !default.perspectivev3
123 |
124 | ### Xcode Patch ###
125 | /*.gcno
126 | **/xcshareddata/WorkspaceSettings.xcsettings
127 |
128 | # End of https://www.gitignore.io/api/macos,swift
129 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Zhexiong Liu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | ## To-do Bar
16 | To-do Bar empowers [MacOS](https://www.apple.com/macos/) [Reminders](https://support.apple.com/guide/reminders/)/[Calendar](https://support.apple.com/guide/calendar/), allowing todo tasks available in menu bar.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ## Manual download
25 |
26 | * [Download latest version](https://github.com/ZhexiongLiu/TodoBar/releases/latest)
27 | * Open and drag the app to the Applications folder.
28 |
29 | ## Usage
30 |
31 | * Grant To-do Bar access to your MacOS Reminders/Calenders.
32 | * Click To-do Bar menu icon to show your todo list.
33 | * Click your reminder items to mark as completed.
34 | * Calendar items would be hidden when time passed.
35 |
36 |
37 |
38 |
39 |
40 | ## Related App
41 | [Ticker Bar](https://github.com/ZhexiongLiu/TickerBar)
42 |
43 | ## License
44 |
45 | MIT © [Zhexiong Liu](https://github.com/ZhexiongLiu)
46 |
--------------------------------------------------------------------------------
/To-do Bar.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 52C2B32D240C46BB006E217F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C2B32C240C46BB006E217F /* AppDelegate.swift */; };
11 | 52C2B331240C46BC006E217F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52C2B330240C46BC006E217F /* Assets.xcassets */; };
12 | 52C2B334240C46BC006E217F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52C2B333240C46BC006E217F /* Preview Assets.xcassets */; };
13 | 52C2B337240C46BC006E217F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52C2B335240C46BC006E217F /* Main.storyboard */; };
14 | 52C2B343240C46BC006E217F /* To_do_BarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C2B342240C46BC006E217F /* To_do_BarTests.swift */; };
15 | 52C2B34E240C46BC006E217F /* To_do_BarUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C2B34D240C46BC006E217F /* To_do_BarUITests.swift */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXContainerItemProxy section */
19 | 52C2B33F240C46BC006E217F /* PBXContainerItemProxy */ = {
20 | isa = PBXContainerItemProxy;
21 | containerPortal = 52C2B321240C46BB006E217F /* Project object */;
22 | proxyType = 1;
23 | remoteGlobalIDString = 52C2B328240C46BB006E217F;
24 | remoteInfo = "To-do Bar";
25 | };
26 | 52C2B34A240C46BC006E217F /* PBXContainerItemProxy */ = {
27 | isa = PBXContainerItemProxy;
28 | containerPortal = 52C2B321240C46BB006E217F /* Project object */;
29 | proxyType = 1;
30 | remoteGlobalIDString = 52C2B328240C46BB006E217F;
31 | remoteInfo = "To-do Bar";
32 | };
33 | /* End PBXContainerItemProxy section */
34 |
35 | /* Begin PBXFileReference section */
36 | 52C2B329240C46BB006E217F /* To-do Bar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "To-do Bar.app"; sourceTree = BUILT_PRODUCTS_DIR; };
37 | 52C2B32C240C46BB006E217F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
38 | 52C2B330240C46BC006E217F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
39 | 52C2B333240C46BC006E217F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
40 | 52C2B336240C46BC006E217F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
41 | 52C2B338240C46BC006E217F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
42 | 52C2B339240C46BC006E217F /* To_do_Bar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = To_do_Bar.entitlements; sourceTree = ""; };
43 | 52C2B33E240C46BC006E217F /* To-do BarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "To-do BarTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 52C2B342240C46BC006E217F /* To_do_BarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = To_do_BarTests.swift; sourceTree = ""; };
45 | 52C2B344240C46BC006E217F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
46 | 52C2B349240C46BC006E217F /* To-do BarUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "To-do BarUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 52C2B34D240C46BC006E217F /* To_do_BarUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = To_do_BarUITests.swift; sourceTree = ""; };
48 | 52C2B34F240C46BC006E217F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
49 | /* End PBXFileReference section */
50 |
51 | /* Begin PBXFrameworksBuildPhase section */
52 | 52C2B326240C46BB006E217F /* Frameworks */ = {
53 | isa = PBXFrameworksBuildPhase;
54 | buildActionMask = 2147483647;
55 | files = (
56 | );
57 | runOnlyForDeploymentPostprocessing = 0;
58 | };
59 | 52C2B33B240C46BC006E217F /* Frameworks */ = {
60 | isa = PBXFrameworksBuildPhase;
61 | buildActionMask = 2147483647;
62 | files = (
63 | );
64 | runOnlyForDeploymentPostprocessing = 0;
65 | };
66 | 52C2B346240C46BC006E217F /* Frameworks */ = {
67 | isa = PBXFrameworksBuildPhase;
68 | buildActionMask = 2147483647;
69 | files = (
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | /* End PBXFrameworksBuildPhase section */
74 |
75 | /* Begin PBXGroup section */
76 | 52C2B320240C46BB006E217F = {
77 | isa = PBXGroup;
78 | children = (
79 | 52C2B32B240C46BB006E217F /* To-do Bar */,
80 | 52C2B341240C46BC006E217F /* To-do BarTests */,
81 | 52C2B34C240C46BC006E217F /* To-do BarUITests */,
82 | 52C2B32A240C46BB006E217F /* Products */,
83 | );
84 | sourceTree = "";
85 | };
86 | 52C2B32A240C46BB006E217F /* Products */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 52C2B329240C46BB006E217F /* To-do Bar.app */,
90 | 52C2B33E240C46BC006E217F /* To-do BarTests.xctest */,
91 | 52C2B349240C46BC006E217F /* To-do BarUITests.xctest */,
92 | );
93 | name = Products;
94 | sourceTree = "";
95 | };
96 | 52C2B32B240C46BB006E217F /* To-do Bar */ = {
97 | isa = PBXGroup;
98 | children = (
99 | 52C2B32C240C46BB006E217F /* AppDelegate.swift */,
100 | 52C2B330240C46BC006E217F /* Assets.xcassets */,
101 | 52C2B335240C46BC006E217F /* Main.storyboard */,
102 | 52C2B338240C46BC006E217F /* Info.plist */,
103 | 52C2B339240C46BC006E217F /* To_do_Bar.entitlements */,
104 | 52C2B332240C46BC006E217F /* Preview Content */,
105 | );
106 | path = "To-do Bar";
107 | sourceTree = "";
108 | };
109 | 52C2B332240C46BC006E217F /* Preview Content */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 52C2B333240C46BC006E217F /* Preview Assets.xcassets */,
113 | );
114 | path = "Preview Content";
115 | sourceTree = "";
116 | };
117 | 52C2B341240C46BC006E217F /* To-do BarTests */ = {
118 | isa = PBXGroup;
119 | children = (
120 | 52C2B342240C46BC006E217F /* To_do_BarTests.swift */,
121 | 52C2B344240C46BC006E217F /* Info.plist */,
122 | );
123 | path = "To-do BarTests";
124 | sourceTree = "";
125 | };
126 | 52C2B34C240C46BC006E217F /* To-do BarUITests */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 52C2B34D240C46BC006E217F /* To_do_BarUITests.swift */,
130 | 52C2B34F240C46BC006E217F /* Info.plist */,
131 | );
132 | path = "To-do BarUITests";
133 | sourceTree = "";
134 | };
135 | /* End PBXGroup section */
136 |
137 | /* Begin PBXNativeTarget section */
138 | 52C2B328240C46BB006E217F /* To-do Bar */ = {
139 | isa = PBXNativeTarget;
140 | buildConfigurationList = 52C2B352240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do Bar" */;
141 | buildPhases = (
142 | 52C2B325240C46BB006E217F /* Sources */,
143 | 52C2B326240C46BB006E217F /* Frameworks */,
144 | 52C2B327240C46BB006E217F /* Resources */,
145 | );
146 | buildRules = (
147 | );
148 | dependencies = (
149 | );
150 | name = "To-do Bar";
151 | productName = "To-do Bar";
152 | productReference = 52C2B329240C46BB006E217F /* To-do Bar.app */;
153 | productType = "com.apple.product-type.application";
154 | };
155 | 52C2B33D240C46BC006E217F /* To-do BarTests */ = {
156 | isa = PBXNativeTarget;
157 | buildConfigurationList = 52C2B355240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do BarTests" */;
158 | buildPhases = (
159 | 52C2B33A240C46BC006E217F /* Sources */,
160 | 52C2B33B240C46BC006E217F /* Frameworks */,
161 | 52C2B33C240C46BC006E217F /* Resources */,
162 | );
163 | buildRules = (
164 | );
165 | dependencies = (
166 | 52C2B340240C46BC006E217F /* PBXTargetDependency */,
167 | );
168 | name = "To-do BarTests";
169 | productName = "To-do BarTests";
170 | productReference = 52C2B33E240C46BC006E217F /* To-do BarTests.xctest */;
171 | productType = "com.apple.product-type.bundle.unit-test";
172 | };
173 | 52C2B348240C46BC006E217F /* To-do BarUITests */ = {
174 | isa = PBXNativeTarget;
175 | buildConfigurationList = 52C2B358240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do BarUITests" */;
176 | buildPhases = (
177 | 52C2B345240C46BC006E217F /* Sources */,
178 | 52C2B346240C46BC006E217F /* Frameworks */,
179 | 52C2B347240C46BC006E217F /* Resources */,
180 | );
181 | buildRules = (
182 | );
183 | dependencies = (
184 | 52C2B34B240C46BC006E217F /* PBXTargetDependency */,
185 | );
186 | name = "To-do BarUITests";
187 | productName = "To-do BarUITests";
188 | productReference = 52C2B349240C46BC006E217F /* To-do BarUITests.xctest */;
189 | productType = "com.apple.product-type.bundle.ui-testing";
190 | };
191 | /* End PBXNativeTarget section */
192 |
193 | /* Begin PBXProject section */
194 | 52C2B321240C46BB006E217F /* Project object */ = {
195 | isa = PBXProject;
196 | attributes = {
197 | LastSwiftUpdateCheck = 1130;
198 | LastUpgradeCheck = 1130;
199 | ORGANIZATIONNAME = "Zhexiong Liu";
200 | TargetAttributes = {
201 | 52C2B328240C46BB006E217F = {
202 | CreatedOnToolsVersion = 11.3.1;
203 | };
204 | 52C2B33D240C46BC006E217F = {
205 | CreatedOnToolsVersion = 11.3.1;
206 | TestTargetID = 52C2B328240C46BB006E217F;
207 | };
208 | 52C2B348240C46BC006E217F = {
209 | CreatedOnToolsVersion = 11.3.1;
210 | TestTargetID = 52C2B328240C46BB006E217F;
211 | };
212 | };
213 | };
214 | buildConfigurationList = 52C2B324240C46BB006E217F /* Build configuration list for PBXProject "To-do Bar" */;
215 | compatibilityVersion = "Xcode 9.3";
216 | developmentRegion = en;
217 | hasScannedForEncodings = 0;
218 | knownRegions = (
219 | en,
220 | Base,
221 | );
222 | mainGroup = 52C2B320240C46BB006E217F;
223 | productRefGroup = 52C2B32A240C46BB006E217F /* Products */;
224 | projectDirPath = "";
225 | projectRoot = "";
226 | targets = (
227 | 52C2B328240C46BB006E217F /* To-do Bar */,
228 | 52C2B33D240C46BC006E217F /* To-do BarTests */,
229 | 52C2B348240C46BC006E217F /* To-do BarUITests */,
230 | );
231 | };
232 | /* End PBXProject section */
233 |
234 | /* Begin PBXResourcesBuildPhase section */
235 | 52C2B327240C46BB006E217F /* Resources */ = {
236 | isa = PBXResourcesBuildPhase;
237 | buildActionMask = 2147483647;
238 | files = (
239 | 52C2B337240C46BC006E217F /* Main.storyboard in Resources */,
240 | 52C2B334240C46BC006E217F /* Preview Assets.xcassets in Resources */,
241 | 52C2B331240C46BC006E217F /* Assets.xcassets in Resources */,
242 | );
243 | runOnlyForDeploymentPostprocessing = 0;
244 | };
245 | 52C2B33C240C46BC006E217F /* Resources */ = {
246 | isa = PBXResourcesBuildPhase;
247 | buildActionMask = 2147483647;
248 | files = (
249 | );
250 | runOnlyForDeploymentPostprocessing = 0;
251 | };
252 | 52C2B347240C46BC006E217F /* Resources */ = {
253 | isa = PBXResourcesBuildPhase;
254 | buildActionMask = 2147483647;
255 | files = (
256 | );
257 | runOnlyForDeploymentPostprocessing = 0;
258 | };
259 | /* End PBXResourcesBuildPhase section */
260 |
261 | /* Begin PBXSourcesBuildPhase section */
262 | 52C2B325240C46BB006E217F /* Sources */ = {
263 | isa = PBXSourcesBuildPhase;
264 | buildActionMask = 2147483647;
265 | files = (
266 | 52C2B32D240C46BB006E217F /* AppDelegate.swift in Sources */,
267 | );
268 | runOnlyForDeploymentPostprocessing = 0;
269 | };
270 | 52C2B33A240C46BC006E217F /* Sources */ = {
271 | isa = PBXSourcesBuildPhase;
272 | buildActionMask = 2147483647;
273 | files = (
274 | 52C2B343240C46BC006E217F /* To_do_BarTests.swift in Sources */,
275 | );
276 | runOnlyForDeploymentPostprocessing = 0;
277 | };
278 | 52C2B345240C46BC006E217F /* Sources */ = {
279 | isa = PBXSourcesBuildPhase;
280 | buildActionMask = 2147483647;
281 | files = (
282 | 52C2B34E240C46BC006E217F /* To_do_BarUITests.swift in Sources */,
283 | );
284 | runOnlyForDeploymentPostprocessing = 0;
285 | };
286 | /* End PBXSourcesBuildPhase section */
287 |
288 | /* Begin PBXTargetDependency section */
289 | 52C2B340240C46BC006E217F /* PBXTargetDependency */ = {
290 | isa = PBXTargetDependency;
291 | target = 52C2B328240C46BB006E217F /* To-do Bar */;
292 | targetProxy = 52C2B33F240C46BC006E217F /* PBXContainerItemProxy */;
293 | };
294 | 52C2B34B240C46BC006E217F /* PBXTargetDependency */ = {
295 | isa = PBXTargetDependency;
296 | target = 52C2B328240C46BB006E217F /* To-do Bar */;
297 | targetProxy = 52C2B34A240C46BC006E217F /* PBXContainerItemProxy */;
298 | };
299 | /* End PBXTargetDependency section */
300 |
301 | /* Begin PBXVariantGroup section */
302 | 52C2B335240C46BC006E217F /* Main.storyboard */ = {
303 | isa = PBXVariantGroup;
304 | children = (
305 | 52C2B336240C46BC006E217F /* Base */,
306 | );
307 | name = Main.storyboard;
308 | sourceTree = "";
309 | };
310 | /* End PBXVariantGroup section */
311 |
312 | /* Begin XCBuildConfiguration section */
313 | 52C2B350240C46BC006E217F /* Debug */ = {
314 | isa = XCBuildConfiguration;
315 | buildSettings = {
316 | ALWAYS_SEARCH_USER_PATHS = NO;
317 | CLANG_ANALYZER_NONNULL = YES;
318 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
319 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
320 | CLANG_CXX_LIBRARY = "libc++";
321 | CLANG_ENABLE_MODULES = YES;
322 | CLANG_ENABLE_OBJC_ARC = YES;
323 | CLANG_ENABLE_OBJC_WEAK = YES;
324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
325 | CLANG_WARN_BOOL_CONVERSION = YES;
326 | CLANG_WARN_COMMA = YES;
327 | CLANG_WARN_CONSTANT_CONVERSION = YES;
328 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
330 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
331 | CLANG_WARN_EMPTY_BODY = YES;
332 | CLANG_WARN_ENUM_CONVERSION = YES;
333 | CLANG_WARN_INFINITE_RECURSION = YES;
334 | CLANG_WARN_INT_CONVERSION = YES;
335 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
336 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
337 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
338 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
339 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
340 | CLANG_WARN_STRICT_PROTOTYPES = YES;
341 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
342 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
343 | CLANG_WARN_UNREACHABLE_CODE = YES;
344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
345 | COPY_PHASE_STRIP = NO;
346 | DEBUG_INFORMATION_FORMAT = dwarf;
347 | ENABLE_STRICT_OBJC_MSGSEND = YES;
348 | ENABLE_TESTABILITY = YES;
349 | GCC_C_LANGUAGE_STANDARD = gnu11;
350 | GCC_DYNAMIC_NO_PIC = NO;
351 | GCC_NO_COMMON_BLOCKS = YES;
352 | GCC_OPTIMIZATION_LEVEL = 0;
353 | GCC_PREPROCESSOR_DEFINITIONS = (
354 | "DEBUG=1",
355 | "$(inherited)",
356 | );
357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
359 | GCC_WARN_UNDECLARED_SELECTOR = YES;
360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
361 | GCC_WARN_UNUSED_FUNCTION = YES;
362 | GCC_WARN_UNUSED_VARIABLE = YES;
363 | MACOSX_DEPLOYMENT_TARGET = 10.15;
364 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
365 | MTL_FAST_MATH = YES;
366 | ONLY_ACTIVE_ARCH = YES;
367 | SDKROOT = macosx;
368 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
369 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
370 | };
371 | name = Debug;
372 | };
373 | 52C2B351240C46BC006E217F /* Release */ = {
374 | isa = XCBuildConfiguration;
375 | buildSettings = {
376 | ALWAYS_SEARCH_USER_PATHS = NO;
377 | CLANG_ANALYZER_NONNULL = YES;
378 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
379 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
380 | CLANG_CXX_LIBRARY = "libc++";
381 | CLANG_ENABLE_MODULES = YES;
382 | CLANG_ENABLE_OBJC_ARC = YES;
383 | CLANG_ENABLE_OBJC_WEAK = YES;
384 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
385 | CLANG_WARN_BOOL_CONVERSION = YES;
386 | CLANG_WARN_COMMA = YES;
387 | CLANG_WARN_CONSTANT_CONVERSION = YES;
388 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
389 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
390 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
391 | CLANG_WARN_EMPTY_BODY = YES;
392 | CLANG_WARN_ENUM_CONVERSION = YES;
393 | CLANG_WARN_INFINITE_RECURSION = YES;
394 | CLANG_WARN_INT_CONVERSION = YES;
395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
396 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
397 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
399 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
400 | CLANG_WARN_STRICT_PROTOTYPES = YES;
401 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
402 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
403 | CLANG_WARN_UNREACHABLE_CODE = YES;
404 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
405 | COPY_PHASE_STRIP = NO;
406 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
407 | ENABLE_NS_ASSERTIONS = NO;
408 | ENABLE_STRICT_OBJC_MSGSEND = YES;
409 | GCC_C_LANGUAGE_STANDARD = gnu11;
410 | GCC_NO_COMMON_BLOCKS = YES;
411 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
412 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
413 | GCC_WARN_UNDECLARED_SELECTOR = YES;
414 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
415 | GCC_WARN_UNUSED_FUNCTION = YES;
416 | GCC_WARN_UNUSED_VARIABLE = YES;
417 | MACOSX_DEPLOYMENT_TARGET = 10.15;
418 | MTL_ENABLE_DEBUG_INFO = NO;
419 | MTL_FAST_MATH = YES;
420 | SDKROOT = macosx;
421 | SWIFT_COMPILATION_MODE = wholemodule;
422 | SWIFT_OPTIMIZATION_LEVEL = "-O";
423 | };
424 | name = Release;
425 | };
426 | 52C2B353240C46BC006E217F /* Debug */ = {
427 | isa = XCBuildConfiguration;
428 | buildSettings = {
429 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
430 | CODE_SIGN_ENTITLEMENTS = "To-do Bar/To_do_Bar.entitlements";
431 | CODE_SIGN_STYLE = Automatic;
432 | COMBINE_HIDPI_IMAGES = YES;
433 | DEVELOPMENT_ASSET_PATHS = "\"To-do Bar/Preview Content\"";
434 | DEVELOPMENT_TEAM = 5TAHPLUH9U;
435 | ENABLE_PREVIEWS = YES;
436 | INFOPLIST_FILE = "To-do Bar/Info.plist";
437 | LD_RUNPATH_SEARCH_PATHS = (
438 | "$(inherited)",
439 | "@executable_path/../Frameworks",
440 | );
441 | MACOSX_DEPLOYMENT_TARGET = 10.15;
442 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-Bar";
443 | PRODUCT_NAME = "$(TARGET_NAME)";
444 | SWIFT_VERSION = 5.0;
445 | };
446 | name = Debug;
447 | };
448 | 52C2B354240C46BC006E217F /* Release */ = {
449 | isa = XCBuildConfiguration;
450 | buildSettings = {
451 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
452 | CODE_SIGN_ENTITLEMENTS = "To-do Bar/To_do_Bar.entitlements";
453 | CODE_SIGN_STYLE = Automatic;
454 | COMBINE_HIDPI_IMAGES = YES;
455 | DEVELOPMENT_ASSET_PATHS = "\"To-do Bar/Preview Content\"";
456 | DEVELOPMENT_TEAM = 5TAHPLUH9U;
457 | ENABLE_PREVIEWS = YES;
458 | INFOPLIST_FILE = "To-do Bar/Info.plist";
459 | LD_RUNPATH_SEARCH_PATHS = (
460 | "$(inherited)",
461 | "@executable_path/../Frameworks",
462 | );
463 | MACOSX_DEPLOYMENT_TARGET = 10.15;
464 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-Bar";
465 | PRODUCT_NAME = "$(TARGET_NAME)";
466 | SWIFT_VERSION = 5.0;
467 | };
468 | name = Release;
469 | };
470 | 52C2B356240C46BC006E217F /* Debug */ = {
471 | isa = XCBuildConfiguration;
472 | buildSettings = {
473 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
474 | BUNDLE_LOADER = "$(TEST_HOST)";
475 | CODE_SIGN_STYLE = Automatic;
476 | COMBINE_HIDPI_IMAGES = YES;
477 | INFOPLIST_FILE = "To-do BarTests/Info.plist";
478 | LD_RUNPATH_SEARCH_PATHS = (
479 | "$(inherited)",
480 | "@executable_path/../Frameworks",
481 | "@loader_path/../Frameworks",
482 | );
483 | MACOSX_DEPLOYMENT_TARGET = 10.15;
484 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-BarTests";
485 | PRODUCT_NAME = "$(TARGET_NAME)";
486 | SWIFT_VERSION = 5.0;
487 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/To-do Bar.app/Contents/MacOS/To-do Bar";
488 | };
489 | name = Debug;
490 | };
491 | 52C2B357240C46BC006E217F /* Release */ = {
492 | isa = XCBuildConfiguration;
493 | buildSettings = {
494 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
495 | BUNDLE_LOADER = "$(TEST_HOST)";
496 | CODE_SIGN_STYLE = Automatic;
497 | COMBINE_HIDPI_IMAGES = YES;
498 | INFOPLIST_FILE = "To-do BarTests/Info.plist";
499 | LD_RUNPATH_SEARCH_PATHS = (
500 | "$(inherited)",
501 | "@executable_path/../Frameworks",
502 | "@loader_path/../Frameworks",
503 | );
504 | MACOSX_DEPLOYMENT_TARGET = 10.15;
505 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-BarTests";
506 | PRODUCT_NAME = "$(TARGET_NAME)";
507 | SWIFT_VERSION = 5.0;
508 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/To-do Bar.app/Contents/MacOS/To-do Bar";
509 | };
510 | name = Release;
511 | };
512 | 52C2B359240C46BC006E217F /* Debug */ = {
513 | isa = XCBuildConfiguration;
514 | buildSettings = {
515 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
516 | CODE_SIGN_STYLE = Automatic;
517 | COMBINE_HIDPI_IMAGES = YES;
518 | INFOPLIST_FILE = "To-do BarUITests/Info.plist";
519 | LD_RUNPATH_SEARCH_PATHS = (
520 | "$(inherited)",
521 | "@executable_path/../Frameworks",
522 | "@loader_path/../Frameworks",
523 | );
524 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-BarUITests";
525 | PRODUCT_NAME = "$(TARGET_NAME)";
526 | SWIFT_VERSION = 5.0;
527 | TEST_TARGET_NAME = "To-do Bar";
528 | };
529 | name = Debug;
530 | };
531 | 52C2B35A240C46BC006E217F /* Release */ = {
532 | isa = XCBuildConfiguration;
533 | buildSettings = {
534 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
535 | CODE_SIGN_STYLE = Automatic;
536 | COMBINE_HIDPI_IMAGES = YES;
537 | INFOPLIST_FILE = "To-do BarUITests/Info.plist";
538 | LD_RUNPATH_SEARCH_PATHS = (
539 | "$(inherited)",
540 | "@executable_path/../Frameworks",
541 | "@loader_path/../Frameworks",
542 | );
543 | PRODUCT_BUNDLE_IDENTIFIER = "Zhexiong-Liu.To-do-BarUITests";
544 | PRODUCT_NAME = "$(TARGET_NAME)";
545 | SWIFT_VERSION = 5.0;
546 | TEST_TARGET_NAME = "To-do Bar";
547 | };
548 | name = Release;
549 | };
550 | /* End XCBuildConfiguration section */
551 |
552 | /* Begin XCConfigurationList section */
553 | 52C2B324240C46BB006E217F /* Build configuration list for PBXProject "To-do Bar" */ = {
554 | isa = XCConfigurationList;
555 | buildConfigurations = (
556 | 52C2B350240C46BC006E217F /* Debug */,
557 | 52C2B351240C46BC006E217F /* Release */,
558 | );
559 | defaultConfigurationIsVisible = 0;
560 | defaultConfigurationName = Release;
561 | };
562 | 52C2B352240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do Bar" */ = {
563 | isa = XCConfigurationList;
564 | buildConfigurations = (
565 | 52C2B353240C46BC006E217F /* Debug */,
566 | 52C2B354240C46BC006E217F /* Release */,
567 | );
568 | defaultConfigurationIsVisible = 0;
569 | defaultConfigurationName = Release;
570 | };
571 | 52C2B355240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do BarTests" */ = {
572 | isa = XCConfigurationList;
573 | buildConfigurations = (
574 | 52C2B356240C46BC006E217F /* Debug */,
575 | 52C2B357240C46BC006E217F /* Release */,
576 | );
577 | defaultConfigurationIsVisible = 0;
578 | defaultConfigurationName = Release;
579 | };
580 | 52C2B358240C46BC006E217F /* Build configuration list for PBXNativeTarget "To-do BarUITests" */ = {
581 | isa = XCConfigurationList;
582 | buildConfigurations = (
583 | 52C2B359240C46BC006E217F /* Debug */,
584 | 52C2B35A240C46BC006E217F /* Release */,
585 | );
586 | defaultConfigurationIsVisible = 0;
587 | defaultConfigurationName = Release;
588 | };
589 | /* End XCConfigurationList section */
590 | };
591 | rootObject = 52C2B321240C46BB006E217F /* Project object */;
592 | }
593 |
--------------------------------------------------------------------------------
/To-do Bar.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/To-do Bar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/To-do Bar/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // To-do Bar
4 | //
5 | // Created by Zhexiong Liu on 3/1/20.
6 | // Copyright © 2020 Zhexiong Liu. All rights reserved.
7 | //
8 |
9 |
10 | import Cocoa
11 | import SwiftUI
12 | import EventKit
13 | import NaturalLanguage
14 |
15 | @NSApplicationMain
16 | class AppDelegate: NSObject, NSApplicationDelegate {
17 |
18 | var window: NSWindow!
19 | var statusBarItem: NSStatusItem!
20 |
21 | var eventStore = EKEventStore()
22 | var statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
23 |
24 | var calendarGrant = false
25 | var remindersGrant = false
26 | var reminderCount = 0
27 |
28 | let defaultWidth = 200
29 | let maxStringLength = 30
30 |
31 | func applicationDidFinishLaunching(_ aNotification: Notification) {
32 |
33 | let group = DispatchGroup()
34 |
35 | group.enter()
36 | eventStore.requestAccess(to: .event) {
37 | (granted, error) in
38 | if let error = error {
39 | print(error)
40 | return
41 | }
42 | if granted {
43 | self.calendarGrant = true
44 | }
45 | group.leave()
46 | }
47 |
48 |
49 | group.enter()
50 | eventStore.requestAccess(to: .reminder) {
51 | (granted, error) in
52 | if let error = error {
53 | print(error)
54 | return
55 | }
56 | if granted {
57 | self.remindersGrant = true
58 | }
59 | group.leave()
60 | }
61 |
62 |
63 | group.wait()
64 | print(calendarGrant, remindersGrant)
65 |
66 |
67 | if let button = self.statusItem.button {
68 | DispatchQueue.main.async {
69 | button.image = NSImage(named: "BarIcon")
70 | if self.reminderCount > 0 {
71 | button.imagePosition = NSControl.ImagePosition.imageLeft
72 | button.title = String(self.reminderCount)
73 | // print(self.reminderCount)
74 | }
75 | }
76 | }
77 |
78 | // calendarGrant = false
79 | // remindersGrant = true
80 | if !calendarGrant && !remindersGrant {
81 | defaultMenu()
82 | }
83 |
84 |
85 | if calendarGrant || remindersGrant {
86 |
87 | let timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(fetchItems), userInfo: nil, repeats: true)
88 | RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
89 |
90 | }
91 | }
92 |
93 |
94 | @ objc func defaultMenu() {
95 |
96 | let statusBarMenu = NSMenu(title: "TodoMenuBar")
97 | statusBarMenu.minimumWidth = CGFloat(defaultWidth)
98 | self.statusItem.menu = statusBarMenu
99 |
100 |
101 | statusBarMenu.addItem(NSMenuItem.separator())
102 |
103 | statusBarMenu.addItem(
104 | withTitle: "Please Grant APP Access",
105 | action: nil,
106 | keyEquivalent: "")
107 |
108 | statusBarMenu.addItem(
109 | withTitle: "to Reminders or Calendar",
110 | action: nil,
111 | keyEquivalent: "")
112 |
113 | statusBarMenu.addItem(
114 | withTitle: "Open System Preferences",
115 | action: #selector(AppDelegate.OpenSystemPreferencesAPP),
116 | keyEquivalent: ",")
117 |
118 | statusBarMenu.addItem(
119 | withTitle: "Quit",
120 | action: #selector(AppDelegate.QuitAPP),
121 | keyEquivalent: "q")
122 | }
123 |
124 |
125 | @objc func calendarMenu(statusBarMenu: NSMenu) {
126 |
127 | // statusBarMenu.addItem(NSMenuItem.separator())
128 | if calendarGrant {
129 | statusBarMenu.addItem(
130 | withTitle: "Calendar",
131 | action: #selector(AppDelegate.OpenCalendar),
132 | keyEquivalent: "c")
133 | }
134 | // statusBarMenu.addItem(NSMenuItem.separator())
135 |
136 | var allEvents: [EKEvent] = []
137 | let calendars = eventStore.calendars(for: .event)
138 | for calendar in calendars {
139 | let predicate = eventStore.predicateForEvents(withStart: Date(), end: Date.tomorrow, calendars: [calendar])
140 | let events = eventStore.events(matching: predicate)
141 | allEvents.append(contentsOf: events)
142 | }
143 |
144 | var startDateString = ""
145 | var endDateString = ""
146 | var Datestring = ""
147 | var EventSring = ""
148 | var allDayEvent: [EKEvent] = []
149 | for event in allEvents {
150 | startDateString = event.startDate.toString(dateFormat: "h:mm a")
151 | endDateString = event.endDate.toString(dateFormat: "h:mm a")
152 |
153 | if event.isAllDay {
154 | allDayEvent.append(event)
155 | }
156 |
157 | if Date().toLocalTime() < event.startDate.toLocalTime() {
158 |
159 | Datestring = startDateString + "-" + endDateString
160 | EventSring = event.title as String
161 | if EventSring.count >= self.maxStringLength {
162 | EventSring = String(EventSring.prefix(self.maxStringLength)) + "..."
163 | }
164 |
165 | statusBarMenu.addItem(NSMenuItem.separator())
166 |
167 | statusBarMenu.addItem(
168 | withTitle: Datestring,
169 | action: #selector(AppDelegate.DoNothingAPP),
170 | keyEquivalent: "")
171 |
172 | statusBarMenu.addItem(
173 | withTitle: EventSring,
174 | action: #selector(AppDelegate.DoNothingAPP),
175 | keyEquivalent: "")
176 |
177 | }
178 |
179 | if !event.isAllDay && Date().toLocalTime() > event.startDate.toLocalTime() && Date().toLocalTime() < event.endDate.toLocalTime() {
180 |
181 | Datestring = "Now" + "-" + endDateString
182 |
183 | EventSring = event.title as String
184 | if EventSring.count >= self.maxStringLength {
185 | EventSring = String(EventSring.prefix(self.maxStringLength)) + "..."
186 | }
187 |
188 | statusBarMenu.addItem(NSMenuItem.separator())
189 |
190 | statusBarMenu.addItem(
191 | withTitle: Datestring,
192 | action: #selector(AppDelegate.DoNothingAPP),
193 | keyEquivalent: "")
194 |
195 | statusBarMenu.addItem(
196 | withTitle: EventSring,
197 | action: #selector(AppDelegate.DoNothingAPP),
198 | keyEquivalent: "")
199 | }
200 | }
201 |
202 | for event in allDayEvent {
203 |
204 | Datestring = "All Day"
205 | EventSring = event.title as String
206 | if EventSring.count >= self.maxStringLength {
207 | EventSring = String(EventSring.prefix(self.maxStringLength)) + "..."
208 | }
209 |
210 | statusBarMenu.addItem(NSMenuItem.separator())
211 |
212 | statusBarMenu.addItem(
213 | withTitle: Datestring,
214 | action: #selector(AppDelegate.DoNothingAPP),
215 | keyEquivalent: "")
216 |
217 | statusBarMenu.addItem(
218 | withTitle: EventSring,
219 | action: #selector(AppDelegate.DoNothingAPP),
220 | keyEquivalent: "")
221 | }
222 | }
223 |
224 |
225 | @objc func remindersMenu(statusBarMenu: NSMenu) {
226 |
227 |
228 | let textView = NSView(frame: NSRect(x: 0, y: 0, width: 280, height: 25))
229 | let textField = TextInput()
230 | textField.frame = NSRect(x: 10, y: 0, width: textView.frame.width - 20, height: textView.frame.height)
231 | textView.addSubview(textField)
232 | // textView.becomeFirstResponder()
233 |
234 |
235 | let textFieldInMenutest = NSMenuItem()
236 | statusBarMenu.addItem(textFieldInMenutest)
237 | textFieldInMenutest.view = textView
238 |
239 | self.reminderCount = 0
240 |
241 | statusBarMenu.addItem(NSMenuItem.separator())
242 | if remindersGrant {
243 | statusBarMenu.addItem(
244 | withTitle: "Reminders",
245 | action: #selector(AppDelegate.OpenReminders),
246 | keyEquivalent: "r")
247 | }
248 | statusBarMenu.addItem(NSMenuItem.separator())
249 |
250 | let predicated: NSPredicate? = eventStore.predicateForIncompleteReminders(withDueDateStarting: nil, ending: nil, calendars: nil)
251 | if let aPredicate = predicated {
252 | eventStore.fetchReminders(matching: aPredicate, completion: {
253 | (_ reminders: [Any]?) -> Void in
254 | for reminder: EKReminder? in reminders as? [EKReminder?] ?? [EKReminder?]() {
255 |
256 | // print(reminder?.dueDateComponents?.date)
257 | // print(reminder?.dueDateComponents?.date?.toLocalTime())
258 | // print(Date())
259 | // print(Date().toLocalTime())
260 | // print(Date.tomorrow.toLocalTime())
261 |
262 | if reminder?.dueDateComponents?.date?.toLocalTime() ?? Date().toLocalTime() <= Date.tomorrow.toLocalTime() {
263 | self.reminderCount = self.reminderCount + 1
264 | // print(self.reminderCount)
265 | var ReminderString = reminder!.title as String
266 | if ReminderString.count >= self.maxStringLength {
267 | ReminderString = String(ReminderString.prefix(self.maxStringLength)) + "..."
268 | }
269 | statusBarMenu.addItem(
270 | withTitle: ReminderString,
271 | action: #selector(AppDelegate.removeItems(sender:)),
272 | keyEquivalent: "")
273 | }
274 | }
275 |
276 | statusBarMenu.addItem(NSMenuItem.separator())
277 |
278 | self.calendarMenu(statusBarMenu: statusBarMenu)
279 | self.referenceMenu(statusBarMenu: statusBarMenu)
280 |
281 | self.updateMenuBar()
282 | })
283 | }
284 | }
285 |
286 | @objc func updateMenuBar() {
287 | if let button = self.statusItem.button {
288 | DispatchQueue.main.async {
289 | button.image = NSImage(named: "BarIcon")
290 | if self.reminderCount > 0 {
291 | button.imagePosition = NSControl.ImagePosition.imageLeft
292 | button.title = String(self.reminderCount)
293 | // print(self.reminderCount)
294 | } else {
295 | button.imagePosition = NSControl.ImagePosition.imageOnly
296 | }
297 | }
298 | }
299 | }
300 |
301 | @objc func referenceMenu(statusBarMenu: NSMenu) {
302 |
303 | statusBarMenu.addItem(NSMenuItem.separator())
304 |
305 | statusBarMenu.addItem(
306 | withTitle: "Reference",
307 | action: #selector(AppDelegate.OpenReferenceURL),
308 | keyEquivalent: ",")
309 |
310 | statusBarMenu.addItem(
311 | withTitle: "Quit",
312 | action: #selector(AppDelegate.QuitAPP),
313 | keyEquivalent: "q")
314 | }
315 |
316 |
317 | @objc func fetchItems() {
318 |
319 |
320 | let statusBarMenu = NSMenu(title: "TodoMenuBar ")
321 | statusBarMenu.minimumWidth = 200
322 | statusItem.menu = statusBarMenu
323 |
324 | // statusBarMenu.addItem(
325 | // withTitle: "Tasks for " + Date().toString(dateFormat: "EEE MMM d, yyyy"),
326 | // action: nil,
327 | // keyEquivalent: "")
328 |
329 | statusBarMenu.addItem(NSMenuItem.separator())
330 |
331 | remindersMenu(statusBarMenu: statusBarMenu)
332 |
333 | }
334 |
335 |
336 | @objc func removeItems(sender: Any) {
337 | let statusBarMenu = NSMenu(title: "TodoMenuBar")
338 | statusItem.menu = statusBarMenu
339 |
340 | let item = (sender as! NSMenuItem).title
341 |
342 | let predicated: NSPredicate? = eventStore.predicateForIncompleteReminders(withDueDateStarting: nil, ending: nil, calendars: nil)
343 | if let aPredicate = predicated {
344 | eventStore.fetchReminders(matching: aPredicate, completion: {
345 | (_ reminders: [Any]?) -> Void in
346 |
347 | for reminder: EKReminder? in reminders as? [EKReminder?] ?? [EKReminder?]() {
348 |
349 | if reminder?.dueDateComponents?.date?.toLocalTime() ?? Date().toLocalTime() <= Date.tomorrow.toLocalTime() {
350 | let target_item = reminder?.title!
351 | // print(item.prefix(self.maxStringLength), target_item?.prefix(self.maxStringLength))
352 | if item.prefix(self.maxStringLength) == target_item?.prefix(self.maxStringLength) {
353 | reminder?.isCompleted = true
354 | do {
355 | // self.reminderCount = self.reminderCount - 1
356 | try self.eventStore.save(reminder!, commit: true)
357 | print("completed reminder successed")
358 | // self.fetchItems()
359 | } catch {
360 | print("error \(error)")
361 | }
362 | }
363 | }
364 | }
365 | })
366 | }
367 | }
368 |
369 |
370 | @objc func QuitAPP(_ sender: Any) {
371 | NSApplication.shared.terminate(self)
372 | }
373 |
374 |
375 | @objc func RefreshAPP(_ sender: Any) {
376 | fetchItems()
377 | }
378 |
379 | @objc func DoNothingAPP(_ sender: Any) {
380 | }
381 |
382 | @objc func OpenReminders(_ sender: Any) {
383 | let url = NSURL(fileURLWithPath: "/System/Applications/Reminders.app", isDirectory: true) as URL
384 | let configuration = NSWorkspace.OpenConfiguration()
385 | NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil)
386 | }
387 |
388 |
389 | @objc func OpenCalendar(_ sender: Any) {
390 | let url = NSURL(fileURLWithPath: "/System/Applications/Calendar.app", isDirectory: true) as URL
391 | let configuration = NSWorkspace.OpenConfiguration()
392 | NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil)
393 | }
394 |
395 | @objc func OpenSystemPreferencesAPP(_ sender: Any) {
396 | let url = NSURL(fileURLWithPath: "/System/Applications/System Preferences.app", isDirectory: true) as URL
397 | let configuration = NSWorkspace.OpenConfiguration()
398 | NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: nil)
399 | }
400 |
401 | @objc func OpenReferenceURL() {
402 | let url = URL(string: "https://github.com/ZhexiongLiu/TodoBar")!
403 | NSWorkspace.shared.open(url)
404 | }
405 |
406 | @objc func OpenContentWindow() {
407 |
408 | let alert = NSAlert.init()
409 | alert.messageText = "To-do Bar"
410 | alert.informativeText = "Grant access to Reminders/Calendar in Privacy Preference if you'd like to show tasks in menu bar. @ZhexiongLiu"
411 | alert.addButton(withTitle: "OK")
412 | alert.addButton(withTitle: "Cancel")
413 | alert.runModal()
414 | }
415 |
416 |
417 | }
418 |
419 |
420 | class TextInput: NSTextField, NSTextFieldDelegate {
421 |
422 | override func viewDidChangeBackingProperties() {
423 | self.delegate = self
424 | self.placeholderString = "Enter your reminders here..."
425 | self.isEditable = true
426 | self.textColor = .black
427 | self.selectText(nil)
428 | self.isSelectable = true
429 | self.drawsBackground = false
430 | self.font = NSFont.systemFont(ofSize: 14)
431 | self.cell?.usesSingleLineMode = true
432 | self.bezelStyle = NSTextField.BezelStyle.roundedBezel
433 | // _ = self.becomeFirstResponder()
434 | self.window?.makeFirstResponder(self)
435 | }
436 |
437 |
438 | func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
439 | if (commandSelector == #selector(NSResponder.insertNewline(_:))) {
440 | // hit enter key
441 | if let textField = control as? NSTextField {
442 | let data = getDateString(thisString: textField.stringValue as NSString)
443 | let dateString = data[0] as NSString
444 | let text = data[1]
445 |
446 | var date = Date()
447 | if dateString != "" {
448 | let dateDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
449 | if let tcr: NSTextCheckingResult = dateDetector?.firstMatch(in: dateString as String, options: [], range: NSMakeRange(0, dateString.length)), let this_date = tcr.date {
450 | date = this_date
451 | }
452 |
453 | let eventStore = EKEventStore()
454 | let reminder = EKReminder(eventStore: eventStore)
455 | let alarm = EKAlarm(absoluteDate: date)
456 |
457 | reminder.title = text
458 | reminder.addAlarm(alarm)
459 | reminder.dueDateComponents = dateComponentFromNSDate(date: date as NSDate) as DateComponents
460 | reminder.calendar = eventStore.defaultCalendarForNewReminders()
461 | do {
462 | try eventStore.save(reminder, commit: true)
463 | print("saved")
464 | } catch {
465 | print("error: \(error)")
466 | }
467 |
468 | } else {
469 | let eventStore = EKEventStore()
470 | let reminder = EKReminder(eventStore: eventStore)
471 | reminder.title = text
472 | reminder.calendar = eventStore.defaultCalendarForNewReminders()
473 | do {
474 | try eventStore.save(reminder, commit: true)
475 | print("saved")
476 | } catch {
477 | print("error: \(error)")
478 | }
479 | }
480 | textField.stringValue = ""
481 | }
482 | return true
483 | }
484 | return false
485 | }
486 |
487 | func dateComponentFromNSDate(date: NSDate) -> NSDateComponents {
488 | let calendarUnit = Set([.minute, .hour, .day, .month, .year])
489 | let dateComponents = Calendar.current.dateComponents(calendarUnit,
490 | from: date as Date)
491 | return dateComponents as NSDateComponents
492 | }
493 |
494 |
495 | func getDateString(thisString: NSString) -> Array {
496 |
497 | var newString = thisString as String
498 | var newDateString = ""
499 |
500 | let types: NSTextCheckingResult.CheckingType = [.address, .date]
501 | let dataDetector = try? NSDataDetector(types: types.rawValue)
502 |
503 | dataDetector?.enumerateMatches(in: thisString as String, range: NSMakeRange(0, thisString.length), using: {
504 | (match, flags, _) in
505 |
506 | let matchString = thisString.substring(with: (match?.range)!) as NSString
507 | if match?.resultType == .date {
508 | newString = thisString.replacingOccurrences(of: matchString as String, with: "")
509 | newDateString = matchString as String
510 | }
511 | })
512 |
513 | newString = newString.replacingOccurrences(of: " " as String, with: " ")
514 | newString = newString.trimmingCharacters(in: .whitespacesAndNewlines)
515 |
516 | print([newDateString, newString])
517 | return [newDateString, newString]
518 | }
519 | }
520 |
521 |
522 | extension Date {
523 | func toString(dateFormat format: String) -> String {
524 | let dateFormatter = DateFormatter()
525 | dateFormatter.locale = Locale(identifier: "en-US")
526 | dateFormatter.dateFormat = format
527 | return dateFormatter.string(from: self)
528 | }
529 |
530 | static var yesterday: Date {
531 | return Date().dayBefore
532 | }
533 | static var tomorrow: Date {
534 | return Date().dayAfter
535 | }
536 | var dayBefore: Date {
537 | return Calendar.current.date(byAdding: .day, value: -1, to: midnight)!
538 | }
539 | var dayAfter: Date {
540 | return Calendar.current.date(byAdding: .day, value: 1, to: midnight)!
541 | }
542 | var midnight: Date {
543 | return Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: self)!
544 | }
545 | var month: Int {
546 | return Calendar.current.component(.month, from: self)
547 | }
548 | var isLastDayOfMonth: Bool {
549 | return dayAfter.month != month
550 | }
551 |
552 | func toGlobalTime() -> Date {
553 | let timezone = TimeZone.current
554 | let seconds = -TimeInterval(timezone.secondsFromGMT(for: self))
555 | return Date(timeInterval: seconds, since: self)
556 | }
557 |
558 | func toLocalTime() -> Date {
559 | let timezone = TimeZone.current
560 | let seconds = TimeInterval(timezone.secondsFromGMT(for: self))
561 | return Date(timeInterval: seconds, since: self)
562 | }
563 | }
564 |
565 |
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/128.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/16.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/256-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/256-1.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/256.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/32-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/32-1.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/32.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/512-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/512-1.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/512.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/AppIcon.appiconset/64.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "32-1.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "256-1.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "512-1.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/BarIcon.imageset/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/BarIcon.imageset/20.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/BarIcon.imageset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/BarIcon.imageset/40.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/BarIcon.imageset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/To-do Bar/Assets.xcassets/BarIcon.imageset/60.png
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/BarIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "20.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "40.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "60.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | },
23 | "properties" : {
24 | "template-rendering-intent" : "template"
25 | }
26 | }
--------------------------------------------------------------------------------
/To-do Bar/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/To-do Bar/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
--------------------------------------------------------------------------------
/To-do Bar/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2020 Zhexiong Liu. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 | NSSupportsAutomaticTermination
32 |
33 | NSSupportsSuddenTermination
34 |
35 | NSCalendarsUsageDescription
36 |
37 | NSRemindersUsageDescription
38 |
39 | LSUIElement
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/To-do Bar/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/To-do Bar/To_do_Bar.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 | com.apple.security.personal-information.calendars
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/To-do BarTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/To-do BarTests/To_do_BarTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // To_do_BarTests.swift
3 | // To-do BarTests
4 | //
5 | // Created by Zhexiong Liu on 3/1/20.
6 | // Copyright © 2020 Zhexiong Liu. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import To_do_Bar
11 |
12 | class To_do_BarTests: XCTestCase {
13 |
14 | override func setUp() {
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | }
21 |
22 | func testExample() {
23 | // This is an example of a functional test case.
24 | // Use XCTAssert and related functions to verify your tests produce the correct results.
25 | }
26 |
27 | func testPerformanceExample() {
28 | // This is an example of a performance test case.
29 | self.measure {
30 | // Put the code you want to measure the time of here.
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/To-do BarUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/To-do BarUITests/To_do_BarUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // To_do_BarUITests.swift
3 | // To-do BarUITests
4 | //
5 | // Created by Zhexiong Liu on 3/1/20.
6 | // Copyright © 2020 Zhexiong Liu. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class To_do_BarUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 |
16 | // In UI tests it is usually best to stop immediately when a failure occurs.
17 | continueAfterFailure = false
18 |
19 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testExample() {
27 | // UI tests must launch the application that they test.
28 | let app = XCUIApplication()
29 | app.launch()
30 |
31 | // Use recording to get started writing UI tests.
32 | // Use XCTAssert and related functions to verify your tests produce the correct results.
33 | }
34 |
35 | func testLaunchPerformance() {
36 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
37 | // This measures how long it takes to launch your application.
38 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) {
39 | XCUIApplication().launch()
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/img/To-do Bar.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/To-do Bar.icns
--------------------------------------------------------------------------------
/img/To-do Bar.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/To-do Bar.sketch
--------------------------------------------------------------------------------
/img/screen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/screen1.png
--------------------------------------------------------------------------------
/img/screen1_v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/screen1_v1.png
--------------------------------------------------------------------------------
/img/screen2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/screen2.png
--------------------------------------------------------------------------------
/img/screen2_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/screen2_v2.png
--------------------------------------------------------------------------------
/img/tutorial.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/tutorial.gif
--------------------------------------------------------------------------------
/img/tutorial.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/tutorial.mp4
--------------------------------------------------------------------------------
/img/tutorial_v1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/tutorial_v1.gif
--------------------------------------------------------------------------------
/img/tutorial_v1.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhexiongLiu/TodoBar/8874a7c8ed636f0e3a4e8722f438d59f7b35514f/img/tutorial_v1.mp4
--------------------------------------------------------------------------------