Downgrade 2019.R2 to 2019.R1.1

It had to happen sooner or later. But now questions about how to do this are appearing. And the answer is “it depends”.

ALWAYS use a copy or have a backup copy of your entire project JUST in case something goes wrong.

IF you have a license that permits you to save as text (Xojo Project) save your project as a text project. You then need to open the single file called projectname.xojo_project

At the very beginning of this file you will see entries like the following :

Type=Desktop
RBProjectVersion=2019.02
MinIDEVersion=20190200
OrigIDEVersion=20190200

Change the number after MinIDEVersion to 20070100 and save. You can now open the project in whatever version you want.

In an XML project you would see

<?xml version="1.0" encoding="UTF-8"?>
<RBProject version="2019r2" FormatVersion="2" MinIDEVersion="20190200">
<block type="Project" ID="0">

Again change the number following MinIDEVersion to 20070100. Make sure you keep the quotes around the value. And save.

Binary is a lot tougher because it is a proprietary format. And to edit that you WILL need a licensed version of Arbed. Once you have that you can open a binary project and select the top entry which represents the project and change the OriginalVersion to 20070100 and the ProjectSavedInVers to 2007.01 and save.

And now you can open your project in old versions again.

Prepared Statements

Often you’ll hear people say you should use prepared statements to avoid sql injection issues. They’re not wrong. But there can be other reasons to use them.

In some db’s when you create a prepared statement and use it over & over you can avoid a fair amount of a speed hit.

When you initially create a prepared statement many db engines will actually do some work to figure out what the optimal mechanism, or query plan, is to access the data in a table.

And by reusing this over & over you can avoid that computation to determine that optimal query plan.

Not ALL db’s do this, and in some it wont matter if you use a prepared statement because they simply recompute the query plan every time anyways.

But, even if the db engine DOES recompute the query plan over & over you still get all the benefits of avoiding sql injections issues by using them.

I’d say they definitely fall into the category of “best practice”.

A little off the usual

With all the hoopla going on about R2 I have been reading some things that are NOT programming related. Not my usual sort of thing to read.

Stuff like this.

And as I read the article I kept saying to myself Uh huh, Yup and Dead On. And the initial observation he made about

Customers switch because the first company let them down. Customer switch because switching is personal. Customers switch because of how you make them FEEL. Maya Angelou said it best: โ€œPeople will forget what you said, people will forget what you did, but people will never forget how you made them feel.โ€

and thinking this is so true.

Much of what I’ve read has expressed how disappointed people are talk of the dismissal of their concerns. This has been said in more than one messaging system I participate in and on blogs I read.

How this is all going to fall out for Xojo I have no idea.

But there are many who have expressed they are done and departing as they just can’t take being ignored any longer especially when it puts their businesses at risk.

Extension methods

Sometimes you run into a situation where you want to add functionality to an existing data type. It might be a string, integer, array or perhaps one of the built in classes that you want to add new functionality to.

Exactly how do you do this ?

You can’t just edit the framework and add new methods to the String type.

And that is probably a good thing. Imagine if every user could edit String and add methods to it. There might be a huge plethora of incompatible implementations of String very rapidly.

Since you cannot edit String what can you do ?

Extension methods!

You CAN create a method type that is almost indistinguishable from a method in the base type itself. First I would suggest creating a Module named with a meaningful name. For a Module to add new method to the String data type I would name the module StringExtensions so its very clear what this module contains.

The in this module you can add GLOBAL methods. And each method in the module should have a signature that uses the extends keyword.

I’ll demonstrate. Suppose we wanted to add a new method to String that checks if the first letter of the string is a digit. We’ll name that method IsDigit.

For create a new desktop project.

Add a Module to the project called StringExtensions.

Then add a method to the StringExtensions module and name the method IsDigit.

The parameters for this method should be

extends aString as String

Note the use of EXTENDS. This is the hint to the compiler that this method should be treated as if it were one of the methods in the framework for the String datatype.

The return type for this method should be Boolean.

The code in the method should be

If aString.Trim = "" Then 
  Return False
End If

If InStr( "0123456789", aString.Left(1) ) > 0 Then
  Return True
End If

return false

The finished method should appear like

Then in the Open, or in 2019r2 the Opening event, you can test the new extension method with code like


Dim s As String

s = "abc"
If s.IsDigit Then
  Break
End If

s = ""
If s.IsDigit Then
  Break
End If

s = "1abc"
If s.IsDigit Then
  Break
End If

The first two cases should return false. And the last one should return true.

The only thing to be careful about with extension methods is :

  • you cant name the Module the same as the datatype. The IDE will generally prevent you from doing this.
  • you have to not create a new extension that may conflict with one that is in the framework.
  • you have to be careful not to create one that will cause the compiler to decide there is an ambiguous use case where it cant decide which one to call. This can happen when you use optional parameters.

But other than that you can extend most datatypes, even ones in the framework, relatively easily.

Moving to 2019r2

2019r2 changes a LOT of things.

Some of the things it changed are subtle and search and replace may NOT be the best approach to updating these.

For instance string method like Mid changed from being a 1 based offset for the starting position to a 0 based offset.

If all you do is change mid to middle you will inadvertently cause an off by one error because the start is now 0 based not 1 based.

For instance

var foo as string = "1234"
var midString as string = foo.mid(2,2)
var middleString as string = foo.middle(2,2)

will give you different results in midString and middleString.

There are many other places where the offsets have changed from 1 based to 0 based so be careful updating your code. Make sure you examine all the parameters being used as well to avoid causing yourself a lot of extra work trying to hunt down those off by one errors.

Point. Missed.

Reading the latest blog post about “Your path forward with API 2.0” just reassured me that the pain API 2.0 brought to third parties was not understood. It puts those who create and sell code to others or who sell applications to a broad base of customers across many OS versions in a tough spot.

Xojo is a great tool. Many of us have said that over and over. In part what makes it great is that we can, from a single code base, support a diverse set of customers with a single code base. This is often critical to a third party working in the Xojo ecosystem. That ecosystem is just not large enough to focus solely on one version or one OS.

Often this means that we need to use new versions to support those customers who update to the latest OS the day it is released, and older versions to support those who choose to stay on older versions of their OS for whatever reasons.

Until now that has been a very simple proposition in Xojo. Xojo never deliberately did things that they knew would break code until or unless they absolutely had to. That changed dramatically with 2019r2 and API 2.0.

But now we can’t do that. Not just “we can’t do it well”.

We can’t do it at all. Certainly not for third parties that provide code to their customers.

And certainly not from a single code base any longer.

Those who want to update to the very latest version of Xojo want our products to support API 2.0 immediately. And those who use older versions need us to remain supporting the older API’s for now.

With the advent of API 2.0 that is actually not possible in a single code base.

The only reason its not is because of the events that were renamed. Everything else could have been handled invisibly to our users with #if Target or #If Xojo version conditional compilation.

Just. Not. Events. ๐Ÿ™

So there are third parties that simply aren’t updating their products today because doing so would cut their customer base significantly. Not because they don’t WANT to update. But because doing so would hurt their business and their customers.

Others may not provide code but instead create bespoke applications. Their customers may be using the very latest OS versions or really old ones for many reasons. Once again the only option that these Xojo developers have is to avoid using API 2.0 because once they do so they cannot go back to old Xojo versions to update their products for their customers. They can’t use API 2.0 at all. Certainly not any of the renamed events. And they may not be able to even use the latest Xojo versions for anything but a “compile only” option as editing their projects in 2019r2 introduces so many changes that they cant make heads or tails out of their version control system change logs.

All these concerns were voiced during the betas for 2019r2. And apparently fell on deaf ears.

Anyone trying to support users of their code or products across multiple versions of Windows. macOS across many versions of Xojo is simply not possible in a single code base. We’ve been asking how to do this since betas started appearing with API 2.0 and have so far had no response that acknowledges that API 2.0 creates this problem.

So far it seems that Xojo’s advice is “update to 2019r2 and the world is all sunshine and light”. But its not. Not for anyone that has to support customers that may have no choice about NOT moving up to the latest version or cannot move up to the latest OS. 20192 and API 2 are a non-starter in that case despite what Xojo Inc tells us.

The only other choice is to create two ce bases and try and keep them in sync between the API 1.0 code and API 2.0 code. This effectively doubles everyones work load.

And brings few benefits for that doubled workload.

The old APIs continue to work and, as noted in the Xojo bog post,

Many deprecated APIs still share their implementation with the API they replace.

https://blog.xojo.com/2019/10/29/your-path-forward-with-api-2-0/

many third parties are just not touching API 2.0, or 2019r2, for now and are just supporting the old API’s for now since that makes is possible for every Xojo version to use their code.

This situation didn’t have to exist. But, as Bob notes, feedback given during betas seems to have been ignored.

I’m truly disappointed.

EDIT : As was pointed out to me off the forums & lists I neglected to mention that the rename of properties also causes issues. Not quite the same issue as events but still significant backward & forward issues.

Express yourself

I’ve seen a number of people say they are not renewing their subscription to Xojo. And because of that they are also cancelling subscriptions to third party products they rely on when using Xojo.

IF you are one of the people please let Geoff know why you are not renewing or are leaving.

Spot the bug

You might run into this one from time to time. I know it has bitten a LOT of people and it’s not intuitive at all.

In a new desktop app add a new Class. The default name of Class1 will work fine. And create a new subclass of this. Again the default of CustomClass1 will work fine.

Then in the default window add the open event handler. In there put these few lines of code.

Dim c As Class1
c = methodThatCreatesSubclassInstance
foo(c)

Then add two methods to Window1

The first should be defined as

Private Function methodThatCreatesSubclassInstance() as Class1
  Return New CustomClass1
End Function

And then we need two versions of Foo

Private Sub foo(c as Class1)
  Break
End Sub

Private Sub foo(c as CustomClass1)
  Break  
End Sub

Thats it. If you run this you might think that because methodThatCreatesSubclassInstance returns a CustomClass1, which you can see in the debugger at runtime, that the sub that takes a CustomClass1 would be the one that is called.

And you might try various tricks to make that happen like changing the return type of methodThatCreatesSubclassInstance to be CustomClass1.

Short of explicitly casting, which you really might want to avoid, you’ll find that the version of Foo that takes a Class1 parameter is the one that gets called.

And this is something I’ve long considered a bug. But various compiler developers have disagreed ๐Ÿ™

The important thing here is not the runtime type that the variable c holds. At runtime you can inspect it and see that it has exactly what we expected – an instance of CustomClass1.

What IS important is the declared type of c – a Class1. And the compiler decides, at compile time, that the version of Foo that should be used is the one that has a Class1 parameter.

Careful out there.

About incremental compilation

There are ways to see what is / isnt being recompiled

Its one of those many temporary files xojo creates

First thing you need is the PID of the specific instance of xojo you’re interested in.
It makes life simpler IF you just run one version at a time
Once you start Xojo you can look in Activity Monitor & get its PID

If you find the dir referred to by SpecialFolder.Temporary
a two liner in Xojo like this would suffice

dim f as folderitem = SpecialFolder.Temporary

break

for me on 10.14.6 this is buried in

/private/var/folders/3t/wyqsbmsj4bbf_z_wdrjnp0zw0000gn/T/TemporaryItems


Inside there should be a dir named “xojo scratch <PID>” and the PID will be the one you looked up previously in Activity Monitor

Inside there are different dirs for each open project that you have saved & run
They are generally named in a way that its not so hard to tell which is which

If you open this in the Finder then you can sort by last modified
Every time you run you can see the modification dates of things and tell what is / isnt being recompiled