Home/Code tips, Project tweaks/A leg up for using ALAssetsLibrary in Delphi

A leg up for using ALAssetsLibrary in Delphi

Views:
16

Some time back, someone asked on the Embarcadero forums about using ALAssetsLibrary from the Assets Library framework in iOS with Delphi. I was curious because I thought I may be interested in using it myself.

It seemed relatively simple enough: create an Assets Library instance (TALAssetsLibrary from the iOSapi.AssetsLibrary unit), enumerate the groups, and for each group, enumerate the assets. That is until I discovered it wasn’t possible to move past 1st base: calling enumerateGroupsWithTypes was (at the time) causing a crash.

Recently I decided to revisit the issue, and this time the problem was an Invalid Typecast error. After a little digging, I came across someone in a similar situation in this post on stack overflow. He solved the problem by changing the definition for the “block” so that the method parameters all have pointer types.

I changed the method parameters to pointer types myself, and voila! I was able to call enumerateGroupsWithTypes without an error: made it past first base. Next step was to see what I actually have in those pointers. I figured the ALAssetsGroup parameter reference might be an objective-c object id, so I Wrap’d it using TALAssetsGroup, and I was able to successfully use the valueForProperty method. Success! It also turns out that the PBoolean really is one: it just can’t be passed via the block that way; it needs to be typecast inside the method.

The next problem is that similar action needs to be taken for ALAssetsGroup and ALAsset, both of which use block parameters.

Now for the really bad news: ALAssetsLibrary is deprecated (since iOS 9). The preferred method is to use Photos Framework, for which there is no translation available (at least out of the box) for Delphi. I have this task on my to-do list, however it is moving ever further downwards as other tasks are becoming more pressing.

Regardless, here’s some example code fragments that will give those who are also looking to use it, to give them a “leg up”:

uses
  iOSapi.AssetsLibrary, iOSapi.Foundation, Macapi.ObjectiveC;

type
  // A copy of what's in iOSapi.AssetsLibrary, but changed to Pointer parameters
  TALAssetsLibraryGroupsEnumerationResultsBlock = procedure(group: Pointer; stop: Pointer) of object;
  TALAssetsLibraryAccessFailureBlock = procedure(error: Pointer) of object;

  ALAssetsLibrary = interface(NSObject)
    ['{F6073848-1D90-456C-8785-73BEC411D3B6}']
    procedure enumerateGroupsWithTypes(types: ALAssetsGroupType; usingBlock: TALAssetsLibraryGroupsEnumerationResultsBlock; failureBlock: TALAssetsLibraryAccessFailureBlock); cdecl;
    procedure assetForURL(assetURL: NSURL; resultBlock: TALAssetsLibraryAssetForURLResultBlock; failureBlock: TALAssetsLibraryAccessFailureBlock); cdecl;
    procedure groupForURL(groupURL: NSURL; resultBlock: TALAssetsLibraryGroupResultBlock; failureBlock: TALAssetsLibraryAccessFailureBlock); cdecl;
    procedure addAssetsGroupAlbumWithName(name: NSString; resultBlock: TALAssetsLibraryGroupResultBlock; failureBlock: TALAssetsLibraryAccessFailureBlock); cdecl;
    procedure writeImageToSavedPhotosAlbum(imageRef: CGImageRef; orientation: ALAssetOrientation; completionBlock: TALAssetsLibraryWriteImageCompletionBlock); cdecl; overload;
    procedure writeImageToSavedPhotosAlbum(imageRef: CGImageRef; metadata: NSDictionary; completionBlock: TALAssetsLibraryWriteImageCompletionBlock); cdecl; overload;
    procedure writeImageDataToSavedPhotosAlbum(imageData: NSData; metadata: NSDictionary; completionBlock: TALAssetsLibraryWriteImageCompletionBlock); cdecl;
    procedure writeVideoAtPathToSavedPhotosAlbum(videoPathURL: NSURL; completionBlock: TALAssetsLibraryWriteVideoCompletionBlock); cdecl;
    function videoAtPathIsCompatibleWithSavedPhotosAlbum(videoPathURL: NSURL): Boolean; cdecl;
  end;
  TALAssetsLibrary = class(TOCGenericImport<ALAssetsLibraryClass, ALAssetsLibrary>)end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FAL: ALAssetsLibrary;
    procedure GroupsEnumerationResults(group: Pointer; stop: Pointer);
    procedure AccessFailure(error: Pointer);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  Macapi.Helpers;

{ TForm1 }

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  FAL := TALAssetsLibrary.Create;
end;

destructor TForm1.Destroy;
begin
  FAL.release;
  inherited;
end;

procedure TForm1.AccessFailure(error: Pointer);
begin
  //
end;

procedure TForm1.GroupsEnumerationResults(group: Pointer; stop: Pointer);
var
  LGroup: ALAssetsGroup;
  LName: NSString;
  LNameString: string;
  LStop: Boolean;
begin
  LStop := PBoolean(stop)^;
  if not LStop then   // I put a break point here to check the value of LStop
    Sleep(0); 
  LGroup := TALAssetsGroup.Wrap(group);
  LName := TNSString.Wrap(LGroup.valueForProperty(CocoaNSStringConst(libAssetsLibrary, 'ALAssetsGroupPropertyName')));
  LNameString := NSStrtoStr(LName);
  if LNameString = '' then  // I put a break point here too to check LNameString
    Sleep(0);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FAL.enumerateGroupsWithTypes(ALAssetsGroupAll, GroupsEnumerationResults, AccessFailure);
end;
By | 2017-02-16T18:02:35+00:00 January 29, 2016 8:43 pm|Code tips, Project tweaks|1 Comment

About the Author:

One Comment

  1. […] while ago I posted about using the ALAssetsLibrary in iOS. In that article, I briefly mentioned the Photos Framework, which is now the preferred method of […]

Leave a Reply

Show Buttons
Hide Buttons