Jump to content

vfbb

Members
  • Content Count

    278
  • Joined

  • Last visited

  • Days Won

    30

Everything posted by vfbb

  1. vfbb

    Common code base for VCL and FMX control code

    I suggest you to create 2 different packages, with the same units, but need to have 2 different packages, one for FMX and one for VCL. After create packages, add in the project options of each package, in the "Conditional defines" something like USING_VCL for the vcl package and USING_FMX for the fmx package.In code of your .pas just use: {$IFDEF USING_VCL} ... {$ENDIF} or {$IFDEF USING_FMX} ... {$ENDIF}
  2. Depois que as suas tabelas estiverem exatamente da forma como te falei, você poderá usar o seguinte código: var LCountriesIds: TArray<Integer>; LCitiesIds: TArray<Integer>; // Load the cities after country combobox change procedure TForm1.ComboBox1Change(Sender: TObject); begin ComboBox2.Items.Clear; SetLength(LCitiesIds, 0); if ComboBox1.ItemIndex = -1 then Exit; FDQuery1.SQL.Text := 'SELECT city.city_id, city.city_name FROM city WHERE city.country_id=:country_id ORDER BY city.city_name ASC;'; FDQuery1.ParamByName('country_id').AsInteger := LCountriesIds[ComboBox1.ItemIndex]; FDQuery1.Open; try while not FDQuery1.Eof do begin LCitiesIds := LCitiesIds + [FDQuery1.FieldByName('city_id').AsInteger]; ComboBox2.Items.Add(FDQuery1.FieldByName('city_name').AsString); FDQuery1.Next; end; finally FDQuery1.Close; end; end; // Load all countries after form show procedure TForm1.FormShow(Sender: TObject); begin ComboBox1.Items.Clear; SetLength(LCountriesIds, 0); FDQuery1.SQL.Text := 'SELECT country.country_id, country.country_name FROM country ORDER BY country.country_name ASC;'; FDQuery1.Open; try while not FDQuery1.Eof do begin LCountriesIds := LCountriesIds + [FDQuery1.FieldByName('country_id').AsInteger]; ComboBox1.Items.Add(FDQuery1.FieldByName('country_name').AsString); FDQuery1.Next; end; finally FDQuery1.Close; end; ComboBox1Change(nil); end; Entender este simples código é primordial para fazer qualquer query.
  3. vfbb

    FMX- Cross platform embed resources

    You can not change an signed iOS, MacOS or Android app, if the app is not yours. But if you have the source, the app is your, you have many options to save the resources strings, you can save in particular files for it. When we are talking about resources strings for translation, this will allow the store to make smallest packages of your app, because it will not delivery the unnecessary data to the user. For example, a French language will not be included in a user that use English language ... However, this languages files isn’t usually big, and I personally prefer to make a cross platform solution, languages in the source code. Other solution is storing it in a SQLite.
  4. Suas tabelas estão erradas, veja novamente como teria que ser: É sempre melhor executar essas queries dentro do código, você terá mais opções fazendo isso.
  5. You need 2 tables with this fields city city_id country_id city_name country country_id country_name Get all countries SELECT * FROM country ORDER BY country.country_name ASC; Get all cities with their countries SELECT * FROM city LEFT JOIN country ON city.country_id=country.country_id ORDER BY city.city_name ASC; Get all cities of a specific country (change the XXX to the country number) SELECT * FROM city WHERE city.country_id=XXX ORDER BY city.city_name ASC;
  6. vfbb

    FMX- Cross platform embed resources

    What is the real purpose? Are the apps from third parties? Perhaps there is a much simpler solution than changing different platform binaries, explain it better.
  7. vfbb

    ANN HTML Library 4.1 released

    Just search in the web "remove unused css" you will find many tools. Recently, I used the Google Chrome without any extension, just open the DevTools > Sources > Coverage. You will see a report with all unused css codes and javascripts.
  8. vfbb

    Random Access Violation?

    For random AV, I would say you have a multithreaded application without correct synchronization.
  9. vfbb

    ANN HTML Library 4.1 released

    Do you really need it at run-time? If not, you will find many tools to do this in the web.
  10. vfbb

    Frequent compilation errors

    The same here. For it to occur less often, avoid use Build, just use Compile.
  11. Compile with Android 64 with AAB Bundles enabled and selected the Application Store configuration. But before it, check the Libraries (jar files) of the Android 64 bits, some jar file may be missing. Note: when you compile the AAB, to make the Android 32 and Android 64 packages, the delphi will only consider the jar files from Android64 libraries... I had the same problem, because my Android 32 had the google vision jars but after made the AAP Bundles, in the Android 32 bits the jar files weren’t there.
  12. It depends on the hash algorithm being used but in general it will not make a difference. For the dictionary implemented in delphi, the best capacity is always twice the number of items for known collections and 0 for unknown collections.
  13. It is not an issue, it is the cost benefit of higher performance x less memory. But the optimization of creating it with the capacity is valid considering that the performance increases is on average 30%, both for small collections and for large ones. uses System.SysUtils, System.Generics.Collections, System.Diagnostics, FMX.Dialogs; procedure TestWithInitialCapacity; var I, J, LNewId: Integer; LDictionary: TDictionary<Integer, Boolean>; LStopwatch: TStopwatch; begin LStopwatch := TStopwatch.StartNew; for I := 0 to 10000 do begin LDictionary := TDictionary<Integer, Boolean>.Create(200); for J := 0 to 100 do begin repeat LNewId := Random(High(Integer)); until not LDictionary.ContainsKey(LNewId); LDictionary.Add(LNewId, True); end; FreeAndNil(LDictionary); end; showmessage(Format('Test with initial capacity: %g', [LStopwatch.Elapsed.TotalMilliseconds])); end; procedure TestWithoutInitialCapacity; var I, J, LNewId: Integer; LDictionary: TDictionary<Integer, Boolean>; LStopwatch: TStopwatch; begin LStopwatch := TStopwatch.StartNew; for I := 0 to 10000 do begin LDictionary := TDictionary<Integer, Boolean>.Create; for J := 0 to 100 do begin repeat LNewId := Random(High(Integer)); until not LDictionary.ContainsKey(LNewId); LDictionary.Add(LNewId, True); end; FreeAndNil(LDictionary); end; showmessage(Format('Test without initial capacity: %g', [LStopwatch.Elapsed.TotalMilliseconds])); end;
  14. Exactly! The Dictionary is only O(1) in the best case, as there can be many collisions of the key hashes, and this creates a loop that can reach O (n * 3/4) in the worst case, although it is very difficult. But what makes losing more performance are the grows that can occur as teams are added because the implementation maintain the count 50% -75% of the capacity, which I consider high value. But to avoid the many grows, and allow fewer collisions, just create it with the desired capacity. For example, for a collection of 100 items, I simply start with a capacity of 200. LDicionary := TDictionary<TKey, TValue>.Create(200); Remember, when you call Clear from the dictionary, it also removes the capacity, so you better recreate it instead of Clear.
  15. vfbb

    iOS handle incoming url

    @Dave Nottage Hi Dave, I tested and works perfectly!! Thanks you so much, you are the man!! Just to register here the complete solution. (I preferred to patch the delphi source files because I already have others patchs in that files) iOS - Handle incoming url (custom schemes or universal links) In the file iOSapi.Foundation.pas put the code: // Dave Nottage code (https://www.delphiworlds.com/) NSUserActivityPersistentIdentifier = NSString; TNSUserActivityBlockMethod1 = procedure(inputStream: NSInputStream; outputStream: NSOutputStream; error: NSError) of object; TNSUserActivityBlockMethod2 = procedure of object; NSUserActivityClass = interface(NSObjectClass) ['{412EAEBF-5927-4D01-B83F-69D3B5DFE7B5}'] {class} procedure deleteAllSavedUserActivitiesWithCompletionHandler(handler: TNSUserActivityBlockMethod2); cdecl; [MethodName('deleteSavedUserActivitiesWithPersistentIdentifiers:completionHandler:')] {class} procedure deleteSavedUserActivitiesWithPersistentIdentifiers(persistentIdentifiers: NSArray; handler: TNSUserActivityBlockMethod2); cdecl; end; NSUserActivity = interface(NSObject) ['{B8C2F6C9-31FE-4282-B7CA-98C96E163033}'] function activityType: NSString; cdecl; procedure addUserInfoEntriesFromDictionary(otherDictionary: NSDictionary); cdecl; procedure becomeCurrent; cdecl; function delegate: Pointer; cdecl; function expirationDate: NSDate; cdecl; procedure getContinuationStreamsWithCompletionHandler(completionHandler: TNSUserActivityBlockMethod1); cdecl; function initWithActivityType(activityType: NSString): Pointer; cdecl; procedure invalidate; cdecl; function isEligibleForHandoff: Boolean; cdecl; function isEligibleForPrediction: Boolean; cdecl; function isEligibleForPublicIndexing: Boolean; cdecl; function isEligibleForSearch: Boolean; cdecl; function keywords: NSSet; cdecl; function needsSave: Boolean; cdecl; function persistentIdentifier: NSUserActivityPersistentIdentifier; cdecl; function referrerURL: NSURL; cdecl; function requiredUserInfoKeys: NSSet; cdecl; procedure resignCurrent; cdecl; procedure setDelegate(delegate: Pointer); cdecl; procedure setEligibleForHandoff(eligibleForHandoff: Boolean); cdecl; procedure setEligibleForPrediction(eligibleForPrediction: Boolean); cdecl; procedure setEligibleForPublicIndexing(eligibleForPublicIndexing: Boolean); cdecl; procedure setEligibleForSearch(eligibleForSearch: Boolean); cdecl; procedure setExpirationDate(expirationDate: NSDate); cdecl; procedure setKeywords(keywords: NSSet); cdecl; procedure setNeedsSave(needsSave: Boolean); cdecl; procedure setPersistentIdentifier(persistentIdentifier: NSUserActivityPersistentIdentifier); cdecl; procedure setReferrerURL(referrerURL: NSURL); cdecl; procedure setRequiredUserInfoKeys(requiredUserInfoKeys: NSSet); cdecl; procedure setSupportsContinuationStreams(supportsContinuationStreams: Boolean); cdecl; procedure setTargetContentIdentifier(targetContentIdentifier: NSString); cdecl; procedure setTitle(title: NSString); cdecl; procedure setUserInfo(userInfo: NSDictionary); cdecl; procedure setWebpageURL(webpageURL: NSURL); cdecl; function supportsContinuationStreams: Boolean; cdecl; function targetContentIdentifier: NSString; cdecl; function title: NSString; cdecl; function userInfo: NSDictionary; cdecl; function webpageURL: NSURL; cdecl; end; TNSUserActivity = class(TOCGenericImport<NSUserActivityClass, NSUserActivity>) end; ... function NSUserActivityTypeBrowsingWeb: NSString; ... implementation ... function NSUserActivityTypeBrowsingWeb: NSString; begin result := CocoaNSStringConst(FoundationFwk, 'NSUserActivityTypeBrowsingWeb'); end; In the file FMX.Platform.iOS.pas, in the TApplicationDelegate class, in the private section, put the code: class function applicationContinueUserActivityRestorationHandler(self: id; _cmd: SEL; application: PUIApplication; userActivity: Pointer; restorationHandler: Pointer; restorableObjects: Pointer): Boolean; cdecl; static; In the file FMX.Platform.iOS.pas, in the implementation of the method TApplicationDelegate.CreateDelegateMetaClass, before the line "objc_registerClassPair(DelegateClass);", put the code: class_addMethod(DelegateClass, sel_getUid('application:continueUserActivity:restorationHandler:'), @applicationContinueUserActivityRestorationHandler, 'B@:@@@@'); In the file FMX.Platform.iOS.pas, in the TApplicationDelegate implementation, put the code: class function TApplicationDelegate.applicationContinueUserActivityRestorationHandler( self: id; _cmd: SEL; application: PUIApplication; userActivity, restorationHandler, restorableObjects: Pointer): Boolean; var LUserActivity: NSUserActivity; LURLString: string; begin Result := False; if Assigned(userActivity) then begin LUserActivity := TNSUserActivity.Wrap(userActivity); if NSStrToStr(LUserActivity.activityType) = NSStrToStr(NSUserActivityTypeBrowsingWeb) then begin if Assigned(LUserActivity.webpageURL) then LURLString := NSStrToStr(LUserActivity.webpageURL.absoluteString) else LURLString := string.Empty; Result := PlatformCocoaTouch.HandleApplicationEvent(TApplicationEvent.OpenURL, TiOSOpenApplicationContext.Create(string.Empty, LURLString, nil)); end; end; end; Usage uses System.Messaging, FMX.Platform, FMX.Platform.iOS, FMX.Dialogs; constructor TipUrlHandler.Create; begin inherited Create; TMessageManager.DefaultManager.SubscribeToMessage(TApplicationEventMessage, ApplicationEventMessageHandler); end; destructor TipUrlHandler.Destroy; begin TMessageManager.DefaultManager.Unsubscribe(TApplicationEventMessage, ApplicationEventMessageHandler, True); inherited; end; procedure TipUrlHandler.ApplicationEventMessageHandler(const ASender: TObject; const AMessage: TMessage); begin case TApplicationEventData(TApplicationEventMessage(AMessage).Value).Event of TApplicationEvent.OpenUrl: begin Showmessage(TiOSOpenApplicationContext(TApplicationEventData(TApplicationEventMessage(AMessage).Value).Context).URL); end; else end; end;
  16. vfbb

    Memory Management with many objects

    Out of memory I really like to use Grijjy Cloud Logger. You can monitor all objects in your program in real time, and each time you update this list it will indicate the growth (in number of instances and memory) of each class. Best of all, this can be done in both debug mode and release mode. Use this only in the development environment as there will be a small loss of performance. Move to 64 bits I advise you to start writing code with 64-bit support as soon as possible, this is the future, because although microsoft is not forcing this migration yet, several others have already done it, such as Android, iOS and OSX which today only allow 64 bits. It is a trend even from Embarcadero itself since it added support for Linux only 64 bits. But, of course, keep support for 32-bit too, as there may be users with 32-bit windows still and also because the Win32 debugger is better than Win64. 😉
  17. I have a base data class that is used for multithreaded data classess in my project. I made an implementation to reduce thousands of lines of code, which theoretically should work, but even with some successful tests, I am afraid to release a version to the end user without being sure. function TipCustomData.GetSafeValue(var AField: string): string; begin Lock; // Enter critical section try Result := AField; finally Unlock; // Leave critical section end; end; procedure TipCustomData.SetSafeValue(var AField: string; const AValue: string); begin BeginUpdate; // Enter critical section try if AField <> AValue then begin AField := AValue; Change; end; finally EndUpdate; // Leave critical section triggering change event end; end; In the code above, the most intuitive in these cases would be to use the field pointer, but as "var" parameters internally it is already passed as a pointer (regardless of type), so both implementations should work in the same theoretical way (correct me if i'm wrong). Of course, I only access the parameters within the critical section. The implementation of my multithreaded data class, descendant of this, would be for example like this: type TipUser = class(TipCustomData) private FName: string; function GetName: string; procedure SetName(const AValue: string); public property Name: string read GetName write SetName; end; function TipUser.GetName: string; begin Result := GetSafeValue(FName); end; function TipUser.SetName(const AValue: string); begin Result := SetSafeValue(FName, AValue); end; So the question is, is this cleaner implementation that could exist of a multithreaded data class, is it really thread-safe? Sorry for my bad english. 😉
  18. @Hans♫ in the iTunes you can download each user crash report file and view the call stack to identify the problem. You will need to symbolize the crash file using the dSYM file of the version of your app thats crashed. Read about:
  19. Exploring the .crash files generated by the iOS symbolizing it to identify the call stack https://github.com/viniciusfbb/fmx_tutorials/blob/master/delphi_ios_exploring_app_crashes/README.md
  20. vfbb

    This implementation is Thread-safe?

    Yes, the BeginUpdate call Lock internally, and the EndUpdate call Unlock. The same critical section. Just one critical section per object. Yes, my Change event is completely asynchronous, similar to the fmx messaging system (subscribe / unsubscribe), but it uses X threads to send these messages from one thread to another. I don't like the term "big risk" or "low risk" of deadlock. A good design has to be done with the chance of zero deadlock, locking the object just to copy the data to the local var of the thread or else locking only to write local var of the thread in the object (without executing anything inside locking). Example: procedure TipThread.Execute var LName: string; LId: Cardinal; begin // Read copying to local var LUser.Lock; try LName := LUser.Name; LId := LUser.Id; finally LUser.Unlock; end; // Run your code... // Write from local var LUser.BeginUpdate; try LUser.Name := LName; LUser.Id := LId; finally LUser.EndUpdate; end; end; This will never cause a deadlock.
×