Exploring OpenAI: Creating a Proxy with AIProxy for iOS and macOS Apps

I am fascinated by mesh gradients. And when I saw how cheap GPT-4o mini was, I had to work on something to use their API. I loaded my account with ten dollars, and started working on Meshing AI.

Meshing: Explore Mesh Gradients for SwiftUI!
Meshing is the tool you need for working with stunning MeshGradients in SwiftUI with ease!Key Features:- Intuitive visual editor for 2x2, 3x3, and 4x4 MeshGradients- Real-time preview of your gradient creations- Precise control over color positions with draggable points- Customizable background color- Toggle for smooth color transitions- One-click code generation for integration into your SwiftUI projectsPerfect for:- Apple Platforms Developers- UI/UX Designers- SwiftUI Enthusiasts- Anyone looking to add vibrant, complex gradients to their appsBoost Your Workflow!- Experiment with complex gradients in seconds- Save hours of manual coding and tweaking- Achieve professional-looking results quicklyI was tired of working with Previews in Xcode for mesh gradients, so I created an app for it. MeshGradients is a fun visual experience. No more guesswork or manual coding – design the perfect gradient visually and let Meshing generate the SwiftUI code for you!!System Requirements:- macOS 15.0, iOS 18.0, iPadOS 18.0, visionOS 2.0 or later- Xcode 16.0 or later (for use in SwiftUI projects)Download now and start creating beautiful, complex gradients in minutes!

I hard coded the API key, and got the functionality working. But, well, I know the consequences of keeping the key on the client side, and how easy it is to sniff it out from the network response. I have worked with Firebase Cloud Functions before to mitigate this risk, but it was a big hassle to set it up.

Until I saw this tweet by Jordi:

Well, I had no excuse now!

Before I start, it is important to mention that I am writing my experience of using AIProxy and I am not affiliated with them nor this post is sponsored by them.

AIProxy and Some Questions

I was explaining the concept of proxy to my father and the first question he had was, "What happens if the API key gets leaked by them?"

I had the same worry.

So, I did something unusual as a typical developer and I read AIProxy's documentation to understand how they handle this risk.

Here is what I understand on how AIProxy works to keep the OpenAI API key safe:

  • I add my API key through their dashboard.
  • They do not store the actual key on their servers.
  • Instead, they encrypt it and split it in two.
  • Half stays on their backend, and I get the other half.
  • When I make a request, I send the partial key along which is useless standalone.
  • AIProxy combines the two halves and decrypts the key to use with OpenAI.

This method tackles a few security concerns:

  • If someone intercepts the app's network traffic, my biggest nightmare, they cannot piece together the OpenAI key from what they see.
  • By splitting the encrypted key, AIProxy makes itself less attractive to attackers. There is no single database with all the keys to steal.
  • Even AIProxy's own staff cannot access my full key because they do not have both the pieces.

But what about when someone just wants to access my beautiful mesh gradients AIProxy setup without my permission? I think they thought well on that too. I learned about a new framework, DeviceCheck, that reduces fraudulent by managing device state and asserting app integrity.

The app sends a special token with each request and AIProxy verifies this token with Apple to ensure it is from a legit device running the app. They also check that the token hasn't been used before.

Only if a token passes both checks does AIProxy fulfill the request.

This setup gave me peace of mind about using their proxy service. It protects my API key and also helps prevent unauthorized use of my AIProxy endpoint.


Implementing AIProxy

The documentation of AIProxy is well written so I will not reinvent the wheel here and let you go through its implementation guide that has videos and screenshots:

AIProxy
Protect your OpenAI key with a fully managed proxy.

Here is a checklist of what all needs to be done:

  • Create a new project,
  • Setup DeviceCheck (iOS and MacOS only) where you get the key and its detail from your Apple developer account
  • Add a service. In this case, OpenAI.
  • Generate a new partial key using your OpenAI API key.
  • Copy the partial key and service URL to use it in your project later.

Using AIProxySwift

AIProxy has its own Swift client that makes it easier to work with the OpenAI requests:

GitHub - lzell/AIProxySwift
Contribute to lzell/AIProxySwift development by creating an account on GitHub.

Again, their documentation is well written, so I will avoid repeating the setup process:

AIProxySwift/README.md at main · lzell/AIProxySwift
Contribute to lzell/AIProxySwift development by creating an account on GitHub.

One thing to note is that I used it my Swift Package and had to mentioned the package name explicitly under dependencies:

let package = Package(
  name: "MeshingShared",
  defaultLocalization: "en",
  platforms: [
    .macOS(.v15),
    .iOS(.v18),
    .tvOS(.v18),
    .watchOS(.v10),
    .macCatalyst(.v18),
    .visionOS(.v2)
  ],
  products: [
    .library(
      name: "MeshingShared",
      targets: ["MeshingShared"]
    )
  ],
  dependencies: [
    .package(url: "https://github.com/lzell/AIProxySwift.git", branch: "main")
  ],
  targets: [
    .target(
      name: "MeshingShared",
      dependencies: [
        .product(name: "AIProxy", package: "AIProxySwift")
      ],
      resources: [
        .process("Localizable.xcstrings")
      ]
    )
  ]

When you copied the partial key and URL, you would also get the device bypass token. It is helpful when you are using previews or simulator:

The system prompt is important for guiding the AI to generate the desired mesh gradient. Given that GPT-4o mini charges 15 cents per 1M input tokens and 60 cents per 1M output tokens, I made sure the system prompt was extremely well-defined to optimize both cost and output quality.

I start with creating an instance of the AIProxy with the partial key and service URL:

@preconcurrency import AIProxy

let service = AIProxy.openAIService(
  partialKey: "v2|aafb55d4|...",
  serviceURL: "https://api.aiproxy.pro/6d09f1ef..."
)

To ensure a JSON response, I specified the response format in the parameters:

let body = OpenAIChatCompletionRequestBody(model: "gpt-4o-mini", messages: [
  .init(role: "system", content: .text(systemPrompt)),
  .init(role: "user", content: .text(prompt))
], responseFormat: .type("json_object"))

The responseFormat: .type("json_object") parameter is crucial here, as it instructs the API to return a JSON object.

Then, I make the API call and decode the JSON response:

let response = try await service.chatCompletionRequest(body: body)

guard let jsonString = response.choices.first?.message.content,
      let jsonData = jsonString.data(using: .utf8) else {
  throw NSError(domain: "AIGeneration", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to get valid JSON from AI response"])
}

debugPrint(jsonString)

let decoder = JSONDecoder()
return try decoder.decode(MeshGradientData.self, from: jsonData)

This code sends the request to the OpenAI server, receives the JSON response, and decodes it into aMeshGradientData object. The debugPrint(jsonString) line is useful for debugging and verifying the received JSON structure.

And that's really it!

I can then see the requests made on their dashboard with the IP Address, path, input and output token count:

Moving Forward

It was a breeze setting up http://aiproxy.pro/ and they do honor their words when they say no credit card required to sign up. I will subsribe to their paid plan once my requests cross the free limit of 1,000 per month.

Another security measure to look into is the rate limit that you can use to prevent endpoint abuse. They have some fun sample projects too, like AIColorPalette that takes advantage of new stuff in SwiftUI in iOS 18.

Happy proxying!