Friday, January 5, 2024

Delphi Tip of the Day: Prevent SQLite Date Headaches by using a GetDateAs_YYYYMMDD Function

Working with Date data can be very tricky. I recently encountered an "Invalid argument to date encode", error while trying to update a SQLite database table.


This placed a value of 0000-00-00 into the date field of my SQLite table.

Here is the original code which caused the error.

function TForm1.GetOneOffDateAsDate: TDate;
begin
  Result := DateEdit1.Date;
end;

procedure TForm1.SQLInsertPayment;
begin
  qryO.SQL.Clear;

  qryO.SQL.Add( ' INSERT INTO OneOffPayments    ');
  qryO.SQL.Add( '             (                 ');
  qryO.SQL.Add( '             OneOffDate,       ');
  qryO.SQL.Add( '             OneOffPayment     ');
  qryO.SQL.Add( '             )                 ');
  qryO.SQL.Add( '      VALUES (                 ');
  qryO.SQL.Add( '             :ood,             ');
  qryO.SQL.Add( '             :oop              ');
  qryO.SQL.Add( '             );                ');

  qryO.ParamByName( 'ood' ).Value := GetOneOffDateAsDate;
  qryO.ParamByName( 'oop' ).Value := GetOneOffAmount;

  qryO.ExecSQL;
end;

The getter function GetOneOffDateAsDate passes in a TDate which doesn't play nicely with FireDAC. Fortunately, the fix is quite simple. I found a fantastic explanation for this error on stackoverflow which states FireDAC expects DATE data type values to be a string in the fixed format of YYYY-MM-DD.

FireDAC Expects DATE data types
to be strings formatted as YYYY-MM-DD

So I created another getter function to format the date data as a YYYY-MM-DD string.
Problem solved!

Updated code passing FireDAC a YYYY-MM-DD string

function TForm1.GetOneOffDateAs_YYYYMMDD: String;
begin
  Result := FormatDateTime('YYYY-MM-DD', DateEdit1.Date);
end;

procedure TForm1.SQLInsertPayment;
begin
  qryO.SQL.Clear;

  qryO.SQL.Add( ' INSERT INTO OneOffPayments    ');
  qryO.SQL.Add( '             (                 ');
  qryO.SQL.Add( '             OneOffDate,       ');
  qryO.SQL.Add( '             OneOffPayment     ');
  qryO.SQL.Add( '             )                 ');
  qryO.SQL.Add( '      VALUES (                 ');
  qryO.SQL.Add( '             :ood,             ');
  qryO.SQL.Add( '             :oop              ');
  qryO.SQL.Add( '             );                ');

  qryO.ParamByName( 'ood' ).Value := GetOneOffDateAs_YYYYMMDD;
  qryO.ParamByName( 'oop' ).Value := GetOneOffAmount;

  qryO.ExecSQL;
end;

Happy coding!


Enjoy!
Gunny Mike
https://zilchworks.com

Monday, January 1, 2024

How to open URLs with default applications in macOS and Windows

 I'm currently updating an old Delphi 5 Desktop VCL application to to Delphi 11.3 FMX. And one of the capabilities I want to provide is the ability to launch several webpages from within the application. I want to place a link in the main menu to my YouTube channel so customers can easily get to product videos. And there's also a link to my website in the Help > About box.



It was fairly straightforward the last time I did this using VCL because all I had to worry about was the Windows side of things. However, because I want this application to run on both Windows and macOS it presented a challenge.

The Delphi IDE won't recognize the Macapi namespace unless the target is set to MacOS 64-bit

Harry Stahl covers the COCOA API on pages 98-99 of his book Cross-Platform Development with Delphi. He also gives an example of how to use the NSWorkspace object of  Macapi.Appkit. However, he doesn't show how to setup the uses clause.

I also found a fantastic reference on stackoverflow by David Heffernan that was written in 2015. However, there are two issues with Heffernan's if you are looking for a complete answer:

  1. There is a reference to a blog post by Malcolm Groves called Opening files and URLs in default applications in OS X which is no longer available or accessible.
  2. The example doesn't tell you you need to target the MacOS 64-bit platform before the IDE will recognize Macapi namespace..
The Delphi IDE won't recognize the Macapi namespace unless the target is set to MacOS 64-bit. Shame on me for not reading up on the Embarcadero docs. Wrapping my head around how to use the  {$IFDEF MSWindows} and the {$IFDEF MACOS} was a little tricky But I eventually caught on. 

After a couple hours of going back and forth with code that worked for Windows but didn't work for macOS. And code that worked for macOS but didn't work for Windows, I finally got Heffernan's example to work.

The next step was to extract the code out of the main form and place it into it's own unit. And that is the code I'm sharing with you today. I hope you find this helpful. 



Video demonstration of the below source code

Here is the source code:

Unit1.fmx
object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Goto Website'
  ClientHeight = 238
  ClientWidth = 478
  Position = ScreenCenter
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  DesignerMasterStyle = 0
  object Text1: TText
    AutoSize = True
    Cursor = crHandPoint
    Position.X = 116.000000000000000000
    Position.Y = 83.000000000000000000
    Size.Width = 246.078125000000000000
    Size.Height = 31.921875000000000000
    Size.PlatformDefault = False
    Text = 'https://zilchworks.com'
    TextSettings.Font.Size = 24.000000000000000000
    TextSettings.FontColor = claMediumblue
    OnClick = Text1Click
    OnMouseEnter = Text1MouseEnter
    OnMouseLeave = Text1MouseLeave
  end
end

Unit1.pas
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects;

type
  TForm1 = class(TForm)
    Text1: TText;
    procedure Text1MouseEnter(Sender: TObject);
    procedure Text1MouseLeave(Sender: TObject);
    procedure Text1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  u.OpenURL;

{ TForm1 }

procedure TForm1.Text1Click(Sender: TObject);
begin
  GotoWebsite(Text1.Text);
end;

procedure TForm1.Text1MouseEnter(Sender: TObject);
begin
  Text1.Font.Style :=  Text1.Font.Style + [TFontStyle.fsUnderline];
end;

procedure TForm1.Text1MouseLeave(Sender: TObject);
begin
  Text1.Font.Style :=  Text1.Font.Style - [TFontStyle.fsUnderline];
end;

end.

u.OpenUrl.pas
{
+-------------------------------------------------------------------------------
¦ Filename:    u.OpenURL Unit
¦ Author:      Michael J. Riley
¦ Website:     https://zilchworks.com
¦ YouTube:     https://www.youtube.com/@CapeCodGunny
¦ Copyright:   © 2023-2024 by Michael J. Riley. All Rights Reserved.
+-------------------------------------------------------------------------------
¦ Purpose:     Opens a url using the default browser on the users system.
¦
¦ OS:          Windows, macOS
+-------------------------------------------------------------------------------
¦ Developer References:
¦
¦ Book:        Cross-Platform Development with Delphi 10.2 & FireMonkey
¦ Author:      Harry Stahl
¦ ISBN:        https://isbnsearch.org/isbn/9781549545764
¦ Notes:       See pages 98-99.
¦
¦ Websites:    https://stackoverflow.com/q/28858392/195983
¦              https://stackoverflow.com/a/28859000/195983
+-------------------------------------------------------------------------------
¦ DISCLAIMER:
¦
¦ This source code is provided "as is" and without any warranty. Use it at your
¦ own risk. The author(s) make no guarantees or assurances regarding the
¦ correctness or functionality of the code, and disclaim any liability for
¦ damages resulting from its use.
¦
¦ It is advisable to thoroughly review and test the code before deploying it in
¦ any production environment.
¦
¦ By using this code, you agree to these terms and acknowledge that any issues
¦ arising from its use are solely your responsibility.
+-------------------------------------------------------------------------------
}
unit u.OpenURL;

interface

{$IFDEF MSWindows}
uses
  Winapi.ShellAPI,
  Winapi.Windows;
{$ENDIF}

{$IFDEF MACOS}
uses
  Macapi.AppKit,
  Macapi.Foundation,
  Macapi.Helpers;

  procedure macOSGotoWebsite(URL: string);
{$ENDIF}

  procedure GotoWebsite(URL: string);

implementation


procedure GotoWebsite(URL: string);
begin
  {$IFDEF MSWindows}
  ShellExecute(GetDesktopWindow, 'open', PChar(URL), '', '', SW_SHOWNORMAL)
  {$ENDIF}
  {$IFDEF MACOS}
  macOSGotoWebsite(URL);
  {$ENDIF}
end;

{$IFDEF MACOS}
procedure macOSGotoWebsite(URL: string);
var
  macURL: NSURL;
  macWorkspace: NSWorkspace;
begin
  macURL := TNSURL.Wrap(TNSURL.OCClass.URLWithString(StrToNSStr(URL)));
  macWorkspace := TNSWorkspace.Wrap(TNSWorkspace.OCClass.sharedWorkspace);
  macWorkspace.openURL(macURL);
end;
{$ENDIF}

end.

Enjoy!




Wednesday, December 20, 2023

Delphi Tip of the Day: Use Your Own Unique Prefix for Types and Constants

I'm in the process of converting an old Delphi 5 VCL application to Delphi 11 FMX. Yeah, I have Delphi 12 but I'm waiting for the first bug release before I start using it. Well, this morning I learned a valuable lesson. A lesson which stopped me dead in my tracks. 

There's nothing stopping you from creating an already existing type!

I was making some refactoring changes to my code. I didn't realize I was about to make a huge mistake. I went plodding ahead pleased with the progress I was making and admiring the beautiful, refactored code I was creating. When boom... I renamed a type that stepped on a type already inside the System.UITypes unit.

There's nothing to stop you from creating an already existing type. Of course I didn't use the preview changes function. I just hit go!

I had defined my own enumerated type called TMyImageIndex. I decided to just simply call it TImageIndex. So, that's what I did using Refactor > Rename type TMyImageIndex CTRL+SHIFT+E

I knew I messed up when I saw way too much code show up in the results panel.

How dare Embarcadero define a type called TImageIndex

WTF.  If it already existed the IDE should have prevented me or at least warned me. How dare Embarcadero define a type called TImageIndex.

type
//TImageIndex = (iiAdd = 2, iiUpdate = 3);)
  TZwImageIndex = (iiAdd = 2, iiUpdate = 3);)

const
  ZW_WIDTH_COLUMN_GLYPH  = 20;
  ZW_WIDTH_COLUMN_DATE   = 90;
  ZW_WIDTH_COLUMN_AMOUNT = 74

It took me about 30 minutes, but I was able to get the original code back in place, And working.

So going forward I have created my own unique prefix of  TZw (Zw = ZilchWorks). Going forward I will preface all Types and Constants.

Enjoy!
Semper Fi
Gunny Mike
https://zilchworks.com

Saturday, October 28, 2023

How to get the RSS Feed of YouTube Channels

I am a big fan of Harry Stahl's book "Cross-Platform Development with Delphi 10.2 & FireMonkey for Windows, MAC OS X (macOS) & Linux". I purchased this book in May 2021 and it has helped me several times as I'm porting my Zilch application from Delphi 5 VCL to Delphi 11 FMX.

Here's an Amazon link to Stahl's book: https://www.amazon.com/Cross-Platform-Development-Delphi-FireMonkey-Windows/dp/1549545760


I like to maintain RSS feeds of my favorite Delphi blogs. So, I went looking to see if Stahl has a blog I could add to my Outlook RSS Feeds. I found a couple of Stahl's websites. He does have a few blog posts but they are not easily converted into an RSS feed. 

Does YouTube support RSS feeds?

However, Stahl has a fairly active YouTube channel called Programming with Delphi. https://www.youtube.com/@programmingwithdelphi 

I wondered if YouTube supports RSS feeds? The answer is, YES!

Open Chrome and go to the YouTube channel you want to snag the RSS feed from. Right-click and choose the View page source option.



Then click the Line wrap checkbox in the top left corner of Chrome.



Press Ctrl-F to open the search box and enter rssurl.
Click and drag your mouse to highlight the URL associated with the rssurl attribute.
Press Ctrl-C to copy this highlighted URL to the clip board.
Your copied URL should look similar to the value below.

https://www.youtube.com/feeds/videos.xml?channel_id=UCiUVNZgsZZhziFL4oy-evEA


There you have it. That's all there is to grabbing the RSS Feed URL for any YouTube channel.

Enjoy!
Semper Fi
Gunny Mike
https://zilchworks.com




Sunday, July 23, 2023

Here's how I will code better in Delphi. How will you?

When it comes to Delphi, it's amazing how much I don't know, once knew but forgot, or just plain skipped over because it was too mind-boggling at the time. I took a break from reading Alister Christie's new book "Code Better in Delphi", which I purchased last night, to write this blog post.


While reading the bit on "refactoring", I realized how little refactoring I actually put into practice. Yes, I have Fowler's book, the second edition. And no, I haven't read it. I've thumbed through it a little in the past. I even tried looking for the "Introduce Explaining Variable" refactor Christie discusses on page 53 of his book. It turns out Fowler now calls it "Extract Variable". Keeping Fowlers "Refactoring" book on my desk, and referring to it often, is one way I am going to code better in Delphi.

It's amazing how much I don't know or once knew but forgot

I just started reading "The Observer Pattern" in Christie's book and had to stop. I bought "Head First Design Patterns" by O'Reilly, quite a few years ago. I have the 10th Anniversary edition with code examples in Java 8. My intent was to install Java on my machine and force myself to learn these patterns by following the code in the book. It didn't go so well. The authors do a great job using story to make learning fun. I just couldn't wrap my head around Java.

Christie has managed to reignite my desire to learn these patterns. I downloaded the Java source code. I plan to work my way through "Head First Design Patterns" pasting the Java source code into ChatGPT, and asking Chat to convert it to Delphi.

I will navigate through each pattern using this method. I will then revisit the same pattern in Primoz Gabrijelcic's book "Hand-On Design Patterns with Delphi". This is another way I'm going to learn to code better in Delphi. 

Another way is to revisit the Model-View-Controller method of code separation. Here is a the MVC song from 2007 I just learned about today. 

And I also want to implement "Interfaces".

Here's my list of how I will code better in Delphi:

  • Finish reading Christie's book 
  • Practice refactoring often
  • Learn and implement design patterns
  • Discover and utilize ways to separate code concerns such as MVC or MVVM
  • Overcome my stumbling block of Interfaces

Use the comments below to share how you will code better in Delphi.

Enjoy
Semper Fi
Gunny Mike

Sunday, July 2, 2023

Blueprint for Creating Delphi FMX Apps

 I just discovered an unlisted YouTube video Ray Konopka did in 2018 for Embarcadero's CodeRage called "Mobile View Management with Ray Konopka from RodeRage 2018

Although the demo and source code for this presentation is based on a mobile app, the concept Konopka presents is not just for mobile apps. It's actually a blueprint for creating any type of FMX app using Delphi.

Blueprint for Creating Delphi FMX Apps


I was compelled to write this blog for two reasons. The first stems from the fact that this is not just for mobile app development, it's for any FMX app development. And number two, the concept is simple, powerful, and elegant.

The source code Konopka used is available for download using this link https://delphibydesign.com/downloads/

From the Mind of Konopka

It doesn't matter whether you are brand new to Delphi or a seasoned Delphi veteran, if you get the opportunity to download and study an application built by Konopka, TAKE IT. You will learn so much. It's an opportunity to explore the mind of Konopka.

Konopka embodies the meaning of my favorite quote from Albert Einstein. "Make everything as simple as possible but not simpler."


Enjoy!
Semper Fi
Gunny Mike
https://zilchworks.com


Saturday, June 24, 2023

THE ULTIMATE BEGINNER'S GUIDE TO OBSIDIAN NOTES | How to Use Obsidian Notes

I have just started to explore Personal Knowledge Management (PKM). Didn't know it was a thing until a few days ago. Obsidian (https://obsidian.md) seems to be one of the most popular tools being used. This tutorial is a couple years old but it's very good. 

#PKM #obsidian #markdown

   

 Enjoy
Semper Fi
Gunny Mike
https://zilchworks.com