📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

How he built iOS apps that PRINT with Cursor + Claude

Greg Isenberg57:08

Transcription

I heard it was possible to use Cursor to create mobile apps that are native and put them in the app store, but I didn't know how. So, I brought on this guy, Chris, who's building a huge portfolio of mobile apps where he's using AI and Cursor to actually create them. And he reveals his exact process, all the techniques, how he's putting agents into the apps, how he's using OpenRouter, how he's using Chat GPT 4, image generation, and all these uh techniques with Cursor that I thought were super super interesting to actually go and create a native mobile app. iOS is a huge opportunity. There are people who are going to print millions of dollars doing it. And he shows an example of how to create some features in a matter of a few prompts. So, enjoy the episode. I hope you learned something.

[Music]

I've been meaning to make this podcast for a while. I got my friend Chris Baroque on the pod. I've been watching this guy. To be honest, Chris, I thought it was like a big company behind you. You you built like a portfolio of these mobile apps that are really clean, really interesting. Um, some that I think can get some serious MRR, and then I find out that it's just this dude who, you know, didn't go to Stanford, doesn't work at Google, um kind of, you know, a regular guy who's um using AI to actually go and build um build uh these apps. So Chris, welcome to the show. And I want to know if people stick around to the end of this episode, what are they going to tangibly get that they aren't going to get in a million other Cursor tutorials and vibe coding tutorials?

Yeah, first thanks for having me on, Greg. Really awesome to be here. Um, I think I'm hoping that they get um a little bit more of uh behind the scenes of an advanced Cursor workflow. And I use advanced in quotation marks because I'm not the best developer, but I have picked up a lot of things. I'm doing some interesting things with Cursor, like doing native iOS development, which not a lot of people do. So, I'm hoping they pick up maybe some golden nuggets in there. Um, and then even if you're a developer, maybe this hopefully demystifies, you know, if you've never used AI, maybe you start using it and just kind of see if this guy can do it, I can do it, too.

Cool. All right, let's let's get our hands dirty. Let's give out that sauce. You have my eyes lit up because I've been meaning to use Cursor to create, you know, native iOS apps, but I've just seen sort of your React Native apps. Um, and there's just a level of polish that you get with native apps. So, if you can teach us how to do that.

Yeah. Yeah. Um, I think that would that would be huge. Yeah, absolutely. Okay, cool. Well, there's a couple ways we could do this. Um, I think what I want to start first um by the way how does sharing like if I should share my screen like you guys can you'll just be able to see it.

Okay cool. Okay, so the the first thing um I wanted to share was just really quickly just a little bit of context on me. So I built these four apps. Um, I'm not trying to plug the apps or anything. I'll just show one of them. This is the bigger one. So they're they're all productivity apps, but this one's called Ellie. It's a daily planning app. It's got a couple thousand monthly active users. Like I think probably like around like 2,000 paid users. Um, a lot of people a lot of students use this app. The reason I mention it is it's a pretty robust app, and people are pretty surprised when they're like, "Okay, who's the team behind this?" They figure out it's me. They're like, "How is this possible?" And to be honest, I I've done this four times. I have another app, this budgeting app that I have. It's a pretty robust app, a personal CRM app, a very robust app. Um, the only reason I'm able to do this stuff is because I have AI to supercharge my workflow. Um, so I just want to share that context. Like there's a lot of tutorials on how to build, you know, a weekend project, how to build a weekend game, and there's absolutely nothing wrong with that. Like those are awesome. Um, but people are always curious, can you build something like way more advanced? Um, what happens if you do this over a long period of time and you have AI? And I think this is exactly what happens. Is like you can build some pretty serious complex things. So that's some context.

Um, some rapidfire context on like just the tools I use, everything. So I'm primarily using Cursor. I've tried literally everything. Um, I think I've been using AI for coding for about 2 years now. I somehow got my hands on the first version of GitHub Copilot when it first came out. Like it didn't have chat. It didn't have anything. It just you just comment, and I was basically using the comments as a chat. Fast forward, I've used a bunch of tools like Windsurf. Um, I've used Claude Code, like everything. But right now at this time, Cursor is the tool that I use, and people are pretty surprised that I actually use this for native iOS development which uh not many people do. Like I've seen people use it for React and Expo development, but um people are always curious how how do I do that? So I'll share how I do it. Um, and then the other tool I wanted to highlight was I actually use um Chat GPT for asset generation. And um one of the things that I've started getting into was putting really good assets. Now that the Chat I think as of a few weeks ago has gotten so much better. Um, and that I think is going to bring a level of polish to all my apps that I've never seen. So, I really wanted to share that. And I had like a semi-viral tweet going about how like people were always asking how are you making these assets? This is crazy. How are you doing it consistently? I think is the big thing. So, I'm going to reveal that on this podcast, which uh a lot of people have been asking me to make a video about, but I haven't yet. So, you get the first one. You get the first look at that, Greg.

Um, and then in terms of the models that I use, um, I'm using Claude 3.7, which also surprises people. Um, 3.5 is pretty good. 3.7 usually goes off the rails, but to be honest, um, I feel like I'm a a little bit a good enough developer to kind of control it and figure out when is it going in the wrong wrong direction and stopping it. So 3.7 is what I use. I've tried Gemini. I've tried like the new 03 that costs 30 cents every call. I've tried all of these different things, but right now Claude 3.7 is the best thing, especially for native iOS development. For iOS development, it's not even a question. 3.7 is probably the best one right now. Um, so that's the model. And then now, how do I do the iOS coding? To be honest, what I do is it is like the jankiest setup, but I basically open the Xcode project in Cursor.

Quick break in the pod to tell you a little bit about Startup Empire. So, Startup Empire is my private membership where it's a bunch of people like me, like you who want to build out their startup ideas. Now, they're looking for content to help accelerate that. They're looking for potential co-founders. They're looking for uh tutorials from people like me to come in and tell them, "How do you do email marketing? How do you build an audience? How do you go viral on Twitter?" All these different things. That's exactly what Startup Empire is. And it's for people who want to start a startup but are looking for ideas, or it's for people who have a startup but just they're not seeing the traction uh that they need. So you can check out the link to startupmpire.co in the description.

Um, I have my Xcode project running and like what I'm doing is I'm literally just opening the file in Cursor. So this is the Xcode project for my budgeting app Luna. And then I literally just open the file in Cursor to be able to make edits. And then I use the chat feature, make file edits, and then I have to switch back to Xcode to build. And I do this over and over again. And that's actually the way that I've been coding with iOS development. And I honestly just figured this out like I think a month and a half ago. But the old way that I was doing it was literally pasting code into Claude, chatting with Claude, and repacing it back. So even though I'm switching between Cursor and Xcode, this is substantially faster still. So this is the current way that I'm doing it. And people are pretty surprised cuz they're they they're also like, I had no idea you can just do that. They thought you had to do something special to to be able to open this kind of file, but no, you can just open the project. So that's the first um that that's that's literally how I'm doing this workflow right now is just uh opening it. Um, so I don't know if you have any questions, Greg, but that's uh my just reaction to that is I'm surprised.

Oh, okay. Okay. Yes. Okay. Yeah. Um, so that's cool. I'm going to try that out.

Yeah. Yeah. It's pretty good. The only caveats I'd say is that and and here's where people kind of mess up and this is like what I've had to learn is do not try to use Cursor to set up the project for Xcode. It's that is not going to work. So that's I think why people and that's actually how I tried to do it originally and that's why I was like, okay, Cursor doesn't work for this. You got to set up. There's a lot of things you got to do manually in Xcode. Like when you're um like for example, there's a lot of settings in here, I think. God, I hope I don't reveal anything. Um, there's just a lot of settings in here that you need to actually do yourself, and it's something that Cursor just does not have a really good capability of doing in my experience. Like like being able to select some of these frameworks and embedding them in here. Cursor just can't do that. I've noticed that for um for other projects like for uh React Native and all that stuff it is able to do that like it can do all of that in the terminal. Xcode is not that way. You got to do it manually. So I think that's where people get tripped up and stop doing it. So set up the project manually, then just open it in cur in Cursor. And then also when you need to make certain changes like for example to make outgoing network requests like literally hitting up a server, you need to enable that in Xcode like manually like Apple forces you; Cursor is not going to be able to do that for you. So those are some of the nuances, but yeah, it's um still worth it though in my opinion.

Cool. What's next? So okay um what I wanted to do actually was go through a project. Um, so I originally was like maybe I'll go on this podcast. I'll just go spin up a project live and kind of do it. I did it last night. It does not work that way. Like there were so many errors that I ran into that I was like this is going to take five hours. So what I did was last night I spun up a quick feature to kind of demo for you guys. But I recorded all of my steps. Like so I committed all the code, and thankfully Cursor saves all the chat history. So we're going to go through all the prompts that I use to get to this point. So we're not going to really I don't think we're going to skip anything here. I want to show you guys exactly the prompts I use from get to point A to point B. And for fun, um I know you mentioned, Greg, you were kind of interested how do I build AI apps? So, I've seen a couple videos online about how to build AI apps, especially stuff with image generation, but I wanted to take a little bit of a different approach and show you guys how are we going to build a little bit more of an advanced chat app. So, there's also 100 chat app um tutorials, but I decided to do a couple of unique things that I think would be interesting for your listeners to see. Um, so the first thing is I want to show you guys what the like quickly what the app state is. And um we're not building the app from scratch. I also tried that too as a demo and that one took even longer. So, I was like, we're not doing that. But I'm going to show you guys how to put an AI feature into an app that doesn't have AI. So, we're going to build an AI feature into my budgeting app. So, this is this is the budgeting app. So, this is my budgeting app, Luna. There's two sections here. There's a weekly and monthly spend. I think that's honestly the cool feature. I've never seen a budgeting app that shows both at the same time in this format, but it's just like a traditional budgeting app. You can set your budgets. You can add transactions. It's um yeah, it's it's very basic. So, what I want to do is see can we add some AI into this app? And what I was trying I was trying to come up with some features. So, I was brainstorming with my friend and we were like, okay, what would be interesting? and he said it would actually be cool if I can just chat with it and just ask it questions about my spending. And apparently what he does is he literally just exports uh his current budgeting app as a CSV and throws it into Chat GPT. And I think a lot of people do that with their finances. They just take it and do it. So I was like, "Okay, that's actually a maybe this is actually a pretty useful feature. Maybe people will actually try this." So that's the that's the feature that we're going to try to build right now. And it's using AI. Um, and so I want to just walk through um, we're gonna I'm not going to do it live, but I'm going to walk through all the prompts that I used and I we'll walk through like what the app looked like at that stage so you guys can see and I think there's going to be a couple of interesting techniques and things that um, you guys can kind of pick up from watching me do this. So yeah, let's just uh, jump into it. Unless Do you have anything you want to add?

Great. Okay, cool. Okay, so this is where the app's starting point is. There is no AI, but what I want to do is hopefully put another icon at the bottom right for a chat. So, that's what we're going to that's what we're going to try to do right now. This is where the app is uh this is where the app's starting. So, let me show you guys the first prompt that I ended up using.

Okay, so the basically the first prompt that I did again the app it there's no chat, there's no AI or anything. The first thing that I did was I told it um I want you to create a new tab for an AI chat. Can you make the UI for this? Try to follow the similar UI as other parts of the app. You can just hardcode the chat. Just use dummy data. Basically, the what I wanted it to do was just create the UI. And that's actually one of the big uh techniques that I use is I try to only do the UI first and then I start hooking up the back end or hooking up data afterwards. But the reason I do that is I found that the AI has a way harder time or sorry, it has a way easier time um following the instructions if it can just focus on one thing. And it honestly probably could get this in one shot. There's a chance. But I didn't want to take that risk. So I decided let's just do the UI first and let's do dummy data and then we'll hook it up later just cuz I really wanted to see or I just wanted to increase the chances of success of this working. And then I also tagged the entire codebase here which when I'm doing anything major just to make sure that it has the right context. Um I like to tag the entire codebase. Um and this is I think I just tagged the entire Luna folder basically which is uh which is the the iOS codebase just to make sure that it has it. So once this is uh so once I got this oh and sorry another another tip here was um I told it try to follow the UI as similarly as the other parts of the app. This was very important because I've noticed that if you just tell Cursor, hey, can you go build this like feature or whatever, sometimes you get lucky, it does kind of look like it's a part of the app, but then sometimes it just makes up a bunch of components and tries its luck. And I wanted to see, okay, what happens if I tell it to just, you know, follow the rest of the UI. So, we ran this. Um, I did get a bunch of error. Well, this is the output. Um, I did get a bunch of errors and and I did notice that there was a little cut off here, but the only thing that I had to um the only thing that I manually added here was I added this chat icon just to match the other icons. So, I created that. But in literally one shot with the UI, this is what it came up with. All of this is hardcoded. So, it hardcoded this this data. As you can see, it actually did pretty well trying to follow the uh the rest of what the app looked like. like it got this purple color scheme, it really matched it with the rest of it. Um, and the other surprising thing was um, if I just type whatever I want in here, it actually also hardcoded the responses. So, it actually got this animation where it kind of goes down. Um, that was actually kind of surprising. I thought I would have to go do that myself, but I guess it was it was good enough for that. So, um, this was the literally from the first prompt. It added this again. I added the manual icon, but now we have the chat UI functioning. So, I was really happy at this point. I was like, "Okay, maybe I could just, you know, hook this up at this point." Um, the only issues I did run into, um, just to share, were, for example, when I first ran it, this little message area was hidden behind the tab bar. So, I had to do a couple prompts to fix that. Um, I also noticed that, um, you know, I I didn't fix it here yet, but I I hate how it kind of scrolls at the top here. Like, I'd love for it to scroll at the bottom. So, I think I use like one or two prompts to go ahead and do that, but here, let's just walk through some of the other stuff.

What's cool about that? So, I've built a lot of mobile chat apps is um there's actually like a set of UX like standards that you know you need to build when building a messaging app. So, for example, like have I mean it's it's it's so obvious you know because we're in chat all the time but like um like the way it comes down when you like send a message and like where the sender is, where the receiver is. Cursor knew exactly what to do.

Yeah. Yeah, you didn't you didn't have to, you know, it's that was insane.

Yeah. Yeah. It it I think uh there's a couple of UX uh just or just like UX patterns that I think it's really well trained on and thankfully chat I think is one of those things.

Yeah. Yeah. Okay. So then the next thing um so I had a bunch of prompts here. This was like because the the chat area was cut offs and and another thing I do is I always feed it in screenshots of things. So, um I guess it's broken here, but I I did take a screenshot of the app so it could see what I was seeing and I feed it in. That's another thing that I'm surprised people um they they or when I tell them like, "Hey, by the way, you know, you can feed in images into Cursor." They're kind of surprised by that. Honestly, I was surprised the first time I saw that, too. But I do that for UI sometimes if it's not getting the UI, I go to like Mobin or I go somewhere else and I just feed in screenshots of things and say, "Hey, can you try to get close to this UI as a starting point?" So that's another technique is just I'm constantly feeding in images into this thing. Um so then um yeah it did it got it wrong a couple times. Um still not fully visible. So so yeah I had to I had to kind of correct it here. But once I got this done and and it's at this this place I was like okay you know what let's go ahead and let's actually just try to hook up the let's try to hook up the the data here. So, the next thing was I was like, "Okay, let's try to let's actually try to get this hooked up to an actual LLM, to an actual AI." So, one of the things um I don't know if you're familiar with Open Router, Greg, or if anyone's ever talked about that in the

Okay, nice. Okay. But I I

Can you tell people

Yeah. why it's so dope?

Yeah. Yeah. Okay. So, Open Router is basically it's basically a service where um if people are familiar with how they hook into LLMs, you usually go call OpenAI directly or you call Claude directly and you know you feed it in messages and then it kind of spits it out but you have to integrate directly with them. OpenRouter has a service where they've integrated with I think over 300 models and basically you can just switch out the models with one line of code honestly just like a string just a piece of text. So if I wanted to call the Gemini API and just test it out, I can go do that. Then it with one line I can switch it to cla or I can switch it to OpenAI. So OpenRouter has all of these different models. It shows all the price points. And I love using it for development because now I can test the different LLMs and see what kind of responses am I getting or how expensive is this with just one line. So that was another thing I wanted to share here which um I honestly I think I learned like 2 or 3 months ago but it's been so gamechanging. I think there is a bit of a fee on it, but in my experience, it's like totally worth it just because of the speed of having to switch these things out. And then once I think I confirm like, okay, I'm good. I know exactly the models I want to use, then maybe I'll just go ahead and and skip that and just go direct. But during development, it's incredible. So, um, I know I wanted to integrate OpenRouter into this for the chat. And so, that was actually the first prompt that I did was I said, let's can you make this functional and not hardcoded? And again, it was just the hard-coded chat um the chat UI. And I said, can you use OpenRouter so I can swap out the chat model quickly? And can you put a setting at the top right so we can toggle between the models? So I wanted some sort of drop down where I can switch between GPT or G between Claude. Um and then I said, uh when you ask questions, can you use the last 3 months of transactions as context? So, I think honestly it probably would have done something to feed it in context cuz when you when I ask a question, it's not going to know how to answer it if it doesn't have the transaction history. So, I just explicitly decided to put this here just to try to save on prompts. Like I just I was like, I'm not going to take a risk on that. Um, something cool that I also wanted to mention which uh some people don't know about is that you can actually feed in documentation into Cursor. And so that's what I did here. I basically took the OpenRouter

docs. You just literally copy and paste the URL. You can go to Cursor, and you can type at docs, and then when you add a new doc, paste it in here; it's literally going to index the entire documentation of that service, and then now it has context of what are the API calls that are needed to use OpenRouter. Um, it's it's just amazing. So then I don't have to copy and paste documentation in here and tell it, "Hey, this is how you use OpenRouter." So that's been really game-changing.

And I will also say, for Apple-specific development when you're developing on iOS and Mac, a huge issue right now that I found with Cursor is that it is constantly hallucinating what you can do in an iOS app. And then it's even worse for a Mac app. So I started building my first Mac app recently, and it was just hallucinating left and right, like things that just didn't exist. So once I started feeding in the Apple documentation, and I'm constantly doing that, it really brought those hallucinations down. So that's another tip: constantly feed in documentation, especially when you're working with AI products like this, and all these APIs are changing constantly—like the GPT API, the OpenRouter API, all the, uh, like ElevenLabs—like all the docs are always changing. So this is a huge tip that I have.

So once I had—once I—and again, this we're just kind of one-shotting this, like, "Can you make this functional?"—Um, it did a pretty good job. Let me just make sure, let me just see. Okay, so it did a really good job of, uh, of, of one-shotting this. And here, here was the result of that one-shot. So again, now we don't have the hard-coded data anymore. It's it's like gone. Um, and we have the toggle at the right to be able to, uh, to be able to, uh, switch between the different models. Um, and then if I chat with it, "Where did I eat last week?" Um, it is great. Okay. Well, it doesn't work. But, um, it at least we know that it is calling the LLM because it's not a hard-coded response. So that means that it is calling—like it is calling something. Maybe it doesn't have the right—Oh, I know what's going on, but maybe it doesn't have the right context here. Um, actually, I think I—Yeah, I think I do know what's going on. Um, I forgot. This is just a demo account, so like it doesn't have any data, but I did, uh, add this like demo mode, so it'll just like populate data. Um, and then now I think it should be able to like answer it. But yeah, basically though, it's, uh, this is now working. It's connected to the LLM.

So let me also show you guys kind of what the code looks like a little bit so you guys can see, um, the the iOS code that it generated. So I can trust you, right? That it worked. It worked. It worked. Yeah, it worked. Uh, it worked. Um, it—I just—I just realized I forgot to—there was like one little nuance with it where I'll show you in the prompt here, but, um, it it generated two—two files here. And the way that I structure my iOS apps, if you're interested, is, um, I have the a UI folder. So that's where all the UI components are. I have models, which just shows all the different data models, the data types, and then services. And this is how the app talks to the front end and the back end. So those are the folders. But it created two files for me. It created a new AIChatView. So that's where we can see—this is where we can see that actual—the UI of the actual chat—and then it created—it just created this OpenRouterServices. So this is really cool because it—that means that it had context of the other services and it knows—because I fed in the codebase—how I structure the services, and it actually copied and and made a service very similar to the other ones.

Um, I will say though that like you should not put all of this stuff in the front end of the app. So I'm just doing this for demo purposes and for speed purposes. Like the API keys here, I'm going to—don't try to use this—I'm going to kill it after this demo. But, um, in theory, this stuff should be living in the back end. It shouldn't be in the front end, which is something we could talk about—is the security of this stuff—because that is a problem that it is putting this stuff in here. Um, so as a developer that's been doing this, I know that that's an issue. So I'm going to go correct that later. But for demo purposes, we're going to keep everything in the front end just to keep it simple. Um, so it did create this service. Um, I did have to put in the API key here for OpenRouter, but everything else was—it was all—it was all this—uh—it was all Cursor. Um, so it got—it got the—it literally got all the data models that, um, you're going to get from OpenRouter because of the documentation. It got a bunch of the models. Um, and yeah, it—it, uh—it all works. It all functions. Um, it's able to call it correctly, and we're literally basically two major prompts in—like at this point—to be able to get here. Um, so yeah, that's—that's, uh—that's, uh—so that was the basically the second major prompt was, "Now that we have the UI, can you hook it up? Can you, uh, can you hook it up to OpenRouter and make this thing functional?"

Um, I did have to do another prompt because of the issue we saw here. I noticed that it was actually fetching, uh, transactions from the database directly in the OpenRouter service, and I was like, "Actually, let's just save on some costs. Um, you have all of the data locally. Can you instead just pull it locally?" So that'll just, uh, save some latency. That'll just save some cost for me. So I made that change here. Um, then also like the bug we just saw where it says there's no transactions, it's because demo—demo mode was not persisting. So I also corrected that. Um, but yeah, once we got to this step, like you got to trust me on this. It does actually, you know, I'll just like restart it. But no, we—we—we are, uh—what's the expression? TR—ver—trust and verified. Trust. Yeah. Yeah. Yeah. Okay. That's the type of audience we are, you know. Okay, cool. All right. Well, it works. It works. Um, well, well, let's see. Um, Okay, let's see what the next step was. I think the next thing I did once—once I knew that it was hooked up to OpenRouter. Um, the next thing I did was—Yeah. So the next thing I did was, um, I wanted to modify the—the prompt—the base prompt—because the prompt right now was, um, like, uh—right now the prompt—the prompt that it gave me—it's—it's not this prompt, but the prompt that it gave me was literally just like a one-liner, and it all it did was say, "Here's the user message, here's some transactions, can you answer the user's question," and to be honest, the answers were really not good; the answers were pretty bad. Um, you know what? I think it's actually—Yeah. Yeah. Um, it really—it really wasn't good. So I decided, okay, I—I want to make a better prompt. And if you've coded any AI apps, you know that the prompt is everything. Like that's kind of how you control everything. Um, yeah, actually. So here—here's the—here's the original prompt that it—that, um, uh—from that—from that initial, uh, you know, two-shot that we got. It—it—this is what the LLM—this is what Cursor came up with as a prompt: "Here's the user question, here's some additional context on—on the question that's relevant," and it's literally just the three months of transactions, and then it just fed it into Cursor and said—or sorry, it just fed it into OpenRouter and said, "Okay, can you go answer this question?" Not a good prompt at all. So one of the things I wanted to do is make the prompt better, and so a technique that I've been using which you guys can take is, um, I actually use Claude to generate the, uh, to generate really good prompts, and I, for some reason, I've noticed Claude is actually pretty good at this. I probably could do this directly in Cursor, but I'm just—I don't know why I just, uh, out of habit, like I—I like to do it directly in Claude. I feel like I have more control. So, um, can you see the Claude screen here? Great. Yeah. Okay, cool. So I—I just asked it, "Hey, I have a budgeting app. I want to add an AI chat to you. Can you give me a very good prompt in XML format so it can follow the instructions?" And this is another technique that I've learned is that, um, if you format the prompt in what's called XML, and this is what XML looks like—it just has kind of a title and then a description—Um, you can kind of understand just by looking at this how this works. There's nothing special here. Um, formatting an—in XML, formatting in XML actually has a higher chance—I've seen—of producing really good results for the—from the LLM. So that's just a cool technique that I learned, um, after making a bunch of these AI—like AI features and AI apps. So I told it, "Give me a good prompt in XML, um, and make it so the answers are very concise—like a friend answering it for you. Don't show your work unless asked, and just answer the question." So I just wanted this to be—I was trying to think, like, how would I want this thing to respond, and that's exactly how I want it to respond. And this is the prompt that it—it gave me. So it—it, you know, it put the—the first XML tag here as budgeting assist instructions, and it's like, "Here's your persona, here's the response style, here's your knowledge areas, and then here's the instruction guidelines." Um, so this is a way better prompt than this one here that it gave. And so I just told it, "Hey, um, you know, I want you to—like I want you to, uh, actually, yeah, I—I decided to actually see what Cursor would do, but I did give this as, as context. So I said, "Hey, can you, um, can you add something like this into—like as the prompt," and I gave it the example that Claude—that Claude gave us. Um, so that's, uh, that's how I do the prompt generation. And with this prompt, let's see. Yeah, with this prompt, um, it actually was able to be—it was actually a lot better. Um, let me see. By the way, that's such a low-key, such an important takeaway. I just want to highlight that. You know, so many—as you said—so many of these products are about the prompt, right? If you can create a great prompt, the UX ultimately becomes way better. And if the UX comes better, you know, all your metrics are going to go up. Going into—you call it Claude, I call it Clude. People on the channel know I call it Clude. Um, tomato, tomato. Going into—going into Clude and—and—and asking it and optimizing it and thinking empathetically about the user, right? You—you said, "Pretend it's like you're talking to a friend," and that's such a small little detail, but you do—people forget that we're building products for people, right? Um, so I think that's a huge tip around how to—how to figure out prompting. Nice. Okay, cool. Yeah, I think—I think—I think that's great. Um, so let's build this and see. Um, so again, the—what I did here was I told it, "Um, can you improve the prompt? Here's, uh, you know, here's the example I got from Claude. Can you improve the new prompt?" Um, and so it was able to actually just, you know, inject this right here. So this is a way better prompt. So we're going to see—we're going to see how this—we're going to see what this thing does.

So another tip while we wait for this to build was, um, I noticed that when I was testing, um, I added this demo mode. So it'll—again, it'll just—Oops. It—it'll just basically populate—it's just going to populate with, um, with a bunch of dummy data. But I was kind of like, okay, this doesn't really feel that natural because this isn't my data, and it just has like generic—"I went to a shopping store. I went to a restaurant," but I wanted to actually test this thing out. So a cool tip is, um, I actually have, um, I have a mock data file. This is where I put all of the mock data for the app whenever I'm doing testing or whenever I want to—Yeah, just whenever I want to see what the app actually looks like with real data. Um, something cool that I did here though with AI is that you can actually tell it, "Hey, can you make the mock data less generic?" And I said, "I want you to add restaurants and places that look way more realistic for a 28-year-old male in Dallas." And it did a very good job. These are like actually restaurants in Dallas. And this actually made the demo so much better for me to test the AI feature because this is realistic data now. So when I ask it, "Okay, am I overspending?" And to be honest, like I guess a 28-year-old male always overspends or something. So I did go over budget for like almost everything here—just in the—in the, uh, dummy data. But this is a cool tip I have. Uh, if you're, you know, if you're too lazy to generate the data, just use AI to do it. This alone honestly, like saves me so much time so I don't have to like manually mock all this stuff out. Also, it's not even just for you. So what you can do is you can come up with an idea for a startup, use Cursor to build it, use some of this mock data, rec, you know, compile, build, record a video now with that data that's way more interesting, post it on X, see if people actually want the app. Yeah. Yeah. Right. Make it go viral. Because if you have bad dummy, you know, people—I see this all the time. People post, uh, videos or—or content, and it's—it's bad. It's bad dummy data, and I—I'm like, "Yeah, I'm not gonna use this app." Yeah. Yeah. Yeah. That makes so much sense. Like they can't—like visualize themselves seeing it. Exactly. So just the higher shot you have of doing that. Which I will say when I—I demoed this little—this little AI demo for some people, and I think it hit really hard when they saw this type of data and they saw restaurants in the Dallas area. They were like, "Oh, this is actually—Oh, I would actually"—you can envision themselves using it basically. Totally. Which is pretty cool.

Um, okay. So I think this is built. Um, okay. So let's test it now with this new prompt. Let's see how good it is. And let me just make sure. Yeah, the demo mode's on. Okay, so it loaded all the demo data this time. Um, so now if I say, "Where did I eat in the LA"—or—"Where did I recently eat?" Cool. Okay. Wow. So it actually loaded it, and now it said, you know, "Hey, you recently ate at the Ascension Coffee Design District here." And—and again, the prompt is concise. Like I—I couldn't show it in the—'cause we—the demo—the earlier demo wasn't running. But I will say that when I asked that same question with the dummy data, it was like—like a ChatGPT response—like it was like formatted and markdown. It was like very verbose. So that was a huge reason why I told it, "Hey, can you make it concise? Don't show your work. Just do it." Um, and this is actually a really good response, I think. Like this is a pretty—pretty cool one. And obviously you can tweak the prompt. Um, but yeah, that's, uh—so that's, uh—that's this. Let me see the next thing. Um, so then, okay, in theory, I was like, okay, we could just end it here. Technically, like we—we have, you know, it's working, the chat's functioning, we have AI integrated, we can switch between models. It's like really good. But I was like, okay, that's kind of—everyone can do this. Let's like try to do something a little bit more interesting here. Um, so I don't know if anyone has ever done, um, like, uh, shown like tool calling or anything, Greg, on the—on this podcast. I mean, not in depth, that's for sure. Okay. So the next thing that I wanted to try was—So the next thing that—the next thing that I wanted to try, which, um, which was a problem was—again, if you remember the first prompt, I said just feed in—or sorry, the second prompt I said, "Feed in three months worth of transaction history." Um, I hard-coded that. So now every time you ask a question, it's going to feed three months of transaction history. That could be a lot of transactions, and that could be very costly. So I was trying to think of a good solution to this. Should I maybe, you know, maybe I can just feed in a year's worth of transactions because what if someone says, "Summarize the last year," then the three months isn't going to cut it. We're going to need the last year. But then if I summarize the last year, or if I—or if I, you know, what if I just gave it all the transactions every time, 'cause I mean the LLM context windows are pretty big. Gemini can support 1 million. Let me just feed in all the transactions. The only issue with that is it gets incredibly expensive to do that every single time. And then the second thing is what if they just asked about the last week? They're like, "Hey, where did I—what restaurant did I go to last week, um, that you know, it was around $30?" It would be so wasteful to feed in a like, you know, two years worth of transactions into this. So I was trying to think through what's a good solution to this. And the first thing that came to mind, which I think a lot of people try to do, is they're like, "Oh, I'll just use another LLM at the beginning to take in the message." So if the user asks, you know, um, "What—what did I eat last week versus what did I eat last year?" that first LLM will parse the message and then come up with a date range, uh, for the transactions. So that was the first thing I thought about. Maybe then it'll take the dates, feed it into some sort of function to then go get all the transactions for whatever the date range is and then feed it into the second LLM, which will actually answer the question. So then in this case, now we'll have two LLMs: the first one just to parse and figure out what's the date range—"Let me—let me get the transactions," basically. Then a second one to answer the question. But in reality, this is, um—there's actually something that exists in OpenRouter and GPT, and it's called tool or function calling. So you can actually get the LLMs and give it tools to use. And this is—we're kind of getting into the—the territory of agents, which I think has been covered a lot on the channel, like how do you use agents? But I want to show at a low level how you can actually build an agent that has access to tools in your own AI app. I like—and we're not talking about like, "Let's use existing APIs or anything." It's like, "Let's actually build the tools here locally," which a lot of people don't realize you can do and give it to the LLM. And I'll show you guys how this works. So since we have time, I was like, "Let's just go ahead and try this and see how far we get here." Um, and so the way it's going to work is, um—so OpenRouter actually has, um, something called tool and function calling. And so this is where you can actually tell—give the LLM, um, a bunch of tools that it has access to. So you can say—let me see. Yeah, you can basically, uh—you can basically say like, "Hey, you have access to—like here's the messages I want to send into the LLM, and here are the tools you have." And then you can—you can go ahead and define the tools here. So I think in this case, in their example, they specifically have a tool that they defined called search Gutenberg books. And then here's what the function looks like. And it calls the—I guess it calls this API and goes gets books. So now when you ask their LLM, "Hey, do you have any book recommendations?" It could just answer it based on what it knows and what it's been trained on. But I think when you ask it, "Hey, what books do you recommend?" Or like, "Do you recommend any good horror books?" It's actually going to—when you feed it in the tools—it's going to first think and do an initial step of, "Okay, do I have enough context to answer this user's question? If yes, go answer it. If no, do I have the tools to answer it? If there are tools, let's—let's go use the tool and try again." And it kind of just loops through over and over again until it has the relevant context to answer the question. So that's the stop point when it says, "Do I have the relevant information to answer this? Let's kill the loop." Um, so that's at a high level how the tool calling works, and it's actually very easy to implement. And so this is how I did it. Um, I basically told it, um—let me see. Yeah, I basically told it, um, "Can you actually create a new tool function that the LLM is able to call? I want to use function calling from OpenRouter." And again, I did the thing where I fed it in the docs just to make sure that it has it. I think it already probably had it when I fed in the initial doc, but I was like, "Let me just not take a chance here. I'll just feed it in to be safe." Um, and then I told it, "Can you create a few tools? So maybe a tool to get the transactions for a specific date range. And then just for fun, I said, 'Okay, let's just also feed in a tool to maybe get the current budget.'" Then I explicitly told it, "I want all the tools to be local. We're not calling any external APIs." And the reason I did that was I was a little worried because looking at the docs, um, and looking at most docs, a lot of the function calling, they—they usually call external APIs. So I was scared it was going to hallucinate that. So that was just something I did just to like really make sure that it didn't do that. And let's see. I was actually surprised that—Oh, no. I actually did get a ton of errors with this. And the way I deal with errors, by the way, um, is I literally just screenshot the error and then kind of feed it in and say, "Can you fix it? Can you fix it? Can you fix it?" And I do this like 100 times until it doesn't fix it. This one—it looks like, yeah, this one—it looks like it happened like three times where it worked. So the way—and then the way that this works. So after like basically in a couple of different prompts—like one—one prompt to tell it to generate, you know, like use OpenAI tool calling and then just fixing a bunch of the errors I got. This is what it generated. Um, it was actually able to, um—it was able to change the prompt; it was able to like modify this so it uses those tool calling, and it—and this—these were the available tools that it—it came up with. Um, I don't know why it came up with two of these, to be honest. These two are kind of redundant when I look at it—like they're both like get transaction for date range—but so we'll just look at these two, but it did create this tool to get transactions for a specific date range, and then it did also create a tool to go get the user's current budget. Um, and then you can see here it takes in the date range. Um, and then these, you know, these parameters are required. And so this is what it looks like when you define the

Tools and you're going to feed this into OpenRouter.

And then this is where the actual tool definition is. So this is the actual function that it's going to call in your app, like when the LLM decides, "I, we need to call this thing." Um, so it generated this, and this code does work. It basically goes through and it searches my local database in the app, and it just kind of filters and gets transactions for a specific date range. And then, uh, yeah, and it basically is just going to um, and it's just going to loop. It's literally just going to loop until it checks, "You know, do I have the available context? Yes. Okay, go answer it." And I do. Um, you can actually specify what the max number of loops is. That way, if you have a situation where it's like looping too much, or if it's an infinite loop and it's not getting it, you can have a hard cut-off when you want it to stop. So in this case, I think I have it set to three as a default. So it's not going to—or sorry, four—it's not going to loop uh more than four times in this case. And again, this was like—we're talking like three or four prompts to get the function calling working here.

So now that I have the function calling working, um, in theory, if I ask it, "Hey, can you, you know, can you tell me what I ate last week?" It's not going to feed in 3 months of data anymore. It's going to check, "Do I have like, do I have the transaction I needed to answer this question?" It's going to say no because we haven't called any tools. It's going to say, "Okay, I don't have it. What tools do I have at my disposal? Oh, cool. I have a function to get the transactions for a date range. Let me go put in the dates and go call it." So now, yeah. So now if I just say like, like, "Where did I eat last week?" But to be honest, it could, in theory, just answer this with 3 months' worth of data. So then if I ask it something like a little larger, like, "Yeah, that's what I was going to ask—last year," it's the moment of truth. So now if I ask it a little bit of a broader question, hopefully it's it's going to be able to—and it's taking a little bit longer, which signals to me like, "Okay, it's like, you know, it's uh, it's it's thinking a little more. It has more data." And here it does say, "Here's a summary of spending from April 22, 2024 to 2025." So it is now pulling the right year range. But again, we also gave it the budget tool as well, which we've never tried. So I said, "You know, um, how uh, am I overbudget anywhere?" So in theory, this is something we haven't been able to ask it, but let's see if it's able to actually use that tool. And it does. So it does have access to the user's budget. It did call that tool. So now it says, "You know, you allocated $75 for dining out, but you spent $112. Like, what's going on here?" Um, and so you can kind of see how we just gave it two tools, but this is already powerful. Imagine you gave it a tool to generate reports, or you gave it a tool to modify budgets and like make changes on the user's behalf, because now I can say, "Okay, is my budget—like, can you can you rebalance my budget for me?" And that's something users hate doing in budgeting apps. If you gave it that tool, now this thing can then just do it itself and and kind of, you know, it's able to do all this stuff for you like an actual agent.

Um, then obviously if you give it access to the internet, you give it access to other things—like if I give it access to the Robinhood API or something—probably won't do it. But in theory, it can actually probably re—like, you know, deposit money and make investments for me. Basically, you can do anything like once you have access to this low-level um, this low-level code to give agents tools. But it's super simple. And again, literally we got here—I think total to get to this point in the app—minus all the errors that we got and all the prompts I used for errors—this was like four prompts, probably like four prompts like to actually get to this point minus the error calling. And we were able to hook up OpenRouter so we can switch between the different models. We also were able to integrate function calling. So now that it's it's not even a chat anymore. It's really—there's an agent in here that has access to tools. And this all happened in like basically four prompts—with all the errors, probably like 20 prompts. But yeah, this is um, this is kind of like—it's very powerful stuff to be able to like to kind of do this in iOS.

And there's one other—the last thing that I did here just because I was like, "All right, got a little little bit more time." I think this was like 2 hours into doing this. I was like, "You know what I want to do? I want to um, I want to do two things. I want to um, I want to see, can I, you know, like this stuff costs money when you're making these calls," and I was like, "I'm using OpenRouter; they probably have an API to go get the costs. Can I just—can I actually modify this to see what are the actual costs like using this stuff?" So then the next thing I did was—yeah, so then the next thing I did was I told it—I basically told it, "Hey, can you alter the chat. So, right below the messages, I want to see the total tokens that were used and the cost." And then I also told it—it's like really simple with OpenRouter. So, I didn't even bother feeding the documentation. It was—I was like, "You just got to make a call to this endpoint"—like they have a generation endpoint. "So, you can pull any of the runs and then go get the cost and the tokens used." So, I told it, "Can you just go do this for me?" And it actually got it like instantly. It was like perfect—or no, sorry, there was one error, but then it was perfect. Then I also decided to say, "Hey, I actually want to try some other different models. Can you like swap these out?" Um, and then um, something else I was kind of curious was, "Wait, can I just like pull all the OpenRouter models and just like kind of display this here instead of hard-coding that?" Um, and so just for fun I also said—"Where is that?" Yeah, I also said, "Here, I—I gave it a prompt saying like—oh my god, okay, I gave it a prompt saying, 'Hey, can you actually alter it so let's just pull the OpenRouter models directly so I can see and have access to it—here's the response you're going to get from just calling this endpoint,' which I got from their documentation as well"—um, so once we did that—so now if I say, "Where did I eat last week?" So now at the bottom it actually shows, "This is how many tokens this consumed; here's the prompt and the completion tokens. And here was the cost." Okay, that is definitely not right, but I hope that's not right. That says $7. I'm pretty sure. Okay, I think I just did the math there wrong, but there's no way. Okay, but basically like it should show the cost here. And then um, now it also followed the second instruction, which was, "Let's pull all the models from OpenRouter." Okay, it didn't even do that one. Okay. Oh, okay. I know what happened. Um, I realized that some of the models don't support function calling. So I actually went back on that, and instead I—I hardcoded and said, "Hey, still pull it from OpenRouter to get the data, but let's just use these ones for now because not all of them support tool and function calling." So these are the models that do support tool and function calling like consistently with OpenRouter. But it is cool because now like in theory we can get the current cost of all the models—what the—like on the right, this is the context window. So you can see like Cloud 3.5—like this is 200,000 tokens. Gemini is 1 million context window. You can see the cost, and you can kind of see that—well, during development, especially if you're trying to test like, "How much is this stuff going to cost me? Like, what is this—you know, what does this look like"—this is so helpful to be able to you—like to be able to see this data right there and then. Obviously, you can build a dashboard and do whatever you need to to like, you know, see all your costs if you do put this in production, but that was a fun—that was like the fun thing that we did since we had a little bit more time.

And then absolutely last thing uh that we did here because I was like, "Okay, that's—it's like 2 hours and 20 minutes." Um, the last thing I—I—I wanted to showcase was—I really wanted to showcase uh how I do the asset generation. Um, so something that—something that people have been asking me all the time is like, "You know, you have—you have these really good—you have like the assets in the in the apps that I have are very high quality—like, how did I do this?" And um, to be honest, I'm using—I'm using GPT-4 or yeah, GPT-4 for the asset generation, which kind of surprises people. So let me show you guys what this looks like. So, for example, for my new app, Lily, which is a meeting transcription tool, um, this was the character asset that I generated. Um, I—I think I deleted the chat, so I don't really have it here like—like the full one, but um, you can basically throw in any asset here. But what I wanted to show you is how if you take an asset like this mascot, you can basically generate an infinite number of secondary assets which you can use in loading screens, in empty states. So, for example, we took the mascot here for this ghost icon and I said, "Can you give it wired glasses and put it in front of a laptop?" And now, you know, we have this, and this could be—this could be a really cool like, you know, empty state. Um, here's another one. Here's another one where I said, "Oh, I—I want you to make the background purple, and I want a little um, you know, I want it to be floating. I want there to be this little thing underneath it so it looks like it's floating. Can you do the same style but with a coffee cup? Can use the same style, but you know, I don't want the little three lines there." Um, this stuff is really powerful to—to almost make like any asset you can think of. Another one with books under a tree in a hammock, walking a dog. I didn't like the legs. That was kind of weird. So, I told it to remove the legs. Then I fed an image of my dog and I was like, "Can you make the dog look more like my dog?" And then it got that. So, yeah, you can kind of see that you can like—how powerful this stuff is. So, I wanted to do something here. Let's try to do it for this app. And so I was trying to think—"Okay, the current app is this mascot Luna." So my girlfriend actually is the one who drew this image. So this one was not AI-generated, but you can feed in like—again, you just feed in existing assets. So I said, "Hey, can you take this image and modify it so the dog is sitting at a computer?" It didn't get it fully perfectly, and—and I was trying to tweak it, and so—and it didn't really work because when I said, "Hey, can you remove the mouth?" Because in this image there's no mouth—here in this image there is—and I was like, "Okay, can you remove it?" It literally removed the mouth and the nose, so—and then it just—it just kept—it actually didn't even look good when I removed the mouth, but you do have to kind of prompt it and be very specific, and it is like—I think you—it—I think it gets it right 60 to 70% of the time, which is still worth it in my opinion to use, but now with just this image—age, we have this really nice—we have this really nice uh—uh asset that we can use and potentially use in the app. So then I was like, "Okay, let's just like—let's stick it in here and let's see how this looks." Um, so now if we go to the chat, now we have this asset at the top left corner. So we see this illustration that was AI-generated, but it just adds another dimension to your apps when you're able to generate these cool empty states and these illustrations. And now with these AI tools, I feel like everyone should have access to this stuff. And I think we're going to see a lot more beautiful apps now that we're doing this.

Um, and then I actually—my take on that is I think most people are going to create the—the "shad-CN" apps—the apps that are sort of bare bones. I hope that people listening to this actually take your advice and do a once-over of the app and be like, "Okay, if I can, you know, I—I've—I've done a barebone version of this. Now, how do I humanize it?" Yeah. With the loading screen, the—Yeah. the welcome email, like just all that sorts of things and give it just a little delight. Yeah. Um, and you can ask—you can ask Code, for example, and be like, "Hey, this is my app. These are the amount of screens I have. This is even what it looks like. Tell me what I should do to make—to add the light for use," and I'll—I'll be using ChatGPT-4 um, image generation. "Do you have any ideas for—for me?" And—and it might give you some ideas. Yeah. Yeah. Yeah. Honestly, I was like when I was uh, trying to brainstorm—that's how I come up with the initial logo for the ghost—um, for the—for the other app, Lily. I was using Code to literally generate this, and I—I think like it—I was like, "Hey, what's a good mascot for a meeting—you know, a meeting assistant app that lives in the background?" And it was like, "Oh, a fly, 'cause flies on the wall." The fly looked horrible—like I'll send you an image of it. It was like terrifying, but so it was like a fly, or maybe like, you know, "Oh, it's called Lily; maybe a frog 'cause of a lily pad." So it was actually cool to brainstorm, and eventually it actually came up with the ghost. It was like, "Maybe a ghost 'cause they kind of live in the background." So yeah, using—using the stuff to—to even just brainstorm the assets is—is really cool.

Chris, we are out of time. Is there anything you want to leave people with?

Um, yeah, honestly, I think—I think we covered everything, but uh, I just wanted to say too that—um, a lot of developers are pretty averse to using this stuff, but I seriously think that like a lot of developers—the—the—the things or the—the—the people that are going to benefit the most from this AI tooling, it is probably going to be like really good developers. I think everyone's going to benefit. Um, especially nontechnical people, but at this stage where the tools are at, the people I've seen go really far and really accelerate—it's people that have—like the people that do know how to program. So, if you're a developer and you're on the fence of using this stuff, I think you should just do it. I know it is kind of like—there's—it's kind of taboo—like some people are like—it's—it's kind of killing the art, but—um, this stuff is inevitable. I think it's better to really learn, and those are the developers that are going to thrive for the next decade. The ones that don't, I think it's not going to go really well. So if you're a developer, definitely try it out. That's—that's uh, that's probably the last thing I want to leave people with. And if—and if you're not a developer, should you give it a shot? If you're not a developer, you—I definitely think you should give it a shot. But what I would do though is really use it as a learning tool. So I—I honestly—I—I'm kind of hesitant to get—like I think use a tool honestly more like Replit, or use a tool more like Lobe, because those tools—they have a lot more guardrails, so it's a lot harder to break things. Cursor is a little more dangerous because that thing can really destroy things, and it's—it's very hard. Like, for example, what we saw here was it decided to code all of this stuff in the front end. That is very dangerous because like, for example—um, this is like a last example. This is like so interesting. It—we decided to make a tiny tool internally for my other company, and it was just like a—a small Vercel app—like a tiny Vercel app to automate something for the marketing department, but we use Cursor, and it hardcoded the OpenRouter key into the front end. And—and we did not share this link with anyone, but clearly someone has a bot running around looking at open Vercel things looking for these keys that are embedded in the front end. And they racked up like $300 worth of credits in like a day. This was like last week that this happened, and I was like, "Okay, this is really dangerous." Like, if you're—if you don't know what you're doing—like, and you accidentally commit some of this stuff, it can get bad. So use tools like Lobe, use stuff like Replit, because I think they do have a little bit more guardrails and a little bit more checks in their prompt to prevent that. But I—but really, I think everyone should honestly learn some of the basics of programming. They should probably, you know, take a couple courses, even though this AI stuff's here, and use AI to learn and use AI as a teacher to get better at those fundamentals and then go use stuff like this. Um, but yeah, that's—I—I—I definitely think people—I'm honestly very excited to see how stuff like Replit and Lobe and Cursor—how they get better for nontechnical people too. Or you can—by the way, on the courses side of things—or you can just keep listening to this podcast, keep commenting, let—let me know what you want to learn, and I won't even charge you for it. So you just get to learn for free.

Um, Chris, we'll include your social handles in the show notes for people who want to follow your journey. I think it's really interesting following your journey to, you know, building a multi-million dollar portfolio of mobile apps. So, um, thank you for being so generous with your time and your techniques.

Yeah, absolutely. Yeah. Thanks so much for having me, Greg. It was awesome.

Later.

All right. See you guys.