Software for iPhone, Android and Windows, engineered in Canada.
Corp Tech Solutions designs, builds and maintains native applications for organizations whose work depends on them. Scoped honestly, written to last, and handed over in full.
A business application has one job. It has to work on a Tuesday afternoon, in a warehouse with bad signal, for someone who has no patience for it. Everything we do starts there.
One product, three platforms
Every platform has its own rules.
iPhone and iPad
Swift · SwiftUI
Large titles, inset lists, a tab bar at the thumb. Dynamic Type and VoiceOver work from the first build, and TestFlight puts it in your hands early.
Android phones and tablets
Kotlin · Jetpack Compose
Material components, filter chips, a floating action button, the back behaviour people expect. Tested on the mid-range devices your users actually carry.
Windows desktop
C# · .NET · WinUI 3
Master and detail side by side, real keyboard shortcuts, resizable panes, several windows at once. Signed, packaged and rolled out through Intune or the Store.
Work orders
Today
- Pump inspectionWO-4471 · Bay 3Due today
- Conveyor belt swapWO-4468 · Line 2In progress
This week
- Fire door checkWO-4462 · North wingThursday
- Filter replacementWO-4459 · Roof unit 1Friday
Work orders
JL- 71Pump inspectionWO-4471 · Bay 3Due today
- 68Conveyor belt swapWO-4468 · Line 2In progress
- 62Fire door checkWO-4462 · North wingThursday
- 59Filter replacementWO-4459 · Roof unit 1Friday
- Pump inspectionWO-4471 · Bay 3Due today
- Conveyor belt swapWO-4468 · Line 2In progress
- Fire door checkWO-4462 · North wingThursday
- Filter replacementWO-4459 · Roof unit 1Friday
- Forklift serviceWO-4455 · Dock BNext week
Pump inspection
- Assigned
- J. Lavoie
- Due
- Today, 4:00 PM
- Asset
- P-210 Transfer pump
Checklist
- Isolate pump and tag out
- Check seals and coupling
- Record vibration reading
- Return to service
What we build.
iOS App Development
iPhone and iPad applications in Swift and SwiftUI, from the first TestFlight build to App Store review.
Mobile App Development
Android in Kotlin, plus shared-code builds, with a straight answer on which one your product needs.
Windows App Development
Desktop software in .NET and WinUI, including modernization of the systems already running your business.
What we believe
Opinions we are prepared to defend.
If a website will do, we will tell you.
An app has to earn its place on someone’s phone, and it costs more to keep alive than most budgets assume. When a well-built web application solves the problem, that is our recommendation.
Cross-platform is a business decision.
Flutter and React Native are excellent for some products and a false economy for others. The answer depends on your hardware needs, your release cadence and who will maintain the code in five years.
Offline is not an edge case.
Elevators, basements, job sites, northern highways. Software that assumes a connection fails at the moment it is needed most, so local-first storage is where we start.
The launch is the midpoint.
Apple, Google and Microsoft change the ground every year. A budget that ends on launch day is a plan for a slow decline, so we set out the cost of ownership before you commit to building.
Your code should outlive your vendor.
Repositories in your organization from day one. Documentation a new team can follow. No proprietary framework holding you to us. You should stay because the work is good, and for no other reason.
The part you never see
Good software is mostly what happens when things go wrong.
A technician saves a record in a basement with no signal, then closes the app. That record has to arrive later, once, in the right order.
This is the kind of code we write for that problem, shown here in the language of each platform. It is unglamorous, and it is what decides whether your staff trust the software.
OutboxSync.swift
actor OutboxSync {
private let store: OutboxStore
private let api: APIClient
init(store: OutboxStore, api: APIClient) {
self.store = store
self.api = api
}
/// Sends queued writes in the order they were made.
/// The idempotency key means a retry after a dropped
/// connection can never create a duplicate record.
func drain() async throws {
for entry in try await store.pending() {
try Task.checkCancellation()
let outcome = await api.send(entry.payload, key: entry.id)
switch outcome {
case .delivered:
try await store.markDelivered(entry.id)
case .retryLater(let delay):
try await store.scheduleRetry(entry.id, after: delay)
return // Keep the order. Resume from here next pass.
case .rejected(let reason):
try await store.quarantine(entry.id, reason: reason)
}
}
}
}OutboxWorker.kt
class OutboxWorker(
context: Context,
params: WorkerParameters,
private val store: OutboxStore,
private val api: ApiClient,
) : CoroutineWorker(context, params) {
// WorkManager runs this when the network returns,
// even if the user closed the app hours ago.
override suspend fun doWork(): Result {
for (entry in store.pending()) {
when (val outcome = api.send(entry.payload, entry.id)) {
is Outcome.Delivered ->
store.markDelivered(entry.id)
is Outcome.RetryLater ->
return Result.retry() // Keeps the queue in order.
is Outcome.Rejected ->
store.quarantine(entry.id, outcome.reason)
}
}
return Result.success()
}
}OutboxSync.cs
public sealed class OutboxSync(IOutboxStore store, IApiClient api)
{
/// <summary>
/// Sends queued writes in order. A rejected record is set aside
/// for review, so one bad row never blocks the rest of the day.
/// </summary>
public async Task DrainAsync(CancellationToken ct)
{
await foreach (var entry in store.PendingAsync(ct))
{
var outcome = await api.SendAsync(entry.Payload, entry.Id, ct);
switch (outcome)
{
case Delivered:
await store.MarkDeliveredAsync(entry.Id, ct);
break;
case RetryLater retry:
await store.ScheduleRetryAsync(entry.Id, retry.Delay, ct);
return; // Keep the order. Resume from here next pass.
case Rejected rejected:
await store.QuarantineAsync(entry.Id, rejected.Reason, ct);
break;
}
}
}
}How an engagement runs
You can judge us on the first three weeks.
Every engagement starts with a small, fixed-fee discovery. You leave with a written plan you own outright, whether or not you continue with us.
We interview the people who will use the software, map the systems it has to talk to, and find the risks early. It is a small, contained way to judge how we work before you commit to more.
You receive Written scope, architecture recommendation, estimate, release plan
Interaction design and technical design happen together, so nothing gets drawn that cannot be built well. The hardest technical question is answered with a working prototype, on a real device.
You receive Clickable prototype, design system, technical specification
Every two weeks you get an installable build on your own devices, through TestFlight, internal Play tracks or MSIX. You see the real product throughout, and priorities can change as you learn.
You receive Working builds, automated tests, release notes
Store submissions, phased rollouts, crash monitoring and a rollback plan. For internal software: fleet deployment, staff training material and a support handover.
You receive Store listings or signed packages, runbook, monitoring
A maintenance plan covers OS releases, security patches and dependency upgrades. If you would rather bring it in-house, we hand over the code, documentation and accounts, and walk your team through them.
You receive OS compatibility, security updates, or a full handover
Built in Canada
Canadian law is a design input, not a footnote.
PIPEDA and provincial privacy law
Consent flows, data minimization, access requests and retention are treated as product requirements, and that includes the stricter rules of Québec’s Law 25.
Data residency
Where your obligations or your customers call for it, data stays in Canadian cloud regions, and the architecture documents exactly where it goes.
Accessibility
We design to WCAG 2.2 AA on every platform, which supports your obligations under the AODA and the Accessible Canada Act.
English and French
Interfaces are built for localization from the start, because Canadian French is longer than the English layout it has to fit.
In the contract
Promises are easy. These are in writing.
- You own what you pay for
- Source code and intellectual property are assigned to you in the contract, without a licence-back or lock-in.
- Your accounts, your repositories
- Code, store listings and cloud resources sit in your organization from the first day of work.
- A confidentiality agreement first
- We will sign your NDA or provide ours before you share anything sensitive.
- A Canadian contract
- Written under Ontario law and invoiced in Canadian dollars, with HST handled the ordinary way.
- Named people
- You will know who is working on your software. Any subcontractor is disclosed and approved by you beforehand.
- A plain written estimate
- Scope, assumptions, exclusions and cost in language your finance team can read without a translator.