<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by İbrahim Çetin on Medium]]></title>
        <description><![CDATA[Stories by İbrahim Çetin on Medium]]></description>
        <link>https://medium.com/@cetinibrahim?source=rss-e7d608c3cf73------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*yJNJmwOWomnLycgY.</url>
            <title>Stories by İbrahim Çetin on Medium</title>
            <link>https://medium.com/@cetinibrahim?source=rss-e7d608c3cf73------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 13 Jul 2026 09:33:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@cetinibrahim/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Building Generic Network Layers in Swift with RequestSpec]]></title>
            <link>https://medium.com/@cetinibrahim/building-generic-network-layers-in-swift-with-requestspec-6eb24af58716?source=rss-e7d608c3cf73------2</link>
            <guid isPermaLink="false">https://medium.com/p/6eb24af58716</guid>
            <category><![CDATA[http-request]]></category>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[networking]]></category>
            <dc:creator><![CDATA[İbrahim Çetin]]></dc:creator>
            <pubDate>Thu, 20 Nov 2025 16:21:32 GMT</pubDate>
            <atom:updated>2025-11-20T16:23:55.353Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="RequestSpec example" src="https://cdn-images-1.medium.com/max/1024/1*dxIY_F98nFCwy6rmbO64Bg.png" /></figure><p>If you’ve ever built a network layer in Swift, you’ve probably written the same URLSession code again and again. Or <strong>you tried to abstract it</strong> and ended up with something complicated, hard to extend, or <strong>full of enums and switch statements.</strong></p><p>I developed <a href="https://github.com/ibrahimcetin/RequestSpec"><strong>RequestSpec</strong></a>, a lightweight library that makes networking both <strong>approachable</strong> and <strong>scalable</strong> without adding unnecessary complexity.</p><p>It gives you:</p><ul><li>A <strong>declarative</strong> way to define endpoints</li><li>A lightweight NetworkService protocol</li><li>Automatic <strong>URLRequest</strong> building</li><li>A clean async/await API</li></ul><p>In this post, we’ll build a tiny but realistic network layer using JSONPlaceholderService. You’ll define two real endpoints:</p><ul><li><strong>GET /posts</strong></li><li><strong>POST /posts</strong></li></ul><p>Let’s start.</p><h3>Why a Generic Network Layer?</h3><p>A good network layer makes your app:</p><ul><li>Easier to test</li><li>Easier to understand</li><li>Easier to extend</li></ul><p>But it shouldn’t require dozens of generic types or a heavy framework. <strong>RequestSpec</strong> keeps things light: <strong>you only define small, focused request types</strong> and create a service that sends them.</p><h3>1. Install RequestSpec</h3><p>Add it via Swift Package Manager:</p><pre>https://github.com/ibrahimcetin/RequestSpec</pre><p>That’s all you need.</p><h3><strong>2. Define Your Models</strong></h3><pre>struct BlogPost: Decodable {<br>    let id: Int<br>    let userId: Int<br>    let title: String<br>    let body: String<br>}</pre><p>This is the model we receive from the API.<br>It conforms to Decodable because it will be parsed from JSON.</p><pre>struct CreatePostInput: Encodable {<br>    let title: String<br>    let body: String<br>    let userId: Int<br>}</pre><p>This is what we send <em>to</em> the server when creating a post.<br>It conforms to Encodable because it will be JSON encoded.</p><h3>3. Create Your RequestSpecs</h3><p>Each API endpoint becomes its own small struct that conforms to RequestSpec. This keeps things modular and <strong>avoids giant enums or switch statements</strong>.</p><h4>GET Request (Fetch Posts)</h4><pre>struct GetPostsRequest: RequestSpec {<br>    var request: Get&lt;[BlogPost]&gt; {<br>        Get(&quot;posts&quot;)  // URL path segment<br>            .headers {<br>                Accept(&quot;application/json&quot;)  // Tell server we want JSON<br>                UserAgent(&quot;RequestSpec/1.0&quot;)  // Identify our client<br>            }<br>            .timeout(10)  // Wait max 10 seconds<br>    }<br>}</pre><p>This tells RequestSpec:</p><ul><li>Use GET</li><li>Call /posts</li><li>Set headers</li><li>Applies a timeout</li><li>Decodes into [BlogPost]</li></ul><h4>POST Request (Create New Post)</h4><pre>struct CreatePostRequest: RequestSpec {<br>    let input: CreatePostInput  // Data to send<br>    <br>    var request: Post&lt;BlogPost&gt; {<br>        Post(&quot;posts&quot;)<br>            .body {<br>                input  // This gets JSON-encoded automatically!<br>            }<br>            .headers {<br>                ContentType(&quot;application/json&quot;)  // Tell server we&#39;re sending JSON<br>                UserAgent(&quot;RequestSpec/1.0&quot;)<br>            }<br>            .timeout(10)<br>    }<br>}</pre><p>This one:</p><ul><li>Uses POST</li><li>Calls /posts</li><li>Sends the input data as JSON</li><li>Sets headers</li><li>Applies a timeout</li></ul><p>That’s it.</p><h3>4. Build the JSONPlaceholderService</h3><p>RequestSpec provides a NetworkService protocol that you can conform to and use to send your requests.</p><p>We will inherit from it to create our service&#39;s protocol:</p><pre>protocol JSONPlaceholderServiceProtocol: NetworkService {<br>    func getPosts() async throws -&gt; [BlogPost]<br>    func createPost(_ input: CreatePostInput) async throws -&gt; BlogPost<br>}</pre><p>Having a protocol makes it easier to test and mock. You can create a mock implementation of the protocol and use it in your tests but <strong>we will use the real implementation for now.</strong></p><p>Now we can implement the service:</p><pre>final class JSONPlaceholderService: JSONPlaceholderServiceProtocol {<br>    let baseURL = URL(string: &quot;https://jsonplaceholder.typicode.com&quot;)!<br>    let decoder: Decoder = JSONDecoder()<br>    let session: URLSession = URLSession.shared<br><br>    func getPosts() async throws -&gt; [BlogPost] {<br>        let request = GetPostsRequest()<br>        let response = try await send(request)<br>        return response.body<br>    }<br><br>    func createPost(_ input: CreatePostInput) async throws -&gt; BlogPost {<br>        let request = CreatePostRequest(input: input)<br>        let response = try await send(request)<br>        return response.body<br>    }<br>}</pre><p>Here’s what you didn’t write:</p><ul><li>No URLSession code</li><li>No URLRequests</li><li>No JSONEncoder / JSONDecoder</li><li>No manual path building</li><li>No switch statements over enums</li></ul><p>Everything goes through send(request).</p><h3>5. Use the Service</h3><pre>let service = JSONPlaceholderService()<br>let posts = try await service.getPosts()<br>print(posts)</pre><p>Readable. Type-safe. Clean.</p><h3>Explore the Complete Example</h3><p><a href="https://github.com/ibrahimcetin/RequestSpec/tree/main/Examples/RequestSpecExample">RequestSpec/Examples/RequestSpecExample at main · ibrahimcetin/RequestSpec</a></p><h3>Why RequestSpec Makes Networking Approachable</h3><p>Here’s the philosophy:</p><h4>Small types over giant enums</h4><p>Every endpoint is its own struct which makes it easier to build, maintain, and reuse.</p><h4>Zero boilerplate</h4><p>No manual URLRequest creation. No decoding logic scattered everywhere.</p><h4>Scales naturally</h4><p>Adding a new endpoint means adding a new RequestSpec — nothing else.</p><h3>Conclusion</h3><p>I developed <strong>RequestSpec</strong> to make networking approachable and easy to understand. It has more features and is more powerful than this example but the core ideas are the same. You can check out the <a href="https://github.com/ibrahimcetin/RequestSpec">README</a> for more details.</p><p>You don’t need to use RequestSpec exclusively. It is <strong>interoperable</strong> with other libraries like <strong>URLSession</strong>, <strong>Alamofire </strong>to <strong>use it in your existing project. </strong>You can check out the <a href="https://github.com/ibrahimcetin/RequestSpec/tree/main/Examples">Examples</a> directory.</p><h3>Resources</h3><ul><li><a href="https://github.com/ibrahimcetin/RequestSpec">RequestSpec</a></li><li><a href="https://github.com/ibrahimcetin/RequestSpec/tree/main/Examples">RequestSpec Examples</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6eb24af58716" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[MLX Swift: Run VLMs (Image-to-Text) in iOS Apps]]></title>
            <link>https://medium.com/@cetinibrahim/mlx-swift-run-vlms-image-to-text-in-ios-apps-ae34caa33c9b?source=rss-e7d608c3cf73------2</link>
            <guid isPermaLink="false">https://medium.com/p/ae34caa33c9b</guid>
            <category><![CDATA[mlx]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[İbrahim Çetin]]></dc:creator>
            <pubDate>Wed, 05 Mar 2025 15:26:29 GMT</pubDate>
            <atom:updated>2025-03-08T09:20:05.744Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="MLX" src="https://cdn-images-1.medium.com/max/460/1*9opPAjXOboF3O8Y6c7ongg.png" /></figure><p>MLX Swift is able us to run LLMs as well as VLMs which is stands for <strong>Visual Language Models</strong> and these models can <strong>describe an image</strong>, <strong>detect an object</strong>, <strong>answer</strong> <strong>visual questions </strong>or <strong>extract text from image</strong>. Thank to MLX Swift, we can run these model directly in our apps <strong>without any server</strong>.</p><p>This blog post is the last in a three-part series where I’ll guide you through <strong>running VLMs with MLX Swift </strong>by developing <a href="https://github.com/ibrahimcetin/MLXSampleApp">a sample project</a>.</p><p>You can find the other blog posts here:</p><ol><li><a href="https://medium.com/@cetinibrahim/mlx-swift-run-llms-in-ios-apps-8f89c1123588">MLX Swift: Run LLMs in iOS Apps</a></li><li><a href="https://medium.com/@cetinibrahim/run-hugging-face-models-with-mlx-swift-d723437ff12e">Run Hugging Face Models with MLX Swift</a></li><li>MLX Swift: Running VLMs (Image-to-Text) in iOS Apps</li></ol><p><em>I am developing </em><a href="https://github.com/ibrahimcetin/MLXSampleApp"><em>a sample project</em></a><em> along with the posts. It will be minimal yet practical, so you’ll have a working reference at your fingertips.</em></p><p><a href="https://github.com/ibrahimcetin/MLXSampleApp">GitHub - ibrahimcetin/MLXSampleApp</a></p><p><strong>Tip: </strong>If you don’t know what is MLX Swift, you may want to check out the first blog post of the series.</p><h3>Overview</h3><p>The steps are same as running LLMs, <strong>only five</strong> steps:</p><ol><li><strong>Add MLX Swift Examples Package:</strong> This package provides the necessary modules and tools for running VLMs with MLX Swift.</li><li><strong>Select a Model:</strong> Choose the model you want to use, whether an LLM or a VLM.</li><li><strong>Download and Load the Model:</strong> Load the model weights and its configurations. <em>(All process will be handled by MLXVLM)</em></li><li><strong>Create an Input:</strong> Provide a prompt and an image for the model.</li><li><strong>Run the Model:</strong> Process the input through the model and generate the output.</li></ol><h3>Adding MLX Swift Examples Package</h3><p>Since MLX Swift Examples is an SPM package, you can add it directly in Xcode:</p><ol><li>In Xcode, Go to <strong>File -&gt; Add Package Dependencies…</strong></li><li>Paste the following Package URL: <a href="https://github.com/ml-explore/mlx-swift-examples">https://github.com/ml-explore/mlx-swift-examples</a></li><li>Set the <strong>Dependency Rule</strong> to <strong>Branch</strong> and set it to <strong>main</strong>.</li><li>Add the MLXVLM package to your project’s target.</li></ol><p><em>If you already added </em><em>mlx-swift-examples package to your project, you can add </em><em>MLXVLM from </em><strong><em>Target -&gt; Frameworks, Libraries and Embedded Content</em></strong><em> menu.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MpZ3gBR2TjmQVTPJMUwUSg.png" /></figure><p>Then, import the required modules in your file:</p><pre>import MLXLLM<br>import MLXLMCommon</pre><ul><li><strong>MLXVLM</strong> will provide us VLM specific necessary items.</li><li><strong>MLXLMCommon</strong> is a common library to generate language model output. It works both LLMs and VLMs.</li></ul><h3>Selecting a Model</h3><p>First, we need to choose the model we want to use:</p><pre>let modelConfiguration: ModelConfiguration = ModelRegistry.qwen2_5Coder_1_5B_4bit</pre><p>We create a ModelConfiguration instance for the selected model. MLXVLM includes predefined popular models in ModelRegistry.<br>Here, I chose <strong>Qwen2 VL 2B Instruct 4bit</strong> because it is a instruct model. That is, we can make conversation with it.</p><p><strong><em>!</em></strong><em> Using a instruct model here will add a little bit complexity but I think it will be a better and more educational.</em></p><p><strong><em>Recall</em></strong><em>: In the previous post, I show how to use other models from Hugging Face!</em></p><h3>Loading the Selected Model</h3><p>Using the modelConfiguration instance, we can load our model:</p><pre>let modelContainer = try await VLMModelFactory.shared.loadContainer(<br>    configuration: modelConfiguration<br>) { progress in<br>    Task { @MainActor in<br>        self.downloadProgress = progress<br>    }<br>}</pre><p>Here, we use VLMModelFactory instead of LLMModelFactory to load VLMs.</p><p>VLMModelFactory.shared.loadContainer method <strong>automatically downloads</strong> the model from Hugging Face if it is not available locally. It also handles <strong>creating the model</strong> with the appropriate architecture and loading the weights from .safetensors files. It also <strong>takes a closure</strong> to allow us to track the downloading progress.</p><p><strong><em>Tip</em></strong><em>: VLMModelFactory and LLMModelFactory both conform to </em><strong><em>ModelFactory</em></strong><em>.</em></p><h3>Preparing an Input</h3><p>Firstly, we have to select the images we use. We can use PhotosUI package for iOS and fileImporter view modifier for macOS in SwiftUI:</p><pre>import PhotosUI<br><br>@State private var showingPhotoPicker: Bool = false<br>@State private var photoSelection: PhotosPickerItem?<br><br>VStack {<br>  /* UI Code */<br>}<br>#if(os(iOS))<br>.photosPicker(isPresented: $showingPhotoPicker, selection: $photoSelection)<br>.onChange(of: photoSelection, addImage)<br>#elseif(os(macOS))<br>.fileImporter(isPresented: $showingPhotoPicker, allowedContentTypes: [.image], onCompletion: addImage)<br>#endif</pre><ul><li>When an image is selected, the addImage will be called.</li></ul><p><strong>!!!</strong> To use these images as input, we need to convert them to CIImage. However, we also want to display them using the Image view. To achieve both, we store the selected images as Data. This allows us to convert them to CIImage when needed while also displaying them in the Image view using UIImage or NSImage:</p><pre>@State private var selectedImages: [Data] = []<br><br>private func addImage() {<br>  Task {<br>      if let data = try? await photoSelection?.loadTransferable(type: Data.self) {<br>          selectedImages = [data]<br>      }<br>  }<br>}</pre><p>Now, we can call generate method:</p><pre>await vm.generate(prompt: prompt, images: selectedImages)</pre><p><em>This is a boilerplate code don’t try to run it. You can find it in the sample project.</em></p><h3>Creating an Input</h3><p>To run our model, we need to provide an input:</p><pre>let result = try await modelContainer.perform { context in<br>  // Create images and messages<br>  let images: [UserInput.Image] = images.compactMap { CIImage(data: $0) }.map { .ciImage($0) }<br>  let message: Message = [<br>      &quot;role&quot;: &quot;user&quot;,<br>      &quot;content&quot;: [<br>          [&quot;type&quot;: &quot;text&quot;, &quot;text&quot;: prompt]<br>      ] + images.map { _ in [&quot;type&quot;: &quot;image&quot;] }<br>  ]<br><br>  // Create prompt<br>  let prompt = UserInput(messages: [message], images: images)<br>  prompt.processing.resize = CGSize(width: 448, height: 448)<br>  <br>  // Create input<br>  let input = try await context.processor.prepare(input: prompt)<br>  <br>  /* Generate output */<br>}</pre><ul><li>We call modelContainer.perform, which allows us to interact with the ModelContext.</li><li>Since the images are passed as Data, we need to convert them to CIImage and provide them as the .ciImage associated value.</li><li>This time we use Message because the model we use is <strong>Qwen2 VL instruct</strong> model<strong>. </strong>That is<strong>, </strong>we have to use <strong>messages format for Qwen2 VL </strong>to add images. <em>Alternatively, you can use models like </em><a href="https://huggingface.co/mlx-community/paligemma-3b-mix-448-8bit"><em>Paligemma 3B mix</em></a><em> which don’t require any format.</em></li><li>We use UserInput to transform the prompt into an input format.</li><li>Finally, we convert the prompt into an LMInput instance using context.processor.prepare, which will be used for generating the output.</li></ul><h3>Running the LLM Model</h3><p>To run the model, we use the MLXLMCommon module:</p><pre>/* Create input */<br>return try MLXLMCommon.generate(input: input, parameters: .init(), context: context) { tokens in<br>     let text = context.tokenizer.decode(tokens: tokens)<br><br>     Task { @MainActor in<br>        output = text<br>     }<br>     <br>     return .more<br>}</pre><ul><li>MLXCommon.generate takes the input and the context from the perform method and generates the output inside the closure.</li><li>The closure receives the encoded tokens, so we first <strong>decode them</strong> using the tokenizer before using the output.</li><li>Finally, we return .more to continue generating output. If you want to stop generation — for example, when reaching a maximum token limit — you can return .stop instead.</li></ul><h3>Final Application</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FVh43QDCfSUU%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DVh43QDCfSUU&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FVh43QDCfSUU%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="640" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/8f8f6be934869d87cac6898d0a9fec41/href">https://medium.com/media/8f8f6be934869d87cac6898d0a9fec41/href</a></iframe><p>You can find the app I developed for this blog post here:</p><p><a href="https://github.com/ibrahimcetin/MLXSampleApp">GitHub - ibrahimcetin/MLXSampleApp</a></p><p><strong><em>Tip</em></strong><em>: Each blog post also has its own branch to make it easy to find the corresponding code for each part.</em></p><h3>Limit The Buffer Cache</h3><p>In MLX, managing the buffer cache is essential for optimal performance and memory usage. You can control the buffer cache size using the set(cacheLimit:) function, which specifies the maximum memory (in bytes) that the cache can utilize. For example, to limit the buffer cache to 200 megabytes, implement the following code:</p><pre>MLX.GPU.set(cacheLimit: 20 * 1024 * 1024)</pre><p>Adjusting the cache limit helps balance memory consumption and computational efficiency.</p><h3><strong>Moving Forward</strong></h3><p>MLX Swift offers much more beyond what we’ve covered. Here are a few things you can explore:</p><ul><li>Fine-tune generation parameters. If you missed it, we used them in MLXLMCommon.generate.</li><li>Experiment with different models from the mlx-community page on Hugging Face.</li><li>Convert models that haven’t been ported to MLX format yet and run them on Apple devices.</li></ul><p>There’s plenty more to discover, so keep exploring!</p><h3>Conclusion</h3><p>That’s all from me for now! Don’t forget to check out the sample project. I believe that on-device language models will become more accessible and preferred in the future. Even now, they are at a usable level for many tasks. I truly believe that locally running models are the future.</p><p>To summarize, we:</p><ul><li>Learned the basics of running VLMs on-device with MLX Swift.</li><li>Used a <strong>small instruct model</strong>, Qwen2 VL 2B Instruct 4bit.</li><li>Prepared inputs and executed inference.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ae34caa33c9b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Run Hugging Face Models with MLX Swift]]></title>
            <link>https://medium.com/@cetinibrahim/run-hugging-face-models-with-mlx-swift-d723437ff12e?source=rss-e7d608c3cf73------2</link>
            <guid isPermaLink="false">https://medium.com/p/d723437ff12e</guid>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[mlx]]></category>
            <dc:creator><![CDATA[İbrahim Çetin]]></dc:creator>
            <pubDate>Wed, 05 Mar 2025 15:24:42 GMT</pubDate>
            <atom:updated>2025-03-05T15:27:20.410Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="MLX" src="https://cdn-images-1.medium.com/max/460/1*9opPAjXOboF3O8Y6c7ongg.png" /></figure><p>MLX Swift provides us some predefined models in ModelRegistry but these are very limited. If you want to use other models or try to latest ones, you have to add these models yourself. <strong>It is easy and straightforward, so</strong> <strong>don’t hesitate.</strong></p><p>This blog post is the second in a three-part series where I’ll guide you through <strong>adding language models from Hugging Face</strong> to use with MLX Swift.</p><p>You can find the other blog posts here:</p><ol><li><a href="https://medium.com/@cetinibrahim/mlx-swift-run-llms-in-ios-apps-8f89c1123588">MLX Swift: Run LLMs in iOS Apps</a></li><li>Run Hugging Face Models with MLX Swift</li><li><a href="https://medium.com/@cetinibrahim/mlx-swift-run-vlms-image-to-text-in-ios-apps-ae34caa33c9b">MLX Swift: Running VLMs (Image-to-Text) in iOS Apps</a></li></ol><p><em>I am developing </em><a href="https://github.com/ibrahimcetin/MLXSampleApp"><em>a sample project</em></a><em> along with the posts. It will be minimal yet practical, so you’ll have a working reference at your fingertips.</em></p><p><a href="https://github.com/ibrahimcetin/MLXSampleApp">GitHub - ibrahimcetin/MLXSampleApp</a></p><p><strong><em>Tip</em></strong><em>: If you don’t know what is MLX Swift, please checkout the first blog post of the series.</em></p><h3>Adding Custom Models</h3><p>Firstly, a model must be in MLX format to be usable. Fortunately, the MLX community does a great job of converting and sharing models in MLX format on Hugging Face. You can explore all available models from <a href="https://huggingface.co/mlx-community">their HF page</a>.</p><p><a href="https://huggingface.co/mlx-community">mlx-community (MLX Community)</a></p><p>Adding custom models are <strong>as simple as</strong> creating a ModelContext instance. The code example adds <strong>DeepSeek-R1 1.5B</strong> model:</p><pre>extension ModelRegistry {<br>    static let deepSeekR1_1_5B_4bit = ModelConfiguration(<br>        id: &quot;mlx-community/DeepSeek-R1-Distill-Qwen-1.5B-4bit&quot;,<br>        overrideTokenizer: &quot;PreTrainedTokenizer&quot;,<br>        defaultPrompt: &quot;Write a program that sends a request to google in python.&quot;<br>    )<br>}</pre><ul><li>We define an extension on ModelRegistry and define a descriptive name such as deepSeekR1_1_5B_4bit as a static property to easily access to it.</li><li>ModelConfiguration wants id at least from us and <a href="https://huggingface.co/mlx-community/DeepSeek-R1-Distill-Qwen-1.5B-4bit">this is the name of the model from Hugging Face</a>.</li><li>The defaultPrompt is a predefined input and is used when no prompt is provided. (<em>This field is optional, and if not set, it defaults to hello.)</em></li><li>The overrideTokenizer parameter allows us to specify a custom tokenizer instead of using a default one. In this case, we set it to &quot;PreTrainedTokenizer&quot;, meaning the model will use a tokenizer that has already been trained and configured, <em>typically from its Hugging Face repository</em>. This ensures the tokenizer matches the model’s architecture, vocabulary, and text-processing rules, preventing any mismatches in input handling.</li></ul><p><em>Tip: You can investigate static variables on </em><em>ModelRegistry to get more idea.</em></p><h3>Using The Custom Model</h3><p>First, we have to create an instance of the custom ModelConfiguration:</p><pre>let customModelConfiguration = ModelRegistry.deepSeekR1_1_5B_4bit</pre><p>Then, we can load and you it:</p><pre>modelContainer = try await LLMModelFactory.shared.loadContainer(<br>    configuration: customModelConfiguration<br>) { progress in<br>    Task { @MainActor in<br>        self.downloadProgress = progress<br>    }<br>}</pre><p>Lastly, run the model:</p><pre>let result = try await modelContainer.perform { context in<br>    let prompt = UserInput(prompt: prompt)<br>    let input = try await context.processor.prepare(input: prompt)<br><br>    return try MLXLMCommon.generate(input: input, parameters: .init(), context: context) { tokens in<br>        let text = context.tokenizer.decode(tokens: tokens)<br><br>        Task { @MainActor in<br>            output = text<br>        }<br><br>        return .more<br>    }<br>}</pre><p><strong><em>Tip</em></strong><em>: For more details about how to run the model, be sure to check out my previous blog post.</em></p><h3>Final Application</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FjoqkywqnptM%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DjoqkywqnptM&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FjoqkywqnptM%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="640" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/639c8a6434cc56ef5e9d895af340dc11/href">https://medium.com/media/639c8a6434cc56ef5e9d895af340dc11/href</a></iframe><p>You can find the app I developed for this blog post here:</p><p><a href="https://github.com/ibrahimcetin/MLXSampleApp">GitHub - ibrahimcetin/MLXSampleApp</a></p><p><em>Tip: Each blog post also has its own branch to make it easy to find the corresponding code for each part.</em></p><h3>Further Reading</h3><p>MLX Swift abstracts away most of the complexity for us. Even though I said it’s simple, there are actually many details in the background. For example, this is the configuration for Llama from MLXLLM codebase:</p><pre>public struct LlamaConfiguration: Codable, Sendable {<br>    var hiddenSize: Int<br>    var hiddenLayers: Int<br>    var intermediateSize: Int<br>    var attentionHeads: Int<br>    var headDimensions: Int?<br>    var rmsNormEps: Float<br>    var vocabularySize: Int<br>    var kvHeads: Int<br>    var maxPositionEmbeddings: Int?<br>    var ropeTheta: Float = 10_000<br>    var ropeTraditional: Bool = false<br>    var ropeScaling: [String: StringOrNumber]?<br>    var tieWordEmbeddings: Bool = true<br>    var attentionBias: Bool = false<br>    var mlpBias: Bool = false<br>}</pre><p>It contains a lot properties. If you want to go further, you can read <a href="https://swiftpackageindex.com/ml-explore/mlx-swift-examples/main/documentation/mlxllm/adding-model">this documentation</a>.</p><h3>Where Are Models Stored?</h3><p>Models are stored in Documents directory of the application by default. That is, it is /Users/&lt;username&gt;/Documents on <strong>macOS</strong> and the application’s Documents directory on <strong>iOS</strong> and other platforms.</p><p>Fortunately, <strong>we can change the default directory.</strong></p><p>First, we have to import Hub package:</p><pre>import Hub</pre><ul><li>This package provides us HubApi struct to configure download directory.</li></ul><p>Then, create a HubApi instance:</p><pre>let hub = HubApi(downloadBase: URL.downloadsDirectory.appending(path: &quot;huggingface&quot;))</pre><ul><li>This will able us to download the models to Downloads directory on macOS and app’s sandbox directory on iOS.</li></ul><p>Lastly, we can pass it to LLMModelFactory.shared.loadContainer method:</p><pre>modelContainer = try await LLMModelFactory.shared.loadContainer(<br>    hub: hub,<br>    configuration: modelConfiguration<br>) {<br>  /* Handle Progress */<br>}</pre><h3>Conclusion</h3><p>Adding and using other models with MLX Swift is this <strong>simple!</strong><br><strong>If you can’t find a model</strong> you’re looking for on the <a href="https://huggingface.co/mlx-community">mlx-community page</a>, you can also <strong>convert models to MLX format</strong> yourself, though that’s a topic for another blog post.</p><p>To summarize, we:</p><ul><li>Learned how to run custom models from Hugging Face with MLX Swift.</li><li>Integrated the <strong>DeepSeek-R1 1.5B</strong> model</li><li>Changed the default download directory.</li></ul><p>In the <strong>next post</strong>, we’ll explore <strong>how to use VLMs (Image-to-Text)</strong>!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d723437ff12e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[MLX Swift: Run LLMs in iOS Apps]]></title>
            <link>https://medium.com/@cetinibrahim/mlx-swift-run-llms-in-ios-apps-8f89c1123588?source=rss-e7d608c3cf73------2</link>
            <guid isPermaLink="false">https://medium.com/p/8f89c1123588</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[mlx]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[İbrahim Çetin]]></dc:creator>
            <pubDate>Wed, 05 Mar 2025 15:23:44 GMT</pubDate>
            <atom:updated>2025-03-05T15:27:28.720Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="MLX" src="https://cdn-images-1.medium.com/max/460/1*9opPAjXOboF3O8Y6c7ongg.png" /></figure><p>Today, large language models (LLMs) are mostly used via a web API, but running them directly on a device is also possible and <strong>it has several advantages</strong>:</p><ul><li><strong>Privacy:</strong> Since models run on the device, user data never leaves the device for a remote server.</li><li><strong>Cost:</strong> No API usage fees, as the model does not run on a cloud server.</li><li><strong>Offline Functionality:</strong> Once downloaded, models can continue running completely offline.</li></ul><p><strong>MLX Swift</strong> makes it easy to run LLMs and VLMs (Visual Language Model) in Swift, and <strong>it is straightforward to use</strong>.</p><p>This blog post is the first in a three-part series where I’ll guide you through <strong>running LLMs with MLX Swift</strong> by developing <a href="https://github.com/ibrahimcetin/MLXSampleApp">a sample project</a>.</p><p>You can find the other blog posts here:</p><ol><li>MLX Swift: Run LLMs in iOS Apps</li><li><a href="https://medium.com/@cetinibrahim/run-hugging-face-models-with-mlx-swift-d723437ff12e">Run Hugging Face Models with MLX Swift</a></li><li><a href="https://medium.com/@cetinibrahim/mlx-swift-run-vlms-image-to-text-in-ios-apps-ae34caa33c9b">MLX Swift: Run VLMs (Image-to-Text) in iOS Apps</a></li></ol><p><em>The project will be minimal yet practical, so you’ll have a working reference at your fingertips.</em></p><p><a href="https://github.com/ibrahimcetin/MLXSampleApp">GitHub - ibrahimcetin/MLXSampleApp</a></p><h3>What Are MLX and MLX Swift?</h3><p><strong>MLX</strong> is a NumPy-like array framework developed by Apple for machine learning research, originally written in <strong>Python. </strong>It is optimized for <strong>Apple Silicon</strong> architecture.</p><p><strong>MLX Swift </strong>is the Swift extension of <strong>MLX </strong>to<strong> </strong>make ML research and experimentation easier on Apple platforms. It allows us to run LLMs and VLMs in <strong>iOS</strong> and <strong>macOS</strong> apps <strong>easily</strong>.</p><h3>Overview</h3><p>There are <strong>only five steps</strong> to run language models:</p><ol><li><strong>Add MLX Swift Examples Package:</strong> This package provides the necessary modules and tools for running LLMs with MLX Swift.</li><li><strong>Select a Model:</strong> Choose the model you want to use, whether an LLM or a VLM.</li><li><strong>Download and Load the Model:</strong> Load the model weights and its configurations. <em>(All process will be handled by MLXLLM)</em></li><li><strong>Create an Input:</strong> Provide a prompt for the model.</li><li><strong>Run the Model:</strong> Process the input through the model and generate the output.</li></ol><h3><strong>Adding MLX Swift Examples Package</strong></h3><p>Since MLX Swift Examples is an SPM package, you can add it directly in Xcode:</p><ol><li>In Xcode, Go to <strong>File -&gt; Add Package Dependencies…</strong></li><li>Paste the following Package URL: <a href="https://github.com/ml-explore/mlx-swift-examples">https://github.com/ml-explore/mlx-swift-examples</a></li><li>Set the <strong>Dependency Rule</strong> to <strong>Branch</strong> and set it to <strong>main</strong>.</li><li>Add the MLXLLM package to your project’s target.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sMQdOT2a9TLL9AZ-ZBeRBA.png" /></figure><p>Then, import the required modules in your file:</p><pre>import MLXLLM<br>import MLXLMCommon</pre><ul><li><strong>MLXLLM</strong> will provide us LLM specific necessary items.</li><li><strong>MLXLMCommon</strong> is a common library to generate language model output. It works both LLMs and VLMs.</li></ul><h3>Selecting a Model</h3><p>First, we need to choose the model we want to use:</p><pre>let modelConfiguration: ModelConfiguration = ModelRegistry.llama3_2_1B_4bit</pre><p>We create a ModelConfiguration instance for the selected model. MLXLLM includes predefined popular models in ModelRegistry.<br>Here, I chose <strong>Llama 3.2 1B 4-bit</strong> because it is a relatively small model.</p><p><strong><em>Recall</em></strong><em>: In the next post, I will show how to use other models from Hugging Face!</em></p><h3>Loading the Selected Model</h3><p>Using the modelConfiguration instance, we can load our model:</p><pre>let modelContainer = try await LLMModelFactory.shared.loadContainer(<br>    configuration: modelConfiguration<br>) { progress in<br>    Task { @MainActor in<br>        self.downloadProgress = progress<br>    }<br>}</pre><p>LLMModelFactory.shared.loadContainer method <strong>automatically downloads</strong> the model from Hugging Face if it is not available locally. It also handles <strong>creating the model</strong> with the appropriate architecture and loading the weights from .safetensors files. It also <strong>takes a closure</strong> to allow us to track the downloading progress.</p><h3>Creating an Input</h3><p>To run our model, we need to provide an input:</p><pre>let result = try await modelContainer.perform { context in<br>  let prompt = UserInput(prompt: prompt)<br>  let input = try await context.processor.prepare(input: prompt)  <br><br>  /* Generate output */<br>}</pre><ul><li>We call modelContainer.perform, which allows us to interact with the ModelContext.</li><li>We use UserInput to transform the prompt into an input format. <em>UserInput can also include </em><strong><em>images</em></strong><em> and </em><strong><em>videos</em></strong><em>.</em></li><li>Finally, we convert the prompt into an LMInput instance using context.processor.prepare, which will be used for generating the output.</li></ul><h3>Running the LLM Model</h3><p>To run the model, we use the MLXLMCommon module:</p><pre>/* Create input */<br><br>return try MLXLMCommon.generate(input: input, parameters: .init(), context: context) { tokens in<br>    let text = context.tokenizer.decode(tokens: tokens)<br><br>    Task { @MainActor in<br>        output = text<br>    }<br><br>    return .more<br>}</pre><ul><li>MLXCommon.generate takes the input and the context from the perform method and generates the output inside the closure.</li><li>The closure receives the encoded tokens, so we first <strong>decode them</strong> using the tokenizer before using the output.</li><li>Finally, we return .more to continue generating output. If you want to stop generation — for example, when reaching a maximum token limit — you can return .stop instead.</li></ul><h3>Final Application</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FYYQxHcwiCts%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DYYQxHcwiCts&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FYYQxHcwiCts%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="640" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7c2b9aa3652d118708d949c37da3b8ef/href">https://medium.com/media/7c2b9aa3652d118708d949c37da3b8ef/href</a></iframe><p>You can find the app I developed for this blog post here:</p><p><a href="https://github.com/ibrahimcetin/MLXSampleApp">GitHub - ibrahimcetin/MLXSampleApp</a></p><p><strong><em>Tip</em></strong><em>: Each blog post also has its own branch to make it easy to find the corresponding code for each part.</em></p><h3>Developing on iOS Simulators</h3><p>When try to run MLX Swift on an iOS simulator, your app<strong> will crash</strong> because MLX requires a modern <a href="https://developer.apple.com/documentation/metal/mtlgpufamily">Metal MTLGPUFamily</a> and the simulators does not provide that.</p><p><a href="https://swiftpackageindex.com/ml-explore/mlx-swift/main/documentation/mlx/running-on-ios#Developing-for-iOS">The MLX documentation</a> has some recommendations for the development. In summary, they suggests developing the UI on simulator but to be able to run MLX you need to have an actual device.</p><h3>Increasing Memory Limits for Larger Models</h3><p>Running larger language models requires more memory, and iOS imposes memory limits on apps by default. If your model exceeds these limits, the app may crash due to insufficient resources.</p><p>To prevent this, you need to enable <strong>“Increased Memory Limit”</strong> in your app’s entitlements:</p><ol><li>Open your <strong>Xcode project</strong>.</li><li>Go to <strong>Signing &amp; Capabilities</strong>.</li><li>Click <strong>“+ Capability”</strong> and search for <strong>“Increased Memory Limit”</strong>.</li><li>Add it to your app.</li></ol><p>This setting allows your app to use more memory, making it possible to run larger models without crashes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bG9vuD1jCfYVE0_6-NUweQ.png" /></figure><h3>Conclusion</h3><p>Running LLMs with MLX Swift is this <strong>simple!</strong><br>For now, running large parameter models is not feasible, but in cases where small models are sufficient, this approach can be useful.</p><p>To summarize, we:</p><ul><li>Learned the basics of running LLMs on-device with MLX Swift.</li><li>Used a <strong>small model</strong>, Llama 3.2 1B 4-bit.</li><li>Prepared inputs and executed inference.</li></ul><p>In the <strong>next post</strong>, we’ll explore <strong>how to use custom models</strong>!</p><h3>References</h3><ul><li><a href="https://github.com/ml-explore/mlx-swift-examples">GitHub - ml-explore/mlx-swift-examples: Examples using MLX Swift</a></li><li><a href="https://github.com/ml-explore/mlx">GitHub - ml-explore/mlx: MLX: An array framework for Apple silicon</a></li><li><a href="https://github.com/ml-explore/mlx-swift">GitHub - ml-explore/mlx-swift: Swift API for MLX</a></li><li><a href="https://www.rudrank.com/exploring-mlx-swift-adding-on-device-inference-to-your-app/">Exploring MLX Swift: Adding On-Device Inference to your App</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8f89c1123588" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>