-
Notifications
You must be signed in to change notification settings - Fork 0
/
feed.json
1491 lines (1483 loc) · 551 KB
/
feed.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{
"version": "https://jsonfeed.org/version/1",
"title": "Lawrence Gimenez",
"icon": "https://micro.blog/lawgimenez/avatar.jpg",
"home_page_url": "https://law.gmnz.xyz/",
"feed_url": "https://law.gmnz.xyz/feed.json",
"items": [
{
"id": "http://lwgmnz.micro.blog/2024/11/22/collapsible-list-in.html",
"title": "Collapsible List in SwiftUI",
"content_html": "<p>I wanted to share how I developed a comment thread using SwiftUI’s <code>List</code>, like in the video below.</p>\n<p><!-- raw HTML omitted --><!-- raw HTML omitted --></p>\n<p>The code is just very simple, we will leverage List’s <code>children</code> property. The code is very simple</p>\n<p><code>List(topStory, children: \\.chain) { story in StoryDetailsRowView(story: story) } .listStyle(.plain) </code></p>\n<p>The main part is how we structure our data model. Take note of the <code>chain</code> variable.</p>\n<pre tabindex=\"0\"><code>import Foundation\n\nclass Story: Identifiable {\n\n let id = UUID()\n let user: User\n let title: String\n let content: String\n let date: String\n var chain: [Story]?\n \n init(user: User, title: String, content: String, date: String, chain: [Story]? = nil) {\n self.user = user\n self.title = title\n self.content = content\n self.date = date\n self.chain = chain\n }\n \n func setChain(_ chain: inout [Story]) {\n self.chain = chain\n }\n}\n\n</code></pre>",
"content_text": "I wanted to share how I developed a comment thread using SwiftUI's `List`, like in the video below.\n\n <video src=\"https://law.gmnz.xyz/uploads/2024/simulator-screen-recordingiphone-162024-11-22-at-19.00.54.mp4\" controls=\"controls\" preload=\"metadata\"></video>\n\nThe code is just very simple, we will leverage List's `children` property. The code is very simple\n\n`List(topStory, children: \\.chain) { story in\n StoryDetailsRowView(story: story)\n}\n.listStyle(.plain)\n`\n\nThe main part is how we structure our data model. Take note of the `chain` variable.\n\n```\nimport Foundation\n\nclass Story: Identifiable {\n\n let id = UUID()\n let user: User\n let title: String\n let content: String\n let date: String\n var chain: [Story]?\n \n init(user: User, title: String, content: String, date: String, chain: [Story]? = nil) {\n self.user = user\n self.title = title\n self.content = content\n self.date = date\n self.chain = chain\n }\n \n func setChain(_ chain: inout [Story]) {\n self.chain = chain\n }\n}\n\n```\n \n\n",
"date_published": "2024-11-22T19:13:23+08:00",
"url": "https://law.gmnz.xyz/2024/11/22/collapsible-list-in.html",
"tags": ["iOS","Mobile Development","Swift"]
},
{
"id": "http://lwgmnz.micro.blog/2024/07/22/my-uses-as.html",
"title": "My /uses/ as of July 2024",
"content_html": "<h3 id=\"terminal-setup\">Terminal Setup</h3>\n<ul>\n<li><a href=\"https://iterm2.com\">iTerm2 Build 3.5.3</a></li>\n<li><a href=\"https://github.com/githubnext/monaspace\">Monaspace Argon font by GitHub</a></li>\n<li><a href=\"https://github.com/nordtheme/iterm2\">Nord Theme</a></li>\n</ul>\n<h3 id=\"android-development-setup\">Android Development Setup</h3>\n<ul>\n<li><a href=\"https://developer.android.com/studio\">Android Studio</a></li>\n<li><a href=\"https://www.jetbrains.com/lp/mono/\">JetBrains Mono font</a></li>\n<li><a href=\"https://draculatheme.com/jetbrains\">Dracula Theme</a></li>\n</ul>\n<h3 id=\"ios-development-setup\">iOS Development Setup</h3>\n<ul>\n<li><a href=\"https://developer.apple.com/xcode/\">Xcode</a></li>\n<li><a href=\"https://www.sublimetext.com\">Sublime Text 4</a></li>\n</ul>\n<h3 id=\"open-source-and-code-repository\">Open Source and Code Repository</h3>\n<ul>\n<li><a href=\"https://github.com\">GitHub</a></li>\n</ul>\n<h3 id=\"tools\">Tools</h3>\n<ul>\n<li><a href=\"https://www.usebruno.com\">Bruno</a></li>\n<li><a href=\"https://www.jetbrains.com/lp/mono/\">Homebrew</a></li>\n</ul>\n<h3 id=\"project-management\">Project Management</h3>\n<ul>\n<li><a href=\"https://basecamp.com\">Basecamp 3</a></li>\n<li><a href=\"https://www.getharvest.com\">Harvest</a> for time tracking and invoice</li>\n</ul>\n<h3 id=\"hardware\">Hardware</h3>\n<ul>\n<li>MacBook Air M2 running macOS Sonoma 14.5</li>\n<li>Lenovo ThinkPad T480 running Fedora Linux 40</li>\n</ul>\n<h3 id=\"personal\">Personal</h3>\n<ul>\n<li><a href=\"https://culturedcode.com/things/\">Things 3</a> for personal and family tasks</li>\n<li><a href=\"https://bear.app\">Bear</a> for markdown notes and blogging</li>\n<li><a href=\"https://www.icloud.com/notes\">Apple Notes</a> for more personal notes and syncing with wife</li>\n<li><a href=\"https://www.icloud.com/calendar\">Apple Calendar</a></li>\n<li><a href=\"https://www.icloud.com/mail\">Apple Mail</a></li>\n<li><a href=\"https://www.reederapp.com\">Reeder 5</a> with <a href=\"https://feedly.com\">Feedly</a> as the RSS service</li>\n<li><a href=\"https://streaksapp.com\">Streaks</a> for habit tracking</li>\n<li><a href=\"https://dayoneapp.com\">Day One Journal</a> for journaling</li>\n</ul>\n",
"content_text": "### Terminal Setup\r\n- [iTerm2 Build 3.5.3](https://iterm2.com)\r\n- [Monaspace Argon font by GitHub](https://github.com/githubnext/monaspace)\r\n- [Nord Theme](https://github.com/nordtheme/iterm2)\r\n\r\n### Android Development Setup\r\n- [Android Studio](https://developer.android.com/studio)\r\n- [JetBrains Mono font](https://www.jetbrains.com/lp/mono/)\r\n- [Dracula Theme](https://draculatheme.com/jetbrains)\r\n\r\n### iOS Development Setup\r\n- [Xcode](https://developer.apple.com/xcode/) \r\n- [Sublime Text 4](https://www.sublimetext.com)\r\n\r\n### Open Source and Code Repository\r\n- [GitHub](https://github.com)\r\n\r\n### Tools\r\n- [Bruno](https://www.usebruno.com)\r\n- [Homebrew](https://www.jetbrains.com/lp/mono/)\r\n\r\n### Project Management\r\n- [Basecamp 3](https://basecamp.com)\r\n- [Harvest](https://www.getharvest.com) for time tracking and invoice\r\n\r\n### Hardware\r\n- MacBook Air M2 running macOS Sonoma 14.5\r\n- Lenovo ThinkPad T480 running Fedora Linux 40\r\n\r\n### Personal\r\n- [Things 3](https://culturedcode.com/things/) for personal and family tasks\r\n- [Bear](https://bear.app) for markdown notes and blogging\r\n- [Apple Notes](https://www.icloud.com/notes) for more personal notes and syncing with wife\r\n- [Apple Calendar](https://www.icloud.com/calendar)\r\n- [Apple Mail](https://www.icloud.com/mail)\r\n- [Reeder 5](https://www.reederapp.com) with [Feedly](https://feedly.com) as the RSS service\r\n- [Streaks](https://streaksapp.com) for habit tracking\r\n- [Day One Journal](https://dayoneapp.com) for journaling\n",
"date_published": "2024-07-22T16:27:51+08:00",
"url": "https://law.gmnz.xyz/2024/07/22/my-uses-as.html"
},
{
"id": "http://lwgmnz.micro.blog/2024/07/14/uitextview-using-textkit.html",
"title": "UITextView using TextKit 1",
"content_html": "<p>I received this warning and was surprised to know that there are 2 TextKits, TextKit 1 and 2 in the iOS SDK.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">UITextView <span style=\"color:#ae81ff\">0x1063c9000</span> <span style=\"color:#66d9ef\">is</span> switching to TextKit <span style=\"color:#ae81ff\">1</span> compatibility mode because its layoutManager was accessed. Break on void _UITextViewEnablingCompatibilityMode(UITextView <span style=\"color:#f92672\">*</span>__strong, BOOL) to debug.\n</code></pre></div><p>I use <code>UITextView</code>’s <code>LayoutManager</code> so I can detect the text or a group of texts that the user has tapped or selected.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">textViewDidChangeSelection</span>(<span style=\"color:#66d9ef\">_</span> textView: UITextView) {\n\ttextView.addGestureRecognizer(UITapGestureRecognizer(target: <span style=\"color:#66d9ef\">self</span>, action: <span style=\"color:#66d9ef\">#selector</span>(textTapped(<span style=\"color:#66d9ef\">_</span>:))))\n}\n</code></pre></div><div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">@objc</span> <span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">textTapped</span>(<span style=\"color:#66d9ef\">_</span> sender: UITapGestureRecognizer) {\n\t<span style=\"color:#66d9ef\">let</span> textView = sender.view <span style=\"color:#66d9ef\">as</span>! CustomTextView\n\t<span style=\"color:#66d9ef\">let</span> layoutManger = textView.layoutManager\n\t<span style=\"color:#75715e\">// Location of the tap</span>\n\t<span style=\"color:#66d9ef\">var</span> location = sender.location(<span style=\"color:#66d9ef\">in</span>: textView)\n\tlocation.x <span style=\"color:#f92672\">-=</span> textView.textContainerInset.<span style=\"color:#66d9ef\">left</span>\n\tlocation.y <span style=\"color:#f92672\">-=</span> textView.textContainerInset.top\n\t<span style=\"color:#75715e\">// Character index at tap location</span>\n\t<span style=\"color:#66d9ef\">let</span> characterIndex = layoutManger.characterIndex(<span style=\"color:#66d9ef\">for</span>: location, <span style=\"color:#66d9ef\">in</span>: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: <span style=\"color:#66d9ef\">nil</span>)\n}\n</code></pre></div><p>So based on the answer on the question <a href=\"https://stackoverflow.com/questions/75482602/replacing-layout-manager-in-uitextview-using-textkit-1\">Replacing layout manager in UITextView using TextKit 1</a>, I need to override NSLayoutManager.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">CustomTextView</span>: UITextView {\n\n\t<span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">var</span> customLayoutManager = NSLayoutManager()\n\n\t<span style=\"color:#66d9ef\">init</span>(someData: String) {\n <span style=\"color:#66d9ef\">self</span>.someData = someData\n <span style=\"color:#66d9ef\">super</span>.<span style=\"color:#66d9ef\">init</span>(frame: .zero, textContainer: <span style=\"color:#66d9ef\">nil</span>)\n customLayoutManager.textStorage = <span style=\"color:#66d9ef\">self</span>.textStorage\n customLayoutManager.addTextContainer(<span style=\"color:#66d9ef\">self</span>.textContainer)\n <span style=\"color:#66d9ef\">self</span>.textContainer.replaceLayoutManager(customLayoutManager)\n }\n\n\t<span style=\"color:#66d9ef\">override</span> <span style=\"color:#66d9ef\">var</span> layoutManager: NSLayoutManager {\n <span style=\"color:#66d9ef\">return</span> customLayoutManager\n }\n}\n</code></pre></div>",
"content_text": "I received this warning and was surprised to know that there are 2 TextKits, TextKit 1 and 2 in the iOS SDK.\n```swift\nUITextView 0x1063c9000 is switching to TextKit 1 compatibility mode because its layoutManager was accessed. Break on void _UITextViewEnablingCompatibilityMode(UITextView *__strong, BOOL) to debug.\n```\n\nI use `UITextView`’s `LayoutManager` so I can detect the text or a group of texts that the user has tapped or selected.\n\n```swift\nfunc textViewDidChangeSelection(_ textView: UITextView) {\n\ttextView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(textTapped(_:))))\n}\n```\n\n```swift\n@objc private func textTapped(_ sender: UITapGestureRecognizer) {\n\tlet textView = sender.view as! CustomTextView\n\tlet layoutManger = textView.layoutManager\n\t// Location of the tap\n\tvar location = sender.location(in: textView)\n\tlocation.x -= textView.textContainerInset.left\n\tlocation.y -= textView.textContainerInset.top\n\t// Character index at tap location\n\tlet characterIndex = layoutManger.characterIndex(for: location, in: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)\n}\n```\n\nSo based on the answer on the question [Replacing layout manager in UITextView using TextKit 1](https://stackoverflow.com/questions/75482602/replacing-layout-manager-in-uitextview-using-textkit-1), I need to override NSLayoutManager.\n\n```swift\nclass CustomTextView: UITextView {\n\n\tprivate var customLayoutManager = NSLayoutManager()\n\n\tinit(someData: String) {\n self.someData = someData\n super.init(frame: .zero, textContainer: nil)\n customLayoutManager.textStorage = self.textStorage\n customLayoutManager.addTextContainer(self.textContainer)\n self.textContainer.replaceLayoutManager(customLayoutManager)\n }\n\n\toverride var layoutManager: NSLayoutManager {\n return customLayoutManager\n }\n}\n```\n",
"date_published": "2024-07-14T18:01:28+08:00",
"url": "https://law.gmnz.xyz/2024/07/14/uitextview-using-textkit.html",
"tags": ["iOS","Mobile Development","Swift"]
},
{
"id": "http://lwgmnz.micro.blog/2024/06/23/swiftui-text-highlights.html",
"title": "SwiftUI Text highlights and menu",
"content_html": "<p>SwiftUI’s <code>Text()</code> view does not support custom colored highlights and custom menus. Here’s how I did it. The solution may not be up to everyone’s programming standards but it worked.</p>\n<!-- raw HTML omitted -->\n<h2 id=\"customuitextview\">CustomUITextView</h2>\n<p>Create a new file called <code>CustomUITextView</code> which will inherit UITextView. The initial code should look like</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">\n<span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">UIKit</span>\n\n<span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">CustomTextView</span>: UITextView {\n \n}\n</code></pre></div><h2 id=\"textselectable\">TextSelectable</h2>\n<p>Now, let’s create another file called TextSelectable which will inherit <code>UIViewRepresentable</code>.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">\n<span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">TextSelectable</span>: UIViewRepresentable {\n \n <span style=\"color:#66d9ef\">var</span> text: NSAttributedString\n \n <span style=\"color:#66d9ef\">init</span>(text: NSAttributedString) {\n <span style=\"color:#66d9ef\">self</span>.text = text\n }\n \n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">makeUIView</span>(context: Context) -> CustomTextView {\n <span style=\"color:#66d9ef\">let</span> customTextView = CustomTextView()\n customTextView.delegate = context.coordinator\n <span style=\"color:#66d9ef\">return</span> customTextView\n }\n \n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">updateUIView</span>(<span style=\"color:#66d9ef\">_</span> uiView: CustomTextView, context: Context) {\n uiView.attributedText = text\n }\n \n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">makeCoordinator</span>() -> Coordinator {\n Coordinator(text)\n }\n \n <span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">Coordinator</span>: NSObject, UITextViewDelegate {\n \n <span style=\"color:#66d9ef\">var</span> text: NSAttributedString\n \n <span style=\"color:#66d9ef\">init</span>(<span style=\"color:#66d9ef\">_</span> text: NSAttributedString) {\n <span style=\"color:#66d9ef\">self</span>.text = text\n }\n \n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">textViewDidChange</span>(<span style=\"color:#66d9ef\">_</span> textView: UITextView) {\n <span style=\"color:#66d9ef\">self</span>.text = textView.attributedText\n }\n }\n}\n</code></pre></div><h2 id=\"highlightext\">highlighText()</h2>\n<p>Let’s create a <code>highlighText()</code> function inside our <code>CustomUITextView()</code>. And we will <code>override</code> the <code>canPerformAction</code> function. This is where we will capture our highlighted text. The new <code>CustomUITextView</code> should be like the one below.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">UIKit</span>\n\n<span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">CustomTextView</span>: UITextView {\n \n <span style=\"color:#66d9ef\">override</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">canPerformAction</span>(<span style=\"color:#66d9ef\">_</span> action: Selector, withSender sender: Any?) -> Bool {\n <span style=\"color:#66d9ef\">if</span> action == <span style=\"color:#66d9ef\">#selector</span>(highlightText) {\n <span style=\"color:#66d9ef\">return</span> <span style=\"color:#66d9ef\">true</span>\n }\n <span style=\"color:#66d9ef\">return</span> <span style=\"color:#66d9ef\">false</span>\n }\n \n <span style=\"color:#66d9ef\">@objc</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">highlightText</span>() {\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> range = <span style=\"color:#66d9ef\">self</span>.selectedTextRange, <span style=\"color:#66d9ef\">let</span> selectedText = <span style=\"color:#66d9ef\">self</span>.text(<span style=\"color:#66d9ef\">in</span>: range) {\n print(<span style=\"color:#e6db74\">"Selected text is </span><span style=\"color:#e6db74\">\\(</span>selectedText<span style=\"color:#e6db74\">)</span><span style=\"color:#e6db74\">"</span>)\n }\n }\n}\n</code></pre></div><h2 id=\"custom-uimenu\">Custom UIMenu</h2>\n<p>Next, we will create our own UIMenu when a user long presses or clicks on a highlighted text. We will override the <code>editMenu</code> function.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">UIKit</span>\n\n<span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">CustomTextView</span>: UITextView {\n \n <span style=\"color:#66d9ef\">override</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">editMenu</span>(<span style=\"color:#66d9ef\">for</span> textRange: UITextRange, suggestedActions: [UIMenuElement]) -> UIMenu? {\n <span style=\"color:#66d9ef\">let</span> highlightTextAction = UIAction(title: <span style=\"color:#e6db74\">"Highlight Passage"</span>) { action <span style=\"color:#66d9ef\">in</span>\n <span style=\"color:#66d9ef\">self</span>.highlightText()\n }\n <span style=\"color:#66d9ef\">let</span> addNotesAction = UIAction(title: <span style=\"color:#e6db74\">"Add Notes"</span>) { action <span style=\"color:#66d9ef\">in</span>\n \n }\n <span style=\"color:#66d9ef\">var</span> actions = suggestedActions\n actions.insert(highlightTextAction, at: <span style=\"color:#ae81ff\">0</span>)\n actions.insert(addNotesAction, at: <span style=\"color:#ae81ff\">1</span>)\n <span style=\"color:#66d9ef\">return</span> UIMenu(children: actions)\n }\n \n <span style=\"color:#66d9ef\">override</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">canPerformAction</span>(<span style=\"color:#66d9ef\">_</span> action: Selector, withSender sender: Any?) -> Bool {\n <span style=\"color:#66d9ef\">if</span> action == <span style=\"color:#66d9ef\">#selector</span>(highlightText) {\n <span style=\"color:#66d9ef\">return</span> <span style=\"color:#66d9ef\">true</span>\n }\n <span style=\"color:#66d9ef\">return</span> <span style=\"color:#66d9ef\">false</span>\n }\n \n <span style=\"color:#66d9ef\">@objc</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">highlightText</span>() {\n \n }\n}\n</code></pre></div><h2 id=\"swiftui-view\">SwiftUI View</h2>\n<p>Time to implement it in our SwiftUI View. Create a new file, I called mine <code>DetailsView</code>. Let’s use a verse from Kendrick Lamar as an example text passed to our <code>TextSelectable</code>.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">DetailsView</span>: View {\n\n @State <span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">var</span> attributedText = NSAttributedString(string: <span style=\"color:#e6db74\">"""\n</span><span style=\"color:#e6db74\"> This feelin' is brought to you by adrenaline and good rap\n</span><span style=\"color:#e6db74\"> Black Pendleton ball cap (West, west, west)\n</span><span style=\"color:#e6db74\"> We don't share the same synonym, fall back (West, west, west)\n</span><span style=\"color:#e6db74\"> Been in it before Internet had new acts\n</span><span style=\"color:#e6db74\"> Mimicking radio's nemesis made me wack\n</span><span style=\"color:#e6db74\"> My innocence limited, the experience lacked\n</span><span style=\"color:#e6db74\"> Ten of us with no tentative tactic that cracked\n</span><span style=\"color:#e6db74\"> The mind of a literate writer, but I did it, in fact\n</span><span style=\"color:#e6db74\"> You admitted it once I submitted it, wrapped in plastic\n</span><span style=\"color:#e6db74\"> Remember scribblin', scratchin' diligent sentences backwards\n</span><span style=\"color:#e6db74\"> Visiting freestyle cyphers for your reaction\n</span><span style=\"color:#e6db74\"> Now, I can live in a stadium, pack it the fastest\n</span><span style=\"color:#e6db74\"> Gamblin' Benjamin benefits, sinnin' in traffic\n</span><span style=\"color:#e6db74\"> Spinnin' women in cartwheels, linen fabric on fashion\n</span><span style=\"color:#e6db74\"> Winnin' in every decision, Kendrick is master that mastered it\n</span><span style=\"color:#e6db74\"> Isn't it lovely how menaces turned attraction?\n</span><span style=\"color:#e6db74\"> Pivotin' rappers, finish your fraction while writing blue magic\n</span><span style=\"color:#e6db74\"> Thank God for rap\n</span><span style=\"color:#e6db74\"> I would say it got me a plaque, but what's better than that?\n</span><span style=\"color:#e6db74\"> The fact it brought me back home\n</span><span style=\"color:#e6db74\"> """</span>)\n \n <span style=\"color:#66d9ef\">var</span> body: some View {\n VStack {\n TextSelectable(text: NSAttributedString(string: attributedText))\n }\n .padding()\n }\n}\n</code></pre></div><p>Now try to run it in your emulator. And drag a portion of the text and click on our Highlight Passage menu.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>We should be able to capture the highlighted text.</p>\n<h2 id=\"colored-highlights\">Colored highlights</h2>\n<p>Now that we can capture the highlighted texts, we want to add colors to it. Let’s create a file called Highlight.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">Highlight</span> {\n \n <span style=\"color:#66d9ef\">let</span> text: String\n <span style=\"color:#66d9ef\">let</span> location: Int\n <span style=\"color:#66d9ef\">let</span> length: Int\n <span style=\"color:#66d9ef\">let</span> color: Color\n}\n</code></pre></div><p>Now, inside our <code>highlightText()</code> function, let’s create that Highlight object. And we will pass this object to our NotificationCenter, where our DetailsView will listen and capture our object.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">UIKit</span>\n\n<span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">CustomTextView</span>: UITextView {\n \n <span style=\"color:#66d9ef\">override</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">editMenu</span>(<span style=\"color:#66d9ef\">for</span> textRange: UITextRange, suggestedActions: [UIMenuElement]) -> UIMenu? {\n <span style=\"color:#66d9ef\">let</span> highlightTextAction = UIAction(title: <span style=\"color:#e6db74\">"Highlight Passage"</span>) { action <span style=\"color:#66d9ef\">in</span>\n <span style=\"color:#66d9ef\">self</span>.highlightText()\n }\n <span style=\"color:#66d9ef\">let</span> addNotesAction = UIAction(title: <span style=\"color:#e6db74\">"Add Notes"</span>) { action <span style=\"color:#66d9ef\">in</span>\n \n }\n <span style=\"color:#66d9ef\">var</span> actions = suggestedActions\n actions.insert(highlightTextAction, at: <span style=\"color:#ae81ff\">0</span>)\n actions.insert(addNotesAction, at: <span style=\"color:#ae81ff\">1</span>)\n <span style=\"color:#66d9ef\">return</span> UIMenu(children: actions)\n }\n \n <span style=\"color:#66d9ef\">override</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">canPerformAction</span>(<span style=\"color:#66d9ef\">_</span> action: Selector, withSender sender: Any?) -> Bool {\n <span style=\"color:#66d9ef\">if</span> action == <span style=\"color:#66d9ef\">#selector</span>(highlightText) {\n <span style=\"color:#66d9ef\">return</span> <span style=\"color:#66d9ef\">true</span>\n }\n <span style=\"color:#66d9ef\">return</span> <span style=\"color:#66d9ef\">false</span>\n }\n \n <span style=\"color:#66d9ef\">@objc</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">highlightText</span>() {\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> range = <span style=\"color:#66d9ef\">self</span>.selectedTextRange, <span style=\"color:#66d9ef\">let</span> selectedText = <span style=\"color:#66d9ef\">self</span>.text(<span style=\"color:#66d9ef\">in</span>: range) {\n print(<span style=\"color:#e6db74\">"Selected text is </span><span style=\"color:#e6db74\">\\(</span>selectedText<span style=\"color:#e6db74\">)</span><span style=\"color:#e6db74\">"</span>)\n <span style=\"color:#66d9ef\">let</span> highlight = Highlight(text: selectedText, location: selectedRange.location, length: selectedRange.length, color: .purple)\n <span style=\"color:#66d9ef\">let</span> highlightDict = [\n <span style=\"color:#e6db74\">"data"</span>: highlight\n ]\n NotificationCenter.<span style=\"color:#66d9ef\">default</span>.post(name: Notification.Name(<span style=\"color:#e6db74\">"highlightAdded"</span>), object: <span style=\"color:#66d9ef\">nil</span>, userInfo: highlightDict)\n }\n }\n}\n</code></pre></div><p>I am not sure if this is the best way, please let me know if you have any better solutions.</p>\n<p>And now in our <code>DetailsView</code>, let’s capture it using <code>onReceive</code> modifier.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">DetailsView</span>: View {\n \n @State <span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">var</span> attributedText = NSAttributedString(string: <span style=\"color:#e6db74\">"""\n</span><span style=\"color:#e6db74\"> This feelin' is brought to you by adrenaline and good rap\n</span><span style=\"color:#e6db74\"> Black Pendleton ball cap (West, west, west)\n</span><span style=\"color:#e6db74\"> We don't share the same synonym, fall back (West, west, west)\n</span><span style=\"color:#e6db74\"> Been in it before Internet had new acts\n</span><span style=\"color:#e6db74\"> Mimicking radio's nemesis made me wack\n</span><span style=\"color:#e6db74\"> My innocence limited, the experience lacked\n</span><span style=\"color:#e6db74\"> Ten of us with no tentative tactic that cracked\n</span><span style=\"color:#e6db74\"> The mind of a literate writer, but I did it, in fact\n</span><span style=\"color:#e6db74\"> You admitted it once I submitted it, wrapped in plastic\n</span><span style=\"color:#e6db74\"> Remember scribblin', scratchin' diligent sentences backwards\n</span><span style=\"color:#e6db74\"> Visiting freestyle cyphers for your reaction\n</span><span style=\"color:#e6db74\"> Now, I can live in a stadium, pack it the fastest\n</span><span style=\"color:#e6db74\"> Gamblin' Benjamin benefits, sinnin' in traffic\n</span><span style=\"color:#e6db74\"> Spinnin' women in cartwheels, linen fabric on fashion\n</span><span style=\"color:#e6db74\"> Winnin' in every decision, Kendrick is master that mastered it\n</span><span style=\"color:#e6db74\"> Isn't it lovely how menaces turned attraction?\n</span><span style=\"color:#e6db74\"> Pivotin' rappers, finish your fraction while writing blue magic\n</span><span style=\"color:#e6db74\"> Thank God for rap\n</span><span style=\"color:#e6db74\"> I would say it got me a plaque, but what's better than that?\n</span><span style=\"color:#e6db74\"> The fact it brought me back home\n</span><span style=\"color:#e6db74\"> """</span>)\n\n <span style=\"color:#66d9ef\">var</span> body: some View {\n VStack {\n TextSelectable(text: NSAttributedString(string: attributedText))\n }\n .onReceive(NotificationCenter.<span style=\"color:#66d9ef\">default</span>.publisher(<span style=\"color:#66d9ef\">for</span>: Notification.Name(<span style=\"color:#e6db74\">"highlightAdded"</span>))) { output <span style=\"color:#66d9ef\">in</span>\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> highlight = output.userInfo![<span style=\"color:#e6db74\">"data"</span>] <span style=\"color:#66d9ef\">as</span>? Highlight {\n <span style=\"color:#66d9ef\">let</span> mutableString = NSMutableAttributedString.<span style=\"color:#66d9ef\">init</span>(string: attributedText.string)\n <span style=\"color:#66d9ef\">let</span> highlightAttributes: [NSAttributedString.Key: Any] = [\n .backgroundColor: highlight.color,\n ]\n mutableString.addAttributes(highlightAttributes, range: NSRange(location: highlight.location, length: highlight.length))\n attributedText = mutableString\n }\n }\n .padding()\n }\n}\n</code></pre></div><p>Run the app and it will throw an error.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">*** Terminating app due to uncaught exception <span style=\"color:#e6db74\">'NSInvalidArgumentException'</span>, reason: <span style=\"color:#e6db74\">'-[__SwiftValue set]: unrecognized selector sent to instance 0x600000c6c360'</span>\n*** First throw call stack:\n<span style=\"color:#f92672\">(</span>\n\t<span style=\"color:#ae81ff\">0</span> CoreFoundation 0x00000001804ae0f8 __exceptionPreprocess + <span style=\"color:#ae81ff\">172</span>\n</code></pre></div><p>This is because of our color variable in our Highlight. Let’s update our Highlights class to use <code>uiColor</code> instead.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">Highlight</span> {\n \n <span style=\"color:#66d9ef\">let</span> text: String\n <span style=\"color:#66d9ef\">let</span> location: Int\n <span style=\"color:#66d9ef\">let</span> length: Int\n <span style=\"color:#66d9ef\">var</span> uiColor: UIColor\n <span style=\"color:#66d9ef\">var</span> color: Color {\n <span style=\"color:#66d9ef\">get</span> {\n .<span style=\"color:#66d9ef\">init</span>(uiColor: uiColor)\n }\n <span style=\"color:#66d9ef\">set</span> {\n uiColor = .<span style=\"color:#66d9ef\">init</span>(newValue)\n }\n }\n}\n</code></pre></div><p>In our <code>highlighText()</code> function, use <code>uiColor</code> instead of <code>color</code>.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"> <span style=\"color:#66d9ef\">@objc</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">highlightText</span>() {\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> range = <span style=\"color:#66d9ef\">self</span>.selectedTextRange, <span style=\"color:#66d9ef\">let</span> selectedText = <span style=\"color:#66d9ef\">self</span>.text(<span style=\"color:#66d9ef\">in</span>: range) {\n print(<span style=\"color:#e6db74\">"Selected text is </span><span style=\"color:#e6db74\">\\(</span>selectedText<span style=\"color:#e6db74\">)</span><span style=\"color:#e6db74\">"</span>)\n <span style=\"color:#66d9ef\">let</span> highlight = Highlight(text: selectedText, location: selectedRange.location, length: selectedRange.length, uiColor: .purple)\n <span style=\"color:#66d9ef\">let</span> highlightDict = [\n <span style=\"color:#e6db74\">"data"</span>: highlight\n ]\n NotificationCenter.<span style=\"color:#66d9ef\">default</span>.post(name: Notification.Name(<span style=\"color:#e6db74\">"highlightAdded"</span>), object: <span style=\"color:#66d9ef\">nil</span>, userInfo: highlightDict)\n }\n }\n</code></pre></div><p>Also, update our <code>onReceive</code> highlight object.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"> <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> highlight = output.userInfo![<span style=\"color:#e6db74\">"data"</span>] <span style=\"color:#66d9ef\">as</span>? Highlight {\n <span style=\"color:#66d9ef\">let</span> mutableString = NSMutableAttributedString.<span style=\"color:#66d9ef\">init</span>(string: attributedText.string)\n <span style=\"color:#66d9ef\">let</span> highlightAttributes: [NSAttributedString.Key: Any] = [\n .backgroundColor: highlight.uiColor,\n ]\n mutableString.addAttributes(highlightAttributes, range: NSRange(location: highlight.location, length: highlight.length))\n attributedText = mutableString\n }\n }\n</code></pre></div><p>Everything should work now.</p>\n<!-- raw HTML omitted -->\n<p>You can find the code at <a href=\"https://github.com/lawgimenez/customtext\">https://github.com/lawgimenez/customtext</a>.</p>\n",
"content_text": "SwiftUI's `Text()` view does not support custom colored highlights and custom menus. Here's how I did it. The solution may not be up to everyone's programming standards but it worked.\n\n<img src=\"uploads/2024/simulator-screenshotiphone-152024-06-23-at-07.50.23.png\" width=\"600\" height=\"1300\" alt=\"\">\n\n## CustomUITextView\n\nCreate a new file called `CustomUITextView` which will inherit UITextView. The initial code should look like\n\n\n```swift\n\nimport UIKit\n\nclass CustomTextView: UITextView {\n \n}\n```\n\n## TextSelectable\n\nNow, let's create another file called TextSelectable which will inherit `UIViewRepresentable`.\n\n```swift\n\nimport SwiftUI\n\nstruct TextSelectable: UIViewRepresentable {\n \n var text: NSAttributedString\n \n init(text: NSAttributedString) {\n self.text = text\n }\n \n func makeUIView(context: Context) -> CustomTextView {\n let customTextView = CustomTextView()\n customTextView.delegate = context.coordinator\n return customTextView\n }\n \n func updateUIView(_ uiView: CustomTextView, context: Context) {\n uiView.attributedText = text\n }\n \n func makeCoordinator() -> Coordinator {\n Coordinator(text)\n }\n \n class Coordinator: NSObject, UITextViewDelegate {\n \n var text: NSAttributedString\n \n init(_ text: NSAttributedString) {\n self.text = text\n }\n \n func textViewDidChange(_ textView: UITextView) {\n self.text = textView.attributedText\n }\n }\n}\n```\n\n## highlighText()\n\nLet's create a `highlighText()` function inside our `CustomUITextView()`. And we will `override` the `canPerformAction` function. This is where we will capture our highlighted text. The new `CustomUITextView` should be like the one below.\n\n```swift\nimport UIKit\n\nclass CustomTextView: UITextView {\n \n override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {\n if action == #selector(highlightText) {\n return true\n }\n return false\n }\n \n @objc func highlightText() {\n if let range = self.selectedTextRange, let selectedText = self.text(in: range) {\n print(\"Selected text is \\(selectedText)\")\n }\n }\n}\n```\n\n## Custom UIMenu\n\nNext, we will create our own UIMenu when a user long presses or clicks on a highlighted text. We will override the `editMenu` function.\n\n```swift\nimport UIKit\n\nclass CustomTextView: UITextView {\n \n override func editMenu(for textRange: UITextRange, suggestedActions: [UIMenuElement]) -> UIMenu? {\n let highlightTextAction = UIAction(title: \"Highlight Passage\") { action in\n self.highlightText()\n }\n let addNotesAction = UIAction(title: \"Add Notes\") { action in\n \n }\n var actions = suggestedActions\n actions.insert(highlightTextAction, at: 0)\n actions.insert(addNotesAction, at: 1)\n return UIMenu(children: actions)\n }\n \n override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {\n if action == #selector(highlightText) {\n return true\n }\n return false\n }\n \n @objc func highlightText() {\n \n }\n}\n```\n\n## SwiftUI View\n\nTime to implement it in our SwiftUI View. Create a new file, I called mine `DetailsView`. Let's use a verse from Kendrick Lamar as an example text passed to our `TextSelectable`.\n\n```swift\nimport SwiftUI\n\nstruct DetailsView: View {\n\n @State private var attributedText = NSAttributedString(string: \"\"\"\n This feelin' is brought to you by adrenaline and good rap\n Black Pendleton ball cap (West, west, west)\n We don't share the same synonym, fall back (West, west, west)\n Been in it before Internet had new acts\n Mimicking radio's nemesis made me wack\n My innocence limited, the experience lacked\n Ten of us with no tentative tactic that cracked\n The mind of a literate writer, but I did it, in fact\n You admitted it once I submitted it, wrapped in plastic\n Remember scribblin', scratchin' diligent sentences backwards\n Visiting freestyle cyphers for your reaction\n Now, I can live in a stadium, pack it the fastest\n Gamblin' Benjamin benefits, sinnin' in traffic\n Spinnin' women in cartwheels, linen fabric on fashion\n Winnin' in every decision, Kendrick is master that mastered it\n Isn't it lovely how menaces turned attraction?\n Pivotin' rappers, finish your fraction while writing blue magic\n Thank God for rap\n I would say it got me a plaque, but what's better than that?\n The fact it brought me back home\n \"\"\")\n \n var body: some View {\n VStack {\n TextSelectable(text: NSAttributedString(string: attributedText))\n }\n .padding()\n }\n}\n```\n\nNow try to run it in your emulator. And drag a portion of the text and click on our Highlight Passage menu.\n\n<img src=\"uploads/2024/simulator-screenshotiphone-152024-06-29-at-11.36.21.png\" width=\"600\" height=\"1300\" alt=\"\">\n\n<img src=\"uploads/2024/screenshot-2024-06-29-at-11.36.30am.png\" width=\"600\" height=\"222\" alt=\"\">\n\nWe should be able to capture the highlighted text.\n\n## Colored highlights\n\nNow that we can capture the highlighted texts, we want to add colors to it. Let's create a file called Highlight.\n\n```swift\nimport SwiftUI\n\nstruct Highlight {\n \n let text: String\n let location: Int\n let length: Int\n let color: Color\n}\n```\nNow, inside our `highlightText()` function, let's create that Highlight object. And we will pass this object to our NotificationCenter, where our DetailsView will listen and capture our object.\n\n```swift\nimport UIKit\n\nclass CustomTextView: UITextView {\n \n override func editMenu(for textRange: UITextRange, suggestedActions: [UIMenuElement]) -> UIMenu? {\n let highlightTextAction = UIAction(title: \"Highlight Passage\") { action in\n self.highlightText()\n }\n let addNotesAction = UIAction(title: \"Add Notes\") { action in\n \n }\n var actions = suggestedActions\n actions.insert(highlightTextAction, at: 0)\n actions.insert(addNotesAction, at: 1)\n return UIMenu(children: actions)\n }\n \n override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {\n if action == #selector(highlightText) {\n return true\n }\n return false\n }\n \n @objc func highlightText() {\n if let range = self.selectedTextRange, let selectedText = self.text(in: range) {\n print(\"Selected text is \\(selectedText)\")\n let highlight = Highlight(text: selectedText, location: selectedRange.location, length: selectedRange.length, color: .purple)\n let highlightDict = [\n \"data\": highlight\n ]\n NotificationCenter.default.post(name: Notification.Name(\"highlightAdded\"), object: nil, userInfo: highlightDict)\n }\n }\n}\n```\nI am not sure if this is the best way, please let me know if you have any better solutions.\n\nAnd now in our `DetailsView`, let's capture it using `onReceive` modifier.\n\n```swift\nimport SwiftUI\n\nstruct DetailsView: View {\n \n @State private var attributedText = NSAttributedString(string: \"\"\"\n This feelin' is brought to you by adrenaline and good rap\n Black Pendleton ball cap (West, west, west)\n We don't share the same synonym, fall back (West, west, west)\n Been in it before Internet had new acts\n Mimicking radio's nemesis made me wack\n My innocence limited, the experience lacked\n Ten of us with no tentative tactic that cracked\n The mind of a literate writer, but I did it, in fact\n You admitted it once I submitted it, wrapped in plastic\n Remember scribblin', scratchin' diligent sentences backwards\n Visiting freestyle cyphers for your reaction\n Now, I can live in a stadium, pack it the fastest\n Gamblin' Benjamin benefits, sinnin' in traffic\n Spinnin' women in cartwheels, linen fabric on fashion\n Winnin' in every decision, Kendrick is master that mastered it\n Isn't it lovely how menaces turned attraction?\n Pivotin' rappers, finish your fraction while writing blue magic\n Thank God for rap\n I would say it got me a plaque, but what's better than that?\n The fact it brought me back home\n \"\"\")\n\n var body: some View {\n VStack {\n TextSelectable(text: NSAttributedString(string: attributedText))\n }\n .onReceive(NotificationCenter.default.publisher(for: Notification.Name(\"highlightAdded\"))) { output in\n if let highlight = output.userInfo![\"data\"] as? Highlight {\n let mutableString = NSMutableAttributedString.init(string: attributedText.string)\n let highlightAttributes: [NSAttributedString.Key: Any] = [\n .backgroundColor: highlight.color,\n ]\n mutableString.addAttributes(highlightAttributes, range: NSRange(location: highlight.location, length: highlight.length))\n attributedText = mutableString\n }\n }\n .padding()\n }\n}\n```\n\nRun the app and it will throw an error. \n\n```bash\n*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__SwiftValue set]: unrecognized selector sent to instance 0x600000c6c360'\n*** First throw call stack:\n(\n\t0 CoreFoundation 0x00000001804ae0f8 __exceptionPreprocess + 172\n```\n\nThis is because of our color variable in our Highlight. Let's update our Highlights class to use `uiColor` instead.\n\n```swift\nimport SwiftUI\n\nstruct Highlight {\n \n let text: String\n let location: Int\n let length: Int\n var uiColor: UIColor\n var color: Color {\n get {\n .init(uiColor: uiColor)\n }\n set {\n uiColor = .init(newValue)\n }\n }\n}\n```\n\nIn our `highlighText()` function, use `uiColor` instead of `color`.\n\n```swift\n @objc func highlightText() {\n if let range = self.selectedTextRange, let selectedText = self.text(in: range) {\n print(\"Selected text is \\(selectedText)\")\n let highlight = Highlight(text: selectedText, location: selectedRange.location, length: selectedRange.length, uiColor: .purple)\n let highlightDict = [\n \"data\": highlight\n ]\n NotificationCenter.default.post(name: Notification.Name(\"highlightAdded\"), object: nil, userInfo: highlightDict)\n }\n }\n```\n\nAlso, update our `onReceive` highlight object.\n\n```swift .onReceive(NotificationCenter.default.publisher(for: Notification.Name(\"highlightAdded\"))) { output in\n if let highlight = output.userInfo![\"data\"] as? Highlight {\n let mutableString = NSMutableAttributedString.init(string: attributedText.string)\n let highlightAttributes: [NSAttributedString.Key: Any] = [\n .backgroundColor: highlight.uiColor,\n ]\n mutableString.addAttributes(highlightAttributes, range: NSRange(location: highlight.location, length: highlight.length))\n attributedText = mutableString\n }\n }\n```\n\nEverything should work now.\n\n<img src=\"uploads/2024/simulator-screenshotiphone-152024-06-29-at-12.05.59.png\" width=\"600\" height=\"1300\" alt=\"\">\n\nYou can find the code at [https://github.com/lawgimenez/customtext](https://github.com/lawgimenez/customtext).\n",
"date_published": "2024-06-29T12:23:39+08:00",
"url": "https://law.gmnz.xyz/2024/06/23/swiftui-text-highlights.html",
"tags": ["iOS","Mobile Development","Swift"]
},
{
"id": "http://lwgmnz.micro.blog/2024/06/02/gradle-version-catalogs.html",
"title": "Gradle Version Catalogs",
"content_html": "<p>I migrated our Gradle dependency management to <a href=\"https://docs.gradle.org/current/userguide/platforms.html\">Gradle’s new version catalog</a>. It’s very convenient, I didn’t know this feature has been around since last year?</p>\n<p>Here’s the <code>libs.versions.toml</code> file, much more organized.</p>\n<!-- raw HTML omitted -->\n<p>And the dependencies plugin looks cleaner.</p>\n<!-- raw HTML omitted -->\n<p>Of course, I gotta ask Gemini the why. I have to admit Gemini has been a very handy sidekick. No need to open a new tab and Google.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "I migrated our Gradle dependency management to [Gradle's new version catalog](https://docs.gradle.org/current/userguide/platforms.html). It's very convenient, I didn't know this feature has been around since last year?\n\nHere's the `libs.versions.toml` file, much more organized.\n\n<img src=\"uploads/2024/screenshot-2024-06-02-at-3.42.23pm.png\" width=\"600\" height=\"602\" alt=\"\">\n\nAnd the dependencies plugin looks cleaner.\n\n<img src=\"uploads/2024/screenshot-2024-06-02-at-3.42.16pm.png\" width=\"600\" height=\"242\" alt=\"\">\n\nOf course, I gotta ask Gemini the why. I have to admit Gemini has been a very handy sidekick. No need to open a new tab and Google.\n\n<img src=\"uploads/2024/screenshot-2024-06-02-at-3.43.54pm.png\" width=\"600\" height=\"945\" alt=\"\">\n",
"date_published": "2024-06-02T15:50:12+08:00",
"url": "https://law.gmnz.xyz/2024/06/02/gradle-version-catalogs.html",
"tags": ["Mobile Development","Android","Kotlin"]
},
{
"id": "http://lwgmnz.micro.blog/2024/05/25/gemini-android-studio.html",
"title": "Gemini in Android Studio",
"content_html": "<p>While everyone is on the AI train, I am still somewhat skeptical. AI is everywhere, it has come to a point that I can’t escape it anymore. Maybe I am getting older. Or I feel like AI is for the new kids.</p>\n<p>Last week I tried <a href=\"https://developer.android.com/studio/preview/gemini\">Gemini in Android Studio</a>, and the more I use it, Gemini seems to adapt and learn how I code.</p>\n<p>Today, I was writing a bookmarking feature and so far Gemini has been awesome in predicting the next code block.</p>\n<p>Setting up my <code>ViewModel</code>, Gemini threw this suggestion which is correct.</p>\n<!-- raw HTML omitted -->\n<p>I forgot how to return an array in Retrofit, and Gemini was correct again. Wow!</p>\n<!-- raw HTML omitted -->\n<p>And now, the finishing touches to our <code>ViewModel</code>, the variable declarations.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>I am impressed. I also expect Apple’s Xcode to follow suit with their upcoming WWDC. AI in Xcode would be very interesting. But, I am still not Xcode’s biggest fan.</p>\n",
"content_text": "While everyone is on the AI train, I am still somewhat skeptical. AI is everywhere, it has come to a point that I can't escape it anymore. Maybe I am getting older. Or I feel like AI is for the new kids.\n\nLast week I tried [Gemini in Android Studio](https://developer.android.com/studio/preview/gemini), and the more I use it, Gemini seems to adapt and learn how I code.\n\nToday, I was writing a bookmarking feature and so far Gemini has been awesome in predicting the next code block.\n\nSetting up my `ViewModel`, Gemini threw this suggestion which is correct.\n\n<img src=\"uploads/2024/screenshot-2024-05-25-at-4.17.18pm.png\" width=\"600\" height=\"163\" alt=\"\">\n\nI forgot how to return an array in Retrofit, and Gemini was correct again. Wow!\n\n<img src=\"uploads/2024/screenshot-2024-05-25-at-4.18.10pm.png\" width=\"600\" height=\"187\" alt=\"\">\n\nAnd now, the finishing touches to our `ViewModel`, the variable declarations.\n\n<img src=\"uploads/2024/screenshot-2024-05-25-at-5.02.04pm.png\" width=\"600\" height=\"55\" alt=\"\">\n\n<img src=\"uploads/2024/screenshot-2024-05-25-at-5.02.19pm.png\" width=\"600\" height=\"125\" alt=\"\">\n\nI am impressed. I also expect Apple's Xcode to follow suit with their upcoming WWDC. AI in Xcode would be very interesting. But, I am still not Xcode's biggest fan.\n",
"date_published": "2024-05-25T17:13:08+08:00",
"url": "https://law.gmnz.xyz/2024/05/25/gemini-android-studio.html",
"tags": ["Mobile Development","Android","Kotlin"]
},
{
"id": "http://lwgmnz.micro.blog/2024/02/27/kotlin-serialization-shenanigans.html",
"title": "Kotlin Serialization Shenanigans",
"content_html": "<p>It’s been a while since I did some serialization implementation on Android. I forgot how painful this was to implement. This is for my future self, in case I reencounter this stupid shenanigans again.</p>\n<h2 id=\"gradle-imports\">Gradle Imports</h2>\n<p>First, import Retrofit, Kotlinx Serialization and Jake Wharton’s Retrofit Kotlinx Serialization Converter.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-kotlin\" data-lang=\"kotlin\">implementation(<span style=\"color:#e6db74\">"com.squareup.retrofit2:retrofit:2.9.0"</span>)\nimplementation(<span style=\"color:#e6db74\">"org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"</span>)\nimplementation(<span style=\"color:#e6db74\">"com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"</span>)\n</code></pre></div><p>On the <code>build.gradle</code> project level file, don’t forget to also add the dependency inside the plugin section.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-kotlin\" data-lang=\"kotlin\">plugins {\n id(<span style=\"color:#e6db74\">"org.jetbrains.kotlin.plugin.serialization"</span>) version <span style=\"color:#e6db74\">"1.9.22"</span> apply <span style=\"color:#66d9ef\">false</span>\n}\n</code></pre></div><p>Back to your app level <code>build.gradle</code> file, also add the dependency inside the plugin section.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-kotlin\" data-lang=\"kotlin\">plugins {\n id(<span style=\"color:#e6db74\">"kotlinx-serialization"</span>)\n}\n</code></pre></div><h2 id=\"retrofit-helper-class\">Retrofit Helper Class</h2>\n<p>Your Retrofit helper class should be like below. Take note of the import statements.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-kotlin\" data-lang=\"kotlin\"><span style=\"color:#66d9ef\">import</span> com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory\n<span style=\"color:#66d9ef\">import</span> kotlinx.serialization.json.Json\n<span style=\"color:#66d9ef\">import</span> okhttp3.MediaType\n<span style=\"color:#66d9ef\">import</span> retrofit2.Retrofit\n\n<span style=\"color:#66d9ef\">object</span> <span style=\"color:#a6e22e\">ApiHelper</span> {\n\n <span style=\"color:#66d9ef\">val</span> baseApi = <span style=\"color:#e6db74\">"https://api.somewebsite.com/"</span>\n <span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">val</span> contentType = MediaType.<span style=\"color:#66d9ef\">get</span>(<span style=\"color:#e6db74\">"application/json"</span>)\n\n <span style=\"color:#66d9ef\">fun</span> <span style=\"color:#a6e22e\">getInstance</span>(): Retrofit {\n <span style=\"color:#66d9ef\">return</span> Retrofit.Builder()\n .baseUrl(baseApi)\n .addConverterFactory(Json.asConverterFactory(contentType))\n .build()\n }\n}\n</code></pre></div><h2 id=\"proguard-rules\">Proguard rules</h2>\n<p>Don’t forget about the proguard rules.</p>\n<pre tabindex=\"0\"><code>-if @kotlinx.serialization.Serializable class **\n-keepclassmembers class <1> {\n static <1>$Companion Companion;\n}\n\n# Keep `serializer()` on companion objects (both default and named) of serializable classes.\n-if @kotlinx.serialization.Serializable class ** {\n static **$* *;\n}\n-keepclassmembers class <2>$<3> {\n kotlinx.serialization.KSerializer serializer(...);\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n public static <1> INSTANCE;\n kotlinx.serialization.KSerializer serializer(...);\n}\n\n# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.\n-keepattributes RuntimeVisibleAnnotations,AnnotationDefault\n</code></pre><h2 id=\"kotlinxserializationmissingfieldexception\">kotlinx.serialization.MissingFieldException</h2>\n<p>In your data class, it might seem that adding a nullable type to your variable is enough.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-kotlin\" data-lang=\"kotlin\"><span style=\"color:#a6e22e\">@Serializable</span>\n<span style=\"color:#66d9ef\">data</span> <span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">User</span>(\n <span style=\"color:#a6e22e\">@SerialName</span>(<span style=\"color:#e6db74\">"user_id"</span>)\n <span style=\"color:#66d9ef\">val</span> userId: Int?\n)\n</code></pre></div><p>It’s not. You need to add a default null value.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-kotlin\" data-lang=\"kotlin\"><span style=\"color:#a6e22e\">@Serializable</span>\n<span style=\"color:#66d9ef\">data</span> <span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">User</span>(\n <span style=\"color:#a6e22e\">@SerialName</span>(<span style=\"color:#e6db74\">"user_id"</span>)\n <span style=\"color:#66d9ef\">val</span> userId: Int? = <span style=\"color:#66d9ef\">null</span>\n)\n</code></pre></div><p>Shoutout to <a href=\"https://stackoverflow.com/a/72451819/1075466\">Jere Schneider</a> and <a href=\"https://stackoverflow.com/a/64797277/1075466\">tyczj</a> for the helpful answers that can’t be found anywhere on Android’s documentation.</p>\n",
"content_text": "It's been a while since I did some serialization implementation on Android. I forgot how painful this was to implement. This is for my future self, in case I reencounter this stupid shenanigans again.\n\n## Gradle Imports\n\nFirst, import Retrofit, Kotlinx Serialization and Jake Wharton's Retrofit Kotlinx Serialization Converter.\n\n```kotlin\nimplementation(\"com.squareup.retrofit2:retrofit:2.9.0\")\nimplementation(\"org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3\")\nimplementation(\"com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0\")\n```\n\nOn the `build.gradle` project level file, don't forget to also add the dependency inside the plugin section.\n\n```kotlin\nplugins {\n id(\"org.jetbrains.kotlin.plugin.serialization\") version \"1.9.22\" apply false\n}\n```\nBack to your app level `build.gradle` file, also add the dependency inside the plugin section.\n\n```kotlin\nplugins {\n id(\"kotlinx-serialization\")\n}\n``` \n\n## Retrofit Helper Class\nYour Retrofit helper class should be like below. Take note of the import statements.\n\n```kotlin\nimport com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory\nimport kotlinx.serialization.json.Json\nimport okhttp3.MediaType\nimport retrofit2.Retrofit\n\nobject ApiHelper {\n\n val baseApi = \"https://api.somewebsite.com/\"\n private val contentType = MediaType.get(\"application/json\")\n\n fun getInstance(): Retrofit {\n return Retrofit.Builder()\n .baseUrl(baseApi)\n .addConverterFactory(Json.asConverterFactory(contentType))\n .build()\n }\n}\n```\n\n## Proguard rules\nDon't forget about the proguard rules.\n\n```\n-if @kotlinx.serialization.Serializable class **\n-keepclassmembers class <1> {\n static <1>$Companion Companion;\n}\n\n# Keep `serializer()` on companion objects (both default and named) of serializable classes.\n-if @kotlinx.serialization.Serializable class ** {\n static **$* *;\n}\n-keepclassmembers class <2>$<3> {\n kotlinx.serialization.KSerializer serializer(...);\n}\n\n# Keep `INSTANCE.serializer()` of serializable objects.\n-if @kotlinx.serialization.Serializable class ** {\n public static ** INSTANCE;\n}\n-keepclassmembers class <1> {\n public static <1> INSTANCE;\n kotlinx.serialization.KSerializer serializer(...);\n}\n\n# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.\n-keepattributes RuntimeVisibleAnnotations,AnnotationDefault\n```\n\n## kotlinx.serialization.MissingFieldException\nIn your data class, it might seem that adding a nullable type to your variable is enough.\n\n```kotlin\n@Serializable\ndata class User(\n @SerialName(\"user_id\")\n val userId: Int?\n)\n```\n\n It's not. You need to add a default null value.\n\n```kotlin\n@Serializable\ndata class User(\n @SerialName(\"user_id\")\n val userId: Int? = null\n)\n```\n\nShoutout to [Jere Schneider](https://stackoverflow.com/a/72451819/1075466) and [tyczj](https://stackoverflow.com/a/64797277/1075466) for the helpful answers that can't be found anywhere on Android's documentation.\n",
"date_published": "2024-02-27T18:46:11+08:00",
"url": "https://law.gmnz.xyz/2024/02/27/kotlin-serialization-shenanigans.html",
"tags": ["Android"]
},
{
"id": "http://lwgmnz.micro.blog/2024/02/10/rate-my-setup.html",
"title": "Rate My Setup",
"content_html": "<p>Coding on TV might be one of my favorite developer setups. This was done via Apple’s Mirror feature using AirPlay.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Coding on TV might be one of my favorite developer setups. This was done via Apple's Mirror feature using AirPlay.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2024/752a30bd77.jpg\" width=\"600\" height=\"1066\" alt=\"\">\n",
"date_published": "2024-02-10T17:44:01+08:00",
"url": "https://law.gmnz.xyz/2024/02/10/rate-my-setup.html"
},
{
"id": "http://lwgmnz.micro.blog/2024/02/07/ios-panic.html",
"title": "iOS Panic",
"content_html": "<p>My phone suddenly restarted and went into a full-blown panic. After restarting, there were some missing icons, I am on 17.4 beta 1. It’s amazing how seldom iOS crashes, but when it does I also get a sense of panic.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "My phone suddenly restarted and went into a full-blown panic. After restarting, there were some missing icons, I am on 17.4 beta 1. It's amazing how seldom iOS crashes, but when it does I also get a sense of panic.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2024/9d2bb80f2d.png\" width=\"600\" height=\"1298\" alt=\"\">\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2024/65c412dbef.png\" width=\"600\" height=\"1298\" alt=\"\">\n",
"date_published": "2024-02-07T19:59:16+08:00",
"url": "https://law.gmnz.xyz/2024/02/07/ios-panic.html"
},
{
"id": "http://lwgmnz.micro.blog/2024/01/07/wrote-a-new.html",
"title": "Lucas app v0.1",
"content_html": "<p>Yesterday I developed a new typing notepad app for my kid with text-to-speech feature. I wrote a working prototype in under an hour. It’s amazing how quickly you can create an app in SwiftUI.</p>\n<!-- raw HTML omitted -->\n<p>Our kid testing his app.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Yesterday I developed a new typing notepad app for my kid with text-to-speech feature. I wrote a working prototype in under an hour. It's amazing how quickly you can create an app in SwiftUI.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2024/screenshot-2024-01-06-at-6.19.40pm.png\" width=\"600\" height=\"461\" alt=\"\">\n\nOur kid testing his app.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2024/c7fb04d952.png\" width=\"600\" height=\"1298\" alt=\"\">\n",
"date_published": "2024-01-07T15:31:32+08:00",
"url": "https://law.gmnz.xyz/2024/01/07/wrote-a-new.html",
"tags": ["iOS","Projects","macOS"]
},
{
"id": "http://lwgmnz.micro.blog/2024/01/04/goals.html",
"title": "2024 Goals",
"content_html": "<p>I should write here more often. This is one of the few goals I have for 2024, personally.</p>\n<p>2023 was just a very hectic year for me and my family. Half of it was spent preparing for our new house, in terms of time and money. Now that we are settled in, this year should be all about upgrading and fixing our home. Make it more comfy, etc.</p>\n<p>One major goal for this year is for our family to start traveling, either on vacation or some errands. I am not sure if I should write about this too but who’s reading anyway.</p>\n",
"content_text": "I should write here more often. This is one of the few goals I have for 2024, personally.\n\n2023 was just a very hectic year for me and my family. Half of it was spent preparing for our new house, in terms of time and money. Now that we are settled in, this year should be all about upgrading and fixing our home. Make it more comfy, etc.\n\nOne major goal for this year is for our family to start traveling, either on vacation or some errands. I am not sure if I should write about this too but who's reading anyway.\n\n\n\n",
"date_published": "2024-01-04T11:42:37+08:00",
"url": "https://law.gmnz.xyz/2024/01/04/goals.html",
"tags": ["Personal"]
},
{
"id": "http://lwgmnz.micro.blog/2023/12/17/retroapp-postcard.html",
"title": "Thank you Retro",
"content_html": "<p>I just want to say thank you to <a href=\"https://retro.app/\">Retro App</a> for their Postcard feature. I was able to send a postcard to my parents in the US, while I am currently on the other side of the world.</p>\n<p>I sent it for $0.</p>\n<!-- raw HTML omitted -->\n<p>I missed them so much, and something physical from here would be nice to send to them this Christmas. After, 6-7 days, they were able to receive it.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>To be honest, I would gladly pay for this service again in the future.</p>\n<p>Thank you Retro!</p>\n",
"content_text": "I just want to say thank you to [Retro App](https://retro.app/) for their Postcard feature. I was able to send a postcard to my parents in the US, while I am currently on the other side of the world.\n\nI sent it for $0.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/63109fe73a.jpg\" width=\"600\" height=\"1298\" alt=\"\">\n\nI missed them so much, and something physical from here would be nice to send to them this Christmas. After, 6-7 days, they were able to receive it.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/7da9b8b5e7.jpg\" width=\"600\" height=\"800\" alt=\"\">\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/0a620c8778.jpg\" width=\"600\" height=\"403\" alt=\"\">\n\nTo be honest, I would gladly pay for this service again in the future.\n\nThank you Retro!\n",
"date_published": "2023-12-17T17:48:39+08:00",
"url": "https://law.gmnz.xyz/2023/12/17/retroapp-postcard.html",
"tags": ["Personal"]
},
{
"id": "http://lwgmnz.micro.blog/2023/12/08/just-a-single.html",
"title": "Single Dot Crash",
"content_html": "<p>It’s amazing how a single dot can crash the whole app. Xcode should throw a compile error, but not sure why it did not.</p>\n<p>Looking at the <code>git diff</code>, you can see the difference is just a <code>.</code>.</p>\n<!-- raw HTML omitted -->\n<p>Xcode crash logs are not helping.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "It's amazing how a single dot can crash the whole app. Xcode should throw a compile error, but not sure why it did not.\n\nLooking at the `git diff`, you can see the difference is just a `.`. \n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-12-08-at-4.18.00pm.png\" width=\"600\" height=\"462\" alt=\"\">\n\nXcode crash logs are not helping.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-12-08-at-4.24.24pm.png\" width=\"600\" height=\"272\" alt=\"\">\n",
"date_published": "2023-12-08T16:25:25+08:00",
"url": "https://law.gmnz.xyz/2023/12/08/just-a-single.html",
"tags": ["iOS","Mobile Development"]
},
{
"id": "http://lwgmnz.micro.blog/2023/11/30/my-spotify-wrapped.html",
"title": "My 2023 Spotify Wrapped",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "<img src=\"https://cdn.uploads.micro.blog/67704/2023/453bed0b9d.jpg\" width=\"600\" height=\"1066\" alt=\"\">\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/f60f08a9fc.jpg\" width=\"600\" height=\"1066\" alt=\"\">\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/d0e8b459c3.jpg\" width=\"600\" height=\"1066\" alt=\"\">\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/74d8adef50.jpg\" width=\"600\" height=\"1066\" alt=\"\">\n\n",
"date_published": "2023-11-30T15:47:52+08:00",
"url": "https://law.gmnz.xyz/2023/11/30/my-spotify-wrapped.html"
},
{
"id": "http://lwgmnz.micro.blog/2023/10/01/cookcantongo.html",
"title": "cook-canton.go",
"content_html": "<p>Found a funny reel on Facebook titled <code>From Coder to Cooker</code>. The person was cooking pancit canton with the help of codes. You can watch it <a href=\"https://www.facebook.com/reel/3573374636244292\">here</a>.</p>\n<p>I believe he was using C# on this one. Anyway, here’s my version in <a href=\"https://go.dev/\">Go language</a>.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-go\" data-lang=\"go\"><span style=\"color:#f92672\">package</span> <span style=\"color:#a6e22e\">main</span>\n\n<span style=\"color:#f92672\">import</span> (<span style=\"color:#e6db74\">"fmt"</span>; \t<span style=\"color:#e6db74\">"time"</span>)\n\n<span style=\"color:#66d9ef\">var</span> <span style=\"color:#a6e22e\">boilingWaterDone</span>, <span style=\"color:#a6e22e\">waterIsBoiling</span> = <span style=\"color:#66d9ef\">false</span>, <span style=\"color:#66d9ef\">true</span>\n\n<span style=\"color:#66d9ef\">type</span> <span style=\"color:#a6e22e\">Step</span> <span style=\"color:#66d9ef\">int</span>\n<span style=\"color:#66d9ef\">const</span> (<span style=\"color:#a6e22e\">Boil</span> <span style=\"color:#a6e22e\">Step</span> = <span style=\"color:#66d9ef\">iota</span>; <span style=\"color:#a6e22e\">CookNoodles</span>; <span style=\"color:#a6e22e\">DrainWater</span>; <span style=\"color:#a6e22e\">MixSeasoning</span>; <span style=\"color:#a6e22e\">MixWell</span>)\n\n<span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">main</span>() {\n\t<span style=\"color:#66d9ef\">for</span> {\n\t\t<span style=\"color:#a6e22e\">runStep</span>(<span style=\"color:#a6e22e\">Boil</span>)\n\t\t<span style=\"color:#66d9ef\">if</span> <span style=\"color:#a6e22e\">boilingWaterDone</span> <span style=\"color:#f92672\">==</span> <span style=\"color:#66d9ef\">true</span> {\n\t\t\t<span style=\"color:#a6e22e\">waterIsBoiling</span> = <span style=\"color:#66d9ef\">true</span>\n\t\t\t<span style=\"color:#66d9ef\">break</span>\n\t\t}\n\t}\n\t<span style=\"color:#66d9ef\">if</span> <span style=\"color:#a6e22e\">waterIsBoiling</span> {\n\t\t<span style=\"color:#a6e22e\">runStep</span>(<span style=\"color:#a6e22e\">CookNoodles</span>)\n\t\t<span style=\"color:#a6e22e\">runStep</span>(<span style=\"color:#a6e22e\">DrainWater</span>)\n\t\t<span style=\"color:#a6e22e\">runStep</span>(<span style=\"color:#a6e22e\">MixSeasoning</span>)\n\t\t<span style=\"color:#a6e22e\">runStep</span>(<span style=\"color:#a6e22e\">MixWell</span>)\n\t}\n}\n\n<span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">runStep</span>(<span style=\"color:#a6e22e\">step</span> <span style=\"color:#a6e22e\">Step</span>) {\n\t<span style=\"color:#66d9ef\">switch</span> <span style=\"color:#a6e22e\">step</span> {\n\t<span style=\"color:#66d9ef\">case</span> <span style=\"color:#a6e22e\">Boil</span>:\n\t\t<span style=\"color:#a6e22e\">fmt</span>.<span style=\"color:#a6e22e\">Println</span>(<span style=\"color:#e6db74\">"Boiling water..."</span>)\n\t<span style=\"color:#66d9ef\">case</span> <span style=\"color:#a6e22e\">CookNoodles</span>:\n\t\t<span style=\"color:#a6e22e\">fmt</span>.<span style=\"color:#a6e22e\">Println</span>(<span style=\"color:#e6db74\">"Cooking noodles..."</span>)\n\t\t<span style=\"color:#a6e22e\">time</span>.<span style=\"color:#a6e22e\">Sleep</span>(<span style=\"color:#ae81ff\">5</span> <span style=\"color:#f92672\">*</span> <span style=\"color:#a6e22e\">time</span>.<span style=\"color:#a6e22e\">Minute</span>) <span style=\"color:#75715e\">// Cook noodles for 5 minutes\n</span><span style=\"color:#75715e\"></span>\t<span style=\"color:#66d9ef\">case</span> <span style=\"color:#a6e22e\">DrainWater</span>:\n\t\t<span style=\"color:#a6e22e\">fmt</span>.<span style=\"color:#a6e22e\">Println</span>(<span style=\"color:#e6db74\">"Drain water..."</span>)\n\t<span style=\"color:#66d9ef\">case</span> <span style=\"color:#a6e22e\">MixSeasoning</span>:\n\t\t<span style=\"color:#a6e22e\">fmt</span>.<span style=\"color:#a6e22e\">Println</span>(<span style=\"color:#e6db74\">"Mix seasoning..."</span>)\n\t<span style=\"color:#66d9ef\">case</span> <span style=\"color:#a6e22e\">MixWell</span>:\n\t\t<span style=\"color:#a6e22e\">fmt</span>.<span style=\"color:#a6e22e\">Println</span>(<span style=\"color:#e6db74\">"Mix well..."</span>)\n\t}\n}\n</code></pre></div>",
"content_text": "Found a funny reel on Facebook titled `From Coder to Cooker`. The person was cooking pancit canton with the help of codes. You can watch it [here](https://www.facebook.com/reel/3573374636244292).\n\nI believe he was using C# on this one. Anyway, here's my version in [Go language](https://go.dev/).\n\n```go\npackage main\n\nimport (\"fmt\"; \t\"time\")\n\nvar boilingWaterDone, waterIsBoiling = false, true\n\ntype Step int\nconst (Boil Step = iota; CookNoodles; DrainWater; MixSeasoning; MixWell)\n\nfunc main() {\n\tfor {\n\t\trunStep(Boil)\n\t\tif boilingWaterDone == true {\n\t\t\twaterIsBoiling = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif waterIsBoiling {\n\t\trunStep(CookNoodles)\n\t\trunStep(DrainWater)\n\t\trunStep(MixSeasoning)\n\t\trunStep(MixWell)\n\t}\n}\n\nfunc runStep(step Step) {\n\tswitch step {\n\tcase Boil:\n\t\tfmt.Println(\"Boiling water...\")\n\tcase CookNoodles:\n\t\tfmt.Println(\"Cooking noodles...\")\n\t\ttime.Sleep(5 * time.Minute) // Cook noodles for 5 minutes\n\tcase DrainWater:\n\t\tfmt.Println(\"Drain water...\")\n\tcase MixSeasoning:\n\t\tfmt.Println(\"Mix seasoning...\")\n\tcase MixWell:\n\t\tfmt.Println(\"Mix well...\")\n\t}\n}\n```\n",
"date_published": "2023-10-01T11:35:43+08:00",
"url": "https://law.gmnz.xyz/2023/10/01/cookcantongo.html",
"tags": ["Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2023/09/06/apple-vision-ready.html",
"title": "Apple Vision Ready",
"content_html": "<p>OnlineJobs app now works on <a href=\"https://developer.apple.com/visionos/\">Apple’s visionOS</a>.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "OnlineJobs app now works on [Apple's visionOS](https://developer.apple.com/visionos/).\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/simulator-screenshotapple-vision-pro2023-09-06-at-09.56.12.png\" width=\"600\" height=\"449\" alt=\"\">\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/simulator-screenshotapple-vision-pro2023-09-06-at-09.55.25.png\" width=\"600\" height=\"449\" alt=\"\">\n",
"date_published": "2023-09-06T10:37:14+08:00",
"url": "https://law.gmnz.xyz/2023/09/06/apple-vision-ready.html",
"tags": ["iOS","Mobile Development"]
},
{
"id": "http://lwgmnz.micro.blog/2023/08/23/versionedschema-protocol-updates.html",
"title": "VersionedSchema Protocol Updates",
"content_html": "<p>After updating to Xcode 15 beta 7, I got a <code>VersionedSchema</code> error saying that</p>\n<pre tabindex=\"0\"><code>does not conform to protocol 'VersionedSchema'\n</code></pre><p>Previously, the variable <code>versionIdentifier</code> was of the type String.</p>\n<pre tabindex=\"0\"><code>static var versionIdentifier: String = "v1"\n</code></pre><p>On the latest Xcode 15 beta 7, <code>versionIdentifier</code> is now of the type <code>Schema.Version</code>.</p>\n<pre tabindex=\"0\"><code>static var versionIdentifier: Schema.Version = Schema.Version(1, 0, 0)\n</code></pre>",
"content_text": "After updating to Xcode 15 beta 7, I got a `VersionedSchema` error saying that\n```\ndoes not conform to protocol 'VersionedSchema'\n```\n\nPreviously, the variable `versionIdentifier` was of the type String.\n\n```\nstatic var versionIdentifier: String = \"v1\"\n```\n\nOn the latest Xcode 15 beta 7, `versionIdentifier` is now of the type `Schema.Version`.\n\n```\nstatic var versionIdentifier: Schema.Version = Schema.Version(1, 0, 0)\n```\n",
"date_published": "2023-08-23T23:11:35+08:00",
"url": "https://law.gmnz.xyz/2023/08/23/versionedschema-protocol-updates.html",
"tags": ["iOS","Mobile Development","Swift"]
},
{
"id": "http://lwgmnz.micro.blog/2023/08/07/build-go-programs.html",
"title": "Build Go programs with GitHub modules present",
"content_html": "<p>I was hit with an error when I try to run a Go script, <code>go run somescript.go</code>. This is a note to my future self, in case I totally forgot.</p>\n<pre tabindex=\"0\"><code>no required module provides package github.com/gofrs/uuid: go.mod file not found in current directory or any parent directory; see 'go help modules'\n</code></pre><p>I need to run the command below.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-go\" data-lang=\"go\"><span style=\"color:#66d9ef\">go</span> <span style=\"color:#a6e22e\">mod</span> <span style=\"color:#a6e22e\">init</span> <span style=\"color:#a6e22e\">somescript</span>.<span style=\"color:#66d9ef\">go</span>\n</code></pre></div><p>Then it will output the following lines.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-go\" data-lang=\"go\"><span style=\"color:#66d9ef\">go</span>: <span style=\"color:#a6e22e\">creating</span> <span style=\"color:#a6e22e\">new</span> <span style=\"color:#66d9ef\">go</span>.<span style=\"color:#a6e22e\">mod</span>: <span style=\"color:#a6e22e\">module</span> <span style=\"color:#a6e22e\">somescript</span>.<span style=\"color:#66d9ef\">go</span>\n<span style=\"color:#66d9ef\">go</span>: <span style=\"color:#a6e22e\">to</span> <span style=\"color:#a6e22e\">add</span> <span style=\"color:#a6e22e\">module</span> <span style=\"color:#a6e22e\">requirements</span> <span style=\"color:#a6e22e\">and</span> <span style=\"color:#a6e22e\">sums</span>:\n\t<span style=\"color:#66d9ef\">go</span> <span style=\"color:#a6e22e\">mod</span> <span style=\"color:#a6e22e\">tidy</span>\n</code></pre></div><p>Then run mod tidy.</p>\n<pre tabindex=\"0\"><code>go mod tidy\n</code></pre><p>And the output will be.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-go\" data-lang=\"go\"><span style=\"color:#66d9ef\">go</span>: <span style=\"color:#a6e22e\">finding</span> <span style=\"color:#a6e22e\">module</span> <span style=\"color:#66d9ef\">for</span> <span style=\"color:#f92672\">package</span> <span style=\"color:#a6e22e\">github</span>.<span style=\"color:#a6e22e\">com</span><span style=\"color:#f92672\">/</span><span style=\"color:#a6e22e\">gofrs</span><span style=\"color:#f92672\">/</span><span style=\"color:#a6e22e\">uuid</span>\n<span style=\"color:#66d9ef\">go</span>: <span style=\"color:#a6e22e\">downloading</span> <span style=\"color:#a6e22e\">github</span>.<span style=\"color:#a6e22e\">com</span><span style=\"color:#f92672\">/</span><span style=\"color:#a6e22e\">gofrs</span><span style=\"color:#f92672\">/</span><span style=\"color:#a6e22e\">uuid</span> <span style=\"color:#a6e22e\">v4</span><span style=\"color:#ae81ff\">.4.0</span><span style=\"color:#f92672\">+</span><span style=\"color:#a6e22e\">incompatible</span>\n<span style=\"color:#66d9ef\">go</span>: <span style=\"color:#a6e22e\">found</span> <span style=\"color:#a6e22e\">github</span>.<span style=\"color:#a6e22e\">com</span><span style=\"color:#f92672\">/</span><span style=\"color:#a6e22e\">gofrs</span><span style=\"color:#f92672\">/</span><span style=\"color:#a6e22e\">uuid</span> <span style=\"color:#a6e22e\">in</span> <span style=\"color:#a6e22e\">github</span>.<span style=\"color:#a6e22e\">com</span><span style=\"color:#f92672\">/</span><span style=\"color:#a6e22e\">gofrs</span><span style=\"color:#f92672\">/</span><span style=\"color:#a6e22e\">uuid</span> <span style=\"color:#a6e22e\">v4</span><span style=\"color:#ae81ff\">.4.0</span><span style=\"color:#f92672\">+</span><span style=\"color:#a6e22e\">incompatible</span>\n</code></pre></div><p>Then build it.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-go\" data-lang=\"go\"><span style=\"color:#66d9ef\">go</span> <span style=\"color:#a6e22e\">build</span> <span style=\"color:#a6e22e\">somescript</span>.<span style=\"color:#66d9ef\">go</span>\n</code></pre></div><p>It will create the following files</p>\n<pre tabindex=\"0\"><code>ls\ngo.mod\t\tgo.sum\t\tsomescript\t somescript.go\n</code></pre><p>Run the compiled script.</p>\n<pre tabindex=\"0\"><code>./somescript\n</code></pre>",
"content_text": "I was hit with an error when I try to run a Go script, `go run somescript.go`. This is a note to my future self, in case I totally forgot.\n\n```\nno required module provides package github.com/gofrs/uuid: go.mod file not found in current directory or any parent directory; see 'go help modules'\n```\n\nI need to run the command below.\n```go\ngo mod init somescript.go\n```\n\nThen it will output the following lines.\n```go\ngo: creating new go.mod: module somescript.go\ngo: to add module requirements and sums:\n\tgo mod tidy\n```\n\nThen run mod tidy.\n```\ngo mod tidy\n```\n\nAnd the output will be.\n```go\ngo: finding module for package github.com/gofrs/uuid\ngo: downloading github.com/gofrs/uuid v4.4.0+incompatible\ngo: found github.com/gofrs/uuid in github.com/gofrs/uuid v4.4.0+incompatible\n```\n\nThen build it.\n```go\ngo build somescript.go\n```\n\nIt will create the following files\n```\nls\ngo.mod\t\tgo.sum\t\tsomescript\t somescript.go\n```\n\nRun the compiled script.\n```\n./somescript\n```\n",
"date_published": "2023-08-07T10:35:37+08:00",
"url": "https://law.gmnz.xyz/2023/08/07/build-go-programs.html",
"tags": ["Programming","TIL"]
},
{
"id": "http://lwgmnz.micro.blog/2023/08/07/throwback-android-developer.html",
"title": "Throwback Android Developer Google Group",
"content_html": "<p>Out of the blue, I remember that I was in a Google Group community of Filipino Android developers back in 2011-ish. After some quick Googling, I found the group. This was in 2011? Wow.</p>\n<p>I am <a href=\"https://groups.google.com/g/philippine-android-developers/c/w-qVYh5ciqY/m/Uf7swh-QLHYJ\">surprised the link is still alive</a>, knowing Google’s history of shutting down things.</p>\n<!-- raw HTML omitted -->\n<p>Yeah, the post feels kinda lonely. There weren’t that many Android developers in my area back then. At this point in time I am not even entirely sure if I could make a living out of this profession. The majority was going into PHP web development.</p>\n<p>But my sister always told me to “swim against the current”. So, that’s what I did. 12 years later I am still developing Android. And, iOS development.</p>\n",
"content_text": "Out of the blue, I remember that I was in a Google Group community of Filipino Android developers back in 2011-ish. After some quick Googling, I found the group. This was in 2011? Wow.\n\nI am [surprised the link is still alive](https://groups.google.com/g/philippine-android-developers/c/w-qVYh5ciqY/m/Uf7swh-QLHYJ), knowing Google's history of shutting down things.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-08-07-at-9.49.50am.png\" width=\"600\" height=\"296\" alt=\"\">\n\nYeah, the post feels kinda lonely. There weren't that many Android developers in my area back then. At this point in time I am not even entirely sure if I could make a living out of this profession. The majority was going into PHP web development.\n\nBut my sister always told me to \"swim against the current\". So, that's what I did. 12 years later I am still developing Android. And, iOS development.\n\n\n",
"date_published": "2023-08-07T09:57:52+08:00",
"url": "https://law.gmnz.xyz/2023/08/07/throwback-android-developer.html",
"tags": ["Personal","Mobile Development","Android"]
},
{
"id": "http://lwgmnz.micro.blog/2023/08/05/that-annoying-xcode.html",
"title": "That Annoying Xcode 15 IDELogRedirectionPolicy",
"content_html": "<p>This issue will cause debugging on either device or simulator to slow down to a crawl.</p>\n<!-- raw HTML omitted -->\n<p>For future reference, here’s how I fixed it.</p>\n<p>Edit the current scheme by going to Manage Schemes.</p>\n<!-- raw HTML omitted -->\n<p>In your run scheme, add the environment variable <code>IDELogRedirectionPolicy</code> with a value of <code>oslogToStdio</code>. It should fix that issue now.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "This issue will cause debugging on either device or simulator to slow down to a crawl.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-08-05-at-9.14.15pm.png\" width=\"600\" height=\"90\" alt=\"\">\n\n\nFor future reference, here's how I fixed it.\n\nEdit the current scheme by going to Manage Schemes.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-08-05-at-9.17.28pm.png\" width=\"442\" height=\"360\" alt=\"\">\n\nIn your run scheme, add the environment variable `IDELogRedirectionPolicy` with a value of `oslogToStdio`. It should fix that issue now.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-08-05-at-9.19.08pm.png\" width=\"600\" height=\"324\" alt=\"\">\n\n",
"date_published": "2023-08-05T21:32:16+08:00",
"url": "https://law.gmnz.xyz/2023/08/05/that-annoying-xcode.html",
"tags": ["iOS","Coding","macOS"]
},
{
"id": "http://lwgmnz.micro.blog/2023/07/27/chess-milestone-advanced.html",
"title": "Chess Milestone: Advanced to Legend",
"content_html": "<p>A few days ago, I was able to advance to the most elite division of chess.com, the legend division. After playing a few games from this division, as I predicted, the games are so hard to win.</p>\n<!-- raw HTML omitted -->\n<p>Previously: <a href=\"https://law.gmnz.xyz/2023/04/25/chess-milestone.html\">https://law.gmnz.xyz/2023/04/25/chess-milestone</a></p>\n",
"content_text": "A few days ago, I was able to advance to the most elite division of chess.com, the legend division. After playing a few games from this division, as I predicted, the games are so hard to win.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/3135519597.png\" width=\"600\" height=\"1298\" alt=\"\">\n\n\nPreviously: [https://law.gmnz.xyz/2023/04/25/chess-milestone](https://law.gmnz.xyz/2023/04/25/chess-milestone.html)\n\n",
"date_published": "2023-07-27T10:21:23+08:00",
"url": "https://law.gmnz.xyz/2023/07/27/chess-milestone-advanced.html",
"tags": ["Personal","Games"]
},
{
"id": "http://lwgmnz.micro.blog/2023/07/26/sharing-between-linux.html",
"title": "Sharing from Linux to macOS",
"content_html": "<p>Note: The OS versions I used here are macOS Sonoma 14 Beta 3 and Fedora Workstation 38.</p>\n<p>On the first try, I was supposed to enable file sharing from macOS and access it on my Fedora machine. But Apple’s Samba did not work. So, I tried the opposite instead.</p>\n<h4 id=\"install-and-setup-samba-on-fedora\">Install and Setup Samba on Fedora</h4>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">sudo dnf install samba\nsudo systemctl enable smb --now\nfirewall-cmd --get-active-zones\nsudo firewall-cmd --permanent --zone<span style=\"color:#f92672\">=</span>FedoraWorkstation --add-service<span style=\"color:#f92672\">=</span>samba\nsudo firewall-cmd --reload\n</code></pre></div><p>Make sure the user already exists in fedora, in my case lwgmnz@fedora.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">sudo smbpasswd -a lwgmnz\n</code></pre></div><p>Create the shared folder.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">mkdir /home/lwgmnz/share\nsudo semanage fcontext --add --type <span style=\"color:#e6db74\">"samba_share_t"</span> <span style=\"color:#e6db74\">"/home/lwgmnz/share(/.*)?"</span>\nsudo restorecon -R /home/lwgmnz/share\n</code></pre></div><p>Open <code>smb.conf</code> and add the values below.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">sudo vim /etc/samba/smb.conf\n</code></pre></div><p>Add the following settings at the end of the file.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-vim\" data-lang=\"vim\">[<span style=\"color:#a6e22e\">share</span>]<span style=\"color:#960050;background-color:#1e0010\">\n</span><span style=\"color:#960050;background-color:#1e0010\"></span> <span style=\"color:#a6e22e\">comment</span> = <span style=\"color:#a6e22e\">My</span> <span style=\"color:#a6e22e\">Share</span><span style=\"color:#960050;background-color:#1e0010\">\n</span><span style=\"color:#960050;background-color:#1e0010\"></span> <span style=\"color:#a6e22e\">path</span> = <span style=\"color:#e6db74\">/home/</span><span style=\"color:#a6e22e\">lwgmnz</span>/<span style=\"color:#a6e22e\">share</span><span style=\"color:#960050;background-color:#1e0010\">\n</span><span style=\"color:#960050;background-color:#1e0010\"></span> <span style=\"color:#a6e22e\">writeable</span> = <span style=\"color:#a6e22e\">yes</span><span style=\"color:#960050;background-color:#1e0010\">\n</span><span style=\"color:#960050;background-color:#1e0010\"></span> <span style=\"color:#a6e22e\">browseable</span> = <span style=\"color:#a6e22e\">yes</span><span style=\"color:#960050;background-color:#1e0010\">\n</span><span style=\"color:#960050;background-color:#1e0010\"></span> <span style=\"color:#a6e22e\">public</span> = <span style=\"color:#a6e22e\">yes</span><span style=\"color:#960050;background-color:#1e0010\">\n</span><span style=\"color:#960050;background-color:#1e0010\"></span> <span style=\"color:#a6e22e\">create</span> <span style=\"color:#a6e22e\">mask</span> = <span style=\"color:#ae81ff\">0644</span><span style=\"color:#960050;background-color:#1e0010\">\n</span><span style=\"color:#960050;background-color:#1e0010\"></span> <span style=\"color:#a6e22e\">directory</span> <span style=\"color:#a6e22e\">mask</span> = <span style=\"color:#ae81ff\">0755</span><span style=\"color:#960050;background-color:#1e0010\">\n</span><span style=\"color:#960050;background-color:#1e0010\"></span> <span style=\"color:#a6e22e\">write</span> <span style=\"color:#a6e22e\">list</span> = <span style=\"color:#a6e22e\">user</span><span style=\"color:#960050;background-color:#1e0010\">\n</span></code></pre></div><p>Restart Samba.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">sudo systemctl restart smb\n</code></pre></div><h4 id=\"access-time\">Access time</h4>\n<p>Get Linux’s local IP address, in my case it is 192.168.0.112</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">ifconfig -a\n</code></pre></div><p>On macOS, open Finder, Go - Connect to Server and enter smb://192.168.0.112 and enter the username and password from the command line above.</p>\n<!-- raw HTML omitted -->\n<p>The <em>share</em> folder will now appear.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Note: The OS versions I used here are macOS Sonoma 14 Beta 3 and Fedora Workstation 38.\n\nOn the first try, I was supposed to enable file sharing from macOS and access it on my Fedora machine. But Apple's Samba did not work. So, I tried the opposite instead.\n\n#### Install and Setup Samba on Fedora\n\n``` bash\nsudo dnf install samba\nsudo systemctl enable smb --now\nfirewall-cmd --get-active-zones\nsudo firewall-cmd --permanent --zone=FedoraWorkstation --add-service=samba\nsudo firewall-cmd --reload\n```\n\nMake sure the user already exists in fedora, in my case lwgmnz@fedora.\n\n```bash\nsudo smbpasswd -a lwgmnz\n```\n\nCreate the shared folder.\n\n```bash\nmkdir /home/lwgmnz/share\nsudo semanage fcontext --add --type \"samba_share_t\" \"/home/lwgmnz/share(/.*)?\"\nsudo restorecon -R /home/lwgmnz/share\n```\n\nOpen `smb.conf` and add the values below.\n\n```bash\nsudo vim /etc/samba/smb.conf\n```\n\nAdd the following settings at the end of the file.\n\n```vim\n[share]\n comment = My Share\n path = /home/lwgmnz/share\n writeable = yes\n browseable = yes\n public = yes\n create mask = 0644\n directory mask = 0755\n write list = user\n```\n\nRestart Samba.\n\n```bash\nsudo systemctl restart smb\n```\n\n#### Access time\n\nGet Linux's local IP address, in my case it is 192.168.0.112\n\n```bash\nifconfig -a\n```\n\nOn macOS, open Finder, Go - Connect to Server and enter smb://192.168.0.112 and enter the username and password from the command line above.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-07-26-at-10.59.32am.png\" width=\"600\" height=\"344\" alt=\"\">\n\nThe *share* folder will now appear.\n\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-07-26-at-10.59.42am.png\" width=\"600\" height=\"530\" alt=\"\">\n\n",
"date_published": "2023-07-26T11:34:25+08:00",
"url": "https://law.gmnz.xyz/2023/07/26/sharing-between-linux.html",
"tags": ["Linux","TIL"]
},
{
"id": "http://lwgmnz.micro.blog/2023/07/08/lastfm-api.html",
"title": "Last.fm API",
"content_html": "<p>I recently recovered my Last.fm account, which was one of the top music services before Spotify or streaming in general. It says my account has been around since 2010, thus the cringy username <a href=\"https://www.last.fm/user/xthekingisdeadx\"><code>xthekingisdeadx</code></a>. My profile picture and username still stay the same.</p>\n<p>I want to retrieve my Last.fm data, particularly from my top artists and albums I have listened to for the past week or month. So I could share it somewhere.</p>\n<p>I am going to write my script using Go. I started this project in Python but encountered a couple of errors and I am just not in the mood to debug or solve them. Weekend projects should be fun, no?</p>\n<h4 id=\"get-data\">GET data</h4>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-go\" data-lang=\"go\">\n<span style=\"color:#f92672\">package</span> <span style=\"color:#a6e22e\">main</span>\n\n<span style=\"color:#f92672\">import</span> <span style=\"color:#e6db74\">"fmt"</span>\n<span style=\"color:#f92672\">import</span> <span style=\"color:#e6db74\">"net/http"</span>\n\n<span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">main</span>() {\n <span style=\"color:#a6e22e\">response</span>, <span style=\"color:#66d9ef\">error</span> <span style=\"color:#f92672\">:=</span> <span style=\"color:#a6e22e\">http</span>.<span style=\"color:#a6e22e\">Get</span>(<span style=\"color:#e6db74\">"https://ws.audioscrobbler.com/2.0/?method=user.gettopalbums&user=xthekingisdeadx&api_key=[api-key-here]&format=json&period=7day"</span>)\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">error</span> <span style=\"color:#f92672\">!=</span> <span style=\"color:#66d9ef\">nil</span> {\n\n }\n <span style=\"color:#66d9ef\">defer</span> <span style=\"color:#a6e22e\">response</span>.<span style=\"color:#a6e22e\">Body</span>.<span style=\"color:#a6e22e\">Close</span>()\n <span style=\"color:#a6e22e\">body</span>, <span style=\"color:#66d9ef\">error</span> <span style=\"color:#f92672\">:=</span> <span style=\"color:#a6e22e\">ioutil</span>.<span style=\"color:#a6e22e\">ReadAll</span>(<span style=\"color:#a6e22e\">response</span>.<span style=\"color:#a6e22e\">Body</span>)\n <span style=\"color:#a6e22e\">fmt</span>.<span style=\"color:#a6e22e\">Println</span>(string(<span style=\"color:#a6e22e\">body</span>))\n}\n\n</code></pre></div><p>So far so good, script compiled. Now I can retrieve the data, but no parsing yet. Time to Google, “golang parse JSON”.</p>\n<h4 id=\"json-parsing\">JSON parsing</h4>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-go\" data-lang=\"go\">\n<span style=\"color:#f92672\">package</span> <span style=\"color:#a6e22e\">main</span>\n\n<span style=\"color:#f92672\">import</span> <span style=\"color:#e6db74\">"fmt"</span>\n<span style=\"color:#f92672\">import</span> <span style=\"color:#e6db74\">"net/http"</span>\n<span style=\"color:#f92672\">import</span> <span style=\"color:#e6db74\">"io/ioutil"</span>\n<span style=\"color:#f92672\">import</span> <span style=\"color:#e6db74\">"encoding/json"</span>\n\n<span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">main</span>() {\n <span style=\"color:#a6e22e\">response</span>, <span style=\"color:#66d9ef\">error</span> <span style=\"color:#f92672\">:=</span> <span style=\"color:#a6e22e\">http</span>.<span style=\"color:#a6e22e\">Get</span>(<span style=\"color:#e6db74\">"https://ws.audioscrobbler.com/2.0/?method=user.gettopalbums&user=xthekingisdeadx&api_key=[api-key-here]&format=json&period=7day"</span>)\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">error</span> <span style=\"color:#f92672\">!=</span> <span style=\"color:#66d9ef\">nil</span> {\n\n }\n <span style=\"color:#66d9ef\">defer</span> <span style=\"color:#a6e22e\">response</span>.<span style=\"color:#a6e22e\">Body</span>.<span style=\"color:#a6e22e\">Close</span>()\n <span style=\"color:#a6e22e\">body</span>, <span style=\"color:#66d9ef\">error</span> <span style=\"color:#f92672\">:=</span> <span style=\"color:#a6e22e\">ioutil</span>.<span style=\"color:#a6e22e\">ReadAll</span>(<span style=\"color:#a6e22e\">response</span>.<span style=\"color:#a6e22e\">Body</span>)\n <span style=\"color:#75715e\">// fmt.Println(string(body))\n</span><span style=\"color:#75715e\"></span>\n <span style=\"color:#66d9ef\">var</span> <span style=\"color:#a6e22e\">jsonResponse</span> <span style=\"color:#a6e22e\">Response</span>\n <span style=\"color:#a6e22e\">json</span>.<span style=\"color:#a6e22e\">Unmarshal</span>(<span style=\"color:#a6e22e\">body</span>, <span style=\"color:#f92672\">&</span><span style=\"color:#a6e22e\">jsonResponse</span>)\n\n <span style=\"color:#66d9ef\">var</span> <span style=\"color:#a6e22e\">albums</span> = <span style=\"color:#a6e22e\">jsonResponse</span>.<span style=\"color:#a6e22e\">TopAlbums</span>.<span style=\"color:#a6e22e\">Album</span>\n <span style=\"color:#66d9ef\">for</span> <span style=\"color:#a6e22e\">_</span>, <span style=\"color:#a6e22e\">album</span> <span style=\"color:#f92672\">:=</span> <span style=\"color:#66d9ef\">range</span> <span style=\"color:#a6e22e\">albums</span> {\n <span style=\"color:#a6e22e\">fmt</span>.<span style=\"color:#a6e22e\">Println</span>(<span style=\"color:#a6e22e\">album</span>.<span style=\"color:#a6e22e\">Artist</span>.<span style=\"color:#a6e22e\">Name</span>, <span style=\"color:#e6db74\">":"</span>, <span style=\"color:#a6e22e\">album</span>.<span style=\"color:#a6e22e\">PlayCount</span>)\n }\n}\n\n<span style=\"color:#66d9ef\">type</span> <span style=\"color:#a6e22e\">Response</span> <span style=\"color:#66d9ef\">struct</span> {\n <span style=\"color:#a6e22e\">TopAlbums</span> <span style=\"color:#a6e22e\">TopAlbums</span> <span style=\"color:#e6db74\">`json:"topalbums"`</span>\n}\n\n<span style=\"color:#66d9ef\">type</span> <span style=\"color:#a6e22e\">TopAlbums</span> <span style=\"color:#66d9ef\">struct</span> {\n <span style=\"color:#a6e22e\">Album</span> []<span style=\"color:#a6e22e\">Album</span> <span style=\"color:#e6db74\">`json:album`</span>\n}\n\n<span style=\"color:#66d9ef\">type</span> <span style=\"color:#a6e22e\">Album</span> <span style=\"color:#66d9ef\">struct</span> {\n <span style=\"color:#a6e22e\">Artist</span> <span style=\"color:#a6e22e\">Artist</span> <span style=\"color:#e6db74\">`json:"artist"`</span>\n <span style=\"color:#a6e22e\">Image</span> []<span style=\"color:#a6e22e\">Image</span> <span style=\"color:#e6db74\">`json:"image"`</span>\n <span style=\"color:#a6e22e\">PlayCount</span> <span style=\"color:#66d9ef\">string</span>\n}\n\n<span style=\"color:#66d9ef\">type</span> <span style=\"color:#a6e22e\">Artist</span> <span style=\"color:#66d9ef\">struct</span> {\n <span style=\"color:#a6e22e\">ArtistUrl</span> <span style=\"color:#66d9ef\">string</span> <span style=\"color:#e6db74\">`json:"url"`</span>\n <span style=\"color:#a6e22e\">Name</span> <span style=\"color:#66d9ef\">string</span>\n}\n\n<span style=\"color:#66d9ef\">type</span> <span style=\"color:#a6e22e\">Image</span> <span style=\"color:#66d9ef\">struct</span> {\n <span style=\"color:#a6e22e\">Size</span> <span style=\"color:#66d9ef\">string</span> <span style=\"color:#e6db74\">`json:"size"`</span>\n <span style=\"color:#a6e22e\">Text</span> <span style=\"color:#66d9ef\">string</span> <span style=\"color:#e6db74\">`json:"#text"`</span>\n}\n\n</code></pre></div><h4 id=\"gui-toolkit\">GUI Toolkit</h4>\n<p>Since I only have very minimal experience with the Go language, I am not sure what my next steps are. Either, I will go with the GUI toolkit route or image manipulation.</p>\n<h4 id=\"switching-to-swiftui\">Switching to SwiftUI</h4>\n<p>After researching Go UI frameworks, I eventually decided to use what I am currently familiar and, that is SwiftUI. Another reason is the sad state of native UI frameworks using the Go language because it seems they aren’t many options available. I don’t want to use cross-platforms or UI frameworks using web technologies for this is just a simple project.</p>\n<h4 id=\"one-hour-later\">One hour later</h4>\n<p>Using <code>LazyHGrid</code>, this is what it looks like now.</p>\n<!-- raw HTML omitted -->\n<h4 id=\"next\">Next</h4>\n<p>My next step would be to be able to export this whole <code>View</code> into an image.</p>\n<h4 id=\"source-code\">Source Code</h4>\n<p>Code can be found here: <a href=\"https://github.com/lawgimenez/lastfmdata\">https://github.com/lawgimenez/lastfmdata</a></p>\n",
"content_text": "I recently recovered my Last.fm account, which was one of the top music services before Spotify or streaming in general. It says my account has been around since 2010, thus the cringy username [`xthekingisdeadx`](https://www.last.fm/user/xthekingisdeadx). My profile picture and username still stay the same.\n\nI want to retrieve my Last.fm data, particularly from my top artists and albums I have listened to for the past week or month. So I could share it somewhere.\n\nI am going to write my script using Go. I started this project in Python but encountered a couple of errors and I am just not in the mood to debug or solve them. Weekend projects should be fun, no?\n\n#### GET data\n\n```go\n\npackage main\n\nimport \"fmt\"\nimport \"net/http\"\n\nfunc main() {\n response, error := http.Get(\"https://ws.audioscrobbler.com/2.0/?method=user.gettopalbums&user=xthekingisdeadx&api_key=[api-key-here]&format=json&period=7day\")\n if error != nil {\n\n }\n defer response.Body.Close()\n body, error := ioutil.ReadAll(response.Body)\n fmt.Println(string(body))\n}\n\n```\n\nSo far so good, script compiled. Now I can retrieve the data, but no parsing yet. Time to Google, \"golang parse JSON\".\n\n#### JSON parsing\n\n```go\n\npackage main\n\nimport \"fmt\"\nimport \"net/http\"\nimport \"io/ioutil\"\nimport \"encoding/json\"\n\nfunc main() {\n response, error := http.Get(\"https://ws.audioscrobbler.com/2.0/?method=user.gettopalbums&user=xthekingisdeadx&api_key=[api-key-here]&format=json&period=7day\")\n if error != nil {\n\n }\n defer response.Body.Close()\n body, error := ioutil.ReadAll(response.Body)\n // fmt.Println(string(body))\n\n var jsonResponse Response\n json.Unmarshal(body, &jsonResponse)\n\n var albums = jsonResponse.TopAlbums.Album\n for _, album := range albums {\n fmt.Println(album.Artist.Name, \":\", album.PlayCount)\n }\n}\n\ntype Response struct {\n TopAlbums TopAlbums `json:\"topalbums\"`\n}\n\ntype TopAlbums struct {\n Album []Album `json:album`\n}\n\ntype Album struct {\n Artist Artist `json:\"artist\"`\n Image []Image `json:\"image\"`\n PlayCount string\n}\n\ntype Artist struct {\n ArtistUrl string `json:\"url\"`\n Name string\n}\n\ntype Image struct {\n Size string `json:\"size\"`\n Text string `json:\"#text\"`\n}\n\n```\n\n#### GUI Toolkit\n\nSince I only have very minimal experience with the Go language, I am not sure what my next steps are. Either, I will go with the GUI toolkit route or image manipulation.\n\n\n#### Switching to SwiftUI\n\nAfter researching Go UI frameworks, I eventually decided to use what I am currently familiar and, that is SwiftUI. Another reason is the sad state of native UI frameworks using the Go language because it seems they aren't many options available. I don't want to use cross-platforms or UI frameworks using web technologies for this is just a simple project.\n\n#### One hour later\n\nUsing `LazyHGrid`, this is what it looks like now.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/screenshot-2023-07-08-at-9.37.16pm.png\" width=\"600\" height=\"276\" alt=\"\">\n\n#### Next\n\nMy next step would be to be able to export this whole `View` into an image.\n\n#### Source Code\n\nCode can be found here: [https://github.com/lawgimenez/lastfmdata](https://github.com/lawgimenez/lastfmdata)\n\n\n",
"date_published": "2023-07-09T09:35:26+08:00",
"url": "https://law.gmnz.xyz/2023/07/08/lastfm-api.html",
"tags": ["Mobile Development","Swift","Projects"]
},
{
"id": "http://lwgmnz.micro.blog/2023/07/05/wwdc.html",
"title": "WWDC 2023",
"content_html": "<p>I just realized that I have not written something or anything about Apple’s WWDC 2023. I was too busy catching up with the WWDC sessions and some fatherly stuff I have to deal with.</p>\n<p>Overall, I am impressed by the event and the news coming out of it. I just want to write down a couple of thoughts before I forget.</p>\n<h4 id=\"xcode-15\">Xcode 15</h4>\n<p>Xcode 15 is buggy as hell. Running an emulator or preview <a href=\"https://developer.apple.com/forums/thread/731219\">makes my machine overheat</a>. As of this writing, I am on the second beta release of Xcode 15, and still have the same overheating issue.</p>\n<h4 id=\"betas\">Betas</h4>\n<p>I installed the beta releases right away, iOS 17 and, Sonoma. My first actually. So I ended up not being able to release new versions on Xcode 14. Luckily, we are currently rewriting the app for the upcoming version of iOS and iPadOS 17. Remind me next time not to do this shit again.</p>\n<h4 id=\"notes-codeblock\">Notes codeblock</h4>\n<p>Notes has already the long-awaited code block feature. For me, it is a sort of a code block-ish attempt, Apple called it <code>monostyled</code>. Now my code won’t look awkward anymore on my notes.</p>\n<h4 id=\"symbols-5\">Symbols 5</h4>\n<p>It has been a joy to use Symbols 5 with its animations. But, I wish <code>symbolEffect()</code> will work inside <code>TabView</code>. I haven’t yet figured out how to make it work.</p>\n<h4 id=\"assets-static-access\">Assets static access</h4>\n<p>One of the best lowkey features of Xcode 15, I don’t have to risk misspelling my assets while accessing them in my code.</p>\n<h4 id=\"others\">Others</h4>\n<p>I can’t wait to play with SwiftUI animations, SwiftData, and the not yet released TipKit. And Macros.</p>\n",
"content_text": "I just realized that I have not written something or anything about Apple's WWDC 2023. I was too busy catching up with the WWDC sessions and some fatherly stuff I have to deal with.\n\nOverall, I am impressed by the event and the news coming out of it. I just want to write down a couple of thoughts before I forget.\n\n#### Xcode 15\n\nXcode 15 is buggy as hell. Running an emulator or preview [makes my machine overheat](https://developer.apple.com/forums/thread/731219). As of this writing, I am on the second beta release of Xcode 15, and still have the same overheating issue.\n\n#### Betas\n\nI installed the beta releases right away, iOS 17 and, Sonoma. My first actually. So I ended up not being able to release new versions on Xcode 14. Luckily, we are currently rewriting the app for the upcoming version of iOS and iPadOS 17. Remind me next time not to do this shit again.\n\n#### Notes codeblock\n\nNotes has already the long-awaited code block feature. For me, it is a sort of a code block-ish attempt, Apple called it `monostyled`. Now my code won't look awkward anymore on my notes.\n\n#### Symbols 5\n\nIt has been a joy to use Symbols 5 with its animations. But, I wish `symbolEffect()` will work inside `TabView`. I haven't yet figured out how to make it work.\n\n#### Assets static access\n\nOne of the best lowkey features of Xcode 15, I don't have to risk misspelling my assets while accessing them in my code.\n\n#### Others\n\nI can't wait to play with SwiftUI animations, SwiftData, and the not yet released TipKit. And Macros. \n\n",
"date_published": "2023-07-05T18:42:56+08:00",
"url": "https://law.gmnz.xyz/2023/07/05/wwdc.html",
"tags": ["iOS","Mobile Development","Swift"]
},
{
"id": "http://lwgmnz.micro.blog/2023/06/14/philippines-developer-count.html",
"title": "Thoughts on Stack Overflow 2023 Survey",
"content_html": "<p>Note: I always participate in this survey every year.</p>\n<h3 id=\"developer-type\">Developer Type</h3>\n<p>On the developer type question, there is only 3.38% that consider themselves mobile developers. This is good I think? It means less competition for me. It has almost the same percentage as students.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"geography\">Geography</h3>\n<p>The Philippines has only 0.4% of the respondents who participated in this survey.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"age\">Age</h3>\n<p>My age bracket is still a significant percentage of the professional developer category.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"programming-language\">Programming Language</h3>\n<p>I code in Kotlin and Swift professionally. Kotlin still holds a significant lead over Swift.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"other-frameworks\">Other Frameworks</h3>\n<p>I believe this is the first time SwiftUI has appeared in the other frameworks section. As someone who’s coding on SwiftUI for the past several months, this is a huge step and hopefully will continue to increase significantly in the next several years.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"ide\">IDE</h3>\n<p>I agree with this one, Android Studio is way much better and more fun to use than Xcode.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Note: I always participate in this survey every year.\n\n### Developer Type\n\nOn the developer type question, there is only 3.38% that consider themselves mobile developers. This is good I think? It means less competition for me. It has almost the same percentage as students.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/0f3fdde3e4.png\" width=\"600\" height=\"443\" alt=\"\" />\n\n### Geography\n\nThe Philippines has only 0.4% of the respondents who participated in this survey.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/e34b67e424.png\" width=\"600\" height=\"168\" alt=\"\" />\n\n### Age\n\nMy age bracket is still a significant percentage of the professional developer category.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/5cd4d9d871.png\" width=\"600\" height=\"463\" alt=\"\" />\n\n### Programming Language\n\nI code in Kotlin and Swift professionally. Kotlin still holds a significant lead over Swift.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/08ea00233b.png\" width=\"600\" height=\"512\" alt=\"\" />\n\n### Other Frameworks\n\nI believe this is the first time SwiftUI has appeared in the other frameworks section. As someone who's coding on SwiftUI for the past several months, this is a huge step and hopefully will continue to increase significantly in the next several years.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/5b5dc02a3c.png\" width=\"600\" height=\"442\" alt=\"\" />\n\n### IDE\n\nI agree with this one, Android Studio is way much better and more fun to use than Xcode.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/a46c0347c9.png\" width=\"600\" height=\"313\" alt=\"\" />\n",
"date_published": "2023-06-14T10:37:59+08:00",
"url": "https://law.gmnz.xyz/2023/06/14/philippines-developer-count.html"
},
{
"id": "http://lwgmnz.micro.blog/2023/06/08/stackoverflow-keycap-reward.html",
"title": "Stack Overflow Keycap Reward",
"content_html": "<p>My tiny reward from Stack Overflow arrived yesterday, a small single keycap. I won it when I answered their Saved the Day story. I did not remember when, it was a long time ago.</p>\n<p>Initially, I told them to not even bother shipping a small item overseas, the Philippines' Customs are not worth the trouble.</p>\n<!-- raw HTML omitted -->\n<p>But they insisted. Thanks!</p>\n<!-- raw HTML omitted -->\n",
"content_text": "My tiny reward from Stack Overflow arrived yesterday, a small single keycap. I won it when I answered their Saved the Day story. I did not remember when, it was a long time ago.\n\nInitially, I told them to not even bother shipping a small item overseas, the Philippines' Customs are not worth the trouble.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/a154ed6907.png\" width=\"600\" height=\"310\" alt=\"\" />\n\nBut they insisted. Thanks!\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/ca811cbc6f.jpg\" width=\"600\" height=\"1066\" alt=\"\" />\n",
"date_published": "2023-06-08T08:51:33+08:00",
"url": "https://law.gmnz.xyz/2023/06/08/stackoverflow-keycap-reward.html",
"tags": ["Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2023/06/05/reddit-blackout.html",
"title": "r/BusinessPH will be going dark",
"content_html": "<p>The sub r/BusinessPH will join the Reddit blackout on June 12 - 14, 2023, this is in solidarity with developers of third-party clients who will have to pay a hefty price for access to Reddit’s APIs.</p>\n<p>For more details, <a href=\"https://www.reddit.com/r/Save3rdPartyApps/comments/13yh0jf/dont_let_reddit_kill_3rd_party_apps/\">you can read it here</a>.</p>\n<p>As a mobile developer myself and an Apollo app user, I feel and understand very well their current plight. If you have any questions, or comments don’t hesitate to contact the mods.</p>\n",
"content_text": "The sub r/BusinessPH will join the Reddit blackout on June 12 - 14, 2023, this is in solidarity with developers of third-party clients who will have to pay a hefty price for access to Reddit’s APIs.\n\nFor more details, [you can read it here](https://www.reddit.com/r/Save3rdPartyApps/comments/13yh0jf/dont_let_reddit_kill_3rd_party_apps/).\n\nAs a mobile developer myself and an Apollo app user, I feel and understand very well their current plight. If you have any questions, or comments don't hesitate to contact the mods.\n",
"date_published": "2023-06-05T19:57:11+08:00",
"url": "https://law.gmnz.xyz/2023/06/05/reddit-blackout.html"
},
{
"id": "http://lwgmnz.micro.blog/2023/05/23/dsym-workaround-in.html",
"title": "dSYM workaround in Xcode 14",
"content_html": "<p>Starting with Xcode 14, <a href=\"https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes#Deprecations\">Bitcode has been deprecated</a>. So, in our team’s case, it is impossible to download dSYM files for debugging crash logs. If you are using Crashlytics or Bugsnag, this is a requirement to make sense of the received crash logs from iOS.</p>\n<p>But there is a workaround. Previously, all you have to do was go to App Store Connect and download the dSYMs directly from there. But that option is not available anymore.</p>\n<p>But there is a workaround.</p>\n<h3 id=\"workaround\">Workaround</h3>\n<p>Open the Window organizer in your Xcode and go to the Archive option and right-click on a particular build. Then, select Show in Finder option.</p>\n<!-- raw HTML omitted -->\n<p>You will see files with <code>.xcarchive</code> extensions. Right-click on one of those and select Show Package Contents. There you will see a dSYMs folder. It should contain the dSYMs file needed for your debugging.</p>\n",
"content_text": "Starting with Xcode 14, [Bitcode has been deprecated](https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes#Deprecations). So, in our team's case, it is impossible to download dSYM files for debugging crash logs. If you are using Crashlytics or Bugsnag, this is a requirement to make sense of the received crash logs from iOS.\n\nBut there is a workaround. Previously, all you have to do was go to App Store Connect and download the dSYMs directly from there. But that option is not available anymore.\n\nBut there is a workaround.\n\n### Workaround\n\nOpen the Window organizer in your Xcode and go to the Archive option and right-click on a particular build. Then, select Show in Finder option. \n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/c3bfbdf39c.png\" width=\"600\" height=\"161\" alt=\"\" />\n\nYou will see files with `.xcarchive` extensions. Right-click on one of those and select Show Package Contents. There you will see a dSYMs folder. It should contain the dSYMs file needed for your debugging.\n",
"date_published": "2023-05-23T08:59:55+08:00",
"url": "https://law.gmnz.xyz/2023/05/23/dsym-workaround-in.html",
"tags": ["iOS","Mobile Development"]
},
{
"id": "http://lwgmnz.micro.blog/2023/05/18/underwhelming-google-io.html",
"title": "Underwhelming Google I/O 2023",
"content_html": "<p>After watching half of the Google I/O Keynote, I had to close the video because it was starting to bore me. There was no excitement for me, AI/ML is a topic I am not interested in. I took notes of the keynote while watching, and I just wanted to publish it here for keepsake purposes.</p>\n<h3 id=\"google-io-2023-notes\">Google IO 2023 Notes</h3>\n<p>Keynote</p>\n<ul>\n<li>So many AI/ML keynotes and features. Not interested. Bard? Does not interest me.</li>\n<li>Bard is available now in 180 countries. Philippines?</li>\n<li>Why would I like to edit photos? It seems fake.</li>\n<li>Help me write. Nice, now spams will feel more real.</li>\n<li>There is gonna be AI also in Google Search. Does this means I don’t have to concatenate “Reddit” in every search query?</li>\n<li>AI on Google Cloud. Of course. Google AI.</li>\n<li>AI on Android.</li>\n</ul>\n<p>Stuffs to check out</p>\n<ul>\n<li><input disabled=\"\" type=\"checkbox\"> Android design hub: UI Design | Android Developers</li>\n<li><input disabled=\"\" type=\"checkbox\"> Download Android Studio beta**</li>\n</ul>\n",
"content_text": "After watching half of the Google I/O Keynote, I had to close the video because it was starting to bore me. There was no excitement for me, AI/ML is a topic I am not interested in. I took notes of the keynote while watching, and I just wanted to publish it here for keepsake purposes.\n\n### Google IO 2023 Notes\n\nKeynote\n* So many AI/ML keynotes and features. Not interested. Bard? Does not interest me.\n* Bard is available now in 180 countries. Philippines?\n* Why would I like to edit photos? It seems fake.\n* Help me write. Nice, now spams will feel more real.\n* There is gonna be AI also in Google Search. Does this means I don’t have to concatenate “Reddit” in every search query?\n* AI on Google Cloud. Of course. Google AI.\n* AI on Android.\n\nStuffs to check out\n- [ ] Android design hub: UI Design | Android Developers\n- [ ] Download Android Studio beta**\n",
"date_published": "2023-05-18T09:52:20+08:00",
"url": "https://law.gmnz.xyz/2023/05/18/underwhelming-google-io.html",
"tags": ["Mobile Development","Android"]
},
{
"id": "http://lwgmnz.micro.blog/2023/05/12/swiftui-tap-anywhere.html",
"title": "SwiftUI Tap Anywhere",
"content_html": "<p>Today I learned how to implement tap anywhere to dismiss the keyboard on SwiftUI.</p>\n<p>In your parent stack, either <code>VStack</code> or <code>HStack</code>, add the following code.</p>\n<pre tabindex=\"0\"><code>.contentShape(Rectangle())\n.onTapGesture {\n UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)\n}\n</code></pre><p>The most important thing to take note here is the contentShape modifier or tap gesture will not work.</p>\n",
"content_text": "Today I learned how to implement tap anywhere to dismiss the keyboard on SwiftUI.\n\nIn your parent stack, either `VStack` or `HStack`, add the following code.\n\n```\n.contentShape(Rectangle())\n.onTapGesture {\n UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)\n}\n```\n\nThe most important thing to take note here is the contentShape modifier or tap gesture will not work.\n",
"date_published": "2023-05-12T10:19:12+08:00",
"url": "https://law.gmnz.xyz/2023/05/12/swiftui-tap-anywhere.html",
"tags": ["iOS","Mobile Development","Swift","TIL"]
},
{
"id": "http://lwgmnz.micro.blog/2023/05/10/youtube-coding.html",
"title": "YouTube Coding",
"content_html": "<p>3 days ago I uploaded on YouTube my coding in SwiftUI, working on a couple of simple bug fixes for my hobby project.</p>\n<p>My wife gave me this idea and also I remember I have some coding sessions in Loom from 2 years ago, where I did a coding demo for a client. And, looking back at those videos is really cool and nostalgic.</p>\n<p>So, I decided why not upload it on YouTube.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "3 days ago I uploaded on YouTube my coding in SwiftUI, working on a couple of simple bug fixes for my hobby project.\n\nMy wife gave me this idea and also I remember I have some coding sessions in Loom from 2 years ago, where I did a coding demo for a client. And, looking back at those videos is really cool and nostalgic.\n\nSo, I decided why not upload it on YouTube.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/5cn09JumclA\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen></iframe>\n",
"date_published": "2023-05-10T08:23:53+08:00",
"url": "https://law.gmnz.xyz/2023/05/10/youtube-coding.html",
"tags": ["iOS","Mobile Development"]
},
{
"id": "http://lwgmnz.micro.blog/2023/04/25/chess-milestone.html",
"title": "Chess Milestone",
"content_html": "<p>I achieved a very rare milestone last night. I had a 10-game winning streak in a “Champions” Division. I am not entirely privy to the division but I am pretty sure this is not a beginner’s division.</p>\n<!-- raw HTML omitted -->\n<p>My new routine for the past several months is to play a couple of chess games before going to sleep or after putting our son to sleep at night.</p>\n<p>And I am also currently no. 1 as of last night.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "I achieved a very rare milestone last night. I had a 10-game winning streak in a \"Champions\" Division. I am not entirely privy to the division but I am pretty sure this is not a beginner's division.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/81671ee145.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\nMy new routine for the past several months is to play a couple of chess games before going to sleep or after putting our son to sleep at night.\n\nAnd I am also currently no. 1 as of last night.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/3b7f0148dd.png\" width=\"600\" height=\"1298\" alt=\"\" />\n",
"date_published": "2023-04-26T10:44:12+08:00",
"url": "https://law.gmnz.xyz/2023/04/25/chess-milestone.html",
"tags": ["Personal","Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2023/04/24/xylophone-app.html",
"title": "Xylophone App",
"content_html": "<p>In roughly an hour or two, I created an iOS app for our son who loves playing his Xylophone. I came upon this <a href=\"https://github.com/appbrewery/Xylophone-iOS13-Completed\">old repo</a> for inspiration and reference. This was still developed using Storyboard and UIKit. So, I set out to write my own using SwiftUI.</p>\n<p>I also borrowed their sound files. The owner of these files belongs to them.</p>\n<p>The code looks like this initially.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#75715e\">//</span>\n<span style=\"color:#75715e\">// PlayerView.swift</span>\n<span style=\"color:#75715e\">// Xylo</span>\n<span style=\"color:#75715e\">//</span>\n<span style=\"color:#75715e\">// Created by Lawrence Gimenez on 4/23/23.</span>\n<span style=\"color:#75715e\">//</span>\n\n<span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n<span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">AVFoundation</span>\n\n<span style=\"color:#66d9ef\">var</span> audioPlayer: AVAudioPlayer!\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">PlayerView</span>: View {\n \n <span style=\"color:#66d9ef\">var</span> body: some View {\n VStack {\n Button(action: {\n play(sound: <span style=\"color:#e6db74\">"C"</span>)\n }) {\n Text(<span style=\"color:#e6db74\">"C"</span>)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">90</span>)\n }\n .background(Color.red)\n Button(action: {\n play(sound: <span style=\"color:#e6db74\">"D"</span>)\n }) {\n Text(<span style=\"color:#e6db74\">"D"</span>)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">90</span>)\n }\n .background(Color.orange)\n Button(action: {\n play(sound: <span style=\"color:#e6db74\">"E"</span>)\n }) {\n Text(<span style=\"color:#e6db74\">"E"</span>)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">90</span>)\n }\n .background(Color.yellow)\n Button(action: {\n play(sound: <span style=\"color:#e6db74\">"F"</span>)\n }) {\n Text(<span style=\"color:#e6db74\">"F"</span>)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">90</span>)\n }\n .background(Color.green)\n Button(action: {\n play(sound: <span style=\"color:#e6db74\">"G"</span>)\n }) {\n Text(<span style=\"color:#e6db74\">"G"</span>)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">90</span>)\n }\n .background(Color.teal)\n Button(action: {\n play(sound: <span style=\"color:#e6db74\">"A"</span>)\n }) {\n Text(<span style=\"color:#e6db74\">"A"</span>)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">90</span>)\n }\n .background(Color.indigo)\n Button(action: {\n play(sound: <span style=\"color:#e6db74\">"B"</span>)\n }) {\n Text(<span style=\"color:#e6db74\">"B"</span>)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">90</span>)\n }\n .background(Color.purple)\n Button(action: {\n play(sound: <span style=\"color:#e6db74\">"C"</span>)\n }) {\n Text(<span style=\"color:#e6db74\">"C"</span>)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">90</span>)\n }\n .background(Color.red)\n }\n .frame(maxWidth: .infinity, maxHeight: .infinity)\n }\n \n <span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">play</span>(sound: String) {\n <span style=\"color:#66d9ef\">let</span> _ = print(<span style=\"color:#e6db74\">"Play </span><span style=\"color:#e6db74\">\\(</span>sound<span style=\"color:#e6db74\">)</span><span style=\"color:#e6db74\">"</span>)\n <span style=\"color:#66d9ef\">let</span> url = Bundle.main.url(forResource: sound, withExtension: <span style=\"color:#e6db74\">".wav"</span>)\n audioPlayer = <span style=\"color:#66d9ef\">try</span>! AVAudioPlayer(contentsOf: url!)\n audioPlayer.prepareToPlay()\n audioPlayer.play()\n }\n}\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">PlayerView_Previews</span>: PreviewProvider {\n <span style=\"color:#66d9ef\">static</span> <span style=\"color:#66d9ef\">var</span> previews: some View {\n PlayerView()\n }\n}\n\n</code></pre></div><p>And here’s what it looks like on the device.</p>\n<!-- raw HTML omitted -->\n<p>If you notice, the code is so long and redundant. We can do better. Let’s refactor our code by creating a model class so we can use it inside a loop.</p>\n<h3 id=\"refactor-time\">Refactor Time</h3>\n<p>Let’s create a Key model. I imported SwiftUI since I need the Color class.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">Key</span>: Identifiable {\n \n <span style=\"color:#66d9ef\">var</span> id: Int\n <span style=\"color:#66d9ef\">var</span> note: String\n <span style=\"color:#66d9ef\">var</span> color: Color\n}\n</code></pre></div><p>Go back to our <code>PlayerView</code> class and let’s create an array based on the Key model.</p>\n<pre tabindex=\"0\"><code>private var arrayKeys = [\n Key(id: 0, note: "C", color: Color.red),\n Key(id: 1, note: "D", color: Color.orange),\n Key(id: 2, note: "E", color: Color.yellow),\n Key(id: 3, note: "F", color: Color.green),\n Key(id: 4, note: "G", color: Color.teal),\n Key(id: 5, note: "A", color: Color.indigo),\n Key(id: 6, note: "B", color: Color.purple),\n Key(id: 7, note: "C", color: Color.red)\n]\n</code></pre><p>Let’s update our body based on our newly created array.</p>\n<pre tabindex=\"0\"><code>var body: some View {\n VStack {\n ForEach(arrayKeys) { key in\n Button(action: {\n play(sound: key.note)\n }) {\n Text(key.note)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(key.color)\n }\n }\n .frame(maxWidth: .infinity, maxHeight: .infinity)\n}\n</code></pre><p>Much better. Code is now shorter, if I want to add another key, I would just add another Key object to our array.</p>\n<p>You can check the <a href=\"https://github.com/lawgimenez/xylo\">whole source code here</a>. The project is compiled using the latest Xcode 14.3.</p>\n",
"content_text": "In roughly an hour or two, I created an iOS app for our son who loves playing his Xylophone. I came upon this [old repo](https://github.com/appbrewery/Xylophone-iOS13-Completed) for inspiration and reference. This was still developed using Storyboard and UIKit. So, I set out to write my own using SwiftUI.\n\nI also borrowed their sound files. The owner of these files belongs to them.\n\nThe code looks like this initially.\n\n```swift\n//\n// PlayerView.swift\n// Xylo\n//\n// Created by Lawrence Gimenez on 4/23/23.\n//\n\nimport SwiftUI\nimport AVFoundation\n\nvar audioPlayer: AVAudioPlayer!\n\nstruct PlayerView: View {\n \n var body: some View {\n VStack {\n Button(action: {\n play(sound: \"C\")\n }) {\n Text(\"C\")\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(Color.red)\n Button(action: {\n play(sound: \"D\")\n }) {\n Text(\"D\")\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(Color.orange)\n Button(action: {\n play(sound: \"E\")\n }) {\n Text(\"E\")\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(Color.yellow)\n Button(action: {\n play(sound: \"F\")\n }) {\n Text(\"F\")\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(Color.green)\n Button(action: {\n play(sound: \"G\")\n }) {\n Text(\"G\")\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(Color.teal)\n Button(action: {\n play(sound: \"A\")\n }) {\n Text(\"A\")\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(Color.indigo)\n Button(action: {\n play(sound: \"B\")\n }) {\n Text(\"B\")\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(Color.purple)\n Button(action: {\n play(sound: \"C\")\n }) {\n Text(\"C\")\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(Color.red)\n }\n .frame(maxWidth: .infinity, maxHeight: .infinity)\n }\n \n private func play(sound: String) {\n let _ = print(\"Play \\(sound)\")\n let url = Bundle.main.url(forResource: sound, withExtension: \".wav\")\n audioPlayer = try! AVAudioPlayer(contentsOf: url!)\n audioPlayer.prepareToPlay()\n audioPlayer.play()\n }\n}\n\nstruct PlayerView_Previews: PreviewProvider {\n static var previews: some View {\n PlayerView()\n }\n}\n\n```\n\nAnd here's what it looks like on the device.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/19af9b8b99.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\nIf you notice, the code is so long and redundant. We can do better. Let's refactor our code by creating a model class so we can use it inside a loop.\n\n### Refactor Time\n\nLet's create a Key model. I imported SwiftUI since I need the Color class.\n\n```swift\nimport SwiftUI\n\nstruct Key: Identifiable {\n \n var id: Int\n var note: String\n var color: Color\n}\n```\n\nGo back to our `PlayerView` class and let's create an array based on the Key model.\n\n```\nprivate var arrayKeys = [\n Key(id: 0, note: \"C\", color: Color.red),\n Key(id: 1, note: \"D\", color: Color.orange),\n Key(id: 2, note: \"E\", color: Color.yellow),\n Key(id: 3, note: \"F\", color: Color.green),\n Key(id: 4, note: \"G\", color: Color.teal),\n Key(id: 5, note: \"A\", color: Color.indigo),\n Key(id: 6, note: \"B\", color: Color.purple),\n Key(id: 7, note: \"C\", color: Color.red)\n]\n```\n\nLet's update our body based on our newly created array.\n\n```\nvar body: some View {\n VStack {\n ForEach(arrayKeys) { key in\n Button(action: {\n play(sound: key.note)\n }) {\n Text(key.note)\n .foregroundColor(.white)\n .frame(maxWidth: .infinity, minHeight: 90)\n }\n .background(key.color)\n }\n }\n .frame(maxWidth: .infinity, maxHeight: .infinity)\n}\n```\n\nMuch better. Code is now shorter, if I want to add another key, I would just add another Key object to our array.\n\nYou can check the [whole source code here](https://github.com/lawgimenez/xylo). The project is compiled using the latest Xcode 14.3.\n",
"date_published": "2023-04-24T18:38:55+08:00",
"url": "https://law.gmnz.xyz/2023/04/24/xylophone-app.html",
"tags": ["iOS","Mobile Development","Swift"]
},
{
"id": "http://lwgmnz.micro.blog/2023/04/20/fedora.html",
"title": "Fedora 38 Released",
"content_html": "<p>Just installed <a href=\"https://fedoramagazine.org/announcing-fedora-38/\">Fedora 38</a>. So far, no issues found.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>The default wallpaper sucks though.</p>\n",
"content_text": "Just installed [Fedora 38](https://fedoramagazine.org/announcing-fedora-38/). So far, no issues found.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/17cac87d3d.png\" width=\"600\" height=\"528\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/116cec6b90.png\" width=\"600\" height=\"528\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/72bbbdcfb6.png\" width=\"600\" height=\"337\" alt=\"\" />\n\nThe default wallpaper sucks though.\n",
"date_published": "2023-04-20T21:01:46+08:00",
"url": "https://law.gmnz.xyz/2023/04/20/fedora.html",
"tags": ["Linux"]
},
{
"id": "http://lwgmnz.micro.blog/2023/04/19/sound-of-metal.html",
"title": "Sound of Metal Movie",
"content_html": "<p>Just finished watching the movie Sound of Metal. It was a great film and just found out that it was nominated for best picture back in 2021.</p>\n<!-- raw HTML omitted -->\n<p>If you watch the film, Ruben the main character was wearing a Youth of Today hoodie several times throughout the movie. It was revealed that he was recovering addict. 4 years drug-free he said.</p>\n<!-- raw HTML omitted -->\n<p>For the uninitiated, Youth of Today is a hardline vegan straight-edge (refrain from drugs and alcohol movement) band. I am not much of a fan of the band but their frontman Ray Cappo is one of the huge influences that made me quit drinking and smoking.</p>\n<!-- raw HTML omitted -->\n<p>No more.</p>\n",
"content_text": "Just finished watching the movie Sound of Metal. It was a great film and just found out that it was nominated for best picture back in 2021.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/VFOrGkAvjAE\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen></iframe>\n\nIf you watch the film, Ruben the main character was wearing a Youth of Today hoodie several times throughout the movie. It was revealed that he was recovering addict. 4 years drug-free he said.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/cdd324ce54.jpg\" width=\"600\" height=\"385\" alt=\"\" />\n\nFor the uninitiated, Youth of Today is a hardline vegan straight-edge (refrain from drugs and alcohol movement) band. I am not much of a fan of the band but their frontman Ray Cappo is one of the huge influences that made me quit drinking and smoking.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/kPloyz_514M\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen></iframe>\n\nNo more.\n",
"date_published": "2023-04-19T18:29:05+08:00",
"url": "https://law.gmnz.xyz/2023/04/19/sound-of-metal.html",
"tags": ["Personal"]
},
{
"id": "http://lwgmnz.micro.blog/2023/04/15/new-macbook-air.html",
"title": "New MacBook Air M2",
"content_html": "<p>My new Macbook came in today, from my sister in Houston. Thank you!</p>\n<p>Coming from Macbook Pro 2015 (won’t run the latest Ventura or Xcode 14.3 anymore), this is a huge leap for me.</p>\n<p>I just want to take note of the software and applications I initially installed for future reference.</p>\n<h2 id=\"applications-installed-in-order\">Applications Installed in order</h2>\n<ul>\n<li><a href=\"https://www.mozilla.org/en-US/firefox/\">Firefox</a></li>\n<li><a href=\"https://www.dashlane.com/\">Dashlane</a></li>\n<li><a href=\"https://grammarly.com/\">Grammarly</a></li>\n<li><a href=\"https://developer.apple.com/xcode/\">Xcode</a></li>\n<li><a href=\"https://developer.android.com/studio\">Android Studio</a></li>\n<li><a href=\"https://brew.sh/\">Homebrew</a></li>\n<li><a href=\"https://git-scm.com/\">git</a></li>\n<li><a href=\"https://todoist.com/\">Todoist</a></li>\n<li><a href=\"https://iterm2.com/\">iTerm2</a></li>\n<li><a href=\"https://www.sublimetext.com/\">Sublime Text 4</a></li>\n<li><a href=\"https://flexibits.com/fantastical\">Fantastical</a></li>\n</ul>\n",
"content_text": "My new Macbook came in today, from my sister in Houston. Thank you! \n\nComing from Macbook Pro 2015 (won't run the latest Ventura or Xcode 14.3 anymore), this is a huge leap for me.\n\nI just want to take note of the software and applications I initially installed for future reference.\n\n## Applications Installed in order\n\n- [Firefox](https://www.mozilla.org/en-US/firefox/)\n- [Dashlane](https://www.dashlane.com/)\n- [Grammarly](https://grammarly.com/)\n- [Xcode](https://developer.apple.com/xcode/)\n- [Android Studio](https://developer.android.com/studio)\n- [Homebrew](https://brew.sh/)\n- [git](https://git-scm.com/)\n- [Todoist](https://todoist.com/)\n- [iTerm2](https://iterm2.com/)\n- [Sublime Text 4](https://www.sublimetext.com/)\n- [Fantastical](https://flexibits.com/fantastical)\n",
"date_published": "2023-04-16T09:58:49+08:00",
"url": "https://law.gmnz.xyz/2023/04/15/new-macbook-air.html",
"tags": ["Personal","Mobile Development","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2023/04/08/early-birthday-gift.html",
"title": "Early Birthday Gift",
"content_html": "<p>My wife gifted me an early present, a Fitbit. Thank you so much! I love you!</p>\n<p>I used to have a Huawei smartwatch but lost it and hopefully, I can find them. This is my first Fitbit device. I always enjoy tracking my health data, especially my sleep pattern and steps.</p>\n<p>And as someone who’s getting older by the day, tracking my health is something I am planning to take seriously.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "My wife gifted me an early present, a Fitbit. Thank you so much! I love you!\n\nI used to have a Huawei smartwatch but lost it and hopefully, I can find them. This is my first Fitbit device. I always enjoy tracking my health data, especially my sleep pattern and steps. \n\nAnd as someone who's getting older by the day, tracking my health is something I am planning to take seriously.\n\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/55ffe401c1.jpg\" width=\"600\" height=\"1066\" alt=\"\" />\n",
"date_published": "2023-04-08T19:56:19+08:00",
"url": "https://law.gmnz.xyz/2023/04/08/early-birthday-gift.html",
"tags": ["Personal","Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2023/03/25/jetpack-compose-part.html",
"title": "Jetpack Compose Journey Part 1",
"content_html": "<p>This will be my first time delving into Jetpack Compose for a rewrite of our existing app. I believe I have no time to study or do a full Google code lab for this one, due to the constraints of the timeline and deadline. So I will have to go head first and rely on my > 11 years of experience.</p>\n<h3 id=\"setup\">Setup</h3>\n<p>So with every new Android project, I created one. Every new project from Android has been plagued by inconsistency. For example, some Groovy formats are in double quotes and single quotes.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"build-issues\">Build Issues</h3>\n<p>When upgrading <code>kotlinCompilerExtensionVersion</code>, I encountered build issues as expected. It turns out I need to sync it with the latest Kotlin version too, based on this <a href=\"https://developer.android.com/jetpack/androidx/releases/compose-kotlin\">compatibility map</a>. Leaving out `compose_version" seems to not affect the build issues. So,</p>\n<pre tabindex=\"0\"><code>id 'org.jetbrains.kotlin.android' version "1.8.10" apply false\n</code></pre><p>and</p>\n<pre tabindex=\"0\"><code>composeOptions {\n kotlinCompilerExtensionVersion "1.4.4"\n}\n</code></pre><p>needs to sync together based on the compatibility map. After fixing the sync, I haven’t experienced any build issues when upgrading core components for the Compose.</p>\n<p>The Android version I’m using is</p>\n<pre tabindex=\"0\"><code>Android Studio Electric Eel | 2022.1.1 Patch 2\nBuild #AI-221.6008.13.2211.9619390, built on February 17, 2023\n</code></pre>",
"content_text": "This will be my first time delving into Jetpack Compose for a rewrite of our existing app. I believe I have no time to study or do a full Google code lab for this one, due to the constraints of the timeline and deadline. So I will have to go head first and rely on my > 11 years of experience.\n\n### Setup\nSo with every new Android project, I created one. Every new project from Android has been plagued by inconsistency. For example, some Groovy formats are in double quotes and single quotes.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/64c77b44d2.png\" width=\"561\" height=\"183\" alt=\"\" />\n\n### Build Issues\nWhen upgrading `kotlinCompilerExtensionVersion`, I encountered build issues as expected. It turns out I need to sync it with the latest Kotlin version too, based on this [compatibility map](https://developer.android.com/jetpack/androidx/releases/compose-kotlin). Leaving out `compose_version\" seems to not affect the build issues. So,\n\n```\nid 'org.jetbrains.kotlin.android' version \"1.8.10\" apply false\n```` \n\nand\n\n```\ncomposeOptions {\n kotlinCompilerExtensionVersion \"1.4.4\"\n}\n```\n\nneeds to sync together based on the compatibility map. After fixing the sync, I haven't experienced any build issues when upgrading core components for the Compose.\n\nThe Android version I'm using is\n```\nAndroid Studio Electric Eel | 2022.1.1 Patch 2\nBuild #AI-221.6008.13.2211.9619390, built on February 17, 2023\n```\n",
"date_published": "2023-03-27T12:08:34+08:00",
"url": "https://law.gmnz.xyz/2023/03/25/jetpack-compose-part.html",
"tags": ["Mobile Development","Android"]
},
{
"id": "http://lwgmnz.micro.blog/2023/03/25/belated-happy-birthday.html",
"title": "Belated Happy Birthday To My Wife",
"content_html": "<p>After a very busy day yesterday, there was not enough time to post a greeting here.</p>\n<p>Happy birthday to the best person I have ever known and my lovely wife! I love you so much!</p>\n",
"content_text": "After a very busy day yesterday, there was not enough time to post a greeting here.\n\nHappy birthday to the best person I have ever known and my lovely wife! I love you so much!\n",
"date_published": "2023-03-25T11:49:22+08:00",
"url": "https://law.gmnz.xyz/2023/03/25/belated-happy-birthday.html"
},
{
"id": "http://lwgmnz.micro.blog/2023/03/09/google-io.html",
"title": "Google I/O 2023",
"content_html": "<p>It’s that time of the year. I’m surprised that Google announced the event this early.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "It's that time of the year. I'm surprised that Google announced the event this early.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/4de51dfc9a.png\" width=\"600\" height=\"274\" alt=\"\" />\n",
"date_published": "2023-03-09T08:28:41+08:00",
"url": "https://law.gmnz.xyz/2023/03/09/google-io.html",
"tags": ["Android"]
},
{
"id": "http://lwgmnz.micro.blog/2023/03/08/proud-homeowners.html",
"title": "New Home",
"content_html": "<p>My wife and I are proud homeowners today. After 3 years in the making, we finally got the keys to our new house.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "My wife and I are proud homeowners today. After 3 years in the making, we finally got the keys to our new house.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/3324073397.jpg\" width=\"337\" height=\"600\" alt=\"\">\n",
"date_published": "2023-03-08T23:59:45+08:00",
"url": "https://law.gmnz.xyz/2023/03/08/proud-homeowners.html",
"tags": ["Personal","Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2023/02/24/diablo-immortal-on.html",
"title": "Diablo Immortal on Linux",
"content_html": "<p>I was able to run Diablo Immortal on Linux, despite only being available on iOS, Android, and Windows. I know for a fact that I needed <a href=\"https://www.winehq.org/\">Wine</a> to be able to run a Windows environment or application in Linux. But, I just found out you need to install <a href=\"https://lutris.net/\">Lutris</a> too.</p>\n<p>For future reference, the steps are below. I am on <a href=\"https://getfedora.org/\">Fedora</a>.</p>\n<ol>\n<li>Install Wine</li>\n</ol>\n<pre tabindex=\"0\"><code>sudo dnf install wine\n</code></pre><ol start=\"2\">\n<li>Install Lutris</li>\n</ol>\n<pre tabindex=\"0\"><code>sudo dnf install lutris\n</code></pre><blockquote>\n<p>If Lutris won’t start, try installing it via Flatpak.</p>\n</blockquote>\n<ol start=\"3\">\n<li>Install Winetricks</li>\n</ol>\n<pre tabindex=\"0\"><code>sudo dnf install winetricks\n</code></pre><ol start=\"4\">\n<li>Visit <a href=\"https://lutris.net/games/battlenet/\">this Lutris Battle.net link</a> and click on Install. It should display a dialog and click on continue. And just proceed and follow instructions.</li>\n<li>It took some time to figure out the perfect settings to run Battle.net, I was able to get a reference from this <a href=\"https://www.reddit.com/r/DiabloImmortal/comments/v3esq1/diablo_immortal_on_linux/\">Reddit post</a>. The settings that worked for me were, check the image below. Take note of the Wine version and the settings below it.</li>\n</ol>\n<ul>\n<li>Latest Lutris Proton runner</li>\n<li>DXVK Version 1.10L</li>\n<li>VKD3D Version 2.6</li>\n<li>D3D Extras Version 2</li>\n<li>DXVK NVAPI Version 0.5.3</li>\n</ul>\n<!-- raw HTML omitted -->\n<p>And after all that, Battle.net should run after signing in.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "I was able to run Diablo Immortal on Linux, despite only being available on iOS, Android, and Windows. I know for a fact that I needed [Wine](https://www.winehq.org/) to be able to run a Windows environment or application in Linux. But, I just found out you need to install [Lutris](https://lutris.net/) too.\n\nFor future reference, the steps are below. I am on [Fedora](https://getfedora.org/).\n\n1. Install Wine\n```\nsudo dnf install wine\n```\n2. Install Lutris\n```\nsudo dnf install lutris\n```\n> If Lutris won’t start, try installing it via Flatpak.\n3. Install Winetricks\n ```\nsudo dnf install winetricks\n```\n4. Visit [this Lutris Battle.net link](https://lutris.net/games/battlenet/) and click on Install. It should display a dialog and click on continue. And just proceed and follow instructions.\n5. It took some time to figure out the perfect settings to run Battle.net, I was able to get a reference from this [Reddit post](https://www.reddit.com/r/DiabloImmortal/comments/v3esq1/diablo_immortal_on_linux/). The settings that worked for me were, check the image below. Take note of the Wine version and the settings below it.\n- Latest Lutris Proton runner\n - DXVK Version 1.10L\n - VKD3D Version 2.6\n - D3D Extras Version 2\n - DXVK NVAPI Version 0.5.3\n \n <img src=\"https://cdn.uploads.micro.blog/67704/2023/a64c5403e1.png\" width=\"600\" height=\"467\" alt=\"\" />\n\nAnd after all that, Battle.net should run after signing in.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/092adcbedc.png\" width=\"600\" height=\"337\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/fe8a410d37.png\" width=\"600\" height=\"337\" alt=\"\" />\n",
"date_published": "2023-02-24T21:14:04+08:00",
"url": "https://law.gmnz.xyz/2023/02/24/diablo-immortal-on.html",
"tags": ["Linux","Games"]
},
{
"id": "http://lwgmnz.micro.blog/2023/02/07/my-first-contribution.html",
"title": "My first contribution to Google/Android",
"content_html": "<p>I just had my first GitHub contribution to Android/Google merged into their codebase. I have never thought of taking my free time to contribute to open source in my over > 11 YOE.</p>\n<pre tabindex=\"0\"><code>lawgimenez/agp-v7.4.1\n</code></pre><p>It’s not much of a contribution though, it’s just an update to the latest Android Gradle plugin. But nevertheless, I contributed and I’m proud of myself.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "I just had my first GitHub contribution to Android/Google merged into their codebase. I have never thought of taking my free time to contribute to open source in my over > 11 YOE.\n\n```\nlawgimenez/agp-v7.4.1\n```\n\nIt's not much of a contribution though, it's just an update to the latest Android Gradle plugin. But nevertheless, I contributed and I'm proud of myself.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/4bca253477.png\" width=\"600\" height=\"395\" alt=\"\" />\n",
"date_published": "2023-02-07T09:40:46+08:00",
"url": "https://law.gmnz.xyz/2023/02/07/my-first-contribution.html",
"tags": ["Mobile Development","Android"]
},
{
"id": "http://lwgmnz.micro.blog/2023/02/03/bought-thinkpad-t.html",
"title": "Bought ThinkPad T480",
"content_html": "<p>Yesterday I bought a ThinkPad T480 for 17,000 PHP or ~315 USD. It has 512 SSD storage and 16GB RAM which is a very good specification-to-price ratio.</p>\n<!-- raw HTML omitted -->\n<p>The screen resolution is underwhelming though with only 1366x768, compared to my Macbook Pro with Retina.</p>\n<p>But the machine is decent enough to work on Android Studio and some light programming.</p>\n",
"content_text": "Yesterday I bought a ThinkPad T480 for 17,000 PHP or ~315 USD. It has 512 SSD storage and 16GB RAM which is a very good specification-to-price ratio.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/7edacf3b89.jpg\" width=\"600\" height=\"1066\" alt=\"\" />\n\nThe screen resolution is underwhelming though with only 1366x768, compared to my Macbook Pro with Retina.\n\nBut the machine is decent enough to work on Android Studio and some light programming.\n",
"date_published": "2023-02-03T08:59:08+08:00",
"url": "https://law.gmnz.xyz/2023/02/03/bought-thinkpad-t.html"
},
{
"id": "http://lwgmnz.micro.blog/2023/01/19/leftover-android-studios.html",
"title": "Leftover Android Studios",
"content_html": "<p>Thanks to this <a href=\"https://old.reddit.com/r/androiddev/comments/10fg8dd/delete_leftover_android_studio_storage_directories/\">Reddit post</a>, I just found out that you could remove unused Android Studio cache or leftover versions.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>I saved around ~4GB of storage.</p>\n",
"content_text": "Thanks to this [Reddit post](https://old.reddit.com/r/androiddev/comments/10fg8dd/delete_leftover_android_studio_storage_directories/), I just found out that you could remove unused Android Studio cache or leftover versions.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/577e01e043.png\" width=\"600\" height=\"375\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2023/7934329e41.png\" width=\"600\" height=\"374\" alt=\"\" />\n\nI saved around ~4GB of storage.\n",
"date_published": "2023-01-19T20:59:50+08:00",
"url": "https://law.gmnz.xyz/2023/01/19/leftover-android-studios.html",
"tags": ["Mobile Development","Android"]
},
{
"id": "http://lwgmnz.micro.blog/2022/12/24/books-ive-read.html",
"title": "Books I've Read this 2022",
"content_html": "<p>I haven’t read that many books this year. For 2023, I hope I would be able to improve my reading habit.</p>\n<p>The following below are the books I’ve read (finished).</p>\n<ul>\n<li><a href=\"https://www.goodreads.com/book/show/55077569-checkmate-in-berlin\">Checkmate in Berlin: The Cold War Showdown That Shaped the Modern World</a></li>\n<li><a href=\"https://www.goodreads.com/book/show/38799788-beastie-boys-book\">Beastie Boys Book</a></li>\n<li><a href=\"https://www.goodreads.com/book/show/59366248-the-gotti-wars\">The Gotti Wars: Taking Down America’s Most Notorious Mobster</a></li>\n<li><a href=\"https://www.goodreads.com/book/show/1105956.Gomorrah\">Gomorrah by Roberto Saviano</a></li>\n<li><a href=\"https://www.goodreads.com/book/show/6658129-tokyo-vice\">Tokyo Vice: An American Reporter on the Police Beat in Japan</a></li>\n<li><a href=\"https://www.goodreads.com/book/show/48816586-software-engineering-at-google\">Software Engineering at Google: Lessons Learned from Programming Over Time</a></li>\n<li><a href=\"https://www.goodreads.com/book/show/53011383-unix\">UNIX: A History and a Memoir</a></li>\n<li><a href=\"https://www.goodreads.com/book/show/60310785-three-assassins\">Three Assassins by Kōtarō Isaka</a></li>\n<li><a href=\"https://www.goodreads.com/book/show/50358100-do-what-you-want\">Do What You Want: The Story of Bad Religion</a></li>\n</ul>\n",
"content_text": "I haven't read that many books this year. For 2023, I hope I would be able to improve my reading habit.\n\nThe following below are the books I've read (finished).\n\n- [Checkmate in Berlin: The Cold War Showdown That Shaped the Modern World](https://www.goodreads.com/book/show/55077569-checkmate-in-berlin)\n- [Beastie Boys Book](https://www.goodreads.com/book/show/38799788-beastie-boys-book)\n- [The Gotti Wars: Taking Down America's Most Notorious Mobster](https://www.goodreads.com/book/show/59366248-the-gotti-wars)\n- [Gomorrah by Roberto Saviano](https://www.goodreads.com/book/show/1105956.Gomorrah)\n- [Tokyo Vice: An American Reporter on the Police Beat in Japan](https://www.goodreads.com/book/show/6658129-tokyo-vice)\n- [Software Engineering at Google: Lessons Learned from Programming Over Time](https://www.goodreads.com/book/show/48816586-software-engineering-at-google)\n- [UNIX: A History and a Memoir](https://www.goodreads.com/book/show/53011383-unix)\n- [Three Assassins by Kōtarō Isaka](https://www.goodreads.com/book/show/60310785-three-assassins)\n- [Do What You Want: The Story of Bad Religion](https://www.goodreads.com/book/show/50358100-do-what-you-want)\n",
"date_published": "2022-12-24T09:35:59+08:00",
"url": "https://law.gmnz.xyz/2022/12/24/books-ive-read.html",
"tags": ["books"]
},
{
"id": "http://lwgmnz.micro.blog/2022/12/16/my-personal-feedbin.html",
"title": "My Personal Feedbin Reader - v0.2.1",
"content_html": "<p>The name of my personal Feedbin reader is called FeedMe. The more I worked on it the more it feels like this is not an RSS reader app, but a food or recipe app.</p>\n<h2 id=\"v021-releasehttpsgithubcomlawgimenezfeedme-iosreleasestagv021\"><a href=\"https://github.com/lawgimenez/feedme-ios/releases/tag/v0.2.1\">v0.2.1 Release</a></h2>\n<p>I finally fixed the format alignment in the feed view.</p>\n<!-- raw HTML omitted -->\n<p>And, the mark as read button is now located at the bottom. For easier reach with anyone who has small hands.</p>\n<!-- raw HTML omitted -->\n<p>There is still more work to do and I am currently enjoying iterating on the fixes, improvements and features.</p>\n",
"content_text": "The name of my personal Feedbin reader is called FeedMe. The more I worked on it the more it feels like this is not an RSS reader app, but a food or recipe app.\n\n## [v0.2.1 Release](https://github.com/lawgimenez/feedme-ios/releases/tag/v0.2.1)\n\nI finally fixed the format alignment in the feed view.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/643438d7d6.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\nAnd, the mark as read button is now located at the bottom. For easier reach with anyone who has small hands.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/946d3d5663.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\nThere is still more work to do and I am currently enjoying iterating on the fixes, improvements and features.\n",
"date_published": "2022-12-16T17:50:07+08:00",
"url": "https://law.gmnz.xyz/2022/12/16/my-personal-feedbin.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/12/10/my-personal-feedbin.html",
"title": "My personal Feedbin Reader - v0.2",
"content_html": "<p>In three days, I developed my own Feedbin Reader since the official one is not written in native. In other words, it feels so slow.</p>\n<p><a href=\"https://github.com/lawgimenez/feedme-ios/commit/192280c2263171b6b6c9b30b6972c0f9184ce59b\">First commit</a> was on December 7, 2022.</p>\n<p>The app is written in SwiftUI and I am just blown away by how fast you could create an iOS app. Compared with Storyboarding or UIKit.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>I <a href=\"https://github.com/lawgimenez/feedme-ios\">open sourced</a> for my future reference.</p>\n<p>There’s still so much to work on it, so many bugs and critical ones but for now, it is functional for my everyday use. Shoutout to Feedbin for having such an amazing API.</p>\n",
"content_text": "In three days, I developed my own Feedbin Reader since the official one is not written in native. In other words, it feels so slow.\n\n[First commit](https://github.com/lawgimenez/feedme-ios/commit/192280c2263171b6b6c9b30b6972c0f9184ce59b) was on December 7, 2022.\n\nThe app is written in SwiftUI and I am just blown away by how fast you could create an iOS app. Compared with Storyboarding or UIKit.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/ef47977ed5.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/cc20017a70.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/1bd9a6d31f.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\nI [open sourced](https://github.com/lawgimenez/feedme-ios) for my future reference.\n\nThere's still so much to work on it, so many bugs and critical ones but for now, it is functional for my everyday use. Shoutout to Feedbin for having such an amazing API.\n",
"date_published": "2022-12-10T22:07:17+08:00",
"url": "https://law.gmnz.xyz/2022/12/10/my-personal-feedbin.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/12/02/according-to-mit.html",
"title": "Philippines Top 10 In Pivoting to Clean Energy",
"content_html": "<p>According to MIT, the Philippines is one of the countries pivoting towards clean energy. Perhaps we are the only one throughout Southeast Asia? Good to know our leaders here, and past, are not climate change deniers.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "According to MIT, the Philippines is one of the countries pivoting towards clean energy. Perhaps we are the only one throughout Southeast Asia? Good to know our leaders here, and past, are not climate change deniers.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/6ec487a983.png\" width=\"600\" height=\"209\" alt=\"\">\n",
"date_published": "2022-12-02T20:50:30+08:00",
"url": "https://law.gmnz.xyz/2022/12/02/according-to-mit.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/11/25/updating-old-android.html",
"title": "Updating old Android codebase",
"content_html": "<p>An old client of mine contacted me to fix some bugs and the tricky part was that I have to update an Android codebase that hasn’t been updated in 3 years.</p>\n<p>I have updated <code>com.android.tools.build:gradle</code> from <code>v3.6.4</code> to <code>v4.2.2</code>.</p>\n<p>And from <code>v4.2.2</code> to <code>v7.3.1</code>.</p>\n<!-- raw HTML omitted -->\n<p>This is how old the codebase is.</p>\n",
"content_text": "An old client of mine contacted me to fix some bugs and the tricky part was that I have to update an Android codebase that hasn't been updated in 3 years.\n\nI have updated `com.android.tools.build:gradle` from `v3.6.4` to `v4.2.2`.\n\nAnd from `v4.2.2` to `v7.3.1`.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/aefbe0ea52.png\" width=\"600\" height=\"229\" alt=\"\">\n\nThis is how old the codebase is.\n",
"date_published": "2022-11-25T18:26:26+08:00",
"url": "https://law.gmnz.xyz/2022/11/25/updating-old-android.html",
"tags": ["Mobile Development","Android"]
},
{
"id": "http://lwgmnz.micro.blog/2022/11/20/gcf-on-hacker.html",
"title": "GCF on Hacker News",
"content_html": "<p>In high school, in me and my brother’s room, I had a vandal of a phrase on my wall saying</p>\n<blockquote>\n<p>On the streets saving the scene from the forces of evil</p>\n</blockquote>\n<p>It came from Good Clean Fun’s hardcore album of the same name. It was one of my favorite albums growing up.</p>\n<!-- raw HTML omitted -->\n<p>About 2 weeks ago, I came upon a username <code>issa</code> on Hacker News and was talking about some hardcore riffs. The post was about <code>How to write a hardcore riff</code>. When I saw the username, the first thing that came to my mind was Good Clean Fun. And straight up asked him. And I was right.</p>\n<!-- raw HTML omitted -->\n<p>The awesome part is that he still remembered my email way back in 2007-ish. I interviewed him back then through email.</p>\n<p>Thank you Issa and to Good Clean Fun for making my high school soundtrack awesome!</p>\n",
"content_text": "In high school, in me and my brother's room, I had a vandal of a phrase on my wall saying\n\n> On the streets saving the scene from the forces of evil\n\nIt came from Good Clean Fun's hardcore album of the same name. It was one of my favorite albums growing up.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/453f3c777d.jpg\" width=\"600\" height=\"587\" alt=\"\" />\n\nAbout 2 weeks ago, I came upon a username `issa` on Hacker News and was talking about some hardcore riffs. The post was about `How to write a hardcore riff`. When I saw the username, the first thing that came to my mind was Good Clean Fun. And straight up asked him. And I was right.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/07f0b98502.png\" width=\"600\" height=\"345\" alt=\"\" />\n\nThe awesome part is that he still remembered my email way back in 2007-ish. I interviewed him back then through email.\n\nThank you Issa and to Good Clean Fun for making my high school soundtrack awesome!\n",
"date_published": "2022-11-20T17:39:17+08:00",
"url": "https://law.gmnz.xyz/2022/11/20/gcf-on-hacker.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/11/11/render-problem.html",
"title": "Android Studio Render Problem",
"content_html": "<p>If you encounter a Render Problem issue on your Android Studio, like the one below.</p>\n<!-- raw HTML omitted -->\n<p>The solution I came up with is to use the Material Styles as your app theme.</p>\n<pre tabindex=\"0\"><code><style name="AppTheme" parent="@style/Theme.Material3.Light">\n</style>\n</code></pre>",
"content_text": "If you encounter a Render Problem issue on your Android Studio, like the one below.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/bfc3bef317.png\" width=\"600\" height=\"85\" alt=\"\" />\n\nThe solution I came up with is to use the Material Styles as your app theme.\n\n```\n<style name=\"AppTheme\" parent=\"@style/Theme.Material3.Light\">\n</style>\n```\n",
"date_published": "2022-11-11T19:35:19+08:00",
"url": "https://law.gmnz.xyz/2022/11/11/render-problem.html",
"tags": ["Android"]
},
{
"id": "http://lwgmnz.micro.blog/2022/10/22/reddit-gift.html",
"title": "Reddit Gift",
"content_html": "<p>Reddit made an error with their Premium and in turn, they gifted me with 21,000 coins. I would never expect them to even respond or solve this issue.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Reddit made an error with their Premium and in turn, they gifted me with 21,000 coins. I would never expect them to even respond or solve this issue.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/aaeb768976.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\n",
"date_published": "2022-10-22T10:13:38+08:00",
"url": "https://law.gmnz.xyz/2022/10/22/reddit-gift.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/10/01/android-job-post.html",
"title": "Android Job Post Nostalgia",
"content_html": "<p>Just found this photo of a newspaper ad for an Android Developer job post. This was my first break in the mobile development industry.</p>\n<p>This was published back in October 2011. I emailed them my application and was interviewed the next month. And was hired, if I remember correctly, the same month in November that year.</p>\n<!-- raw HTML omitted -->\n<p>Amazing, I still have that email sent saved.</p>\n<!-- raw HTML omitted -->\n<p><a href=\"https://github.com/lawgimenez/birthdayreminder-android\">This is the source code</a> of the app I developed for the demo for the technical interview.</p>\n<p>I am eternally grateful to this company for giving me my big brake in the industry.</p>\n",
"content_text": "Just found this photo of a newspaper ad for an Android Developer job post. This was my first break in the mobile development industry.\n\nThis was published back in October 2011. I emailed them my application and was interviewed the next month. And was hired, if I remember correctly, the same month in November that year.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/e54c1fc2e8.jpg\" width=\"600\" height=\"800\" alt=\"\" />\n\nAmazing, I still have that email sent saved.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/d013d8fdf2.png\" width=\"600\" height=\"335\" alt=\"\" />\n\n[This is the source code](https://github.com/lawgimenez/birthdayreminder-android) of the app I developed for the demo for the technical interview.\n\n I am eternally grateful to this company for giving me my big brake in the industry.\n",
"date_published": "2022-10-01T20:13:54+08:00",
"url": "https://law.gmnz.xyz/2022/10/01/android-job-post.html",
"tags": ["Personal"]
},
{
"id": "http://lwgmnz.micro.blog/2022/09/29/xcode-beta-new.html",
"title": "Xcode 14.1 Beta 3 New Splash?",
"content_html": "<p>I just saw this new Xcode splash screen for the first time. This popped up on Xcode 14.1 Beta 3. I’m surprised this wasn’t included in Xcode 14.0?</p>\n<!-- raw HTML omitted -->\n<p>Had to <a href=\"https://www.reddit.com/r/swift/comments/xquoph/just_installed_xcode_141_b3_is_this_new/\">confirm it on Reddit</a>.</p>\n",
"content_text": "I just saw this new Xcode splash screen for the first time. This popped up on Xcode 14.1 Beta 3. I'm surprised this wasn't included in Xcode 14.0?\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/6c8be317e6.png\" width=\"600\" height=\"651\" alt=\"\" />\n\nHad to [confirm it on Reddit](https://www.reddit.com/r/swift/comments/xquoph/just_installed_xcode_141_b3_is_this_new/).\n",
"date_published": "2022-09-29T09:44:43+08:00",
"url": "https://law.gmnz.xyz/2022/09/29/xcode-beta-new.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/09/28/one-of-the.html",
"title": "One of the Top Mobile Developers in Cebu",
"content_html": "<p>Before this gets purged on the internet or LinkedIn. Back in 2018, someone messaged me on LinkedIn that I was chosen as one of the top mobile developers in Cebu.</p>\n<p>Flattered of course. Thanks! But unfortunately, the project was scrapped.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Before this gets purged on the internet or LinkedIn. Back in 2018, someone messaged me on LinkedIn that I was chosen as one of the top mobile developers in Cebu.\n\nFlattered of course. Thanks! But unfortunately, the project was scrapped.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/9073b7202f.jpg\" width=\"600\" height=\"1151\" alt=\"\" />\n",
"date_published": "2022-09-28T07:37:30+08:00",
"url": "https://law.gmnz.xyz/2022/09/28/one-of-the.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/09/25/weekend-project-imagedownloaderpy.html",
"title": "Weekend Project: ImageDownloader.py",
"content_html": "<p>My personal project for this weekend is to migrate my portfolio from <a href=\"https://lwgmnz.me/portfolio-2/\">my old WordPress blog</a> to my current one.</p>\n<h3 id=\"export\">Export</h3>\n<p>First, I exported my content using WordPress admin. And was saved into a <code>lwgmnzme.wordpress.com-2022-09-25-02_03_10</code> folder in XML format.</p>\n<h3 id=\"wordpress-xml-to-markdown\">WordPress XML to Markdown</h3>\n<p>I then used this <a href=\"https://github.com/lonekorean/wordpress-export-to-markdown\">handy library</a> for migrating all the pages to Markdown.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"curl-option\">Curl Option</h3>\n<p>At first I was downloading each image individually, and it seems I have more than 50 images to process. So this is not an option.</p>\n<pre tabindex=\"0\"><code>curl https://lwgmnzme.files.wordpress.com/2021/02/simulator-screen-shot-iphone-12-pro-2021-02-27-at-17.22.11.png > image1.png\n</code></pre><h3 id=\"python-script\">Python Script</h3>\n<p>Time to brush up my Python skills once again. I downloaded the <code>requests</code> Python library.</p>\n<pre tabindex=\"0\"><code>sudo pip3 install requests\n</code></pre><p>The plan is to be able to input several URLs and maybe prompt a shortcut key or something to cue the script that it’s time to download the images.</p>\n<p>Or better yet look for the png URLs inside the <code>.md</code> file? Seems like the most logical option for me.</p>\n<h3 id=\"initial-commit\">Initial commit</h3>\n<pre tabindex=\"0\"><code>import requests\n\nfile = open("index.md", "r")\nline = file.read()\nprint("Read = %s" % (line))\n</code></pre><p>So far, so good.</p>\n<h3 id=\"find-all-the-png-files\">Find all the png files</h3>\n<p>The next step is to find all the URL images with <code>.png</code> extensions. Time to use Regex I guess.</p>\n<!-- raw HTML omitted -->\n<p>Update the scripts.</p>\n<pre tabindex=\"0\"><code>import requests\nimport re\n\nfile = open("index.md", "r")\nline = file.read()\n\nresult = re.findall(r'(https?://[^\\s]+)', line)\nprint(result)\n</code></pre><!-- raw HTML omitted -->\n<p>Looks promising. Let’s loop through it.</p>\n<pre tabindex=\"0\"><code>import requests\nimport re\n\nfile = open("index.md", "r")\nline = file.read()\n\nresults = re.findall(r'(https?://[^\\s]+)', line)\n\nfor result in results:\n\tprint(result)\n</code></pre><!-- raw HTML omitted -->\n<p>Much clearer. Okay, let’s try downloading each one of them. But, wait I noticed an extra <code>)</code> character on the results. The hell.</p>\n<p>Maybe I should remove the extra <code>?w=</code> while I’m at it too.</p>\n<pre tabindex=\"0\"><code>import requests\nimport re\n\nfile = open("index.md", "r")\nline = file.read()\n\nresults = re.findall(r'(https?://[^\\s]+)', line)\n\nfor result in results:\n\tindexOfPng = result.find("?")\n\tupdatedResult = result[:indexOfPng]\n\tprint(updatedResult)\n</code></pre><p>Not the most elegant of solutions but it worked.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"get-the-filename\">Get the filename</h3>\n<p>The plan is to get the filename from the URL and use it for saving as a file.</p>\n<pre tabindex=\"0\"><code>import requests\nimport re\nimport os\nfrom urllib.parse import urlparse\n\nfile = open("index.md", "r")\nline = file.read()\n\nresults = re.findall(r'(https?://[^\\s]+)', line)\n\nfor result in results:\n\tindexOfPng = result.find("?")\n\tupdatedResult = result[:indexOfPng]\n\t# Get the filename\n\tparse = urlparse(result)\n\tprint(os.path.basename(parse.path))\n</code></pre><h3 id=\"time-to-download\">Time to download</h3>\n<p>I was having a <code>IsADirectoryError</code> trouble. And it seems that I need to filter out to download only files coming from a <code>wordpress.com</code> domain.</p>\n<h3 id=\"final-code\">Final code</h3>\n<pre tabindex=\"0\"><code>import requests\nimport re\nimport os\nfrom urllib.parse import urlparse\nfrom urllib.request import urlopen\n\nfile = open("index.md", "r")\nline = file.read()\n\nresults = re.findall(r'(https?://[^\\s]+)', line)\n\nfor result in results:\n\n\t# Filter only Wordpress domains\n\tif "wordpress.com" in result:\n\t\t# print(result)\n\t\t# Create folder\n\t\tif not os.path.exists("images"):\n\t\t\tos.makedirs("images")\n\n\t\t# Remove unnecessary characters in the URL\n\t\tindexOfPng = result.find("?")\n\t\tupdatedResult = result[:indexOfPng]\n\t\t# print(updatedResult)\n\n\t\t# Get the filename\n\t\tparse = urlparse(result)\n\t\tfilename = os.path.basename(parse.path)\n\n\t\t# Create a file path\n\t\tfilePath = os.path.join("images", filename)\n\t\tprint(filePath)\n\n\t\trequest = requests.get(updatedResult)\n\t\twith open(filePath, "wb") as file:\n\t\t\tfile.write(request.content)\n\n</code></pre><!-- raw HTML omitted -->\n",
"content_text": "My personal project for this weekend is to migrate my portfolio from [my old WordPress blog](https://lwgmnz.me/portfolio-2/) to my current one.\n\n### Export\n\nFirst, I exported my content using WordPress admin. And was saved into a `lwgmnzme.wordpress.com-2022-09-25-02_03_10` folder in XML format.\n\n### WordPress XML to Markdown\n\nI then used this [handy library](https://github.com/lonekorean/wordpress-export-to-markdown) for migrating all the pages to Markdown.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/63d6ae5a44.png\" width=\"600\" height=\"383\" alt=\"\" />\n\n### Curl Option\n\nAt first I was downloading each image individually, and it seems I have more than 50 images to process. So this is not an option.\n\n```\ncurl https://lwgmnzme.files.wordpress.com/2021/02/simulator-screen-shot-iphone-12-pro-2021-02-27-at-17.22.11.png > image1.png\n```\n\n### Python Script\n\nTime to brush up my Python skills once again. I downloaded the `requests` Python library.\n\n```\nsudo pip3 install requests\n```\n\nThe plan is to be able to input several URLs and maybe prompt a shortcut key or something to cue the script that it's time to download the images.\n\nOr better yet look for the png URLs inside the `.md` file? Seems like the most logical option for me.\n\n### Initial commit\n\n```\nimport requests\n\nfile = open(\"index.md\", \"r\")\nline = file.read()\nprint(\"Read = %s\" % (line))\n```\nSo far, so good.\n\n### Find all the png files\n\nThe next step is to find all the URL images with `.png` extensions. Time to use Regex I guess.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/ab65af3551.jpg\" width=\"600\" height=\"504\" alt=\"\" />\n\nUpdate the scripts.\n\n```\nimport requests\nimport re\n\nfile = open(\"index.md\", \"r\")\nline = file.read()\n\nresult = re.findall(r'(https?://[^\\s]+)', line)\nprint(result)\n```\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/d59783cb2f.png\" width=\"600\" height=\"485\" alt=\"\" />\n\nLooks promising. Let's loop through it.\n\n```\nimport requests\nimport re\n\nfile = open(\"index.md\", \"r\")\nline = file.read()\n\nresults = re.findall(r'(https?://[^\\s]+)', line)\n\nfor result in results:\n\tprint(result)\n```\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/5558311fea.png\" width=\"600\" height=\"485\" alt=\"\" />\n\nMuch clearer. Okay, let's try downloading each one of them. But, wait I noticed an extra `)` character on the results. The hell.\n\nMaybe I should remove the extra `?w=` while I'm at it too.\n\n```\nimport requests\nimport re\n\nfile = open(\"index.md\", \"r\")\nline = file.read()\n\nresults = re.findall(r'(https?://[^\\s]+)', line)\n\nfor result in results:\n\tindexOfPng = result.find(\"?\")\n\tupdatedResult = result[:indexOfPng]\n\tprint(updatedResult)\n```\n\nNot the most elegant of solutions but it worked.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/b950cb39f1.png\" width=\"600\" height=\"485\" alt=\"\" />\n\n### Get the filename\n\nThe plan is to get the filename from the URL and use it for saving as a file.\n\n```\nimport requests\nimport re\nimport os\nfrom urllib.parse import urlparse\n\nfile = open(\"index.md\", \"r\")\nline = file.read()\n\nresults = re.findall(r'(https?://[^\\s]+)', line)\n\nfor result in results:\n\tindexOfPng = result.find(\"?\")\n\tupdatedResult = result[:indexOfPng]\n\t# Get the filename\n\tparse = urlparse(result)\n\tprint(os.path.basename(parse.path))\n```\n### Time to download\n\nI was having a `IsADirectoryError` trouble. And it seems that I need to filter out to download only files coming from a `wordpress.com` domain.\n\n### Final code\n\n```\nimport requests\nimport re\nimport os\nfrom urllib.parse import urlparse\nfrom urllib.request import urlopen\n\nfile = open(\"index.md\", \"r\")\nline = file.read()\n\nresults = re.findall(r'(https?://[^\\s]+)', line)\n\nfor result in results:\n\n\t# Filter only Wordpress domains\n\tif \"wordpress.com\" in result:\n\t\t# print(result)\n\t\t# Create folder\n\t\tif not os.path.exists(\"images\"):\n\t\t\tos.makedirs(\"images\")\n\n\t\t# Remove unnecessary characters in the URL\n\t\tindexOfPng = result.find(\"?\")\n\t\tupdatedResult = result[:indexOfPng]\n\t\t# print(updatedResult)\n\n\t\t# Get the filename\n\t\tparse = urlparse(result)\n\t\tfilename = os.path.basename(parse.path)\n\n\t\t# Create a file path\n\t\tfilePath = os.path.join(\"images\", filename)\n\t\tprint(filePath)\n\n\t\trequest = requests.get(updatedResult)\n\t\twith open(filePath, \"wb\") as file:\n\t\t\tfile.write(request.content)\n\n```\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/0a504b7786.png\" width=\"600\" height=\"485\" alt=\"\" />\n \n",
"date_published": "2022-09-25T20:15:39+08:00",
"url": "https://law.gmnz.xyz/2022/09/25/weekend-project-imagedownloaderpy.html",
"tags": ["Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/09/20/just-happen-to.html",
"title": "My Wife's Pregnancy Journey",
"content_html": "<p>Just happen to see this picture again in my photo library and I am still amazed at how strong my wife is. These were her medications after her C-section.</p>\n<p>I can’t even take two medicines in a day without losing my shit.</p>\n<p>So proud of her!</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Just happen to see this picture again in my photo library and I am still amazed at how strong my wife is. These were her medications after her C-section. \n\nI can’t even take two medicines in a day without losing my shit.\n\nSo proud of her! \n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/ab2de4bd14.jpg\" width=\"600\" height=\"583\" alt=\"\" />\n",
"date_published": "2022-09-20T16:17:15+08:00",
"url": "https://law.gmnz.xyz/2022/09/20/just-happen-to.html",
"tags": ["Personal","Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2022/09/18/swiftui-how-to.html",
"title": "SwiftUI: How to use NavigationStack inside the .toolbar",
"content_html": "<p>First, why do I mostly find a solution after <a href=\"https://stackoverflow.com/questions/73759452/migrating-to-navigationstack-with-toolbar\">posting a Stackoverflow question</a>?</p>\n<p>So most examples and tutorials only use <code>NavigationStack</code> and <code>NavigationLink</code> inside a <code>List</code>. I’m surprised by how nobody seems to implement using the <code>.toolbar</code> modifier.</p>\n<p>The solution was a little straightforward I found out.</p>\n<p>Instead of <code>NavigationView</code>, you use the new and shiny <code>NavigationStack</code>. And use <code>.navigationDestination()</code> instead of <code>NavigationLink</code>.</p>\n<pre tabindex=\"0\"><code>@State private var goToSettings = false\n\nNavigationStack {\n ZStack {\n // Some more codes\n }\n .toolbar {\n Button(role: .destructive, action: {\n goToSettings = true\n }) {\n Label("Settings", systemImage: "gearshape.fill").foregroundColor(colorForeground)\n }\n }\n .navigationDestination(isPresented: $goToSettings, destination: {\n SettingsView()\n })\n}\n</code></pre><p>I need to get more familiar with SwiftUI’s modifiers as it is still a little confusing for me.</p>\n",
"content_text": "First, why do I mostly find a solution after [posting a Stackoverflow question](https://stackoverflow.com/questions/73759452/migrating-to-navigationstack-with-toolbar)? \n\nSo most examples and tutorials only use `NavigationStack` and `NavigationLink` inside a `List`. I'm surprised by how nobody seems to implement using the `.toolbar` modifier.\n\nThe solution was a little straightforward I found out.\n\nInstead of `NavigationView`, you use the new and shiny `NavigationStack`. And use `.navigationDestination()` instead of `NavigationLink`.\n\n```\n@State private var goToSettings = false\n\nNavigationStack {\n ZStack {\n // Some more codes\n }\n .toolbar {\n Button(role: .destructive, action: {\n goToSettings = true\n }) {\n Label(\"Settings\", systemImage: \"gearshape.fill\").foregroundColor(colorForeground)\n }\n }\n .navigationDestination(isPresented: $goToSettings, destination: {\n SettingsView()\n })\n}\n```\n\nI need to get more familiar with SwiftUI's modifiers as it is still a little confusing for me.\n",
"date_published": "2022-09-18T09:25:04+08:00",
"url": "https://law.gmnz.xyz/2022/09/18/swiftui-how-to.html",
"tags": ["Swift","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/09/17/buildexception-error.html",
"title": "BuildException Error",
"content_html": "<p>Updated last night to the latest Android Studio Dolphin build but encountered this annoying build error from the JetBrains IDE.</p>\n<!-- raw HTML omitted -->\n<p>Invalidating cache and restarting seems to not work. <a href=\"https://youtrack.jetbrains.com/issue/KTIJ-20694/IDE-Gradle-import-leads-to-javalangNoClassDefFoundError-orggradletoolingBuildException\">Jetbrains seems to be clueless too</a>.</p>\n<p>This bug has already taken much of my time. This is annoying.</p>\n<h3 id=\"edit-september-17-at-914pm\">Edit: September 17 at 9:14PM</h3>\n<p>It seems that the error went away after declining the “standalone script” option in Android Studio.</p>\n",
"content_text": "Updated last night to the latest Android Studio Dolphin build but encountered this annoying build error from the JetBrains IDE.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/fae9f85c0b.png\" width=\"600\" height=\"128\" alt=\"\" />\n\nInvalidating cache and restarting seems to not work. [Jetbrains seems to be clueless too](https://youtrack.jetbrains.com/issue/KTIJ-20694/IDE-Gradle-import-leads-to-javalangNoClassDefFoundError-orggradletoolingBuildException).\n\nThis bug has already taken much of my time. This is annoying.\n\n### Edit: September 17 at 9:14PM\n\nIt seems that the error went away after declining the \"standalone script\" option in Android Studio.\n",
"date_published": "2022-09-17T21:15:56+08:00",
"url": "https://law.gmnz.xyz/2022/09/17/buildexception-error.html",
"tags": ["Mobile Development","Android","Kotlin","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/09/15/compiling-ios-project.html",
"title": "Compiling iOS Project in GitHub Actions",
"content_html": "<p>While setting up <code>YAML</code> with GitHub Actions, it seems the build failed.</p>\n<!-- raw HTML omitted -->\n<p>These are the steps I took to integrate GitHub Actions in our iOS project. It seems for CI/CD to work we have to create a <code>Package.swift</code> file in our project. Currently, we do it manually through Xcode’s Swift Package Manager.</p>\n<p>To create this <code>Package.swift</code> file, we need to run <code>swift package init</code> in the command line and it will create the following files.</p>\n<pre tabindex=\"0\"><code>swift package init\nCreating library package: iOS\nCreating Package.swift\nCreating .gitignore\nCreating Sources/\nCreating Sources/iOS/iOS.swift\nCreating Tests/\nCreating Tests/iOSTests/\nCreating Tests/iOSTests/iOSTests.swift\n</code></pre><p>After, I opened <code>Package.swift</code> file and added the libraries inside the <code>dependencies</code> bracket.</p>\n<pre tabindex=\"0\"><code>dependencies: [\n // Dependencies declare other packages that this package depends on.\n // .package(url: /* package url */, from: "1.0.0"),\n .package(url: "https://github.com/scinfu/SwiftSoup", branch: "master"),\n .package(url: "https://github.com/onevcat/Kingfisher", branch: "master"),\n .package(url: "https://github.com/realm/realm-cocoa", branch: "master"),\n .package(url: "https://github.com/optonaut/ActiveLabel.swift", branch: "master"),\n .package(url: "https://github.com/mixpanel/mixpanel-swift", branch: "master"),\n .package(url: "https://github.com/BastiaanJansen/toast-swift", branch: "main")\n ],\n</code></pre><p>After saving, run <code>swift package resolve</code> in the command line. Swift will then fetch the libraries and compile them. Swift will also create a file called <code>Package.resolved</code>.</p>\n<p>Also, as of September 15 using <code>macos-latest</code> as the runner in GitHub Actions won’t work. So I have to use <code>macos-12</code> instead. I got this answer from <a href=\"https://stackoverflow.com/questions/67185817/package-resolved-file-is-corrupted-or-malformed\">here</a>.</p>\n<p>Push to git repo. <code>xcodebuild</code> is now working and seems to be fetching the libraries now but threw another error about my “provisioning profiles not found”. I don’t need those profiles setup for now, I just want GA to compile our iOS project successfully.</p>\n<p>So, I updated the run command in our <code>YAML</code> file.</p>\n<pre tabindex=\"0\"><code>- name: Build\n run: xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO -scheme OnlineJobsPH\n working-directory: ./iOS\n</code></pre><!-- raw HTML omitted -->\n<p>Build successful. Yay!</p>\n",
"content_text": "While setting up `YAML` with GitHub Actions, it seems the build failed.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/1dd4250e09.png\" width=\"600\" height=\"335\" alt=\"\" />\n\nThese are the steps I took to integrate GitHub Actions in our iOS project. It seems for CI/CD to work we have to create a `Package.swift` file in our project. Currently, we do it manually through Xcode's Swift Package Manager.\n\nTo create this `Package.swift` file, we need to run `swift package init` in the command line and it will create the following files.\n\n```\nswift package init\nCreating library package: iOS\nCreating Package.swift\nCreating .gitignore\nCreating Sources/\nCreating Sources/iOS/iOS.swift\nCreating Tests/\nCreating Tests/iOSTests/\nCreating Tests/iOSTests/iOSTests.swift\n```\n\nAfter, I opened `Package.swift` file and added the libraries inside the `dependencies` bracket.\n\n\n```\ndependencies: [\n // Dependencies declare other packages that this package depends on.\n // .package(url: /* package url */, from: \"1.0.0\"),\n .package(url: \"https://github.com/scinfu/SwiftSoup\", branch: \"master\"),\n .package(url: \"https://github.com/onevcat/Kingfisher\", branch: \"master\"),\n .package(url: \"https://github.com/realm/realm-cocoa\", branch: \"master\"),\n .package(url: \"https://github.com/optonaut/ActiveLabel.swift\", branch: \"master\"),\n .package(url: \"https://github.com/mixpanel/mixpanel-swift\", branch: \"master\"),\n .package(url: \"https://github.com/BastiaanJansen/toast-swift\", branch: \"main\")\n ],\n```\n\nAfter saving, run `swift package resolve` in the command line. Swift will then fetch the libraries and compile them. Swift will also create a file called `Package.resolved`.\n\nAlso, as of September 15 using `macos-latest` as the runner in GitHub Actions won't work. So I have to use `macos-12` instead. I got this answer from [here](https://stackoverflow.com/questions/67185817/package-resolved-file-is-corrupted-or-malformed).\n\nPush to git repo. `xcodebuild` is now working and seems to be fetching the libraries now but threw another error about my \"provisioning profiles not found\". I don't need those profiles setup for now, I just want GA to compile our iOS project successfully.\n\nSo, I updated the run command in our `YAML` file.\n\n```\n- name: Build\n run: xcodebuild clean build CODE_SIGN_IDENTITY=\"\" CODE_SIGNING_REQUIRED=NO -scheme OnlineJobsPH\n working-directory: ./iOS\n```\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/5ba37c8b65.png\" width=\"600\" height=\"179\" alt=\"\" />\n\nBuild successful. Yay!\n\n\n\n\n",
"date_published": "2022-09-15T10:00:54+08:00",
"url": "https://law.gmnz.xyz/2022/09/15/compiling-ios-project.html",
"tags": ["iOS","Mobile Development"]
},
{
"id": "http://lwgmnz.micro.blog/2022/09/09/onlinejobs-is-ios.html",
"title": "OnlineJobs is iOS 16 Ready",
"content_html": "<p>I just compiled our codebase to the latest Xcode 14 RC and everything looks good during testing. I am still not sure how I feel about the new iPhone 14’s Dynamic Island. It seems like a huge black unknown mass floating on the top. I am on a simulator though so, take my opinion with a grain of salt.</p>\n<!-- raw HTML omitted -->\n<p>With that being said, time to publish this build today and for Apple to review.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "I just compiled our codebase to the latest Xcode 14 RC and everything looks good during testing. I am still not sure how I feel about the new iPhone 14's Dynamic Island. It seems like a huge black unknown mass floating on the top. I am on a simulator though so, take my opinion with a grain of salt.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/7e0f39724d.png\" width=\"600\" height=\"1300\" alt=\"\" />\n\nWith that being said, time to publish this build today and for Apple to review.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/4d163a6c47.png\" width=\"600\" height=\"1300\" alt=\"\" />\n",
"date_published": "2022-09-09T09:47:22+08:00",
"url": "https://law.gmnz.xyz/2022/09/09/onlinejobs-is-ios.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/09/07/our-son-turns.html",
"title": "Our Son Turned 4",
"content_html": "<p>Our kid turned 4 this week. We came back to the hotel where he was still tiny. Where we had to request a crib for our room.</p>\n<p>But now we can’t believe he’s 4 now!</p>\n<!-- raw HTML omitted -->\n<p>Sorry for the table shot, but I captured this perfect moment of them hugging. Our son is on the autism spectrum but it doesn’t mean he won’t show his appreciation and love to us.</p>\n<p>The next day’s aftermath, he can’t wait to play with his new toys.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Our kid turned 4 this week. We came back to the hotel where he was still tiny. Where we had to request a crib for our room.\n\nBut now we can’t believe he’s 4 now!\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/15feb52bb2.jpg\" width=\"600\" height=\"1066\" alt=\"\" />\n\nSorry for the table shot, but I captured this perfect moment of them hugging. Our son is on the autism spectrum but it doesn't mean he won't show his appreciation and love to us.\n\nThe next day's aftermath, he can't wait to play with his new toys.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/3843ae9c9e.jpg\" width=\"600\" height=\"1066\" alt=\"\" />\n",
"date_published": "2022-09-07T07:53:22+08:00",
"url": "https://law.gmnz.xyz/2022/09/07/our-son-turns.html",
"tags": ["Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2022/08/31/stackoverflow-chat-recent.html",
"title": "Stackoverflow Chat Recent History Backup",
"content_html": "<p>I just want to post my <a href=\"https://chat.stackoverflow.com/users/1075466/lawrence-gimenez?tab=recent\">Stackoverflow recent chat history</a> for nostalgic purposes. I hope that day never comes when Stackoverflow would also sunset their chat feature just like they did with <a href=\"https://initviews.com/2022/03/30/stackoverflow-developer-story.html\">developer profile</a>.</p>\n<!-- raw HTML omitted -->\n<p>I joined the Android channel around 2012-ish. Hopefully, Stackoverflow would let me download the whole history one day.</p>\n",
"content_text": "I just want to post my [Stackoverflow recent chat history](https://chat.stackoverflow.com/users/1075466/lawrence-gimenez?tab=recent) for nostalgic purposes. I hope that day never comes when Stackoverflow would also sunset their chat feature just like they did with [developer profile](https://initviews.com/2022/03/30/stackoverflow-developer-story.html).\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/8673f53b0e.png\" width=\"600\" height=\"513\" alt=\"\" />\n\nI joined the Android channel around 2012-ish. Hopefully, Stackoverflow would let me download the whole history one day.\n",
"date_published": "2022-08-31T23:29:07+08:00",
"url": "https://law.gmnz.xyz/2022/08/31/stackoverflow-chat-recent.html",
"tags": ["Journal"]
},
{
"id": "http://lwgmnz.micro.blog/2022/08/31/years-ago-i.html",
"title": "Legacy Android Library - Networking Valley",
"content_html": "<p>7 years ago I open-sourced my <a href=\"https://github.com/lawgimenez/networking-valley\">first Android library on GitHub</a>.</p>\n<p>And shared it to my fellow Android devs in Stackoverflow’s Android channel.</p>\n<!-- raw HTML omitted -->\n<p>This is for sure one of my favorite highlights of my career.</p>\n",
"content_text": "7 years ago I open-sourced my [first Android library on GitHub](https://github.com/lawgimenez/networking-valley). \n\nAnd shared it to my fellow Android devs in Stackoverflow's Android channel.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/076016ef79.png\" width=\"600\" height=\"201\" alt=\"\" />\n\nThis is for sure one of my favorite highlights of my career.\n",
"date_published": "2022-08-31T22:07:08+08:00",
"url": "https://law.gmnz.xyz/2022/08/31/years-ago-i.html",
"tags": ["Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/08/23/mixpanel-refactor-our.html",
"title": "Mixpanel Code Refactor",
"content_html": "<p>Our app uses Mixpanel for analytical and strategic purposes. This post is about my refactoring journey.</p>\n<p>Mixpanel introduced another parameter in their <code>getInstance()</code> method called <code>trackAutomaticEvents</code> which is of a <code>boolean</code> data type. Now, this is the part that I didn’t anticipate because located throughout our codebase is this single line.</p>\n<pre tabindex=\"0\"><code>MixpanelAPI.getInstance(this, Constants.MIXPANEL_TOKEN).track("Log In")\n</code></pre><p>But right now it is broken since the proper way to implement on the new version is this.</p>\n<pre tabindex=\"0\"><code>MixpanelAPI.getInstance(this, Constants.MIXPANEL_TOKEN, true).track("Log In")\n</code></pre><p>Behold them compile errors.</p>\n<!-- raw HTML omitted -->\n<p>You might think why not use the Find All option in Android Studio? I could, but this is to future proof our codebase in case another <code>getInstance()</code> refactor in the future.</p>\n<h3 id=\"kotlin-object\">Kotlin Object</h3>\n<p>For the Kotlin singleton pattern, I always use <code>object</code> since they are good for one-time use purposes.</p>\n<pre tabindex=\"0\"><code>import android.content.Context\nimport com.mixpanel.android.mpmetrics.MixpanelAPI\n\nobject Mixpanel {\n\n fun track(context: Context, track: String) {\n return MixpanelAPI.getInstance(context, Constants.MIXPANEL_TOKEN, true)\n .track(track)\n }\n}\n</code></pre><p>Now that I have isolated or “wrapped” in a separate class, I can now implement condition statements inside it. Most of my Mixpanel method calls were wrapped in a <code>!isDebuggable</code> condition and are sprinkled everywhere in our codebase. Not good.</p>\n<pre tabindex=\"0\"><code>val isDebuggable = 0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE\nif (!isDebuggable) {\n MixpanelAPI.getInstance(this@MainActivity, Constants.MIXPANEL_TOKEN).track("Session - Employer")\n}\n</code></pre><h3 id=\"code-refactor\">Code Refactor</h3>\n<p>Now, that I have a separate <code>object</code> class, I can just call the condition there instead.</p>\n<pre tabindex=\"0\"><code>import android.content.Context\nimport android.content.pm.ApplicationInfo\nimport com.mixpanel.android.mpmetrics.MixpanelAPI\n\nobject Mixpanel {\n\n fun track(context: Context, track: String) {\n val isDebuggable = 0 != context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE\n if (!isDebuggable) {\n MixpanelAPI.getInstance(context, Constants.MIXPANEL_TOKEN, true).track(track)\n }\n }\n}\n</code></pre><p>So from this code block</p>\n<pre tabindex=\"0\"><code>val isDebuggable = 0 != context?.applicationInfo!!.flags and ApplicationInfo.FLAG_DEBUGGABLE\n if (!isDebuggable) {\n MixpanelAPI.getInstance(activity, Constants.MIXPANEL_TOKEN).track("Share Profile")\n }\n</code></pre><p>to</p>\n<pre tabindex=\"0\"><code>Mixpanel.track(requireActivity(), "Share Profile")\n</code></pre><h2 id=\"future-proof-and-conclusion\">Future Proof and Conclusion</h2>\n<p>If Mixpanel decides to update their <code>getInstance()</code> method, then I only have to change that one line in my <code>object Mixpanel</code>. This code refactor has been a long time coming. I’m just glad that I was able to push this out of the way. Now, on to the next refactor.</p>\n",
"content_text": "Our app uses Mixpanel for analytical and strategic purposes. This post is about my refactoring journey.\n\nMixpanel introduced another parameter in their `getInstance()` method called `trackAutomaticEvents` which is of a `boolean` data type. Now, this is the part that I didn't anticipate because located throughout our codebase is this single line.\n\n```\nMixpanelAPI.getInstance(this, Constants.MIXPANEL_TOKEN).track(\"Log In\")\n```\n\nBut right now it is broken since the proper way to implement on the new version is this.\n\n```\nMixpanelAPI.getInstance(this, Constants.MIXPANEL_TOKEN, true).track(\"Log In\")\n```\n\nBehold them compile errors.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/6d82facd21.png\" width=\"600\" height=\"120\" alt=\"\" />\n\nYou might think why not use the Find All option in Android Studio? I could, but this is to future proof our codebase in case another `getInstance()` refactor in the future.\n\n### Kotlin Object\n\nFor the Kotlin singleton pattern, I always use `object` since they are good for one-time use purposes.\n\n\n```\nimport android.content.Context\nimport com.mixpanel.android.mpmetrics.MixpanelAPI\n\nobject Mixpanel {\n\n fun track(context: Context, track: String) {\n return MixpanelAPI.getInstance(context, Constants.MIXPANEL_TOKEN, true)\n .track(track)\n }\n}\n```\n\nNow that I have isolated or \"wrapped\" in a separate class, I can now implement condition statements inside it. Most of my Mixpanel method calls were wrapped in a `!isDebuggable` condition and are sprinkled everywhere in our codebase. Not good.\n\n```\nval isDebuggable = 0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE\nif (!isDebuggable) {\n MixpanelAPI.getInstance(this@MainActivity, Constants.MIXPANEL_TOKEN).track(\"Session - Employer\")\n}\n```\n\n### Code Refactor\n\nNow, that I have a separate `object` class, I can just call the condition there instead.\n\n```\nimport android.content.Context\nimport android.content.pm.ApplicationInfo\nimport com.mixpanel.android.mpmetrics.MixpanelAPI\n\nobject Mixpanel {\n\n fun track(context: Context, track: String) {\n val isDebuggable = 0 != context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE\n if (!isDebuggable) {\n MixpanelAPI.getInstance(context, Constants.MIXPANEL_TOKEN, true).track(track)\n }\n }\n}\n```\n\nSo from this code block\n\n```\nval isDebuggable = 0 != context?.applicationInfo!!.flags and ApplicationInfo.FLAG_DEBUGGABLE\n if (!isDebuggable) {\n MixpanelAPI.getInstance(activity, Constants.MIXPANEL_TOKEN).track(\"Share Profile\")\n }\n```\n\nto\n\n```\nMixpanel.track(requireActivity(), \"Share Profile\")\n```\n\n## Future Proof and Conclusion\n\nIf Mixpanel decides to update their `getInstance()` method, then I only have to change that one line in my `object Mixpanel`. This code refactor has been a long time coming. I’m just glad that I was able to push this out of the way. Now, on to the next refactor.\n",
"date_published": "2022-08-24T00:29:58+08:00",
"url": "https://law.gmnz.xyz/2022/08/23/mixpanel-refactor-our.html",
"tags": ["Android","Kotlin","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/08/18/manjaro-wtf-just.html",
"title": "Manjaro WTF!?",
"content_html": "<p>Just today I read two bad news regarding Manjaro Linux. The first one is <a href=\"https://manjarno.snorlax.sh/\">why you shouldn’t use Manjaro</a> and the other one is <a href=\"https://blog.brixit.nl/why-i-left-pine64/\">why I left PINE64</a>.</p>\n<p>Both articles went viral on Hacker News. On the same day. Same morning. I guess I better start looking for another distro.</p>\n",
"content_text": "Just today I read two bad news regarding Manjaro Linux. The first one is [why you shouldn't use Manjaro](https://manjarno.snorlax.sh/) and the other one is [why I left PINE64](https://blog.brixit.nl/why-i-left-pine64/).\n\nBoth articles went viral on Hacker News. On the same day. Same morning. I guess I better start looking for another distro.\n",
"date_published": "2022-08-18T12:20:49+08:00",
"url": "https://law.gmnz.xyz/2022/08/18/manjaro-wtf-just.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/07/31/my-first-professional.html",
"title": "My First Professional Android Project",
"content_html": "<p>I found my first “professional” Android project while backing up data from my old laptop. I called it a “birthday reminder” and this project was the one I demoed to the company I sent my application to.</p>\n<p>Now, I am unsure if I got the gig because the Android team lead was amazed by the project I demoed or because I am the only applicant. Yes, I was the only applicant for the job interview. This was back in 2011.</p>\n<p>You can find the code <a href=\"https://github.com/lawgimenez/birthdayreminder-android\">here</a> if you are curious about what a 2011 Android project looks like.</p>\n<p>To give you an overview, there was no Android Studio back then. You have to use Eclipse IDE and download the Android platform plugin.</p>\n<p>Based on <a href=\"https://github.com/lawgimenez/birthdayreminder-android/blob/main/.settings/org.eclipse.jdt.core.prefs\">this file’s timestamp</a> I created the project precisely on <code>#Fri Oct 14 09:22:04 CST 2011</code>. In Philippine time that is October 15 at 9:22 PM.</p>\n<p>This project wasn’t even under any sort of version control. This project kicked off my Android development career as I was hired several weeks later.</p>\n",
"content_text": "I found my first \"professional\" Android project while backing up data from my old laptop. I called it a \"birthday reminder\" and this project was the one I demoed to the company I sent my application to.\n\nNow, I am unsure if I got the gig because the Android team lead was amazed by the project I demoed or because I am the only applicant. Yes, I was the only applicant for the job interview. This was back in 2011.\n\nYou can find the code [here](https://github.com/lawgimenez/birthdayreminder-android) if you are curious about what a 2011 Android project looks like.\n\nTo give you an overview, there was no Android Studio back then. You have to use Eclipse IDE and download the Android platform plugin.\n\nBased on [this file's timestamp](https://github.com/lawgimenez/birthdayreminder-android/blob/main/.settings/org.eclipse.jdt.core.prefs) I created the project precisely on `#Fri Oct 14 09:22:04 CST 2011`. In Philippine time that is October 15 at 9:22 PM. \n\nThis project wasn't even under any sort of version control. This project kicked off my Android development career as I was hired several weeks later.\n",
"date_published": "2022-08-17T18:50:30+08:00",
"url": "https://law.gmnz.xyz/2022/07/31/my-first-professional.html",
"tags": ["Mobile Development","Android","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/08/13/be-nice-to.html",
"title": "Be Nice To Your Mobile Developers",
"content_html": "<p>We need more wholesome reviews like the photo below. I received this notification this morning and I don’t care if it is a bot (at least somebody took the time to target and review) or a real person. As long as it is a positive review.</p>\n<!-- raw HTML omitted -->\n<p>Mobile developers don’t get a lot of positive reviews lately. It’s just either someone threatens you on a personal level or tells you to quit your career and job. Behind every app is a dev with a family to feed. So I suggest be nice to your mobile developers.</p>\n",
"content_text": "We need more wholesome reviews like the photo below. I received this notification this morning and I don't care if it is a bot (at least somebody took the time to target and review) or a real person. As long as it is a positive review.\r\n\r\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/d33234a237.png\" width=\"600\" height=\"1298\" alt=\"\" />\r\n\r\nMobile developers don't get a lot of positive reviews lately. It's just either someone threatens you on a personal level or tells you to quit your career and job. Behind every app is a dev with a family to feed. So I suggest be nice to your mobile developers.\n",
"date_published": "2022-08-13T09:04:16+08:00",
"url": "https://law.gmnz.xyz/2022/08/13/be-nice-to.html",
"tags": ["Mobile Development"]
},
{
"id": "http://lwgmnz.micro.blog/2022/08/09/stay-safe-bug.html",
"title": "Stay Safe Bug",
"content_html": "<p>During the Covid pandemic, every country has its own app or service to help track and monitor citizens for infection. And to prevent spread.</p>\n<p>One of these was the <a href=\"https://www.staysafe.ph/\">Stay Safe app</a>. Before being bought by the Philippine government, this was developed by a company called MultiSys. I have checked their website and that’s it. One thing I know is that their email doesn’t work.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"the-bug\">The Bug</h3>\n<p>I don’t have a Stay Safe account but one thing I know is that they were using a third-party called <a href=\"https://uploadcare.com/\">Uploadcare</a> for handling the assets. What are these assets? These are the photos of every person in the Philippines who signed up and their government-issued IDS. And some other personal stuff like a selfie I guess. I know there’s a selfie because of a class named <code>ConfirmSelfieActivity.java</code>.</p>\n<p>Based on my analysis, this bug only exists in <code>v2.*</code> and was fixed in <code>v3.*</code>. Or removed.</p>\n<p>The issue was rather simple and preventable. The developers hardcoded both private and public keys in the code to be used in the UploadCare API. You can see in the photo below, the variables <code>UPLOAD_CARE_PRIVATE_KEY</code> and <code>UPLOAD_CARE_PUBLIC_KEY</code>.</p>\n<!-- raw HTML omitted -->\n<p>You only need these two keys to communicate with the UploadCare API. So anyone, who knows how to decompile an Android app, can retrieve all the data uploaded by every user.</p>\n<p>By just calling the API URL <code>https://api.uploadcare.com</code> with the HTTP Header <code>Uploadcare.Simple <UPLOAD_CARE_PUBLIC_KEY>:<UPLOAD_CARE_PRIVATE_KEY></code> you can communicate and retrieve all the data.</p>\n<p>The code block below is confirmation that all you need are the two sets of keys.</p>\n<!-- raw HTML omitted -->\n<p>This was very reckless for the developers and company. The app wasn’t obfuscated too. Let this be a lesson to not hardcode your keys in the code.</p>\n",
"content_text": "During the Covid pandemic, every country has its own app or service to help track and monitor citizens for infection. And to prevent spread.\n\nOne of these was the [Stay Safe app](https://www.staysafe.ph/). Before being bought by the Philippine government, this was developed by a company called MultiSys. I have checked their website and that's it. One thing I know is that their email doesn't work.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/e6fced5c49.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\n### The Bug\n\nI don't have a Stay Safe account but one thing I know is that they were using a third-party called [Uploadcare](https://uploadcare.com/) for handling the assets. What are these assets? These are the photos of every person in the Philippines who signed up and their government-issued IDS. And some other personal stuff like a selfie I guess. I know there's a selfie because of a class named `ConfirmSelfieActivity.java`.\n\nBased on my analysis, this bug only exists in `v2.*` and was fixed in `v3.*`. Or removed.\n\nThe issue was rather simple and preventable. The developers hardcoded both private and public keys in the code to be used in the UploadCare API. You can see in the photo below, the variables `UPLOAD_CARE_PRIVATE_KEY` and `UPLOAD_CARE_PUBLIC_KEY`.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/3cafe369e3.png\" width=\"600\" height=\"605\" alt=\"\" />\n\nYou only need these two keys to communicate with the UploadCare API. So anyone, who knows how to decompile an Android app, can retrieve all the data uploaded by every user.\n\nBy just calling the API URL `https://api.uploadcare.com` with the HTTP Header `Uploadcare.Simple <UPLOAD_CARE_PUBLIC_KEY>:<UPLOAD_CARE_PRIVATE_KEY>` you can communicate and retrieve all the data. \n\nThe code block below is confirmation that all you need are the two sets of keys.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/148b9b506b.png\" width=\"600\" height=\"200\" alt=\"\" />\n\nThis was very reckless for the developers and company. The app wasn't obfuscated too. Let this be a lesson to not hardcode your keys in the code.\n\n\n\n",
"date_published": "2022-08-11T19:07:33+08:00",
"url": "https://law.gmnz.xyz/2022/08/09/stay-safe-bug.html",
"tags": ["Bug Hunting"]
},
{
"id": "http://lwgmnz.micro.blog/2022/08/06/last-night-i.html",
"title": "Became a Subreddit Moderator",
"content_html": "<p>Last night I was invited to moderate the subreddit <a href=\"https://www.reddit.com/r/PinoyProgrammer/\">/pinoyprogrammer</a> to which I have been contributing a lot for around a year already. It truly is an honor to get recognized and moderate an online community, in particular Reddit. You could say my first time doing this. I hope I don’t screw this up.</p>\n",
"content_text": "Last night I was invited to moderate the subreddit [/pinoyprogrammer](https://www.reddit.com/r/PinoyProgrammer/) to which I have been contributing a lot for around a year already. It truly is an honor to get recognized and moderate an online community, in particular Reddit. You could say my first time doing this. I hope I don't screw this up.\n\n\n\n",
"date_published": "2022-08-06T10:48:57+08:00",
"url": "https://law.gmnz.xyz/2022/08/06/last-night-i.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/08/02/compiling-android-gradle.html",
"title": "Compiling Android on the Command Line",
"content_html": "<p>First of all, I am on Manjaro Xfce environment for this example.</p>\n<p>We need to install Gradle. I tried to install Gradle using ArchLinux’s <code>pacman</code> but it was very outdated. So based on <a href=\"https://docs.gradle.org/current/userguide/installation.html\">Gradle’s installation guide</a> one option is to use a package manager like Sdkman.</p>\n<p>Let’s go ahead and install SDKMAN!.</p>\n<pre tabindex=\"0\"><code>curl -s "https://get.sdkman.io" | bash\nsource "$HOME/.sdkman/bin/sdkman-init.sh"\n</code></pre><p>After running SDKMAN!, close the current terminal and open a new one.\nThen run</p>\n<pre tabindex=\"0\"><code>sdk install gradle\n</code></pre><p>But when I try to run <code>gradle</code> or <code>java</code>, it will throw an error No Java found.</p>\n<pre tabindex=\"0\"><code>ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\n</code></pre><p>We need to install Java’s JDK.</p>\n<pre tabindex=\"0\"><code>sudo pacman -S jre-openjdk-headless jre-openjdk jdk-openjdk openjdk-doc openjdk-src\n</code></pre><p>At this point, running <code>java</code> should now work.</p>\n<pre tabindex=\"0\"><code>java --version\nopenjdk 18.0.2 2022-07-19\nOpenJDK Runtime Environment (build 18.0.2+9)\nOpenJDK 64-Bit Server VM (build 18.0.2+9, mixed mode)\n</code></pre><p>And running <code>gradle</code> should also work likewise.</p>\n<pre tabindex=\"0\"><code>gradle -v\nWelcome to Gradle 7.5!\n\nHere are the highlights of this release:\n - Support for Java 18\n - Support for building with Groovy 4\n - Much more responsive continuous builds\n - Improved diagnostics for dependency resolution\n\nFor more details see https://docs.gradle.org/7.5/release-notes.html\n\n\n------------------------------------------------------------\nGradle 7.5\n------------------------------------------------------------\n\nBuild time: 2022-07-14 12:48:15 UTC\nRevision: c7db7b958189ad2b0c1472b6fe663e6d654a5103\n\nKotlin: 1.6.21\nGroovy: 3.0.10\nAnt: Apache Ant(TM) version 1.10.11 compiled on July 10 2021\nJVM: 18.0.2 (Oracle Corporation 18.0.2+9)\nOS: Linux 5.15.57-2-MANJARO amd64\n</code></pre><p>When you <code>cd</code> to your Android project, <code>gradle</code> should work.</p>\n<pre tabindex=\"0\"><code>gradle build\n</code></pre>",
"content_text": "First of all, I am on Manjaro Xfce environment for this example.\n\nWe need to install Gradle. I tried to install Gradle using ArchLinux's `pacman` but it was very outdated. So based on [Gradle's installation guide](https://docs.gradle.org/current/userguide/installation.html) one option is to use a package manager like Sdkman.\n\nLet's go ahead and install SDKMAN!.\n\n```\ncurl -s \"https://get.sdkman.io\" | bash\nsource \"$HOME/.sdkman/bin/sdkman-init.sh\"\n```\n\nAfter running SDKMAN!, close the current terminal and open a new one.\nThen run\n\n```\nsdk install gradle\n```\n\nBut when I try to run `gradle` or `java`, it will throw an error No Java found.\n\n```\nERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\n```\n\nWe need to install Java's JDK.\n\n```\nsudo pacman -S jre-openjdk-headless jre-openjdk jdk-openjdk openjdk-doc openjdk-src\n```\n\nAt this point, running `java` should now work.\n\n```\njava --version\nopenjdk 18.0.2 2022-07-19\nOpenJDK Runtime Environment (build 18.0.2+9)\nOpenJDK 64-Bit Server VM (build 18.0.2+9, mixed mode)\n```\n\nAnd running `gradle` should also work likewise.\n```\ngradle -v\nWelcome to Gradle 7.5!\n\nHere are the highlights of this release:\n - Support for Java 18\n - Support for building with Groovy 4\n - Much more responsive continuous builds\n - Improved diagnostics for dependency resolution\n\nFor more details see https://docs.gradle.org/7.5/release-notes.html\n\n\n------------------------------------------------------------\nGradle 7.5\n------------------------------------------------------------\n\nBuild time: 2022-07-14 12:48:15 UTC\nRevision: c7db7b958189ad2b0c1472b6fe663e6d654a5103\n\nKotlin: 1.6.21\nGroovy: 3.0.10\nAnt: Apache Ant(TM) version 1.10.11 compiled on July 10 2021\nJVM: 18.0.2 (Oracle Corporation 18.0.2+9)\nOS: Linux 5.15.57-2-MANJARO amd64\n```\n\nWhen you `cd` to your Android project, `gradle` should work.\n\n```\ngradle build\n```\n",
"date_published": "2022-08-02T11:41:32+08:00",
"url": "https://law.gmnz.xyz/2022/08/02/compiling-android-gradle.html",
"tags": ["Android","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/07/16/legacy-projects-part.html",
"title": "Legacy Projects Part 1",
"content_html": "<h3 id=\"development-date-last-quarter-2012\">Development Date: Last Quarter 2012</h3>\n<p>This was one of my app ideas back in 2012 where users can save important notes. I developed this using Eclipse IDE with Android SDK. And the screenshots are from Google’s Nexus 7, still their best device.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "### Development Date: Last Quarter 2012\n\nThis was one of my app ideas back in 2012 where users can save important notes. I developed this using Eclipse IDE with Android SDK. And the screenshots are from Google's Nexus 7, still their best device.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/817233aa56.png\" width=\"600\" height=\"960\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/3984009abd.png\" width=\"600\" height=\"960\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/7245427f40.png\" width=\"600\" height=\"960\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/339ad1715a.png\" width=\"600\" height=\"960\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/ea6c4945f8.png\" width=\"600\" height=\"960\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/4562996ef0.png\" width=\"600\" height=\"960\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/0c5ee6e4bb.png\" width=\"600\" height=\"960\" alt=\"\" />\n",
"date_published": "2022-07-16T10:08:29+08:00",
"url": "https://law.gmnz.xyz/2022/07/16/legacy-projects-part.html",
"tags": ["Android","Projects","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/07/14/080823.html",
"title": "Familiarizing Linux Again Part 2",
"content_html": "<!-- raw HTML omitted -->\n<p>So I was able to install Manjaro KDE last night because the new USB drive was delivered yesterday. I tried to install Pop!OS but somehow I was unsuccessful. I’m glad since I really enjoyed Manjaro.</p>\n<p>The first order of business is upgrading all the packages and then I installed Google Chrome.</p>\n<!-- raw HTML omitted -->\n<p>After which I installed Thunderbird and set up my email iCloud account from Apple. This morning I was able to install Android Studio.</p>\n<pre tabindex=\"0\"><code>sudo snap install android-studio --classic\n</code></pre><p>Right now I am trying to figure out how to sync my calendar which is on iCloud.</p>\n",
"content_text": "<img src=\"https://cdn.uploads.micro.blog/67704/2022/8db3e9a2c8.png\" width=\"600\" height=\"337\" alt=\"\" />\n\nSo I was able to install Manjaro KDE last night because the new USB drive was delivered yesterday. I tried to install Pop!OS but somehow I was unsuccessful. I'm glad since I really enjoyed Manjaro.\n\nThe first order of business is upgrading all the packages and then I installed Google Chrome.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/8ac1355f77.png\" width=\"600\" height=\"460\" alt=\"\" />\n\nAfter which I installed Thunderbird and set up my email iCloud account from Apple. This morning I was able to install Android Studio. \n\n```\nsudo snap install android-studio --classic\n```\n\nRight now I am trying to figure out how to sync my calendar which is on iCloud. \n",
"date_published": "2022-07-14T08:58:40+08:00",
"url": "https://law.gmnz.xyz/2022/07/14/080823.html",
"tags": ["Linux"]
},
{
"id": "http://lwgmnz.micro.blog/2022/07/12/diablo-immortal-me.html",
"title": "Diablo Immortal",
"content_html": "<p>My wife and I started playing Diablo Immortal yesterday and I’m so happy she’s enjoying it. I played my usual barbarian character while she played the wizard.</p>\n<p>I love how the graphics are so much improved and the storyline is solid same as the previous Diablo II PC game.</p>\n<p>I told my brother about the game because he was the one who introduced me to this game when we were just kids.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "My wife and I started playing Diablo Immortal yesterday and I'm so happy she's enjoying it. I played my usual barbarian character while she played the wizard. \n\nI love how the graphics are so much improved and the storyline is solid same as the previous Diablo II PC game.\n\nI told my brother about the game because he was the one who introduced me to this game when we were just kids.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/43f6a895fc.png\" width=\"600\" height=\"277\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/4a1464ab0d.png\" width=\"600\" height=\"277\" alt=\"\" />\n\n",
"date_published": "2022-07-12T09:18:28+08:00",
"url": "https://law.gmnz.xyz/2022/07/12/diablo-immortal-me.html",
"tags": ["Games"]
},
{
"id": "http://lwgmnz.micro.blog/2022/07/08/the-curse-of.html",
"title": "The Curse of Monkey Island Game Revisit",
"content_html": "<p>Since the upcoming return of the game franchise, Monkey Island was everywhere on the gaming news. I’m not into games anymore, but I followed Ron Gilbert lately and I was frustrated by the backlash he was receiving.</p>\n<!-- raw HTML omitted -->\n<p>I think the animation is cool! Can’t wait to play it.</p>\n<p>I played The Curse of Monkey Island when I was in grade school. And it was my favorite game. I am now playing it again. I bought it on Steam for around 75 Philippine pesos or less than $2.</p>\n<!-- raw HTML omitted -->\n<p>I’m going to try to finish this game in a month, so I can play the next sequel.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "Since the upcoming return of the game franchise, Monkey Island was everywhere on the gaming news. I'm not into games anymore, but I followed Ron Gilbert lately and I was frustrated by the backlash he was receiving.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/p3mxq44HhnU\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n\nI think the animation is cool! Can't wait to play it.\n\nI played The Curse of Monkey Island when I was in grade school. And it was my favorite game. I am now playing it again. I bought it on Steam for around 75 Philippine pesos or less than $2.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/3c61bf70a8.jpg\" width=\"600\" height=\"356\" alt=\"\" />\n\nI'm going to try to finish this game in a month, so I can play the next sequel. \n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/e418b9a5df.png\" width=\"600\" height=\"337\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/e629e25627.png\" width=\"600\" height=\"337\" alt=\"\" />\n",
"date_published": "2022-07-09T09:43:25+08:00",
"url": "https://law.gmnz.xyz/2022/07/08/the-curse-of.html",
"tags": ["Games"]
},
{
"id": "http://lwgmnz.micro.blog/2022/07/08/hacked-by-kids.html",
"title": "Smartmatic Hacked by Kids",
"content_html": "<p>I just found out that the <a href=\"https://www.manilatimes.net/2022/04/27/news/national/authorities-arrest-smartmatic-hackers/1841406\">XSOX group has been arrested</a>. It turns out, they’re just some kids who hacked Smartmatic. Kids.</p>\n<p>Back in January 2022, I tried to warn everybody about this leak but only a few seemed to believe me. I have to take down my post back to private because of the backlash. After that week, I removed myself from any news about it.</p>\n<p>Below is the excerpt.</p>\n<!-- raw HTML omitted -->\n<hr>\n<h2 id=\"smartmatic-hacked\">Smartmatic Hacked</h2>\n<p><em>Edited on March 2, 2022</em></p>\n<p>I changed the title to Smartmatic Hacked from Comelec Hacking Investigation. Because it seems that Smartmatic was the one that was hacked.</p>\n<p>I believe this has been going on for over a month. And none of the concerned agencies in the Philippines gave a damn. The group has been posting a lot of leaks already.</p>\n<p>It would be great if someone from the cybersecurity community can confirm this leak.</p>\n<p><em>Update as of January 25</em></p>\n<p>There seems to be a Facebook page called XSOX.Group that is currently leaking data and it seems that the Smartmatic servers are hacked</p>\n<!-- raw HTML omitted -->\n<p>Recently a report from the Manila Bulletin indicated that the Comelec was hacked recently. NBI 2 days ago came out with a report stating that there was no hacking involved.</p>\n<p>Curiously, I tried to investigate myself. I Googled several keywords until I spot my first clue. A cached link from RaidForums.</p>\n<!-- raw HTML omitted -->\n<p>I signed up on RaidForums but the link is not visible to new users or members with low reputations. So instead I clicked on Google’s cache instead, you can open this link. Below is the screenshot:</p>\n<!-- raw HTML omitted -->\n<p>Still, inside RaidForums, I searched for “Philippines” and found another data dump. This time it is from a popular grocery chain in the Philippines.</p>\n<!-- raw HTML omitted -->\n<p>You can sign up freely on RaidForums if you want, it is free. But I don’t believe you won’t find any links to download the database dump since it is only available to members with huge reputations.</p>\n<p>Conclusion</p>\n<p>I’m not sure if the database dumps in RaidForums are legit but it certainly looked like one. I wonder if both NBI Cybersecurity Team or DICT found this forum.</p>\n<p>It took me no less than 20 minutes for this investigation.</p>\n<hr>\n<p>This was their first Facebook post I guess.</p>\n<!-- raw HTML omitted -->\n<p>I forgot what Philippine agency I messaged on Messenger, but I was only “seen”. I did not get any reply. I believed I also sent a message to the group but deleted it right away because I was scared of the magnitude of this incident.</p>\n<p>It sucks I was only able to grab a couple of screenshots of the whole ordeal.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>I feel safe posting this now since my hunch was validated by their arrests. It was great to be somewhat part of this history, something I will tell my grandkids someday. Anyway, kudos to the Philippines cybercrime agencies working behind the scenes to get those scums arrested.</p>\n",
"content_text": "I just found out that the [XSOX group has been arrested](https://www.manilatimes.net/2022/04/27/news/national/authorities-arrest-smartmatic-hackers/1841406). It turns out, they're just some kids who hacked Smartmatic. Kids.\n\nBack in January 2022, I tried to warn everybody about this leak but only a few seemed to believe me. I have to take down my post back to private because of the backlash. After that week, I removed myself from any news about it.\n\nBelow is the excerpt.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/9b6d511785.png\" width=\"600\" height=\"349\" alt=\"\" />\n\n----\n\n## Smartmatic Hacked\n\n*Edited on March 2, 2022*\n\nI changed the title to Smartmatic Hacked from Comelec Hacking Investigation. Because it seems that Smartmatic was the one that was hacked.\n\nI believe this has been going on for over a month. And none of the concerned agencies in the Philippines gave a damn. The group has been posting a lot of leaks already.\n\nIt would be great if someone from the cybersecurity community can confirm this leak.\n\n*Update as of January 25*\n\nThere seems to be a Facebook page called XSOX.Group that is currently leaking data and it seems that the Smartmatic servers are hacked\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/19e22870a4.png\" width=\"600\" height=\"334\" alt=\"\" />\n\nRecently a report from the Manila Bulletin indicated that the Comelec was hacked recently. NBI 2 days ago came out with a report stating that there was no hacking involved.\n\nCuriously, I tried to investigate myself. I Googled several keywords until I spot my first clue. A cached link from RaidForums.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/8a7a3e05e0.png\" width=\"600\" height=\"463\" alt=\"\" />\n\nI signed up on RaidForums but the link is not visible to new users or members with low reputations. So instead I clicked on Google's cache instead, you can open this link. Below is the screenshot:\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/008c33ec0b.png\" width=\"600\" height=\"360\" alt=\"\" />\n\nStill, inside RaidForums, I searched for \"Philippines\" and found another data dump. This time it is from a popular grocery chain in the Philippines.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/f81b022846.png\" width=\"600\" height=\"365\" alt=\"\" />\n\nYou can sign up freely on RaidForums if you want, it is free. But I don't believe you won't find any links to download the database dump since it is only available to members with huge reputations.\n\nConclusion\n\nI'm not sure if the database dumps in RaidForums are legit but it certainly looked like one. I wonder if both NBI Cybersecurity Team or DICT found this forum.\n\nIt took me no less than 20 minutes for this investigation.\n\n----\n\nThis was their first Facebook post I guess.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/c9484d087d.png\" width=\"600\" height=\"1298\" alt=\"\" />\n\nI forgot what Philippine agency I messaged on Messenger, but I was only \"seen\". I did not get any reply. I believed I also sent a message to the group but deleted it right away because I was scared of the magnitude of this incident.\n\nIt sucks I was only able to grab a couple of screenshots of the whole ordeal.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/b823a4a4bf.png\" width=\"600\" height=\"335\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/c96ae686ee.png\" width=\"600\" height=\"288\" alt=\"\" />\n\nI feel safe posting this now since my hunch was validated by their arrests. It was great to be somewhat part of this history, something I will tell my grandkids someday. Anyway, kudos to the Philippines cybercrime agencies working behind the scenes to get those scums arrested.\n",
"date_published": "2022-07-08T09:05:16+08:00",
"url": "https://law.gmnz.xyz/2022/07/08/hacked-by-kids.html",
"tags": ["Bug Hunting"]
},
{
"id": "http://lwgmnz.micro.blog/2022/07/06/familiarizing-linux-again.html",
"title": "Familiarizing Linux Again",
"content_html": "<p>After more than 5 years on macOS, I’m excited that I get the chance to use Linux again.</p>\n<p>My brother gave me his old Windows laptop. Since I figured it has 1TB of disk space in it, I decided to create a Linux partition. So I <a href=\"https://news.ycombinator.com/item?id=31985622\">asked Silicon Valley’s best for help</a> on which distro to install. There were no clear winners though.</p>\n<p>I started out my career using Linux and Eclipse IDE for Android development. For some other jobs, I used Windows but never liked it for development purposes.</p>\n<p>After some quick research, I downloaded <a href=\"https://manjaro.org/download/\">Manjaro KDE</a>.</p>\n<!-- raw HTML omitted -->\n<h3 id=\"installation-failed\">Installation Failed</h3>\n<p>Yesterday, nothing worked. I tried burning the ISO images of Kubuntu, Pop!OS and Manjaro using Rufus and Etcher. I believe the USB drive has been the reason and it is defective. Towards the end of the day, the USB just stopped working, when I popped it in Windows will prompt the “need to reformat” warning. I may burn it too much.</p>\n",
"content_text": "After more than 5 years on macOS, I'm excited that I get the chance to use Linux again.\n\nMy brother gave me his old Windows laptop. Since I figured it has 1TB of disk space in it, I decided to create a Linux partition. So I [asked Silicon Valley's best for help](https://news.ycombinator.com/item?id=31985622) on which distro to install. There were no clear winners though.\n\nI started out my career using Linux and Eclipse IDE for Android development. For some other jobs, I used Windows but never liked it for development purposes.\n\nAfter some quick research, I downloaded [Manjaro KDE](https://manjaro.org/download/).\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/dd5d1d8b70.gif\" width=\"400\" height=\"300\" alt=\"\" />\n\n### Installation Failed\n\nYesterday, nothing worked. I tried burning the ISO images of Kubuntu, Pop!OS and Manjaro using Rufus and Etcher. I believe the USB drive has been the reason and it is defective. Towards the end of the day, the USB just stopped working, when I popped it in Windows will prompt the \"need to reformat\" warning. I may burn it too much.\n",
"date_published": "2022-07-07T08:30:32+08:00",
"url": "https://law.gmnz.xyz/2022/07/06/familiarizing-linux-again.html",
"tags": ["Linux"]
},
{
"id": "http://lwgmnz.micro.blog/2022/07/04/social-distortion-bad.html",
"title": "Social Distortion - Bad Luck Cover",
"content_html": "<p>This made me feel old. This is one of my favorite Social Distortion songs and the song was covered by Chuck Ragan. Chuck is one of my favorite band vocalists and with him singing on a boat while the background looks like something from the Netflix show Ozark. Currently, I am deep in Season 2 right now.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "This made me feel old. This is one of my favorite Social Distortion songs and the song was covered by Chuck Ragan. Chuck is one of my favorite band vocalists and with him singing on a boat while the background looks like something from the Netflix show Ozark. Currently, I am deep in Season 2 right now.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/_tAwFIBlfMM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n",
"date_published": "2022-07-04T21:15:57+08:00",
"url": "https://law.gmnz.xyz/2022/07/04/social-distortion-bad.html",
"tags": ["Music"]
},
{
"id": "http://lwgmnz.micro.blog/2022/06/29/git-checkout-to.html",
"title": "Git Checkout to Switch ",
"content_html": "<p>I have finally made the switch from using mainly</p>\n<pre tabindex=\"0\"><code>git checkout develop\n</code></pre><p>to</p>\n<pre tabindex=\"0\"><code>git switch develop\n</code></pre><p>And a couple of things new to Git v2.37.0 and that is</p>\n<pre tabindex=\"0\"><code>git config core.fsmonitor true\n\ngit config core.untrackedcache true\n</code></pre>",
"content_text": "I have finally made the switch from using mainly\n\n```\ngit checkout develop\n```\n\nto\n\n```\ngit switch develop\n```\n\nAnd a couple of things new to Git v2.37.0 and that is\n\n```\ngit config core.fsmonitor true\n\ngit config core.untrackedcache true\n```\n",
"date_published": "2022-06-29T20:59:41+08:00",
"url": "https://law.gmnz.xyz/2022/06/29/git-checkout-to.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/06/22/125907.html",
"title": "SwiftUI Journey Part 11: Passing parameters",
"content_html": "<p>Right now I am still figuring out how to pass a parameter from one <code>View</code> to another. I read I need to use <code>@Binding</code> but don’t know what it is. Or what differs it from <code>@State</code>. Currently, I am still studying <code>@Binding</code>.</p>\n<p>All the tutorials and Apple documentation look good but they did not include what to do with the compile error in <code>PreviewProvider</code>.</p>\n<!-- raw HTML omitted -->\n<p>Well, it seems <code>PreviewProvider</code> is separate and you can pass static variables different from the <code>View</code> class.</p>\n<pre tabindex=\"0\"><code>import SwiftUI\n\nstruct DetailsView: View {\n \n @Binding var url: String\n \n var body: some View {\n Text("Hello, World!")\n }\n}\n\nstruct DetailsView_Previews: PreviewProvider {\n \n @State static var urlPreview: String = Urls.main.advanceSearch\n \n static var previews: some View {\n DetailsView(url: $urlPreview)\n }\n}\n</code></pre><p>Compiled successfully.</p>\n",
"content_text": "Right now I am still figuring out how to pass a parameter from one `View` to another. I read I need to use `@Binding` but don't know what it is. Or what differs it from `@State`. Currently, I am still studying `@Binding`.\n\nAll the tutorials and Apple documentation look good but they did not include what to do with the compile error in `PreviewProvider`.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/72d868bb19.png\" width=\"600\" height=\"390\" alt=\"\" />\n\nWell, it seems `PreviewProvider` is separate and you can pass static variables different from the `View` class.\n\n```\nimport SwiftUI\n\nstruct DetailsView: View {\n \n @Binding var url: String\n \n var body: some View {\n Text(\"Hello, World!\")\n }\n}\n\nstruct DetailsView_Previews: PreviewProvider {\n \n @State static var urlPreview: String = Urls.main.advanceSearch\n \n static var previews: some View {\n DetailsView(url: $urlPreview)\n }\n}\n```\n\nCompiled successfully.\n",
"date_published": "2022-06-23T20:20:14+08:00",
"url": "https://law.gmnz.xyz/2022/06/22/125907.html",
"tags": ["Mobile Development","Swift","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/06/20/swiftui-journey-part.html",
"title": "SwiftUI Journey Part 10: Settings",
"content_html": "<p>I can’t believe how easy it is to implement a settings page in SwiftUI. For the UI, I only need 37 lines.</p>\n<!-- raw HTML omitted -->\n<p>Of course, this implementation won’t be accepted by the programming gods. Why you may ask. This is implemented in a static way. What if you want to turn every <code>Text()</code> background to <code>Color.yellow</code>. Then you will have to add the modifier background to every <code>Text()</code> view.</p>\n<p>Let’s make it dynamic.</p>\n<!-- raw HTML omitted -->\n<p>Ah much better.</p>\n",
"content_text": "I can't believe how easy it is to implement a settings page in SwiftUI. For the UI, I only need 37 lines.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/91a820abf6.png\" width=\"600\" height=\"415\" alt=\"\" />\n\nOf course, this implementation won't be accepted by the programming gods. Why you may ask. This is implemented in a static way. What if you want to turn every `Text()` background to `Color.yellow`. Then you will have to add the modifier background to every `Text()` view.\n\nLet's make it dynamic.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/cc0bb5786b.png\" width=\"600\" height=\"474\" alt=\"\" />\n\nAh much better.\n",
"date_published": "2022-06-20T19:45:36+08:00",
"url": "https://law.gmnz.xyz/2022/06/20/swiftui-journey-part.html",
"tags": ["iOS","Mobile Development","Swift","Programming"]
},
{
"id": "http://lwgmnz.micro.blog/2022/06/18/wwdc.html",
"title": "WWDC 2022",
"content_html": "<p>I haven’t yet written about this year’s WWDC 2022. In fact, I am still going through all the developer videos while also exploring Xcode’s Multiplatform introduced this year. I will try to write down some of my initial thoughts and frustrations from this year’s Apple WWDC event.</p>\n<h4 id=\"webview-in-swiftui-where-art-thou\">WebView in SwiftUI, where art thou?</h4>\n<p>I tried to ask this question in Slack during one of Apple’s Digital Labs, I guess it was with the SwiftUI team. But it was filtered out and deemed unworthy of the answer. This is why it is frustrating, especially if I’m using Multiplatform. Around 30-40% of our app needs WebView. Don’t ask, this is the boss' request.</p>\n<p>To implement WebView in our app, I have to create two separate files, each subclassing <code>UIViewRepresentable</code> for iOS and <code>NSViewRepresentable</code> for macOS. Also, the <code>coordinator</code> implementation has the exact implementation for both classes, thus another wasted time to refactor in the next iteration.</p>\n<p>If Apple could just give us a freaking <code>WebView()</code>.</p>\n<h4 id=\"wwdc-digital-lounges\">WWDC Digital Lounges</h4>\n<p>I really enjoyed WWDC’s Digital Lounges. I get to ask and communicate directly with Apple’s developers on different topics. But I am just not sure if Slack was the appropriate medium to do this. I mean there’s the Developer app, maybe Apple could add a chat feature inside it?</p>\n<p>Another thing, there is no way to backread or review the topics and chat history in Slack. This is why people have to do it themselves like this <a href=\"https://github.com/roblack/WWDCLounges\">GitHub project</a>.</p>\n<h4 id=\"xcode-14-their-best-release-yet\">Xcode 14, their best release yet?</h4>\n<p>Xcode is 30% more lightweight in terms of download size than Xcode 13. So far everything is stable and fast unless you have to start playing around with SwiftUI previews. Then all sorts of errors and bugs appear. This is given since this is just a developer release.</p>\n<h4 id=\"single-size-app-icon-feature\">Single Size App Icon Feature</h4>\n<p>This is my favorite so far. I only needed one 1024x1024px icon instead of uploading different px icon sizes. Ain’t got no time for that, life’s short.</p>\n<p>It would be great to also add this feature to macOS in the future.</p>\n<h4 id=\"books\">Books</h4>\n<p>I am a huge Books app user and I love the new update and customization. But there was a couple of annoyance, first I can’t seem to go back to the previous library. And they removed the good old page-flipping animation.</p>\n<h4 id=\"conditions-in-tabitem\">Conditions in TabItem</h4>\n<p>I just found out you can wrap a condition on a <code>TabItem</code>. Imagine doing this in <code>UIKit</code>.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">TabView {\n <span style=\"color:#66d9ef\">if</span> UserDefaults.standard.bool(forKey: Keys.isEmployer) {\n WorkersView()\n .tabItem {\n Label(<span style=\"color:#e6db74\">"Workers"</span>, systemImage: <span style=\"color:#e6db74\">"person.3"</span>)\n }\n }\n ProfileView()\n .tabItem {\n Label(<span style=\"color:#e6db74\">"Profile"</span>, systemImage: <span style=\"color:#e6db74\">"person"</span>)\n }\n}\n</code></pre></div><p><img src=\"https://media.giphy.com/media/dgLfHZCh77utSjgpG9/giphy.gif\" alt=\"This is the way\"></p>\n<h4 id=\"xcode-cloud-is-cool-but\">Xcode Cloud is cool but</h4>\n<!-- raw HTML omitted -->\n<p>I’m not sure if I want to add some Xcode tags in our git commit. It might make it unreadable.</p>\n<h4 id=\"code-blocks-in-notes\">Code blocks in Notes</h4>\n<p>I just realized that the Notes app doesn’t support code blocks. Look at this ugliness.</p>\n<!-- raw HTML omitted -->\n<h4 id=\"metrickit-looks-promising\">MetricKit Looks Promising</h4>\n<p>MetricKit looks promising and cannot wait to implement it. Not sure if this will replace Bugsnag as a whole.</p>\n<h4 id=\"interesting-insight\">Interesting Insight</h4>\n<p><a href=\"https://news.ycombinator.com/item?id=31717821\">Ask HN: Why does WWDC get 10x more views than Google I/O?</a></p>\n<hr>\n<p>So far, these are the only things that captured me in this year’s WWDC event. I am on vacation and the internet is spotty most of the time. Plus, there’s the Google I/O backlog I have to catch up to. In other words, I appreciate all the work that Apple and Google put out this year.</p>\n",
"content_text": "I haven't yet written about this year's WWDC 2022. In fact, I am still going through all the developer videos while also exploring Xcode's Multiplatform introduced this year. I will try to write down some of my initial thoughts and frustrations from this year's Apple WWDC event.\n\n#### WebView in SwiftUI, where art thou?\n\nI tried to ask this question in Slack during one of Apple's Digital Labs, I guess it was with the SwiftUI team. But it was filtered out and deemed unworthy of the answer. This is why it is frustrating, especially if I'm using Multiplatform. Around 30-40% of our app needs WebView. Don't ask, this is the boss' request.\n\nTo implement WebView in our app, I have to create two separate files, each subclassing `UIViewRepresentable` for iOS and `NSViewRepresentable` for macOS. Also, the `coordinator` implementation has the exact implementation for both classes, thus another wasted time to refactor in the next iteration.\n\nIf Apple could just give us a freaking `WebView()`.\n\n#### WWDC Digital Lounges\n\nI really enjoyed WWDC's Digital Lounges. I get to ask and communicate directly with Apple's developers on different topics. But I am just not sure if Slack was the appropriate medium to do this. I mean there's the Developer app, maybe Apple could add a chat feature inside it?\n\nAnother thing, there is no way to backread or review the topics and chat history in Slack. This is why people have to do it themselves like this [GitHub project](https://github.com/roblack/WWDCLounges).\n\n#### Xcode 14, their best release yet?\n\nXcode is 30% more lightweight in terms of download size than Xcode 13. So far everything is stable and fast unless you have to start playing around with SwiftUI previews. Then all sorts of errors and bugs appear. This is given since this is just a developer release.\n\n#### Single Size App Icon Feature\n\nThis is my favorite so far. I only needed one 1024x1024px icon instead of uploading different px icon sizes. Ain't got no time for that, life's short.\n\nIt would be great to also add this feature to macOS in the future.\n\n#### Books\n\nI am a huge Books app user and I love the new update and customization. But there was a couple of annoyance, first I can't seem to go back to the previous library. And they removed the good old page-flipping animation.\n\n#### Conditions in TabItem\n\nI just found out you can wrap a condition on a `TabItem`. Imagine doing this in `UIKit`.\n\n```swift\nTabView {\n if UserDefaults.standard.bool(forKey: Keys.isEmployer) {\n WorkersView()\n .tabItem {\n Label(\"Workers\", systemImage: \"person.3\")\n }\n }\n ProfileView()\n .tabItem {\n Label(\"Profile\", systemImage: \"person\")\n }\n}\n```\n\n![This is the way](https://media.giphy.com/media/dgLfHZCh77utSjgpG9/giphy.gif)\n\n#### Xcode Cloud is cool but\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/1cfcf7134d.png\" width=\"600\" height=\"537\" alt=\"\" />\n\nI'm not sure if I want to add some Xcode tags in our git commit. It might make it unreadable.\n\n#### Code blocks in Notes\n\nI just realized that the Notes app doesn't support code blocks. Look at this ugliness.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/9f3df393b6.png\" width=\"600\" height=\"398\" alt=\"\" />\n\n#### MetricKit Looks Promising\n\nMetricKit looks promising and cannot wait to implement it. Not sure if this will replace Bugsnag as a whole. \n\n#### Interesting Insight\n\n[Ask HN: Why does WWDC get 10x more views than Google I/O?](https://news.ycombinator.com/item?id=31717821)\n\n----\n So far, these are the only things that captured me in this year's WWDC event. I am on vacation and the internet is spotty most of the time. Plus, there's the Google I/O backlog I have to catch up to. In other words, I appreciate all the work that Apple and Google put out this year.\n",
"date_published": "2022-06-18T11:14:20+08:00",
"url": "https://law.gmnz.xyz/2022/06/18/wwdc.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/06/15/urlrequest-not-working.html",
"title": "URLRequest not working right away in macOS (AppKit)?",
"content_html": "<p>URLRequest not working right away in macOS (AppKit)?</p>\n<!-- raw HTML omitted -->\n<p>Do not forget to enable network capabilities in Xcode’s App Sandbox menu.</p>\n",
"content_text": "URLRequest not working right away in macOS (AppKit)?\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/91e834a6dd.png\" width=\"600\" height=\"249\" alt=\"\" />\n\nDo not forget to enable network capabilities in Xcode's App Sandbox menu.\n",
"date_published": "2022-06-15T19:21:53+08:00",
"url": "https://law.gmnz.xyz/2022/06/15/urlrequest-not-working.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/06/15/xcode-multiplatform-migration.html",
"title": "Xcode 14 Multiplatform Migration First Look",
"content_html": "<p>The next version for OnlineJobs will be migrated to Xcode Multiplatform. So far it is looking great.</p>\n<h5 id=\"ios\">iOS</h5>\n<!-- raw HTML omitted -->\n<h5 id=\"macos\">macOS</h5>\n<!-- raw HTML omitted -->\n<p>Both are compiled with the same SwiftUI codebase.</p>\n",
"content_text": "The next version for OnlineJobs will be migrated to Xcode Multiplatform. So far it is looking great.\n\n##### iOS\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/34473fc7c3.png\" width=\"600\" height=\"1174\" alt=\"\" />\n\n##### macOS\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/fae69b1c21.png\" width=\"600\" height=\"379\" alt=\"\" />\n\nBoth are compiled with the same SwiftUI codebase.\n",
"date_published": "2022-06-15T19:14:01+08:00",
"url": "https://law.gmnz.xyz/2022/06/15/xcode-multiplatform-migration.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/06/12/postcovid-vacation-part.html",
"title": "Post-Covid Vacation Part 3",
"content_html": "<p>A lot has happened this week.</p>\n<h4 id=\"wedding-anniversary\">Wedding Anniversary</h4>\n<p>Last Thursday was our wedding anniversary. We decided to bring Lucas to Ayala for the first time in more than 2 years.</p>\n<h4 id=\"playhouse\">Playhouse</h4>\n<p>It was Luke’s first time playing in Ayala’s Playhouse. Or his first time since the pandemic to play in a kid’s public playhouse. We weren’t sure what to expect when we arrived. First few minutes, Luke’s uneasiness was apparent.</p>\n<p>The playhouse wasn’t huge compared to Kidzona (the playhouse near our place). We were also expecting a slow day, but there were around 4-5 kids playing, and Luke is not yet accustomed to playing around with kids.</p>\n<p>A few minutes in, Luke was still uncomfortable and doesn’t like to be put down on the floor. But we were able to find his spot.</p>\n<!-- raw HTML omitted -->\n<p>Luke also explored the other sections and toys in the playhouse. At one point there were kids playing near him, but he didn’t fuss or anything. He just continued to play. We were so happy and proud of our little kiddo! Luke also got to play inside the playhouse with Nice and Juretz!</p>\n<p>We didn’t last the whole hour since Luke got bored. As I was saying, the playhouse wasn’t that big and several of the toys have been damaged already.</p>\n<h4 id=\"toy-shopping\">Toy Shopping</h4>\n<p>Part of our agenda on that day was to buy toys for Luke. We went around Rustan’s toys section and Toys-R-Us. I was half-joking the other day that I would buy the first toy that Luke would show interest in or point to. Seconds into the Rustan’s store and Luke found the first toy he liked! That was quick lol! And more importantly, the toy was within our budget.</p>\n<p>But my wife’s sister and his husband were the ones who paid for the toy. Thank you so much!</p>\n<p>We went to Toys-R-Us after to look for his second toy. In the same scenario, it did not take too long for Luke to find another toy. He likes toys that are either alphabet, words, or numbers. And both of the toys he chose are in tablet form.</p>\n<h4 id=\"lunch-and-poop\">Lunch and poop</h4>\n<p>While we were waiting for our lunch orders, Luke pooped publicly. Luckily we were able to bring diapers. It was funny and at the same time, we were glad that he pooped since his mood will mostly depend on his comfort.</p>\n<p>Lunch was great! It has an open sitting concept and more space for Lucas.</p>\n<hr>\n<h4 id=\"wwdc-2022\">WWDC 2022</h4>\n<p>My work week was consumed mostly by Apple’s WWDC 2022 event. This is for another post, but I hope this time I will be able to catch up with most of the concepts and labs, and new stuff. At last year’s WWDC I was only able to watch the Keynote and a couple of developer videos.</p>\n<hr>\n<h4 id=\"buffet-with-family\">Buffet with Family</h4>\n<p>Yesterday, we celebrated Father’s day in advance. We went back to Ferias at Radisson Hotel for their lunch buffet. For the first time in this vacation, Lucas allowed himself to sit on my lap. All he wants is to watch YouTube on my phone and play with his new toy which we bought the day before.</p>\n<p>For the first time, I was able to eat in peace publicly with Luke. Also, he ran a lot in the lobby, in the restaurant, and in the hotel garden.</p>\n<p>This is just halfway into our month-long vacation, I and my wife are very impressed with how Luke grew up and adjusted.</p>\n<p>Here’s Lucas running away from the restaurant. 😂</p>\n<!-- raw HTML omitted -->\n",
"content_text": "A lot has happened this week.\n\n#### Wedding Anniversary\n\nLast Thursday was our wedding anniversary. We decided to bring Lucas to Ayala for the first time in more than 2 years.\n\n#### Playhouse\n\nIt was Luke's first time playing in Ayala's Playhouse. Or his first time since the pandemic to play in a kid's public playhouse. We weren't sure what to expect when we arrived. First few minutes, Luke's uneasiness was apparent. \n\nThe playhouse wasn't huge compared to Kidzona (the playhouse near our place). We were also expecting a slow day, but there were around 4-5 kids playing, and Luke is not yet accustomed to playing around with kids.\n\nA few minutes in, Luke was still uncomfortable and doesn't like to be put down on the floor. But we were able to find his spot.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/d8d3d19578.jpg\" width=\"600\" height=\"800\" alt=\"\" />\n\nLuke also explored the other sections and toys in the playhouse. At one point there were kids playing near him, but he didn't fuss or anything. He just continued to play. We were so happy and proud of our little kiddo! Luke also got to play inside the playhouse with Nice and Juretz!\n\nWe didn't last the whole hour since Luke got bored. As I was saying, the playhouse wasn't that big and several of the toys have been damaged already.\n\n#### Toy Shopping\n\nPart of our agenda on that day was to buy toys for Luke. We went around Rustan's toys section and Toys-R-Us. I was half-joking the other day that I would buy the first toy that Luke would show interest in or point to. Seconds into the Rustan's store and Luke found the first toy he liked! That was quick lol! And more importantly, the toy was within our budget. \n\nBut my wife's sister and his husband were the ones who paid for the toy. Thank you so much! \n\nWe went to Toys-R-Us after to look for his second toy. In the same scenario, it did not take too long for Luke to find another toy. He likes toys that are either alphabet, words, or numbers. And both of the toys he chose are in tablet form. \n\n#### Lunch and poop\n\nWhile we were waiting for our lunch orders, Luke pooped publicly. Luckily we were able to bring diapers. It was funny and at the same time, we were glad that he pooped since his mood will mostly depend on his comfort.\n\nLunch was great! It has an open sitting concept and more space for Lucas. \n\n---\n\n#### WWDC 2022\n\nMy work week was consumed mostly by Apple's WWDC 2022 event. This is for another post, but I hope this time I will be able to catch up with most of the concepts and labs, and new stuff. At last year's WWDC I was only able to watch the Keynote and a couple of developer videos.\n\n---\n\n#### Buffet with Family\n\nYesterday, we celebrated Father's day in advance. We went back to Ferias at Radisson Hotel for their lunch buffet. For the first time in this vacation, Lucas allowed himself to sit on my lap. All he wants is to watch YouTube on my phone and play with his new toy which we bought the day before.\n\nFor the first time, I was able to eat in peace publicly with Luke. Also, he ran a lot in the lobby, in the restaurant, and in the hotel garden.\n\nThis is just halfway into our month-long vacation, I and my wife are very impressed with how Luke grew up and adjusted.\n\nHere's Lucas running away from the restaurant. 😂\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/d11b5fdb6f.png\" width=\"600\" height=\"943\" alt=\"\" />\n\n\n\n",
"date_published": "2022-06-12T21:29:05+08:00",
"url": "https://law.gmnz.xyz/2022/06/12/postcovid-vacation-part.html",
"tags": ["Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2022/06/06/this-is-the.html",
"title": "Pre iOS 16 - Last home screenshot",
"content_html": "<p>This is the last phone screenshot before Apple introduces iOS 16 later today. Of course, I will install it right away as soon as Apple releases the developer betas.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "This is the last phone screenshot before Apple introduces iOS 16 later today. Of course, I will install it right away as soon as Apple releases the developer betas.\n\n\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/10a21d6bc2.png\" width=\"277\" height=\"600\" alt=\"\" />\n",
"date_published": "2022-06-06T13:00:00+08:00",
"url": "https://law.gmnz.xyz/2022/06/06/this-is-the.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/06/05/postcovid-vacation-part.html",
"title": "Post-Covid Vacation Part 2",
"content_html": "<p>Lucas has been settling well into his new morning routine and every car ride activity for the past few days. Last Friday, it was his first evening car ride. And he enjoyed the traffic lights countdown.</p>\n<h4 id=\"weekend-at-radisson\">Weekend at Radisson</h4>\n<p>We checked in at Radisson Hotel for the weekend for my wife’s sister’s wedding. Luke’s first night went well without any tantrums. As a matter of fact, the whole weekend Luke did not fuss or cried.</p>\n<p>Check out the view.</p>\n<!-- raw HTML omitted -->\n<p>He was just chilling in the bed while both of our family and relatives took turns entertaining him. According to my wife, this might be the best hotel because everything is just convenient and child friendly. I agree and we both look forward to more staycation trips in the city. Of course, the cost is a little way out of our budget. But I try to look past it, this is our first vacation and we both deserve this since parenting during Covid times is just a nightmare.</p>\n<p>Lucas went to the wedding reception, and he showed signs of annoyance at first because the sound system was too noisy. Overall, I could say he enjoyed the event and this is good for his exposure. He met several of my wife’s relatives too, we couldn’t thank them enough for the warm reception for Luke. Auntie Bebe sang twinkle little star to Luke and he loves it. The highlight of the night was when Luke pulled the mask on one of our aunties, I nervously apologized right away. But it was no biggy, I know it must have hurt her. Congratulations to Nice and Juretz!</p>\n<p>Back in the hotel room, Luke showered with us which he has never done before. Maybe it was the warm water from the shower, or it was just him growing up right in front of our eyes.</p>\n<p>After, we were so tired that the in-room food was mostly left untouched, humba rice and a couple of warm coffee drinks. My back and shoulder hurt so much from carrying Luke the whole night. At the end of the day I don’t mind, what matters is the exposure and events our son gets to experience and learn.</p>\n<h4 id=\"luke-went-swimming-in-the-pool\">Luke went swimming in the pool</h4>\n<p>This is Luke’s second swimming pool experience. The first one, was when he was still a baby. After a while, Luke wasn’t afraid anymore to dip in the swimming pool. He played with his hands like he does in his bathtub. So proud of this young dude! We have to cut short his pool experience since it rained after. Looking forward to more staycations with pools in the near future!</p>\n<p>Of course, it is time to go home and check out that noon. Week one concludes.</p>\n",
"content_text": "Lucas has been settling well into his new morning routine and every car ride activity for the past few days. Last Friday, it was his first evening car ride. And he enjoyed the traffic lights countdown.\n\n#### Weekend at Radisson\n\nWe checked in at Radisson Hotel for the weekend for my wife's sister's wedding. Luke's first night went well without any tantrums. As a matter of fact, the whole weekend Luke did not fuss or cried. \n\nCheck out the view.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/c6df17ec9a.jpg\" width=\"600\" height=\"800\" alt=\"\" />\n\nHe was just chilling in the bed while both of our family and relatives took turns entertaining him. According to my wife, this might be the best hotel because everything is just convenient and child friendly. I agree and we both look forward to more staycation trips in the city. Of course, the cost is a little way out of our budget. But I try to look past it, this is our first vacation and we both deserve this since parenting during Covid times is just a nightmare.\n\nLucas went to the wedding reception, and he showed signs of annoyance at first because the sound system was too noisy. Overall, I could say he enjoyed the event and this is good for his exposure. He met several of my wife's relatives too, we couldn't thank them enough for the warm reception for Luke. Auntie Bebe sang twinkle little star to Luke and he loves it. The highlight of the night was when Luke pulled the mask on one of our aunties, I nervously apologized right away. But it was no biggy, I know it must have hurt her. Congratulations to Nice and Juretz!\n\nBack in the hotel room, Luke showered with us which he has never done before. Maybe it was the warm water from the shower, or it was just him growing up right in front of our eyes.\n\nAfter, we were so tired that the in-room food was mostly left untouched, humba rice and a couple of warm coffee drinks. My back and shoulder hurt so much from carrying Luke the whole night. At the end of the day I don't mind, what matters is the exposure and events our son gets to experience and learn.\n\n#### Luke went swimming in the pool\n\nThis is Luke's second swimming pool experience. The first one, was when he was still a baby. After a while, Luke wasn't afraid anymore to dip in the swimming pool. He played with his hands like he does in his bathtub. So proud of this young dude! We have to cut short his pool experience since it rained after. Looking forward to more staycations with pools in the near future!\n\nOf course, it is time to go home and check out that noon. Week one concludes.\n\n\n\n\n",
"date_published": "2022-06-06T12:49:37+08:00",
"url": "https://law.gmnz.xyz/2022/06/05/postcovid-vacation-part.html",
"tags": ["Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2022/06/02/androids-own-http.html",
"title": "Android's own HTTP library",
"content_html": "<p>I always wonder why Android doesn’t have its own HTTP library. They stopped supporting Volley and <code>HttpURLConnection</code>. For example on iOS, I don’t have to use third-party libraries for HTTP requests, because <code>URLSession</code> class is enough.</p>\n<p>On Android, new developers have to choose several HTTP libraries, plus they will encounter different arguments on which one is better and more robust. There is Ktor, Retrofit, Fuel, etc.</p>\n",
"content_text": "I always wonder why Android doesn't have its own HTTP library. They stopped supporting Volley and `HttpURLConnection`. For example on iOS, I don't have to use third-party libraries for HTTP requests, because `URLSession` class is enough.\r\n\r\nOn Android, new developers have to choose several HTTP libraries, plus they will encounter different arguments on which one is better and more robust. There is Ktor, Retrofit, Fuel, etc.\r\n\r\n\r\n",
"date_published": "2022-06-02T17:56:54+08:00",
"url": "https://law.gmnz.xyz/2022/06/02/androids-own-http.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/29/postcovid-staycation-part.html",
"title": "Post-Covid Vacation Part 1",
"content_html": "<h4 id=\"day-1\">Day 1</h4>\n<p>Today is the first time my family has gone out together for about 2 years before Covid hits, lockdown, etc.</p>\n<p>We lived about 2-3 hours (with traffic) from the city. And as one of the effects of the Covid lockdown, our son hates the outdoors. We spent the past several months preparing him for this big day. Preparations like driving around town without freaking out. The last time we did this he cried, puked, and was seasick. So surprisingly, it only took him a day to adjust to the “car ride”. My wife and I can’t really explain the joy and relief of what was happening, on how quickly our son has adjusted to the situation.</p>\n<p>We will be staying in the city for exactly one month. What led to this staycation was so we could attend the wedding of my wife’s sister. And hopefully, get several backlog errands done.</p>\n<p>We also consulted a doctor on what medications for our son to take for the hour-long trip. 30 minutes before the trip, we had him drink vitamins with a drowsiness effect. It kinda worked, but not really at first. First few minutes, Lucas was in his hyperactive self. Then, first few minutes of the trip, he fell asleep. He woke up right after we got out of the car. He was cool, calm, and collected. Another surprise for us. This was his cue to go apeshit, cry, freak out and it never came.</p>\n<p>By the time we went inside the room, he was just very curious the whole time.</p>\n<h4 id=\"take-out-orders\">Take-out Orders</h4>\n<p>From where we reside, the food delivery options are very limited because of the distance from the city and traffic. So during this vacation, we made a point to order foods from our favorites before lockdown.</p>\n<h4 id=\"visits\">Visits</h4>\n<p>We got a visit from my wife’s family too! Since they are just staying near us, about 5 minutes from where we are currently staying. After 2 years I finally got to eat Shakey’s Pizza, thanks to Vida’s fam!</p>\n<p>My brother also visited us from the capital. He decides to use his 2 weeks vacation leave here in Cebu.</p>\n<h4 id=\"vidas-first-mac\">Vida’s First Mac</h4>\n<p>My wife just bought her first Mac machine after years of working on Windows. And after years of hesitating and pushing back.</p>\n<!-- raw HTML omitted -->\n<p>Her first of many look-at-where-I-am-working-now posts.</p>\n<h4 id=\"last-but-not-least\">Last but not least</h4>\n<p>Our son really likes it here too. He loves to run from end to end of the living room. He adjusted really well throughout our stay. I can’t describe how happy we are for our son.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "#### Day 1\n\nToday is the first time my family has gone out together for about 2 years before Covid hits, lockdown, etc.\n\nWe lived about 2-3 hours (with traffic) from the city. And as one of the effects of the Covid lockdown, our son hates the outdoors. We spent the past several months preparing him for this big day. Preparations like driving around town without freaking out. The last time we did this he cried, puked, and was seasick. So surprisingly, it only took him a day to adjust to the \"car ride\". My wife and I can't really explain the joy and relief of what was happening, on how quickly our son has adjusted to the situation.\n\nWe will be staying in the city for exactly one month. What led to this staycation was so we could attend the wedding of my wife's sister. And hopefully, get several backlog errands done.\n\nWe also consulted a doctor on what medications for our son to take for the hour-long trip. 30 minutes before the trip, we had him drink vitamins with a drowsiness effect. It kinda worked, but not really at first. First few minutes, Lucas was in his hyperactive self. Then, first few minutes of the trip, he fell asleep. He woke up right after we got out of the car. He was cool, calm, and collected. Another surprise for us. This was his cue to go apeshit, cry, freak out and it never came.\n\nBy the time we went inside the room, he was just very curious the whole time.\n\n#### Take-out Orders\n \nFrom where we reside, the food delivery options are very limited because of the distance from the city and traffic. So during this vacation, we made a point to order foods from our favorites before lockdown.\n\n#### Visits\n\nWe got a visit from my wife's family too! Since they are just staying near us, about 5 minutes from where we are currently staying. After 2 years I finally got to eat Shakey's Pizza, thanks to Vida's fam!\n\nMy brother also visited us from the capital. He decides to use his 2 weeks vacation leave here in Cebu. \n\n#### Vida's First Mac\n\nMy wife just bought her first Mac machine after years of working on Windows. And after years of hesitating and pushing back.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/3cd8a91a2a.jpg\" width=\"600\" height=\"800\" alt=\"\" />\n\nHer first of many look-at-where-I-am-working-now posts.\n\n#### Last but not least\n\nOur son really likes it here too. He loves to run from end to end of the living room. He adjusted really well throughout our stay. I can't describe how happy we are for our son.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/8829d6f75e.jpg\" width=\"600\" height=\"800\" alt=\"\" />\n",
"date_published": "2022-05-31T22:28:18+08:00",
"url": "https://law.gmnz.xyz/2022/05/29/postcovid-staycation-part.html",
"tags": ["Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2022/05/30/we-are-in.html",
"title": "Hiring for Android SDK",
"content_html": "<p>We are in search of “a Android SDK”. Why do I get the feeling that this email was generated by a bad case of AI/ML.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "We are in search of \"a Android SDK\". Why do I get the feeling that this email was generated by a bad case of AI/ML.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/5817d3aaf8.png\" width=\"600\" height=\"178\" alt=\"\" />\n",
"date_published": "2022-05-30T20:34:20+08:00",
"url": "https://law.gmnz.xyz/2022/05/30/we-are-in.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/24/late-git-tagging.html",
"title": "Late Git Tagging",
"content_html": "<p>Git tagging is useful for marking the point of the release cycle of a version. But sometimes, we forget to even implement <code>git tag</code>. Here are some quick commands on how to do it for those that are still new to git.</p>\n<h4 id=\"detach-from-head\">Detach from head</h4>\n<pre tabindex=\"0\"><code>git checkout 7b027ef\n</code></pre><p><code>7b027ef</code> could be any hash, this is just from a real-world example. Then a long-ass message will appear. This seems scary to read but no need to panic. Just continue with git tagging.</p>\n<pre tabindex=\"0\"><code>Note: switching to '7b027ef'.\n\nYou are in 'detached HEAD' state. You can look around, make experimental\nchanges and commit them, and you can discard any commits you make in this\nstate without impacting any branches by switching back to a branch.\n\nIf you want to create a new branch to retain commits you create, you may\ndo so (now or later) by using -c with the switch command. Example:\n\n git switch -c <new-branch-name>\n\nOr undo this operation with:\n\n git switch -\n\nTurn off this advice by setting config variable advice.detachedHead to false\n</code></pre><p>Time to git tag and push.</p>\n<pre tabindex=\"0\"><code>git tag -a ios-v5.3.4 -m "5.3.4 (113)"\ngit push origin ios-v5.3.4\n</code></pre><p>Here comes the tricky part, how to get back to the previous branch before the head was detached. Simple just run checkout again.</p>\n<pre tabindex=\"0\"><code>git checkout develop\n</code></pre>",
"content_text": "Git tagging is useful for marking the point of the release cycle of a version. But sometimes, we forget to even implement `git tag`. Here are some quick commands on how to do it for those that are still new to git.\n\n#### Detach from head\n```\ngit checkout 7b027ef\n```\n`7b027ef` could be any hash, this is just from a real-world example. Then a long-ass message will appear. This seems scary to read but no need to panic. Just continue with git tagging.\n\n```\nNote: switching to '7b027ef'.\n\nYou are in 'detached HEAD' state. You can look around, make experimental\nchanges and commit them, and you can discard any commits you make in this\nstate without impacting any branches by switching back to a branch.\n\nIf you want to create a new branch to retain commits you create, you may\ndo so (now or later) by using -c with the switch command. Example:\n\n git switch -c <new-branch-name>\n\nOr undo this operation with:\n\n git switch -\n\nTurn off this advice by setting config variable advice.detachedHead to false\n```\n\nTime to git tag and push.\n```\ngit tag -a ios-v5.3.4 -m \"5.3.4 (113)\"\ngit push origin ios-v5.3.4\n```\n\nHere comes the tricky part, how to get back to the previous branch before the head was detached. Simple just run checkout again.\n\n```\ngit checkout develop\n```\n",
"date_published": "2022-05-24T21:33:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/24/late-git-tagging.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/22/swiftui-journey-part.html",
"title": "SwiftUI Journey Part 9: WKNavigationDelegate",
"content_html": "<p>Next on the task is how to extend the class <code>WKNavigationDelegate</code> so I can call the <a href=\"https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript\">evaluateJavascript()</a> function.</p>\n<p>We used an ' extension' in our existing iOS app using <code>UIKit</code>. But I found out, that this won’t work when using SwiftUI.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">\n<span style=\"color:#66d9ef\">extension</span> <span style=\"color:#a6e22e\">MyJobsViewController</span>: WKNavigationDelegate {\n}\n\n</code></pre></div><p>After further Googling, research and internet digging I learned that I need to use <a href=\"https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable/makecoordinator()-32trb\">makeCoordinator()</a> function.</p>\n<blockquote>\n<p>Creates the custom instance that you use to communicate changes from your view controller to other parts of your SwiftUI interface.</p>\n</blockquote>\n<p>First, I wrote an inner class called <code>Coordinator</code>.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">\n<span style=\"color:#66d9ef\">class</span> <span style=\"color:#a6e22e\">Coordinator</span>: NSObject, WKNavigationDelegate {\n <span style=\"color:#66d9ef\">let</span> parent: OnlineJobsWebView\n \n <span style=\"color:#66d9ef\">init</span>(<span style=\"color:#66d9ef\">_</span> parent: OnlineJobsWebView) {\n <span style=\"color:#66d9ef\">self</span>.parent = parent\n }\n \n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">webView</span>(<span style=\"color:#66d9ef\">_</span> webView: WKWebView, didFinish navigation: WKNavigation!) {\n webView.evaluateJavaScript(Keys.javascriptRemoveElements, completionHandler: {\n <span style=\"color:#66d9ef\">_</span>, error <span style=\"color:#66d9ef\">in</span>\n })\n }\n}\n\n</code></pre></div><p>Then in the main class</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">OnlineJobsWebView</span>: NSViewRepresentable {\n\n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">makeCoordinator</span>() -> Coordinator {\n Coordinator(<span style=\"color:#66d9ef\">self</span>)\n }\n\n <span style=\"color:#66d9ef\">var</span> url: URL\n \n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">makeNSView</span>(context: Context) -> WKWebView {\n <span style=\"color:#66d9ef\">return</span> WKWebView()\n }\n\n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">updateNSView</span>(<span style=\"color:#66d9ef\">_</span> webView: WKWebView, context: Context) {\n webView.navigationDelegate = context.coordinator\n <span style=\"color:#75715e\">// Evaluate Javascript will now work at this point.</span>\n }\n}\n\n</code></pre></div><p>This implementation feels a little hacky. Why wouldn’t Apple just introduce a <code>WebView</code> for SwiftUI? Going through all this is just painful.</p>\n<p><img src=\"https://media.giphy.com/media/7T33BLlB7NQrjozoRB/giphy.gif\" alt=\"Hide the pain\"></p>\n",
"content_text": "Next on the task is how to extend the class `WKNavigationDelegate` so I can call the [evaluateJavascript()](https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript) function. \n\nWe used an ' extension' in our existing iOS app using `UIKit`. But I found out, that this won't work when using SwiftUI.\n\n```swift\n\nextension MyJobsViewController: WKNavigationDelegate {\n}\n\n```\n\nAfter further Googling, research and internet digging I learned that I need to use [makeCoordinator()](https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable/makecoordinator()-32trb) function.\n\n> Creates the custom instance that you use to communicate changes from your view controller to other parts of your SwiftUI interface.\n\nFirst, I wrote an inner class called `Coordinator`.\n\n```swift\n\nclass Coordinator: NSObject, WKNavigationDelegate {\n let parent: OnlineJobsWebView\n \n init(_ parent: OnlineJobsWebView) {\n self.parent = parent\n }\n \n func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {\n webView.evaluateJavaScript(Keys.javascriptRemoveElements, completionHandler: {\n _, error in\n })\n }\n}\n\n```\n\nThen in the main class\n\n```swift\n\nstruct OnlineJobsWebView: NSViewRepresentable {\n\n func makeCoordinator() -> Coordinator {\n Coordinator(self)\n }\n\n var url: URL\n \n func makeNSView(context: Context) -> WKWebView {\n return WKWebView()\n }\n\n func updateNSView(_ webView: WKWebView, context: Context) {\n webView.navigationDelegate = context.coordinator\n // Evaluate Javascript will now work at this point.\n }\n}\n\n```\n\nThis implementation feels a little hacky. Why wouldn't Apple just introduce a `WebView` for SwiftUI? Going through all this is just painful. \n\n![Hide the pain](https://media.giphy.com/media/7T33BLlB7NQrjozoRB/giphy.gif)\n",
"date_published": "2022-05-22T11:15:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/22/swiftui-journey-part.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/21/swiftui-journey-part.html",
"title": "SwiftUI Journey Part 8: Using WebView in macOS",
"content_html": "<p>There is no WebView in SwiftUI, so you have to use the <code>NSViewRepresentable</code> class.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">\n<span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n<span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">WebKit</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">OnlineJobsWebView</span>: NSViewRepresentable {\n \n <span style=\"color:#66d9ef\">var</span> url: URL\n \n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">makeNSView</span>(context: Context) -> WKWebView {\n <span style=\"color:#66d9ef\">return</span> WKWebView()\n }\n \n <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">updateNSView</span>(<span style=\"color:#66d9ef\">_</span> webView: WKWebView, context: Context) {\n webView.load(URLRequest(url: url))\n }\n}\n\n</code></pre></div><p>And you call it in your SwiftUI <code>View</code>.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\">\n<span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">AccountView</span>: View {\n <span style=\"color:#66d9ef\">var</span> body: some View {\n OnlineJobsWebView(url: URL(string: <span style=\"color:#e6db74\">"https://google.com"</span>)<span style=\"color:#f92672\">!</span>)\n }\n}\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">AccountView_Previews</span>: PreviewProvider {\n <span style=\"color:#66d9ef\">static</span> <span style=\"color:#66d9ef\">var</span> previews: some View {\n AccountView()\n }\n}\n\n</code></pre></div>",
"content_text": "There is no WebView in SwiftUI, so you have to use the `NSViewRepresentable` class.\r\n\r\n```swift\r\n\r\nimport SwiftUI\r\nimport WebKit\r\n\r\nstruct OnlineJobsWebView: NSViewRepresentable {\r\n \r\n var url: URL\r\n \r\n func makeNSView(context: Context) -> WKWebView {\r\n return WKWebView()\r\n }\r\n \r\n func updateNSView(_ webView: WKWebView, context: Context) {\r\n webView.load(URLRequest(url: url))\r\n }\r\n}\r\n\r\n```\r\n\r\nAnd you call it in your SwiftUI `View`.\r\n\r\n```swift\r\n\r\nimport SwiftUI\r\n\r\nstruct AccountView: View {\r\n var body: some View {\r\n OnlineJobsWebView(url: URL(string: \"https://google.com\")!)\r\n }\r\n}\r\n\r\nstruct AccountView_Previews: PreviewProvider {\r\n static var previews: some View {\r\n AccountView()\r\n }\r\n}\r\n\r\n```\n",
"date_published": "2022-05-21T20:36:17+08:00",
"url": "https://law.gmnz.xyz/2022/05/21/swiftui-journey-part.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/20/so-this-is.html",
"content_html": "<p>So, this is what AppKit’s TabView looks like. Not really sure if I like it or not. Might need to review Apple’s macOS UI guidelines.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "So, this is what AppKit's TabView looks like. Not really sure if I like it or not. Might need to review Apple's macOS UI guidelines.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/30c18143ec.png\" width=\"600\" height=\"434\" alt=\"\" />\n",
"date_published": "2022-05-20T20:50:15+08:00",
"url": "https://law.gmnz.xyz/2022/05/20/so-this-is.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/18/interview-ethical-hacking.html",
"title": "Interview about Ethical Hacking",
"content_html": "<p>I was supposed to be interviewed by the Grade 11 STEM Students from Marist School, Marikina, about Ethical Hacking.</p>\n<h4 id=\"1-can-you-tell-us-what-you-know-about-hacking\">1. Can you tell us what you know about hacking?</h4>\n<p>Hacking is like everything else, there is a good and bad side to it. The bad is what you usually see or hear in the news where criminals hack systems to steal data, destroy systems, install malware, and lock the systems behind ransomware.</p>\n<p>The good is the ethical hackers which are usually the ones testing by penetrating your systems and then reporting it to the concerned party before the bad guys do.</p>\n<h4 id=\"2-can-you-tell-us-about-your-experiences-with-hacking\">2. Can you tell us about your experiences with hacking?</h4>\n<p>Outside of my day job, I check for vulnerabilities found in mobile apps since I am a mobile developer. Last 2020, I found a serious bug in one of Globe’s Rewards apps and others. You can read the writeups here:</p>\n<p><a href=\"https://initviews.com/2022/05/16/globe-rewards-bug.html\">initviews.com/2022/05/1…</a></p>\n<p><a href=\"https://initviews.com/2022/03/31/staysafeph-low-priority.html\">initviews.com/2022/03/3…</a></p>\n<p><a href=\"https://initviews.com/2021/03/07/keepr-storage-bug.html\">initviews.com/2021/03/0…</a></p>\n<p><a href=\"https://initviews.com/2020/09/01/go-manila-bug.html\">initviews.com/2020/09/0…</a></p>\n<h4 id=\"3-from-the-experiences-youve-shared-what-do-you-feel-upon-seeing-the-effects-of-hacking\">3. From the experiences you’ve shared, what do you feel upon seeing the effects of hacking?</h4>\n<p>Hacking can severely affect both institutions large and small. No matter how secure or robust your safeguards, all it takes is one person. Personally, as a father, I will always do my best to protect my family and that includes safeguarding their PII (personally identifiable information) online.</p>\n<h4 id=\"4-can-you-tell-us-what-you-know-about-white-hat-or-ethical-hacking\">4. Can you tell us what you know about ‘white hat’ or ethical hacking?</h4>\n<p>Ethical hacking is finding security vulnerabilities before the bad guys do. Then you report it to the concerned party. Some ethical hackers give out deadlines before disclosing them to the public. In this way, this will put additional pressure on the complacent parties to act on it right away.</p>\n<h4 id=\"5-in-what-situations-can-you-view-hacking-as-a-good-and-practical-thing-to-do-please-elaborate\">5. In what situations can you view hacking as a good and practical thing to do? Please elaborate.</h4>\n<p>If you really care about your user’s data then companies should enroll in bug bounty programs like HackerOne or Bugcrowd.</p>\n<h4 id=\"6-what-do-you-think-isare-the-downsides-of-ethical-hacking\">6. What do you think is/are the downside/s of ethical hacking?</h4>\n<p>The downside is some “ethical” hackers may try to extort thus giving out a bad name to it. There are also trust issues between hackers and companies. As you may notice, not all companies are enrolled in bug bounty programs or allow ethical hacking into their systems.</p>\n<p>There is also a lack of education about this topic. You can tell by asking anyone in your company about it.</p>\n<h4 id=\"7-research-suggests-that-businesses-are-hiring-a-growing-amount-of-ethical-hackers-to-test-the-security-in-their-it-systems-in-your-opinion-how-do-you-think-can-this-impact-the-security-of-our-cyber-society-moving-forward\">7. Research suggests that businesses are hiring a growing amount of ethical hackers to test the security in their IT systems. In your opinion, how do you think can this impact the security of our cyber society moving forward?</h4>\n<p>This is true but I sincerely doubt this is happening in the Philippines. But one thing for sure is that there is a massive concern about the lack of cybersecurity professionals worldwide. If anyone is looking for a branch in IT to explore, they should look into cybersecurity.</p>\n<h4 id=\"8-finally-could-you-describe-to-us-what-do-you-think-about-the-practicality-of-modern-day-businesses-and-services-hiring-ethical-hackers\">8. Finally, could you describe to us what do you think about the practicality of modern-day businesses and services hiring ethical hackers?</h4>\n<p>Hiring ethical hackers is a considerable cost and may disrupt normal day-to-day company processes. Also, not all persons are familiar with ethical hacking.</p>\n<p>Ethical hacking is a two-way street. Both parties, the hacker and the company, should work together to succeed and protect user data or their systems.</p>\n",
"content_text": "I was supposed to be interviewed by the Grade 11 STEM Students from Marist School, Marikina, about Ethical Hacking.\n\n#### 1. Can you tell us what you know about hacking?\n\nHacking is like everything else, there is a good and bad side to it. The bad is what you usually see or hear in the news where criminals hack systems to steal data, destroy systems, install malware, and lock the systems behind ransomware.\n\nThe good is the ethical hackers which are usually the ones testing by penetrating your systems and then reporting it to the concerned party before the bad guys do.\n\n#### 2. Can you tell us about your experiences with hacking? \n\nOutside of my day job, I check for vulnerabilities found in mobile apps since I am a mobile developer. Last 2020, I found a serious bug in one of Globe’s Rewards apps and others. You can read the writeups here: \n\n[initviews.com/2022/05/1...](https://initviews.com/2022/05/16/globe-rewards-bug.html)\n\n[initviews.com/2022/03/3...](https://initviews.com/2022/03/31/staysafeph-low-priority.html)\n\n[initviews.com/2021/03/0...](https://initviews.com/2021/03/07/keepr-storage-bug.html)\n\n[initviews.com/2020/09/0...](https://initviews.com/2020/09/01/go-manila-bug.html)\n\n#### 3. From the experiences you’ve shared, what do you feel upon seeing the effects of hacking?\n\nHacking can severely affect both institutions large and small. No matter how secure or robust your safeguards, all it takes is one person. Personally, as a father, I will always do my best to protect my family and that includes safeguarding their PII (personally identifiable information) online.\n\n#### 4. Can you tell us what you know about ‘white hat’ or ethical hacking?\n\nEthical hacking is finding security vulnerabilities before the bad guys do. Then you report it to the concerned party. Some ethical hackers give out deadlines before disclosing them to the public. In this way, this will put additional pressure on the complacent parties to act on it right away.\n\n#### 5. In what situations can you view hacking as a good and practical thing to do? Please elaborate. \n\nIf you really care about your user’s data then companies should enroll in bug bounty programs like HackerOne or Bugcrowd. \n\n#### 6. What do you think is/are the downside/s of ethical hacking?\n\nThe downside is some “ethical” hackers may try to extort thus giving out a bad name to it. There are also trust issues between hackers and companies. As you may notice, not all companies are enrolled in bug bounty programs or allow ethical hacking into their systems.\n\nThere is also a lack of education about this topic. You can tell by asking anyone in your company about it.\n\n#### 7. Research suggests that businesses are hiring a growing amount of ethical hackers to test the security in their IT systems. In your opinion, how do you think can this impact the security of our cyber society moving forward?\n\nThis is true but I sincerely doubt this is happening in the Philippines. But one thing for sure is that there is a massive concern about the lack of cybersecurity professionals worldwide. If anyone is looking for a branch in IT to explore, they should look into cybersecurity.\n\n#### 8. Finally, could you describe to us what do you think about the practicality of modern-day businesses and services hiring ethical hackers?\n\nHiring ethical hackers is a considerable cost and may disrupt normal day-to-day company processes. Also, not all persons are familiar with ethical hacking.\n\nEthical hacking is a two-way street. Both parties, the hacker and the company, should work together to succeed and protect user data or their systems.\n",
"date_published": "2022-05-18T18:40:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/18/interview-ethical-hacking.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/17/swiftui-journey-part.html",
"title": "SwiftUI Journey Part 6: Async/Await with SwiftUI",
"content_html": "<p>Today I learned how to call async/await functions with SwiftUI. This has the same flow with the sign in page but this one is using concurrency. On that note, I need to refactor the sign in page to use concurrency.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">MainView</span>: View {\n@State <span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">var</span> signInSuccess = <span style=\"color:#66d9ef\">false</span>\n \n <span style=\"color:#66d9ef\">var</span> body: some View {\n <span style=\"color:#66d9ef\">if</span> signInSuccess {\n HomeView()\n } <span style=\"color:#66d9ef\">else</span> {\n VStack {\n Image(<span style=\"color:#e6db74\">"OnlineJobs Logo"</span>)\n .aspectRatio(contentMode: .fit)\n .frame(width: <span style=\"color:#ae81ff\">50</span>, height: <span style=\"color:#ae81ff\">30</span>)\n .padding(.bottom, <span style=\"color:#ae81ff\">50</span>)\n }\n .frame(minWidth: <span style=\"color:#ae81ff\">500</span>, maxWidth: .infinity, minHeight: <span style=\"color:#ae81ff\">300</span>, maxHeight: <span style=\"color:#ae81ff\">800</span>)\n .background(Color(red: <span style=\"color:#ae81ff\">2</span> <span style=\"color:#f92672\">/</span> <span style=\"color:#ae81ff\">255</span>, green: <span style=\"color:#ae81ff\">69</span> <span style=\"color:#f92672\">/</span> <span style=\"color:#ae81ff\">255</span>, blue: <span style=\"color:#ae81ff\">112</span> <span style=\"color:#f92672\">/</span> <span style=\"color:#ae81ff\">255</span>))\n .task {\n await checkLoginStatus()\n }\n }\n }\n}\n</code></pre></div><p>Take note of the <code>.task{}</code> code block, this is the place to call your async function.</p>\n<p>My async function looks like this</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">checkLoginStatus</span>() async {\n<span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> url = URL(string: Urls.main.employerJobs) {\n <span style=\"color:#66d9ef\">var</span> request = URLRequest(url: url)\n request.httpMethod = <span style=\"color:#e6db74\">"GET"</span>\n <span style=\"color:#66d9ef\">do</span> {\n <span style=\"color:#66d9ef\">let</span> (<span style=\"color:#66d9ef\">_</span>, response) = <span style=\"color:#66d9ef\">try</span> await URLSession.shared.data(<span style=\"color:#66d9ef\">for</span>: request)\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> responseURL = response.url {\n <span style=\"color:#66d9ef\">if</span> responseURL.absoluteString.contains(Urls.main.employerJobs) {\n signInSuccess = <span style=\"color:#66d9ef\">true</span>\n }\n }\n } <span style=\"color:#66d9ef\">catch</span> {\n signInSuccess = <span style=\"color:#66d9ef\">false</span>\n }\n }\n}\n</code></pre></div><p>When the server response URL matches the condition then <code>signInSuccess = true</code> is called. And this will then change the layout to <code>HomeView()</code>.</p>\n",
"content_text": "Today I learned how to call async/await functions with SwiftUI. This has the same flow with the sign in page but this one is using concurrency. On that note, I need to refactor the sign in page to use concurrency.\n\n```swift\nimport SwiftUI\nstruct MainView: View {\n@State private var signInSuccess = false\n \n var body: some View {\n if signInSuccess {\n HomeView()\n } else {\n VStack {\n Image(\"OnlineJobs Logo\")\n .aspectRatio(contentMode: .fit)\n .frame(width: 50, height: 30)\n .padding(.bottom, 50)\n }\n .frame(minWidth: 500, maxWidth: .infinity, minHeight: 300, maxHeight: 800)\n .background(Color(red: 2 / 255, green: 69 / 255, blue: 112 / 255))\n .task {\n await checkLoginStatus()\n }\n }\n }\n}\n```\nTake note of the `.task{}` code block, this is the place to call your async function.\n\nMy async function looks like this\n```swift\nprivate func checkLoginStatus() async {\nif let url = URL(string: Urls.main.employerJobs) {\n var request = URLRequest(url: url)\n request.httpMethod = \"GET\"\n do {\n let (_, response) = try await URLSession.shared.data(for: request)\n if let responseURL = response.url {\n if responseURL.absoluteString.contains(Urls.main.employerJobs) {\n signInSuccess = true\n }\n }\n } catch {\n signInSuccess = false\n }\n }\n}\n```\n\nWhen the server response URL matches the condition then `signInSuccess = true` is called. And this will then change the layout to `HomeView()`.\n",
"date_published": "2022-05-17T20:11:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/17/swiftui-journey-part.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/17/several-updates-went.html",
"content_html": "<p>Several updates went out from Apple today: macOS Monterey 12.4, Xcode 13.4, iOS 15.5 and several others.\n<!-- raw HTML omitted --></p>\n",
"content_text": "Several updates went out from Apple today: macOS Monterey 12.4, Xcode 13.4, iOS 15.5 and several others.\r\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/cb92c1afb2.png\" width=\"600\" height=\"267\" alt=\"\" />\n",
"date_published": "2022-05-17T08:14:33+08:00",
"url": "https://law.gmnz.xyz/2022/05/17/several-updates-went.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/16/it-outsourcing-i.html",
"title": "IT Outsourcing",
"content_html": "<p>I read this <a href=\"https://www.protocol.com/newsletters/protocol-enterprise/it-outsourcing-europe-microsoft-security?rebelltitem=1#rebelltitem1\">article today</a> about IT outsourcing and how it is affected by the war in Ukraine. Ukraine is one of the top, if not the top destination for IT outsourcing, so this is a long-term issue.</p>\n<blockquote>\n<p>The global market for IT services has exploded in line with the overall growth of tech, which has become a $4 trillion dollar industry. Finding the talent to meet the growing demand for technical services is more difficult than ever, which has driven companies to hire IT employees across the globe, outsourcing to countries like the Philippines, India and, increasingly in recent years, Ukraine, Poland, Bulgaria and other Eastern European countries.</p>\n</blockquote>\n<p>There are some key points I would like to point out. First, it is humbling to mention Philippines as one of the preferred and key destinations for outsourcing IT services and software.</p>\n<blockquote>\n<p>Demand for that talent has not slowed: It’s no secret that companies across the board are having difficulty finding the right people.</p>\n</blockquote>\n<p>Demand has not slowed. Made me glad that I am working in this field.</p>\n<blockquote>\n<p>Now almost every company has a base in India or the Philippines, he said, noting that companies like Accenture and IBM have just as many employees in India as Infosys does.</p>\n</blockquote>\n<p>Ahh Accenture and IBM, no thanks.</p>\n<blockquote>\n<p>Pakistan and India are illustrative examples of that. Although the two countries have similar talent pools, “Pakistan has not done as well as India or Philippines has done just because [of] the political uncertainity, the political as well as the terrorism part of it, the security,” he said.</p>\n</blockquote>\n<p>This has been my point before and after the election, the software industry in the Philippines benefits largely from political stability. I just hope for the next 6 years there will be enough stability that won’t affect the IT industry worldwide. Also, the war in Ukraine should hopefully stop.</p>\n",
"content_text": "I read this [article today](https://www.protocol.com/newsletters/protocol-enterprise/it-outsourcing-europe-microsoft-security?rebelltitem=1#rebelltitem1) about IT outsourcing and how it is affected by the war in Ukraine. Ukraine is one of the top, if not the top destination for IT outsourcing, so this is a long-term issue.\n\n> The global market for IT services has exploded in line with the overall growth of tech, which has become a $4 trillion dollar industry. Finding the talent to meet the growing demand for technical services is more difficult than ever, which has driven companies to hire IT employees across the globe, outsourcing to countries like the Philippines, India and, increasingly in recent years, Ukraine, Poland, Bulgaria and other Eastern European countries.\n\nThere are some key points I would like to point out. First, it is humbling to mention Philippines as one of the preferred and key destinations for outsourcing IT services and software.\n\n> Demand for that talent has not slowed: It’s no secret that companies across the board are having difficulty finding the right people.\n\nDemand has not slowed. Made me glad that I am working in this field.\n\n> Now almost every company has a base in India or the Philippines, he said, noting that companies like Accenture and IBM have just as many employees in India as Infosys does.\n\nAhh Accenture and IBM, no thanks.\n\n> Pakistan and India are illustrative examples of that. Although the two countries have similar talent pools, “Pakistan has not done as well as India or Philippines has done just because [of] the political uncertainity, the political as well as the terrorism part of it, the security,” he said.\n\nThis has been my point before and after the election, the software industry in the Philippines benefits largely from political stability. I just hope for the next 6 years there will be enough stability that won't affect the IT industry worldwide. Also, the war in Ukraine should hopefully stop.\n",
"date_published": "2022-05-16T13:18:17+08:00",
"url": "https://law.gmnz.xyz/2022/05/16/it-outsourcing-i.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/14/today-is-me.html",
"content_html": "<p>Today is me and my wife’s anniversary! 6 years ago she said yes to me. Time flies!</p>\n",
"content_text": "Today is me and my wife's anniversary! 6 years ago she said yes to me. Time flies!\n",
"date_published": "2022-05-14T20:27:51+08:00",
"url": "https://law.gmnz.xyz/2022/05/14/today-is-me.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/12/google-io-part.html",
"title": "Google I/O 2022 Part 2",
"content_html": "<p>So far I have watched the Google I/O Keynote and What’s New With Android 13. And read a couple of blogs from the Android Developer team. Below are the things that caught my attention, or eyes.</p>\n<h4 id=\"sundar-is-so-thin\">Sundar is so thin</h4>\n<p>I apologize in advance but this was the first thing I noticed when he walked out on that stage. Maybe it’s just because of his attire. But he should be eating more. Look at his hands.</p>\n<h4 id=\"north-korean-google-employee\">North Korean Google Employee?</h4>\n<p>She looks like a North Korean Spokesperson in this shot. Once again, I didn’t mean to.\n<!-- raw HTML omitted --></p>\n<h4 id=\"awesome-features\">Awesome Features</h4>\n<p>The Near Me feature is cool. I like it. Also skintones. Half of the keynote was mostly AI or ML, they were cool but really not my cup of tea. I can’t really relate since in my country most services are doing their businesses on Facebook. Must be cool to live in Silicon Valley.</p>\n<h4 id=\"new-google-wallet-api\">New Google Wallet API</h4>\n<p>I’m so excited about this feature. If I get the time I will try to upload our basic-ass-looking Covid Vaccine card on a proof-of-concept app. This is what the card looks like.</p>\n<!-- raw HTML omitted -->\n<h4 id=\"android-part-was-underwhelming\">Android Part Was Underwhelming</h4>\n<p>The second half of the Google I/O Keynote was about Android. For me, it was underwhelming. Plus the McClaren team cameo was so awkward, last time I checked both of the drivers hated each other right?</p>\n<h4 id=\"pixel-is-cool-but-not-available-in-my-country\">Pixel is cool but not available in my country</h4>\n<p>I sincerely hope Pixel 30A will be available to purchase during my lifetime.</p>\n<h4 id=\"google-shadesglasses-was-my-favorite\">Google Shades/Glasses was my favorite</h4>\n<p>I got teary-eyed watching the demo. So cool. I was able to try Google Glasses back then and it was so awesome.</p>\n<h4 id=\"bug-tracking-which-one-to-use\">Bug Tracking which one to use?</h4>\n<p>On the AS Beta version, Firebase Crashlytics has been integrated. Not sure how that works if I don’t use that SDK? I mainly use the Play Store’s crash report because the report is decent enough to understand. Why not integrate the Play Store’s crash report instead?</p>\n<h4 id=\"cant-wait-to-migrate-to-jetpack-compose\">Can’t wait to migrate to Jetpack Compose</h4>\n<p>I decided to migrate our app to Jetpack Compose starting next year. I am excited about this one.</p>\n<h4 id=\"google-play-sdk-index-if-my-feature-feature\">Google Play SDK Index if my feature feature</h4>\n<p>You know how hard it is to keep track of each dependencies' version. Hopefully, <a href=\"https://play.google.com/sdks\">Google Play SDK Index</a> will solve that problem. Is this integrated already on AS? Will it warn me if a dependency is very old? I’ll try to find out in the near future.</p>\n<h4 id=\"logcat-v2\">Logcat V2</h4>\n<p>I’m a Logcat person. Can’t wait to try it.</p>\n<h4 id=\"baseline-profiles\">Baseline Profiles</h4>\n<p>Not sure what this is but I’m also excited.</p>\n",
"content_text": "So far I have watched the Google I/O Keynote and What's New With Android 13. And read a couple of blogs from the Android Developer team. Below are the things that caught my attention, or eyes.\n\n#### Sundar is so thin\nI apologize in advance but this was the first thing I noticed when he walked out on that stage. Maybe it's just because of his attire. But he should be eating more. Look at his hands.\n\n#### North Korean Google Employee?\nShe looks like a North Korean Spokesperson in this shot. Once again, I didn't mean to.\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/a9bf29de10.png\" width=\"600\" height=\"331\" alt=\"\" />\n\n#### Awesome Features\nThe Near Me feature is cool. I like it. Also skintones. Half of the keynote was mostly AI or ML, they were cool but really not my cup of tea. I can't really relate since in my country most services are doing their businesses on Facebook. Must be cool to live in Silicon Valley.\n\n#### New Google Wallet API\nI'm so excited about this feature. If I get the time I will try to upload our basic-ass-looking Covid Vaccine card on a proof-of-concept app. This is what the card looks like.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/8ebb991989.png\" width=\"297\" height=\"170\" alt=\"\" />\n\n#### Android Part Was Underwhelming\nThe second half of the Google I/O Keynote was about Android. For me, it was underwhelming. Plus the McClaren team cameo was so awkward, last time I checked both of the drivers hated each other right? \n\n#### Pixel is cool but not available in my country\nI sincerely hope Pixel 30A will be available to purchase during my lifetime.\n\n#### Google Shades/Glasses was my favorite\nI got teary-eyed watching the demo. So cool. I was able to try Google Glasses back then and it was so awesome.\n\n#### Bug Tracking which one to use?\nOn the AS Beta version, Firebase Crashlytics has been integrated. Not sure how that works if I don't use that SDK? I mainly use the Play Store's crash report because the report is decent enough to understand. Why not integrate the Play Store's crash report instead?\n\n#### Can't wait to migrate to Jetpack Compose\nI decided to migrate our app to Jetpack Compose starting next year. I am excited about this one.\n\n#### Google Play SDK Index if my feature feature\nYou know how hard it is to keep track of each dependencies' version. Hopefully, [Google Play SDK Index](https://play.google.com/sdks) will solve that problem. Is this integrated already on AS? Will it warn me if a dependency is very old? I'll try to find out in the near future.\n\n#### Logcat V2\nI'm a Logcat person. Can't wait to try it.\n\n#### Baseline Profiles\nNot sure what this is but I'm also excited.\n",
"date_published": "2022-05-12T20:25:12+08:00",
"url": "https://law.gmnz.xyz/2022/05/12/google-io-part.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/12/my-th-google.html",
"title": "Google I/O 2022 Part 1",
"content_html": "<p>Today marks my 10th Google I/O. There is still a lot of new information to check out later. But the first thing I did was explore the virtual adventure. This time I took a lot of selfies.</p>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "Today marks my 10th Google I/O. There is still a lot of new information to check out later. But the first thing I did was explore the virtual adventure. This time I took a lot of selfies.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/5d39c038cf.png\" width=\"600\" height=\"600\" alt=\"\"/>\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/00d9de0b7f.png\" width=\"600\" height=\"600\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/a748d6f0d9.png\" width=\"600\" height=\"600\" alt=\"\" />\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/31f23039cb.png\" width=\"600\" height=\"600\" alt=\"\" />\n",
"date_published": "2022-05-12T19:20:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/12/my-th-google.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/11/i-wonder-whats.html",
"content_html": "<p>I wonder what’s wrong with Firefox?</p>\n<!-- raw HTML omitted -->\n",
"content_text": "I wonder what's wrong with Firefox?\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/59dae7ab90.png\" width=\"600\" height=\"376\" alt=\"\" />\n",
"date_published": "2022-05-11T21:17:53+08:00",
"url": "https://law.gmnz.xyz/2022/05/11/i-wonder-whats.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/11/android-chipmunk-the.html",
"title": "Android Studio Chipmunk",
"content_html": "<p>The only clear difference I noticed is Android Studio’s About page. So simple, I bet they can’t find the time to draw that Chipmunk graphic design.</p>\n<!-- raw HTML omitted -->\n<p>Compared to the previous Android Studio Bumblebee about page.</p>\n<!-- raw HTML omitted -->\n<p>Package namespace also has been moved from <code>AndroidManifest.xml</code> to <code>build.gradle.kts</code>. You can check the <code>git diff</code> below.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "The only clear difference I noticed is Android Studio's About page. So simple, I bet they can't find the time to draw that Chipmunk graphic design.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/e1e9bb5fa8.png\" width=\"600\" height=\"409\" alt=\"\" />\n\nCompared to the previous Android Studio Bumblebee about page.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/bb36047bea.png\" width=\"600\" height=\"375\" alt=\"\" />\n\nPackage namespace also has been moved from `AndroidManifest.xml` to `build.gradle.kts`. You can check the `git diff` below.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/7c106dd7ae.png\" width=\"600\" height=\"461\" alt=\"\" />\n",
"date_published": "2022-05-11T08:36:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/11/android-chipmunk-the.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/10/outdated-apple-documentation.html",
"content_html": "<p>Outdated Apple Documentation alert in <a href=\"https://developer.apple.com/design/human-interface-guidelines/macos/overview/whats-new-in-macos/\">macOS Human Interface Guidelines</a>.</p>\n<!-- raw HTML omitted -->\n<p>Did Apple mean macOS 12?</p>\n",
"content_text": "Outdated Apple Documentation alert in [macOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/macos/overview/whats-new-in-macos/).\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/6a39133338.png\" width=\"600\" height=\"376\" alt=\"\" />\n\nDid Apple mean macOS 12?\n",
"date_published": "2022-05-10T22:10:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/10/outdated-apple-documentation.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/10/swiftui-sign-in.html",
"title": "SwiftUI Journey Part 5: Sign In To Home View",
"content_html": "<p>On iOS, to transition from a <code>SignInViewController</code> to <code>HomeViewController</code> after the user successfully signs in, you just have to call the <a href=\"https://developer.apple.com/documentation/uikit/uiwindow/1621601-makekeyandvisible\">makeKeyAndVisible()</a>.</p>\n<p>But on macOS using SwiftUI, here’s how I did it. It took me a while to get this right. And, still not entirely sure if this is the way.</p>\n<p><img src=\"https://media.giphy.com/media/6UFgdU9hirj1pAOJyN/giphy.gif\" alt=\"This is the way\"></p>\n<p>Here’s what my sign in view looks like, some parts are removed obviously for security reasons. But the conditional logic is the same.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">import</span> <span style=\"color:#a6e22e\">SwiftUI</span>\n\n<span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">SignInView</span>: View {\n @State <span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">var</span> signInSuccess = <span style=\"color:#66d9ef\">false</span>\n <span style=\"color:#66d9ef\">var</span> body: some View {\n <span style=\"color:#66d9ef\">if</span> signInSuccess {\n HomeView()\n <span style=\"color:#66d9ef\">else</span> {\n Text(<span style=\"color:#e6db74\">"I'm in sign in."</span>).padding()\n }\n }\n}\n\n</code></pre></div><p>First of all, the concept of <code>@State</code> is still foreign to me. It is both awesome and terrifying. Terrifying because it seems there are no lifecycles to follow and might cause some logic bugs in the future. This needs more serious study but in the meantime, <code>signInSuccess</code> variable holds the state of whether the user successfully signs in or not.</p>\n<p>Now, here’s my sign in function.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">private</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">signIn</span>() {\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#f92672\">!</span>email.isEmpty <span style=\"color:#f92672\">&&</span> <span style=\"color:#f92672\">!</span>password.isEmpty {\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> url = URL(string: Urls.main.authenticate) {\n <span style=\"color:#66d9ef\">let</span> session = URLSession.shared\n <span style=\"color:#66d9ef\">var</span> request = URLRequest(url: url)\n request.httpMethod = <span style=\"color:#e6db74\">"POST"</span>\n <span style=\"color:#66d9ef\">let</span> body = <span style=\"color:#e6db74\">"info[email]=</span><span style=\"color:#e6db74\">\\(</span>email<span style=\"color:#e6db74\">)</span><span style=\"color:#e6db74\">&info[password]=</span><span style=\"color:#e6db74\">\\(</span>password<span style=\"color:#e6db74\">)</span><span style=\"color:#e6db74\">"</span>\n request.httpBody = body.data(using: .utf8)\n <span style=\"color:#66d9ef\">let</span> task = session.dataTask(with: request) { [<span style=\"color:#66d9ef\">self</span>]\n data, response, error <span style=\"color:#66d9ef\">in</span>\n <span style=\"color:#66d9ef\">if</span> <span style=\"color:#66d9ef\">let</span> error = error {\n print(<span style=\"color:#e6db74\">"Login error </span><span style=\"color:#e6db74\">\\(</span>error<span style=\"color:#e6db74\">)</span><span style=\"color:#e6db74\">"</span>)\n } <span style=\"color:#66d9ef\">else</span> {\n <span style=\"color:#75715e\">// No errors found</span>\n signInSuccess = <span style=\"color:#66d9ef\">true</span>\n }\n }\n task.resume()\n }\n }\n }\n</code></pre></div><p>By the time the variable <code>signInSuccess</code> is set to true, SwiftUI will update the parts of the view heirarchy. That means the condition below will be triggered.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">if</span> signInSuccess {\n HomeView()\n}\n</code></pre></div><p>It’s frustrating how few SwiftUI for macOS tutorials are out there. Anyway, that’s all for now.</p>\n",
"content_text": "On iOS, to transition from a `SignInViewController` to `HomeViewController` after the user successfully signs in, you just have to call the [makeKeyAndVisible()](https://developer.apple.com/documentation/uikit/uiwindow/1621601-makekeyandvisible).\n\nBut on macOS using SwiftUI, here's how I did it. It took me a while to get this right. And, still not entirely sure if this is the way.\n\n![This is the way](https://media.giphy.com/media/6UFgdU9hirj1pAOJyN/giphy.gif)\n\nHere's what my sign in view looks like, some parts are removed obviously for security reasons. But the conditional logic is the same.\n```swift\nimport SwiftUI\n\nstruct SignInView: View {\n @State private var signInSuccess = false\n var body: some View {\n if signInSuccess {\n HomeView()\n else {\n Text(\"I'm in sign in.\").padding()\n }\n }\n}\n\n```\n\nFirst of all, the concept of `@State` is still foreign to me. It is both awesome and terrifying. Terrifying because it seems there are no lifecycles to follow and might cause some logic bugs in the future. This needs more serious study but in the meantime, `signInSuccess` variable holds the state of whether the user successfully signs in or not.\n\nNow, here’s my sign in function.\n```swift\nprivate func signIn() {\n if !email.isEmpty && !password.isEmpty {\n if let url = URL(string: Urls.main.authenticate) {\n let session = URLSession.shared\n var request = URLRequest(url: url)\n request.httpMethod = \"POST\"\n let body = \"info[email]=\\(email)&info[password]=\\(password)\"\n request.httpBody = body.data(using: .utf8)\n let task = session.dataTask(with: request) { [self]\n data, response, error in\n if let error = error {\n print(\"Login error \\(error)\")\n } else {\n // No errors found\n signInSuccess = true\n }\n }\n task.resume()\n }\n }\n }\n```\n\nBy the time the variable `signInSuccess` is set to true, SwiftUI will update the parts of the view heirarchy. That means the condition below will be triggered.\n\n```swift\nif signInSuccess {\n HomeView()\n}\n```\n\nIt’s frustrating how few SwiftUI for macOS tutorials are out there. Anyway, that's all for now.\n",
"date_published": "2022-05-10T21:49:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/10/swiftui-sign-in.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/09/soon-written-in.html",
"title": "SwiftUI Journey Part 4: OnlineJobs for macOS Coming Soon",
"content_html": "<p>Soon. Written in <a href=\"https://developer.apple.com/xcode/swiftui/\">SwiftUI</a>.</p>\n<!-- raw HTML omitted -->\n<p>So far I am completely sold to SwiftUI. As long as I don’t have to deal with AutoLayout anymore, that is a plus in my book. Just look at the image below, the sign-in screen UI took only at least 45 lines of code.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Soon. Written in [SwiftUI](https://developer.apple.com/xcode/swiftui/).\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/a868c546c8.png\" width=\"600\" height=\"446\" alt=\"\" />\n\nSo far I am completely sold to SwiftUI. As long as I don't have to deal with AutoLayout anymore, that is a plus in my book. Just look at the image below, the sign-in screen UI took only at least 45 lines of code.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/2a9a95d6cd.png\" width=\"600\" height=\"401\" alt=\"\" />\n",
"date_published": "2022-05-09T19:35:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/09/soon-written-in.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/08/this-is-my.html",
"content_html": "<p>This is my current dock. As you can see I work mainly on Xcode and Android Studio. I just want to emphasize that out of place icon from IntelliJ.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "This is my current dock. As you can see I work mainly on Xcode and Android Studio. I just want to emphasize that out of place icon from IntelliJ.\n\n<img src=\"https://cdn.uploads.micro.blog/67704/2022/d29d2732d2.png\" width=\"600\" height=\"68\" alt=\"\" />\n",
"date_published": "2022-05-08T17:51:51+08:00",
"url": "https://law.gmnz.xyz/2022/05/08/this-is-my.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/08/jetpack-compose-is.html",
"title": "Jetpack Compose Not Stable Yet",
"content_html": "<p>Jetpack Compose is not yet stable. I could not just make it work in an existing project. There seems to be a conflict between viewBinding and compose.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-kotlin\" data-lang=\"kotlin\">buildFeatures {\n viewBinding = <span style=\"color:#66d9ef\">true</span>\n compose = <span style=\"color:#66d9ef\">true</span>\n}\n</code></pre></div><p>Android’s official documentation is not much of a help either.</p>\n",
"content_text": "Jetpack Compose is not yet stable. I could not just make it work in an existing project. There seems to be a conflict between viewBinding and compose.\n\n```kotlin\nbuildFeatures {\n viewBinding = true\n compose = true\n}\n```\nAndroid's official documentation is not much of a help either.\n",
"date_published": "2022-05-08T11:18:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/08/jetpack-compose-is.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/09/happy-mothers-day.html",
"title": "Happy Mother's Day To My Wife!",
"content_html": "<p>Happy Mother’s Day to my wife. Thank you for everything that you do and I’m so proud of you! I missed hanging out with you in coffee shops, dining out, and watching movies in theaters. I hope we could get to do it again with our little Bimbim.</p>\n",
"content_text": "Happy Mother's Day to my wife. Thank you for everything that you do and I'm so proud of you! I missed hanging out with you in coffee shops, dining out, and watching movies in theaters. I hope we could get to do it again with our little Bimbim.\n",
"date_published": "2022-05-08T09:50:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/09/happy-mothers-day.html",
"tags": ["Dad Blog"]
},
{
"id": "http://lwgmnz.micro.blog/2022/05/07/whats-your-signature.html",
"title": "What's your signature code?",
"content_html": "<p>What’s your signature code? Mine is <code>initViews()</code>, short for “initialize views”. Since the start of my development career, this is the first function that I will always write. In Java, the difference is just the semi-colon: <code>initViews();</code>.</p>\n<p>Looking at my old Android Java codes, it is somewhat amazing (for me) that I have continued this tradition still. This function is called inside <code>viewDidLoad()</code> for iOS or <code>onCreate(Bundle savedInstanceState)</code> for Android. Its purpose is to initialize all views and their properties during the launch of either <code>AppCompatActivity</code> or <code>UIViewController</code>.</p>\n<p>For example:</p>\n<pre tabindex=\"0\"><code class=\"language-kotlint\" data-lang=\"kotlint\"> override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n binding = ActivityMessageRoomBinding.inflate(layoutInflater)\n val view = binding.root\n setContentView(view)\n initViews()\n}\n</code></pre><p>or in iOS</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">override</span> <span style=\"color:#66d9ef\">func</span> <span style=\"color:#a6e22e\">viewDidLoad</span>() {\n <span style=\"color:#66d9ef\">super</span>.viewDidLoad()\n initViews()\n loadHtml()\n initObservers()\n}\n\n</code></pre></div><p>Source codes will always evolve, I have contributed to over 40 (or more not sure) mobile app projects and it would be an honor if my <code>initViews()</code> stayed and were not refactored out. If you see this function call, there’s a huge possibility it was written by me. Thanks!</p>\n",
"content_text": "What's your signature code? Mine is `initViews()`, short for \"initialize views\". Since the start of my development career, this is the first function that I will always write. In Java, the difference is just the semi-colon: `initViews();`.\n\nLooking at my old Android Java codes, it is somewhat amazing (for me) that I have continued this tradition still. This function is called inside `viewDidLoad()` for iOS or `onCreate(Bundle savedInstanceState)` for Android. Its purpose is to initialize all views and their properties during the launch of either `AppCompatActivity` or `UIViewController`.\n\nFor example:\n\n```kotlint\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n binding = ActivityMessageRoomBinding.inflate(layoutInflater)\n val view = binding.root\n setContentView(view)\n initViews()\n}\n```\n\nor in iOS\n\n```swift\noverride func viewDidLoad() {\n super.viewDidLoad()\n initViews()\n loadHtml()\n initObservers()\n}\n\n```\n\nSource codes will always evolve, I have contributed to over 40 (or more not sure) mobile app projects and it would be an honor if my `initViews()` stayed and were not refactored out. If you see this function call, there's a huge possibility it was written by me. Thanks!\n",
"date_published": "2022-05-07T09:42:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/07/whats-your-signature.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/06/i-installed-the.html",
"title": "iOS 15.5 Developer Beta 4",
"content_html": "<p>I installed the latest iOS 15.5 Dev Beta build and so far I haven’t experienced any serious bugs or issues. Especially on the Message app, which from my experience from the previous build was so buggy.</p>\n<p>OnlineJobs app is also running fine on the latest developer beta.</p>\n",
"content_text": "I installed the latest iOS 15.5 Dev Beta build and so far I haven’t experienced any serious bugs or issues. Especially on the Message app, which from my experience from the previous build was so buggy.\n\nOnlineJobs app is also running fine on the latest developer beta.\n",
"date_published": "2022-05-06T23:25:10+08:00",
"url": "https://law.gmnz.xyz/2022/05/06/i-installed-the.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/06/new-album-alert.html",
"title": "New Album Alert: Terror - Pain Into Power",
"content_html": "<p>I liked the new Terror album. Reminds me of there old albums Lowest of the Low and One With The Underdogs.</p>\n<p>Rate: 🔥🔥🔥🔥🔥 out of 5</p>\n",
"content_text": "I liked the new Terror album. Reminds me of there old albums Lowest of the Low and One With The Underdogs.\n\nRate: 🔥🔥🔥🔥🔥 out of 5\n",
"date_published": "2022-05-06T23:08:56+08:00",
"url": "https://law.gmnz.xyz/2022/05/06/new-album-alert.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/06/finished-reading-checkmate.html",
"content_html": "<p>Finished reading: <a href=\"https://micro.blog/books/9781250247551\">Checkmate in Berlin</a> by Giles Milton. Very interesting read, there were some really shocking parts that might never get out of my head.</p>\n",
"content_text": "Finished reading: [Checkmate in Berlin](https://micro.blog/books/9781250247551) by Giles Milton. Very interesting read, there were some really shocking parts that might never get out of my head.\n",
"date_published": "2022-05-06T21:32:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/06/finished-reading-checkmate.html",
"tags": ["Journal"]
},
{
"id": "http://lwgmnz.micro.blog/2022/05/06/now-the-frames.html",
"title": "SwiftUI Journey Part 3",
"content_html": "<p>Now, the frames are static. I need the frames to be resizable. Time to Google. So eventually, you need to set maxWidth and maxHeight inside the <code>frame()</code>.</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">SignInView</span>: View {\n <span style=\"color:#66d9ef\">var</span> body: some View {\n VStack {\n Text(<span style=\"color:#e6db74\">"Welcome to hourz."</span>)\n }\n .frame(maxWidth: <span style=\"color:#ae81ff\">900</span>, maxHeight: <span style=\"color:#ae81ff\">400</span>)\n }\n}\n</code></pre></div>",
"content_text": "Now, the frames are static. I need the frames to be resizable. Time to Google. So eventually, you need to set maxWidth and maxHeight inside the `frame()`.\n```swift\nstruct SignInView: View {\n var body: some View {\n VStack {\n Text(\"Welcome to hourz.\")\n }\n .frame(maxWidth: 900, maxHeight: 400)\n }\n}\n```\n",
"date_published": "2022-05-06T13:02:00+08:00",
"url": "https://law.gmnz.xyz/2022/05/06/now-the-frames.html"
},
{
"id": "http://lwgmnz.micro.blog/2022/05/06/time-to-implement.html",
"title": "SwiftUI Journey Part 2",
"content_html": "<p>Time to implement the sign in page. Welp.\n<!-- raw HTML omitted -->\nAhh, forgot the frame().</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"color:#66d9ef\">struct</span> <span style=\"color:#a6e22e\">SignInView</span>: View {\n <span style=\"color:#66d9ef\">var</span> body: some View {\n Text(<span style=\"color:#e6db74\">"I'm Sign In"</span>)\n .frame(width: <span style=\"color:#ae81ff\">700</span>, height: <span style=\"color:#ae81ff\">300</span>)\n }\n}\n</code></pre></div><!-- raw HTML omitted -->\n",