Somehow, I found myself having to revisit camera support in Business Central. Compared to a few year ago, everything can be done in a few lines of code. Check out the video:

In this video, Erik demonstrates how easy it is to enable camera support in Business Central using just three lines of AL code. He compares two approaches — the Page-based camera and the Codeunit-based camera — and explains why the Codeunit approach is more reliable, particularly on Android devices.
Two Ways to Use the Camera in Business Central
Business Central provides two different ways to access the device camera:
- Page-based approach — Using
Page Camera - Codeunit-based approach — Using
Codeunit Camera
Erik discovered while updating one of his apps that the Page version does not work reliably on Android devices, even though it works fine on iPhone. The Codeunit version, on the other hand, is slicker, slimmer, and more reliable across platforms. This makes it the recommended approach.
The Codeunit Camera API
The Codeunit Camera has a remarkably simple API with just two key methods:
IsAvailable()— Returns a Boolean indicating whether a camera is available on the device.GetPicture(InStream, Text)— Takes a photo using the camera. It populates an InStream with the image data and a Text variable with the file name. Returns a Boolean indicating success.
A Quick Tangent: Running a Codeunit with No OnRun Trigger
Erik noticed that IntelliSense suggests Camera.Run() as an option. Out of curiosity, he tested what happens when you run a codeunit that has no OnRun trigger. The answer? Nothing. It’s valid code, but it doesn’t do anything because there’s no OnRun trigger defined on the Camera codeunit.
The Three Lines of Camera Code
The core logic boils down to three essential operations:
- Check if a camera is available —
Camera.IsAvailable() - Take the photo —
Camera.GetPicture(InS, FileName) - Import the photo into your record — Copy the stream data into a Blob field
A Quick Refresher on Streams
Erik offers his classic streams explanation: an InStream is something you read from, and an OutStream is something you write to. The stream is connected to some data — if you connect an InStream to data, you can read the data out; if you connect an OutStream to data, you can put data in. The directions can feel confusing, but the key concept is that the Codeunit stores the photo internally, and GetPicture connects your InStream to that stored photo data so you can read it out.
The Source Code
The extension uses a simple table to store photos:
table 50108 photo
{
fields
{
field(1; P; Code[20])
{
}
field(2; Picture; Blob)
{
Subtype = Bitmap;
}
}
keys
{
key(PK; P) { }
}
}
The page includes both approaches as separate actions so you can compare them. Here’s the recommended Codeunit-based approach:
action(test)
{
caption = 'Clicky Codeunit';
ApplicationArea = all;
Promoted = true;
PromotedCategory = Process;
PromotedOnly = true;
trigger OnAction()
var
Camera: Codeunit Camera;
InS: InStream;
OutS: OutStream;
FileName: Text;
begin
if Camera.IsAvailable() then begin
if Camera.GetPicture(InS, FileName) then begin
Rec.Picture.CreateOutStream(OutS);
CopyStream(OutS, InS);
Rec.Modify();
end;
end;
end;
}
The flow is straightforward: check if the camera is available, take the picture (which populates the InStream and FileName), then create an OutStream on the Blob field and copy the photo data from the InStream into it.
The Page-Based Approach (For Comparison)
For reference, here’s the Page-based approach that Erik moved away from. Notice how much more verbose it is:
action(test2)
{
caption = 'Clicky';
ApplicationArea = all;
Promoted = true;
PromotedCategory = Process;
PromotedOnly = true;
trigger OnAction()
var
Camera: Page Camera;
InS: InStream;
OutS: OutStream;
FileName: Text;
begin
if Camera.IsAvailable() then begin
Camera.SetAllowEdit(true);
Camera.SetEncodingType("IMage Encoding"::PNG);
Camera.SetQuality(10);
Camera.RunModal();
if Camera.HasPicture() then begin
Camera.GetPicture(InS);
Rec.Picture.CreateOutStream(OutS);
CopyStream(OutS, InS);
Rec.Modify();
end;
end;
end;
}
The Page version requires additional configuration calls (SetAllowEdit, SetEncodingType, SetQuality), uses RunModal() to open the camera, and then checks HasPicture() before retrieving the image. While it offers more configuration options, it comes with the Android compatibility issue Erik encountered.
A Note About Debugging
Erik points out a known quirk in Business Central: when running under the debugger, the OnOpenPage trigger can fire multiple times, causing the camera to activate repeatedly. He recommends running without the debugger (Ctrl+F5 instead of F5) when testing camera functionality to avoid this behavior.
Running Without HTTPS
Erik also mentions that while he was running on a non-encrypted (HTTP) browser connection during the demo, you can configure your browser to allow hardware access (like the camera) on specific sites even without HTTPS. In production, you’d typically be running over HTTPS where camera permissions work by default.
Summary
If you need camera support in your Business Central app, the Codeunit-based approach is the way to go. It’s simpler, more reliable across platforms (especially Android), and really does come down to three essential operations:
- Check availability:
Camera.IsAvailable() - Take the photo:
Camera.GetPicture(InS, FileName) - Store the photo:
CopyStream(OutS, InS)into your Blob field
This is particularly useful if your users work with the Business Central phone app or access Business Central through a mobile browser, enabling them to capture photos directly into your application’s data.