<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Native Development Archives - Mobile USTP MKL</title>
	<atom:link href="https://mobile.fhstp.ac.at/category/development/native-development/feed/" rel="self" type="application/rss+xml" />
	<link>https://mobile.fhstp.ac.at/category/development/native-development/</link>
	<description>Die &#34;Mobile Forschungsgruppe&#34; der USTP, sie  sammelt hier alles zu den Themen Design, UX und Entwicklung mobiler Applikationen</description>
	<lastBuildDate>Tue, 06 Oct 2026 08:47:21 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://mobile.fhstp.ac.at/wp-content/uploads/2025/03/icon-120x120.webp</url>
	<title>Native Development Archives - Mobile USTP MKL</title>
	<link>https://mobile.fhstp.ac.at/category/development/native-development/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Gymetrics: A Self-Hostable Fitness App with Kotlin Multiplatform, NestJS, and Dockerization</title>
		<link>https://mobile.fhstp.ac.at/allgemein/gymetrics-a-self-hostable-fitness-app/</link>
		
		<dc:creator><![CDATA[Jakob Laschober]]></dc:creator>
		<pubDate>Thu, 17 Sep 2026 06:49:00 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Native Development]]></category>
		<category><![CDATA[AI Coding Agents]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[Compose Multiplatform]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Fitness App]]></category>
		<category><![CDATA[KMP]]></category>
		<category><![CDATA[Koin Dependency Injection]]></category>
		<category><![CDATA[Kotlin Multiplatform]]></category>
		<category><![CDATA[MCP Server]]></category>
		<category><![CDATA[Mobile Architecture]]></category>
		<category><![CDATA[Model Context Protocol]]></category>
		<category><![CDATA[NestJs]]></category>
		<category><![CDATA[offline-first]]></category>
		<category><![CDATA[Self-Hosted App]]></category>
		<category><![CDATA[Software Engineering]]></category>
		<guid isPermaLink="false">https://mobile.fhstp.ac.at/?p=15928</guid>

					<description><![CDATA[<p>I set out to build this app in my first semester as a simple replacement for the Excel spreadsheet I used for workout tracking. But in the process of making it, it became so much more. And if you want to check it out you can on gymetrics.at (yeah I&#8217;ve already bought a Domain &#8230;). <a class="read-more" href="https://mobile.fhstp.ac.at/allgemein/gymetrics-a-self-hostable-fitness-app/">[...]</a></p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/gymetrics-a-self-hostable-fitness-app/">Gymetrics: A Self-Hostable Fitness App with Kotlin Multiplatform, NestJS, and Dockerization</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I set out to build this app in my first semester as a simple replacement for the Excel spreadsheet I used for workout tracking. But in the process of making it, it became so much more. And if you want to check it out you can on <a href="https://gymetrics.at/">gymetrics.at</a> (yeah I&#8217;ve already bought a Domain &#8230;).</p>



<p class="wp-block-paragraph">When I started out, I decided to make an application that was really worth trying. I designed a database structure and a system architecture that I meant to be perfect. But for my first frontend (tbh not only the frontend), I was struggling. I am no designer myself and couldn&#8217;t really create a smooth user flow, so I used AI to help me with that. After I was done, I wasn&#8217;t very happy with either the scalability or the look and feel. I had used Ionic, a web framework that ran in a WebView on the device. The touch inputs didn&#8217;t feel instant or responsive enough, and for what was basically a simple CRUD app, it felt a little bit sluggish (or maybe that’s just me).</p>



<p class="wp-block-paragraph">So, in my second semester, I focused on refining that whole experience and making it better in every way.</p>



<h3 class="wp-block-heading">The Rewrite: Embracing Kotlin Multiplatform</h3>



<p class="wp-block-paragraph">I decided on a new frontend framework: Kotlin Multiplatform (KMP), which allows you to build apps for nearly any platform using a shared codebase. I also added new features, like self-hosted AI solutions to generate content and a posibility to choose your  current Gym.</p>



<p class="wp-block-paragraph">Implementing my app with Kotlin was challenging because it was a completely new programming language to me. After learning the fundamentals and coding on my own for the first week, I brought in some AI coding agents to help speed up the process. At first, I used a local model called <code>qwen3-coder:30b</code>, which I quantized to run on constrained hardware. My setup was a Mac Book Air M1 from the USTP rental service running <a href="https://omlx.ai/" data-type="link" data-id="https://omlx.ai/">OMLX</a> (which was especially built for Macs to take advantage of their unified memory). Running in my IDE was the open-source AI code agent <a href="https://marketplace.visualstudio.com/items?itemName=Continue.continue">Continue</a>, which allowed me to implement MCP servers and feed code context from my project to the model. Later I learned that this extention or IDE whatever you wanna call it, is no longer being developed and was acquired by Cursor.</p>



<p class="wp-block-paragraph">After I sadly had to return the rental Mac (originally i wanted to use it for building the App for IOS as well but just failed to finish the App in time <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f641.png" alt="🙁" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ) , I switched to Claude Code. For me, it was essential to double-check Claude’s work and architectural decisions. Honestly, I was a little bit shocked by some of the choices it made, like trying to store API keys or passwords in plain text just somewhere in the Code! To be fair, I wasn&#8217;t using their frontier model to avoid spending too many tokens (as I argued a lot with it during the development process), but still. However, it was particularly handy for creating the frontend layer with Jetpack Compose, thanks to the vast documentation available for Android&#8217;s UI framework.</p>



<p class="wp-block-paragraph">To avoid hallucinations and provide the model with up-to-date documentation, I added an MCP Server called <a href="https://context7.com/">Context7</a>. It allowed me to filter exactly what documentation the model received and what it had access to &#8230; a really handy tool.</p>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="960" height="2142" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/09/Screenshot_20260916-220509.png" alt="" class="wp-image-16013" style="aspect-ratio:0.4481865284974093;width:289px;height:auto" srcset="https://mobile.fhstp.ac.at/wp-content/uploads/2026/09/Screenshot_20260916-220509.png 960w, https://mobile.fhstp.ac.at/wp-content/uploads/2026/09/Screenshot_20260916-220509-688x1536.png 688w, https://mobile.fhstp.ac.at/wp-content/uploads/2026/09/Screenshot_20260916-220509-918x2048.png 918w" sizes="(max-width: 960px) 100vw, 960px" /></figure>



<h3 class="wp-block-heading">Frontend Challenges: Caching, Offline Sync, and Navigation</h3>



<p class="wp-block-paragraph"><br>The hardest part of my frontend implementation was the caching logic. I wanted the app to always instantly display the relevant information you need in the moment. To achieve this, I decided to cache the first 10 items of every category (e.g., Templates, Trainings, UserData).</p>



<p class="wp-block-paragraph">All of this is stored in a Key-Value database called <a href="https://github.com/xxfast/KStore">KStore</a>, a small library that works great for simple data storage on Android and iOS devices. The absolute hardest part was defining the behavior for when I saved something offline, ensuring that the moment I got back online, it would post to the backend so the two storages would &#8220;sync&#8221; up again. We now have a (mostly) robust network-first cache strategy with offline fallbacks.</p>



<p class="wp-block-paragraph">I also completely revamped the app&#8217;s navigation and UI architecture using Compose Multiplatform: </p>



<ul class="wp-block-list">
<li><strong>Two-Level Navigation:</strong> We have an outer navigation graph for the Auth-Flow (Login/Register) and an inner graph wrapped in a <code>Scaffold</code> for the main tabs. This allows for a persistent BottomBar that stays visible while you switch tabs.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Profile Dialog:</strong> Instead of routing to a new screen, the user profile opens as a clean Dialog Card over the current screen, making it feel much faster.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Security:</strong> Tokens are stored securely using <code>KVault</code> (Keychain for iOS, EncryptedSharedPreferences for Android).</li>
</ul>



<ul class="wp-block-list">
<li><strong>Dependency Injection:</strong> I used Koin to keep the architecture clean (MVVM), allowing the UI to remain logic-free.</li>
</ul>



<h3 class="wp-block-heading">The Backend: Keeping it Clean &amp; Adding a Scraping Service</h3>



<p class="wp-block-paragraph">For my backend (a self-hosted NestJS API), I tried to make major improvements to security and code cleanliness. Most of my backend wasn&#8217;t even touched by AI &#8230; I mean aside from a few MongoDB queries &#8230; they&#8217;re just really weird to write ok? &#8230; so I could assure everything was up to my expectations.</p>



<p class="wp-block-paragraph">For extended features, I added a custom scraping service using a separate Python server. It scrapes all the fitness studios listed on my gym&#8217;s website and writes everything about their equipment directly into my database. It&#8217;s scheduled to run on startup and then one time per Month (don&#8217;t want to get accused of creating to much net traffic for them). I deployed this service in a separate Docker container within the same Docker network to ensure minimal downtime in case the scraper ever crashes.</p>



<h3 class="wp-block-heading">Building a Local AI Agent for Training Templates</h3>



<p class="wp-block-paragraph">Having the exact context of my gym&#8217;s equipment in the database was especially helpful for my next big feature: Local AI Training Template generation.</p>



<p class="wp-block-paragraph">To be honest, I initially wanted to use the MCP server I had programmed earlier for this task. But by the time I got to implementing the AI feature, I realized that Ollama doesn&#8217;t actually support MCP natively because it lacks an MCP client.</p>



<p class="wp-block-paragraph">So, I built an Agentic loop directly in my NestJS backend to act as a middleman between Ollama and my server. I <em>could</em> have gone the route of calling my MCP server through my backend and called it a day. But after a short &#8220;question battle&#8221; with Claude Code, I decided that wasn&#8217;t feasible due to the unnecessary network requests I could save by just implementing the agent loop directly in the backend.</p>



<p class="wp-block-paragraph">My custom MCP Server now exists on its own and isn&#8217;t connected to anything just yet. But building it was a fantastic learning experience regarding how these systems work. It lays the groundwork for future implementations, as I eventually plan to integrate external MCP-capable AI tools like Claude or ChatGPT.</p>



<h3 class="wp-block-heading">Technical Deep Dive: Gymetrics Feature List</h3>



<p class="wp-block-paragraph">For those interested in the exact tech stack and architectural decisions, here is a breakdown of what’s running under the hood:</p>



<p class="wp-block-paragraph"><strong>The Stack</strong></p>



<ul class="wp-block-list">
<li><strong>Frontend:</strong> Kotlin Multiplatform, Compose Multiplatform (Shared UI), targeting Android (first priority) and iOS.</li>



<li><strong>Backend:</strong> Self-hosted NestJS API with Dockerized microservices.</li>



<li><strong>Auth:</strong> Short-lived Bearer tokens + Refresh tokens.</li>
</ul>



<p class="wp-block-paragraph"><strong>Core Implementations</strong></p>



<ul class="wp-block-list">
<li><strong>Ktor Client &amp; Auto-Refresh:</strong> Centralized HTTP client handling JSON serialization, timeouts, and dynamic base URLs. Used the Ktor <code>Auth</code> plugin to automatically attach tokens and trigger a silent refresh route (<code>POST /auth/refresh</code>) on 401 errors.</li>



<li><strong>Offline-First Capabilities:</strong> Built a custom <code>ConnectivityObserver</code> that tracks network state app-wide. The app displays an offline badge in the TopBar and handles offline Auth checks by optimistically trusting the securely stored token.</li>



<li><strong>App-Wide Theming:</strong> Implemented a custom <code>GymetricsTheme</code> wrapper around MaterialTheme, featuring a sleek, app-wide dark mode and adaptive Android icons.</li>



<li><strong>State Management &amp; DI:</strong> Strict MVVM architecture. State is handled via <code>sealed interfaces</code> and exhaustive <code>when</code> statements (Idle/Loading/Success/Error). Dependency Injection is handled entirely by <strong>Koin</strong> (using <code>appModule</code>, <code>single</code>, and <code>viewModelOf</code>).</li>
</ul>



<p class="wp-block-paragraph">It’s been a massive journey from a simple Ionic prototype to a fully-fledged, offline-capable KMP application with agentic AI workflows. It started as a way to replace a spreadsheet, but it taught me modern mobile architecture, backend, and how to effectively wrangle AI coding assistants.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/gymetrics-a-self-hostable-fitness-app/">Gymetrics: A Self-Hostable Fitness App with Kotlin Multiplatform, NestJS, and Dockerization</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Designing Offline-First iOS Apps: Local State, Persistence, and Sync Without Losing User Trust</title>
		<link>https://mobile.fhstp.ac.at/allgemein/designing-offline-first-ios-apps-local-state-persistence-and-sync-without-losing-user-trust/</link>
		
		<dc:creator><![CDATA[Melanie Maier]]></dc:creator>
		<pubDate>Wed, 16 Sep 2026 08:37:26 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Native Development]]></category>
		<category><![CDATA[Studium]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[offline-first]]></category>
		<category><![CDATA[persistence]]></category>
		<category><![CDATA[Sync]]></category>
		<guid isPermaLink="false">https://mobile.fhstp.ac.at/?p=15926</guid>

					<description><![CDATA[<p>Abstract Many mobile apps look simple until they must preserve an unfinished edit, work offline, survive a crash, and reconcile changes from two devices. Then “save and sync” becomes an architecture, and a promise to the user. An offline-first iOS app treats its local store as the immediate source of truth. User actions update it <a class="read-more" href="https://mobile.fhstp.ac.at/allgemein/designing-offline-first-ios-apps-local-state-persistence-and-sync-without-losing-user-trust/">[...]</a></p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/designing-offline-first-ios-apps-local-state-persistence-and-sync-without-losing-user-trust/">Designing Offline-First iOS Apps: Local State, Persistence, and Sync Without Losing User Trust</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Abstract</h2>



<p class="wp-block-paragraph">Many mobile apps look simple until they must preserve an unfinished edit, work offline, survive a crash, and reconcile changes from two devices. Then “save and sync” becomes an architecture, and a promise to the user.</p>



<p class="wp-block-paragraph">An offline-first iOS app treats its local store as the immediate source of truth. User actions update it first; synchronization is separate and retryable. State boundaries must be explicit, writes safe, conflicts explainable, and the interface honest about data stored only locally.</p>



<p class="wp-block-paragraph">This article follows that model from SwiftUI state through persistence, autosave, change tracking, conflicts, and failure UX.</p>



<h2 class="wp-block-heading">1. Why Offline-First Still Matters</h2>



<p class="wp-block-paragraph">In 2026, fast networks are common, reliable connectivity is not. A phone can enter a tunnel, encounter a captive portal, or lose authentication. A request may reach the server even when its response never reaches the client.</p>



<p class="wp-block-paragraph">Offline-first begins with a rule: the user performs the app’s core work against local data. The app records the intent durably and synchronizes later. A task app should not discard a completed task because the server timed out.</p>



<p class="wp-block-paragraph">This is different from caching. A cache is usually disposable and can be reconstructed from an authoritative source. Offline-created user data cannot. If deleting the local copy would destroy information that exists nowhere else, it is not “just a cache.”</p>



<p class="wp-block-paragraph">Reachability is not permission to write. Network status can help decide when to attempt synchronization, but it cannot prove that the next request will succeed. Foundation can wait through <code>URLSessionConfiguration.waitsForConnectivity</code>, but the durable queue, not a green icon, is what makes a write reliable (<a href="https://developer.apple.com/documentation/foundation/urlsessionconfiguration/waitsforconnectivity">Apple: </a><code><a href="https://developer.apple.com/documentation/foundation/urlsessionconfiguration/waitsforconnectivity">waitsForConnectivity</a></code>).</p>



<p class="wp-block-paragraph">The useful mental model is:</p>



<ol start="1" class="wp-block-list">
<li>accept the user’s action</li>



<li>validate it locally</li>



<li>commit it locally</li>



<li>render the committed result</li>



<li>enqueue synchronization</li>



<li>retry safely until the server acknowledges it</li>
</ol>



<p class="wp-block-paragraph">The network path affects step five, not the validity of steps one through four.</p>



<h2 class="wp-block-heading">2. Local State Is Not Temporary State</h2>



<p class="wp-block-paragraph">SwiftUI makes state easy to declare, which can make unlike kinds of state look deceptively similar. A robust app distinguishes at least three layers.</p>



<p class="wp-block-paragraph"><strong>View state</strong>&nbsp;exists only to present or control the current interface: whether a sheet is open, which tab is selected, a search field’s draft text, or the expansion state of a disclosure group.&nbsp;<code>@State</code>&nbsp;is often appropriate because SwiftUI owns that value for the lifetime of the view identity.</p>



<p class="wp-block-paragraph"><strong>Domain state</strong>&nbsp;represents meaningful work: a document title, completed task, or pending deletion. It must not depend on whether a view exists. Observation and&nbsp;<code>@Observable</code>&nbsp;help present domain objects, but observability is not persistence.</p>



<p class="wp-block-paragraph"><strong>Persistent application state</strong>&nbsp;has crossed a durability boundary. It has been written to a file or store that can be reconstructed after relaunch. SwiftData’s&nbsp;<code>ModelContainer</code>&nbsp;and&nbsp;<code>ModelContext</code>, a Core Data persistent container, or a purpose-built SQLite layer live here.</p>



<p class="wp-block-paragraph">The distinction matters during editing. Binding a field directly to a persistent model saves half-finished values. That may suit notes, but not a bank transfer or an object whose fields must remain consistent. In those cases, edit a draft, validate it, and commit the complete change in one transaction.</p>



<p class="wp-block-paragraph">A practical dependency direction is:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
SwiftUI view → use case / repository → local store
                                <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2198.png" alt="↘" class="wp-smiley" style="height: 1em; max-height: 1em;" /> sync outbox
</pre></div>


<p class="wp-block-paragraph">Views request operations such as&nbsp;<code>completeTask</code>; they do not decide how JSON is replaced or CloudKit records are merged. This makes offline behavior testable without UI or a real server.</p>



<h2 class="wp-block-heading">3. Choosing a Persistence Strategy</h2>



<p class="wp-block-paragraph">Choose the smallest persistence mechanism that preserves the data’s structure, query needs, migrations, and ownership.</p>



<p class="wp-block-paragraph"><strong>UserDefaults</strong>&nbsp;is for small preferences such as sort order or a dismissed onboarding flag. It is not a document database or an outbox.</p>



<p class="wp-block-paragraph"><strong>JSON or property-list files</strong>&nbsp;suit small, document-shaped datasets loaded and replaced as a unit. They are transparent and exportable. Once the app needs indexed queries, relationships, concurrent writers, or a large sync log, one JSON file becomes an improvised database.</p>



<p class="wp-block-paragraph"><strong>SwiftData</strong>&nbsp;is a natural high-level choice for many new Apple-platform apps. It integrates persistent models with Swift and SwiftUI queries. Its main context supports autosave in app configurations, manually created contexts do not inherit that behavior. Design schema migrations before a model rename ships (<a href="https://developer.apple.com/videos/play/wwdc2023/10196/">Apple: Dive deeper into SwiftData</a>,&nbsp;<a href="https://developer.apple.com/videos/play/wwdc2025/291/">Apple: schema migration</a>).</p>



<p class="wp-block-paragraph"><strong>Core Data</strong>&nbsp;remains strong for mature apps, complex graphs, and established migration plans.&nbsp;<code>NSPersistentContainer</code>encapsulates the stack, while persistent history can expose transactions made elsewhere. SwiftData is not merely “Core Data with nicer syntax”. Evaluate requirements instead of choosing by age.</p>



<p class="wp-block-paragraph"><strong>SQLite</strong>&nbsp;fits explicit schemas, specialized indexes, predictable SQL, or tight transaction control. An atomic commit makes all transaction changes visible or none. Write-ahead logging can let readers continue during writes, though checkpointing and durability settings still matter (<a href="https://sqlite.org/atomiccommit.html">SQLite: Atomic Commit</a>,&nbsp;<a href="https://sqlite.org/wal.html">SQLite: WAL</a>).</p>



<h2 class="wp-block-heading">4. Autosave and Data Integrity</h2>



<p class="wp-block-paragraph">Autosave is a user-experience policy, not an integrity mechanism. It answers&nbsp;<em>when the app attempts to persist</em>, not&nbsp;<em>whether a multi-part change is valid, atomic, or recoverable</em>.</p>



<p class="wp-block-paragraph">For free-form text, saving after every keystroke wastes work. A short debounce coalesces edits, but creates a window where input exists only in memory. Flush critical drafts when editing ends and when the scene changes phase.</p>



<p class="wp-block-paragraph">For files, write to an auxiliary file and replace the original only after success. Foundation’s atomic option follows this approach (<a href="https://developer.apple.com/documentation/foundation/nsdata/writingoptions/atomic">Apple:&nbsp;</a><code><a href="https://developer.apple.com/documentation/foundation/nsdata/writingoptions/atomic">NSData.WritingOptions.atomic</a></code>). Keep a last known-good version when corruption is costly, a backup matters only if restore is tested.</p>



<p class="wp-block-paragraph">For database persistence, group invariant-related changes in one transaction. Creating an order and decrementing inventory in separate saves exposes an impossible intermediate state after a crash. Transaction boundaries should follow domain meaning, not screen layout.</p>



<p class="wp-block-paragraph">Every model needs a migration story. Use stable identifiers independent of local row IDs, version schemas, and test upgrades from every supported release. Never answer migration failure by silently deleting the user’s store.</p>



<p class="wp-block-paragraph">Treat storage errors as first-class outcomes. Disk space can run out, encoding can fail, and validation can reject a save.&nbsp;<code>try? context.save()</code>&nbsp;may create a quiet interface while hiding that data was never durable.</p>



<h2 class="wp-block-heading">5. Sync Is a Trust Problem</h2>



<p class="wp-block-paragraph">Synchronization is often drawn as two arrows between phone and cloud. Inside are duplicate requests, reordering, partial failure, concurrent edits, deletion, and expired authentication.</p>



<p class="wp-block-paragraph">A trustworthy design separates&nbsp;<strong>local durability</strong>&nbsp;from&nbsp;<strong>remote synchronization</strong>. Each mutation creates the domain change and an outbox entry in one transaction. A worker sends operations, records acknowledgements, applies remote changes, and retries with backoff. After a crash, the outbox still exists.</p>



<p class="wp-block-paragraph">Each operation should be idempotent. Give it a stable operation ID so a server can recognize a retry. If the server receives the request but the client loses the response, retrying must not create a duplicate object or repeat a payment.</p>



<p class="wp-block-paragraph">Timestamps are weak ordering mechanisms because device clocks differ. Prefer server revisions, advancing change tokens, or per-record versions. SwiftData History exposes chronological transactions and explicitly supports remote sync and out-of-process changes (<a href="https://developer.apple.com/videos/play/wwdc2024/10075/">Apple: SwiftData history</a>,&nbsp;<a href="https://developer.apple.com/documentation/swiftdata/fetching-and-filtering-time-based-model-changes">Apple: time-based changes</a>).</p>



<p class="wp-block-paragraph">HTTP offers concurrency semantics: the server returns an&nbsp;<code>ETag</code>, and the client sends&nbsp;<code>If-Match</code>&nbsp;when updating. If the resource changed, the precondition fails instead of overwriting newer data (<a href="https://www.rfc-editor.org/rfc/rfc9110">RFC 9110</a>). CloudKit similarly gives&nbsp;<code>CKRecord</code>&nbsp;a change tag for synchronization (<a href="https://developer.apple.com/documentation/cloudkit/ckrecord">Apple:&nbsp;</a><code><a href="https://developer.apple.com/documentation/cloudkit/ckrecord">CKRecord</a></code>).</p>



<p class="wp-block-paragraph">The app should model sync state explicitly, for example:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
enum SyncState: Equatable {
    case localOnly
    case queued
    case syncing
    case synced(revision: String)
    case conflict
    case blocked(reason: BlockReason)
}
</pre></div>


<p class="wp-block-paragraph">Do not infer sync from the absence of an error. A request that has not started is not synchronized, and expired authentication differs from a timeout.</p>



<h2 class="wp-block-heading">6. Conflict Handling and External Sources</h2>



<p class="wp-block-paragraph">Conflict policy must be defined per entity and sometimes per field. “Last write wins” is simple, but it can silently erase work and becomes especially unsafe when “last” depends on device clocks.</p>



<p class="wp-block-paragraph">Some data merges automatically: independent fields can merge separately and tag sets may use union semantics. Collaborative text needs a purpose-built algorithm such as a CRDT. It should not be improvised from&nbsp;<code>updatedAt</code>.</p>



<p class="wp-block-paragraph">Other conflicts need a human. Preserve both versions, show meaningful differences, and offer “Your edit from 10:42” versus “Edit from iPad at 10:45,” not merely “local” and “remote.”</p>



<p class="wp-block-paragraph">Deletions deserve their own model. If a deleted record vanishes immediately, another offline device may later re-upload it. A tombstone—an identifier, deletion revision, and retention period—allows deletion to propagate. Purge it only after the system’s synchronization window and device assumptions make resurrection acceptably unlikely.</p>



<p class="wp-block-paragraph">External sources also need ownership rules:</p>



<ul class="wp-block-list">
<li><strong>Read-only reference data:</strong> cache locally, display its age, and replace it from the source. Never upload it as if the user authored it</li>



<li><strong>Imported data:</strong> record provenance and the external identifier. Decide whether import creates an owned snapshot or maintains a live link</li>



<li><strong>Locally editable mirrors:</strong> keep the source version alongside pending local changes. Rebase or surface a conflict when the source changes</li>



<li><strong>External deletion:</strong> distinguish “no longer available upstream” from “user deleted locally.” Preserve local notes or derived records when policy allows</li>
</ul>



<p class="wp-block-paragraph">CloudKit subscriptions can signal record creation, modification, and deletion, but they only trigger fetching. They do not replace a durable local store or change token (<a href="https://developer.apple.com/documentation/cloudkit/ckrecordzonesubscription">Apple:&nbsp;</a><code><a href="https://developer.apple.com/documentation/cloudkit/ckrecordzonesubscription">CKRecordZoneSubscription</a></code>).</p>



<h2 class="wp-block-heading">7. UX for Failure</h2>



<p class="wp-block-paragraph">Offline-first UX should communicate consequences, not infrastructure. Users need three answers: Is my work saved here? Is it available elsewhere? Do I need to act?</p>



<p class="wp-block-paragraph">Use calm, specific status language:</p>



<ul class="wp-block-list">
<li><strong>Saved on this iPhone</strong> — the local transaction committed</li>



<li><strong>Waiting to sync</strong> — no action is normally required</li>



<li><strong>Syncing…</strong> — work is in progress</li>



<li><strong>Couldn’t sync. We’ll retry.</strong> — the failure is transient</li>



<li><strong>Sign in to continue syncing</strong> — user action is required</li>



<li><strong>Two versions need your review</strong> — silent merging would risk data loss</li>
</ul>



<p class="wp-block-paragraph">Avoid blocking alerts for routine failures. Prefer inline status, a persistent account banner, or sync-details screen. Reserve modal interruption for unsafe actions or imminent data loss.</p>



<p class="wp-block-paragraph">Optimistic UI is appropriate only after the local commit succeeds. If a user marks a task complete, the interface can update immediately because the durable local state already represents that action. If the save fails, keep the draft visible, explain that it is not yet stored, and offer a retry or export path.</p>



<p class="wp-block-paragraph">Expose “last synchronized” and pending changes when consequential. Never show a cloud checkmark merely because the device has connectivity. Do not encode sync state only in color. Provide text for VoiceOver.</p>



<p class="wp-block-paragraph">Test sequences, not isolated errors: edit offline, terminate, relaunch, edit elsewhere, reconnect, receive a conflict, lose connectivity during resolution, and retry. Also test duplicate responses, full storage, corruption, external deletion, and schema upgrades with a non-empty outbox.</p>



<h2 class="wp-block-heading">8. Conclusion</h2>



<p class="wp-block-paragraph">Good offline-first architecture is mostly invisible. The app opens with useful data, accepts work immediately, survives interruption, and later converges without surprises. Achieving that experience requires more than adding a cache or calling&nbsp;<code>save()</code>&nbsp;from SwiftUI.</p>



<p class="wp-block-paragraph">Separate view state from durable domain state. Choose persistence by data shape and guarantees. Make writes atomic at meaningful boundaries. Track mutations with stable identities and versions. Treat sync as a retryable state machine, not a request callback. Define ownership, deletion, and conflict policies before the edge cases arrive. Above all, tell the truth in the interface about what is saved locally and what has reached another device.</p>



<p class="wp-block-paragraph">Users rarely praise an app for its outbox, migration plan, or tombstones. They simply decide that the app feels dependable. That judgment is the real product of an offline-first design.</p>



<h2 class="wp-block-heading">Sources and Further Reading</h2>



<ul class="wp-block-list">
<li>Apple, <a href="https://developer.apple.com/videos/play/wwdc2023/10196/">Dive deeper into SwiftData (WWDC23)</a>.</li>



<li>Apple, <a href="https://developer.apple.com/videos/play/wwdc2024/10075/">Track model changes with SwiftData history (WWDC24)</a>.</li>



<li>Apple, <a href="https://developer.apple.com/videos/play/wwdc2025/291/">SwiftData: Dive into inheritance and schema migration (WWDC25)</a>.</li>



<li>Apple, <a href="https://developer.apple.com/documentation/swiftdata/fetching-and-filtering-time-based-model-changes">Fetching and filtering time-based model changes</a>.</li>



<li>Apple, <code><a href="https://developer.apple.com/documentation/coredata/nspersistentcontainer">NSPersistentContainer</a></code>.</li>



<li>Apple, <code><a href="https://developer.apple.com/documentation/foundation/nsdata/writingoptions/atomic">NSData.WritingOptions.atomic</a></code>.</li>



<li>Apple, <code><a href="https://developer.apple.com/documentation/foundation/urlsessionconfiguration/waitsforconnectivity">URLSessionConfiguration.waitsForConnectivity</a></code>.</li>



<li>Apple, <code><a href="https://developer.apple.com/documentation/cloudkit/ckrecord">CKRecord</a></code> and <code><a href="https://developer.apple.com/documentation/cloudkit/ckrecordzonesubscription">CKRecordZoneSubscription</a></code>.</li>



<li>SQLite, <a href="https://sqlite.org/atomiccommit.html">Atomic Commit in SQLite</a> and <a href="https://sqlite.org/wal.html">Write-Ahead Logging</a>.</li>



<li>IETF, <a href="https://www.rfc-editor.org/rfc/rfc9110">RFC 9110: HTTP Semantics</a>.</li>
</ul>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/designing-offline-first-ios-apps-local-state-persistence-and-sync-without-losing-user-trust/">Designing Offline-First iOS Apps: Local State, Persistence, and Sync Without Losing User Trust</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Building a Personal Planning App: From Interface Idea to Data-Driven iOS Prototype</title>
		<link>https://mobile.fhstp.ac.at/allgemein/building-a-personal-planning-app-from-interface-idea-to-data-driven-ios-prototype/</link>
		
		<dc:creator><![CDATA[Melanie Maier]]></dc:creator>
		<pubDate>Wed, 16 Sep 2026 07:37:26 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Native Development]]></category>
		<category><![CDATA[Projekte]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[swift]]></category>
		<guid isPermaLink="false">https://mobile.fhstp.ac.at/?p=15909</guid>

					<description><![CDATA[<p>Software projects often begin with a feature list. This one began with a feeling: everyday planning tools are everywhere, and yet personal organization still often feels fragmented. Calendars live in one place, tasks in another, reminders somewhere else, and the mental work of connecting them is left to the user. The goal of this project <a class="read-more" href="https://mobile.fhstp.ac.at/allgemein/building-a-personal-planning-app-from-interface-idea-to-data-driven-ios-prototype/">[...]</a></p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/building-a-personal-planning-app-from-interface-idea-to-data-driven-ios-prototype/">Building a Personal Planning App: From Interface Idea to Data-Driven iOS Prototype</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Software projects often begin with a feature list. This one began with a feeling: everyday planning tools are everywhere, and yet personal organization still often feels fragmented. Calendars live in one place, tasks in another, reminders somewhere else, and the mental work of connecting them is left to the user. The goal of this project was not simply to build another productivity app, but to explore how a planning interface could feel more coherent, calmer, and closer to the way people actually think about their days.</p>



<p class="wp-block-paragraph">The app is still in development, and many of its final ideas are intentionally not public yet. For that reason, this article does not reveal the complete product concept or all planned functionality. Instead, it focuses on the development process so far: how the project evolved from a SwiftUI interface prototype into a more structured iOS application with custom data models, persistent storage, reusable components, and first steps toward external calendar integration.</p>



<p class="wp-block-paragraph">At the beginning, the project was mostly visual and exploratory. The first question was not “Which database should this use?” or “How should synchronization work?” but “What should this feel like on a phone?” Since the app is meant to be used frequently and quickly, the interface had to be approachable without becoming decorative for its own sake. SwiftUI was a natural choice because it allowed fast iteration on layout, navigation, state, and component structure. Early work focused on core screens: a home view, overview pages, detail pages, and small reusable interface elements that could later become the visual language of the app.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1540" height="800" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/09/Wireframes-1540x800.png" alt="" class="wp-image-15918" srcset="https://mobile.fhstp.ac.at/wp-content/uploads/2026/09/Wireframes-1540x800.png 1540w, https://mobile.fhstp.ac.at/wp-content/uploads/2026/09/Wireframes-770x400.png 770w" sizes="(max-width: 1540px) 100vw, 1540px" /></figure>



<p class="wp-block-paragraph">One of the first lessons was that a planning app becomes complex faster than expected. Even if the user-facing idea sounds simple, the underlying structures are not. A calendar item is not just a title and a date. It may have a start and end time, an all-day state, a location, notes, tags, a source, an editability status, and possibly a connection to another system. A task is also more than a checkbox. It can belong to a list, have a due date, include notes, be marked as important, be connected to a reminder, or later need rules for rescheduling. Reminders introduce yet another rhythm, because they are not exactly tasks and not exactly events. They sit somewhere between intention and interruption.</p>



<p class="wp-block-paragraph">Because of that, the project moved relatively early from screen-building into model-building. Separate structures were created for calendars, events, lists, tasks, reminders, external sources, and calendar connections. This was an important shift. Instead of letting the UI define the data informally, the data model started to define what the interface could reliably express. Each object needed an identity, needed to be encodable, and needed to be flexible enough for future features without becoming vague. This part of the process was less visually exciting than building screens, but it made the rest of the application much more stable.</p>



<p class="wp-block-paragraph">The central data layer became an observable store that holds the current calendars, events, lists, tasks, reminders, and external connections. This store is used throughout the SwiftUI views via environment injection, which keeps the individual screens relatively focused. A home screen can ask for visible calendars, upcoming events, or incomplete tasks without owning all the logic itself. Detail pages can receive a selected object and rely on shared update and delete methods. This architecture is still lightweight, but it creates a clear boundary between application state and presentation.</p>



<p class="wp-block-paragraph">A useful turning point was implementing filtering and derived data. Once there were multiple entities in the store, the app needed answers to questions such as: Which events belong to this calendar? Which tasks belong to this list? Which events are upcoming? Which tasks are incomplete and still relevant? These may sound like small helper functions, but they changed how the app could be built. Instead of each screen duplicating filtering code, the store became the place where the application’s understanding of its data lived. That made the UI easier to read and helped reduce inconsistencies between screens.</p>



<p class="wp-block-paragraph">Another major step was persistence. In early prototypes, sample data is enough. It helps with layout and gives the app something to display. But a planning app cannot remain a prototype for long if data disappears every time it restarts. The current version stores a snapshot of the application data as JSON in the app’s documents directory. The snapshot includes the core collections and can be loaded again when the app starts. The saving process also keeps a backup file, so the app has a fallback if the primary file cannot be read.</p>



<p class="wp-block-paragraph">This persistence system is intentionally simple. It is not yet a large database layer, and that is partly deliberate. At this stage, JSON-based persistence makes the app easier to inspect, debug, and evolve. The project is still changing, and a lighter storage approach gives more freedom while the domain model continues to settle. At the same time, the persistence layer is separated from the store, so replacing or extending it later would not require rewriting the entire UI. This is one of the recurring themes of the project: avoid overengineering too early, but create enough structure that future changes do not become painful.</p>



<p class="wp-block-paragraph">Autosave introduced its own design questions. A planning app should not require the user to manually save after every small change. The current approach listens for changes across the main collections and saves the current snapshot after a short debounce. This means the app can react naturally to edits while avoiding excessive file writes. It also means the development process had to account for state changes that are not user edits, such as loading persisted data at launch. Small flags and guard conditions became necessary to prevent the app from immediately saving over freshly loaded state or treating internal setup as user activity.</p>



<p class="wp-block-paragraph">The interface also evolved significantly during this phase. The app now has a clearer structure of pages and components: overview views, detail views, card components, a navigation bar, headers, picker panels, floating action controls, and shared form elements. This componentization was not just about cleaner code. It also helped the app develop a consistent interaction style. Planning tools can easily become cluttered because they contain many object types and many possible actions. Reusable components make it easier to keep the experience predictable.</p>



<p class="wp-block-paragraph">One example is the creation flow. Adding something new should not feel like entering a completely different app. The current implementation uses a shared sheet for creating or editing different item types. It supports modes for events, tasks, and reminders, with separate draft objects and validation. This draft-based approach is important because forms are temporary by nature. A user can open a sheet, make changes, switch context, cancel, or save. Keeping draft state separate from the saved model avoids accidental writes and makes validation clearer.</p>



<p class="wp-block-paragraph">The form system also revealed how much detail is hidden inside “simple” input. Dates, times, optional fields, associations with calendars or lists, tags, icons, and reminder relationships all require careful handling. If these concerns are spread directly across the view code, the form quickly becomes difficult to maintain. By separating drafts, form rows, picker state, and saving logic, the app gained a structure that can grow without turning every new field into a cascade of fragile changes.</p>



<p class="wp-block-paragraph">Design-wise, the project has been moving toward a custom visual identity rather than relying entirely on default iOS components. Custom fonts, color assets, icons, rounded shapes, and card components give the app a more personal tone. This is not only aesthetic. For a planning tool, visual hierarchy matters. The user should be able to distinguish calendars, lists, upcoming items, actions, and details quickly. Colors and cards are not decoration here; they carry information and help reduce cognitive load.</p>



<p class="wp-block-paragraph">At the same time, building a custom interface in SwiftUI requires restraint. It is tempting to make every component unique, especially in a project that has a strong visual direction. But the more custom the interface becomes, the more responsibility the developer takes on for spacing, accessibility, responsiveness, and consistency. This has been one of the practical challenges of the project: finding the balance between a distinctive interface and a maintainable one. The current component structure is an attempt to make that balance explicit.</p>



<p class="wp-block-paragraph">The most technically demanding part so far has been external calendar integration. Local planning data is one thing; synchronizing with external systems is another. The app now contains service layers for Apple Calendar and Google Calendar, as well as a synchronization service that imports events into the app’s own model. This required thinking about authorization, external identifiers, read-only versus editable sources, date ranges, mapping between API objects and local models, and what should happen when external events change or disappear.</p>



<p class="wp-block-paragraph">Apple Calendar integration uses EventKit and must deal with platform-specific permission states. On newer iOS versions, access levels are more precise, so the app has to request and verify the correct kind of calendar access. The service can list available calendars and load events within a selected time range. These events are then mapped into the app’s internal event model, preserving information such as title, location, notes, URL, start and end date, all-day status, external identity, and sync metadata.</p>



<p class="wp-block-paragraph">Google Calendar integration is different because it goes through Google Sign-In and web API requests. This required handling OAuth scopes, restoring previous sign-in sessions, refreshing access tokens, making authenticated requests, decoding calendar lists and event responses, and converting Google’s date formats into local date values. All-day events need special handling because calendar APIs often represent them differently from timed events. These are the kinds of details that are easy to underestimate before implementation begins.</p>



<p class="wp-block-paragraph">The synchronization layer sits above the individual provider services. It loops through configured external calendar connections, loads events for each connection, imports them, tracks which external IDs were seen, updates sync timestamps, and reports failures. The store then upserts imported events instead of blindly appending them. This prevents duplicates and creates a place to handle conflicts. If a local event has pending changes and an external version arrives, the app can mark the situation as a conflict rather than silently overwriting data. Even though the full synchronization story is not finished, the current structure already reflects a key principle: user data should be treated carefully.</p>



<p class="wp-block-paragraph">This was one of the biggest conceptual lessons of the project. Synchronization is not just a technical feature. It is a trust feature. If users put their plans into an app, they need confidence that the app will not lose, duplicate, or unexpectedly modify important information. That means the code has to represent uncertainty. It needs states such as local-only, synced, pending local changes, conflict, or deleted externally. These states are not glamorous, but they are what make the difference between a demo and a tool people might rely on.</p>



<p class="wp-block-paragraph">Another lesson was that the app’s “domain” became clearer through implementation. At first, calendars, lists, tasks, and reminders may seem like familiar categories. But once they are modeled together, their relationships become more interesting. Some items belong to collections. Some can be associated with time. Some can be completed, others can only pass. Some are imported from outside and should not be edited locally. Some may later need richer rules. The development process has therefore been partly technical and partly analytical: understanding what each object means in the system.</p>



<p class="wp-block-paragraph">The current prototype also includes several navigation paths: a home view with upcoming information, calendar overviews and detail views, list overviews and detail views, event and task detail screens, and setup flows. This breadth created a new kind of challenge. Once the app had more than one or two screens, consistency became harder. A change in the model might affect a card, a detail page, a form, and a preview all at once. This is where reusable view components started to pay off. They made the app feel less like a set of separate screens and more like one coherent product.</p>



<p class="wp-block-paragraph">The project is still in an active development stage. There are visible signs of ongoing refactoring: components have been moved into clearer folders, global elements have been separated from list-specific and calendar-specific components, and some older file paths are being replaced by a more organized structure. This is a normal and healthy part of development. Early prototypes often grow organically. At some point, the structure has to catch up with the idea. The important part is not to make the architecture perfect immediately, but to keep improving it when the project’s shape becomes clearer.</p>



<p class="wp-block-paragraph">Looking back, the development so far can be described in phases. The first phase was interface exploration: building enough screens to understand the experience. The second phase was domain modeling: defining the objects the app actually needs. The third phase was state and persistence: making the app remember and update data reliably. The fourth phase was integration: connecting the local world of the app with external calendar systems. The current phase is consolidation: refining components, improving flows, reducing duplication, and preparing the project for more advanced behavior.</p>



<p class="wp-block-paragraph">For a student or early-stage software project, this progression is valuable because it shows how practical software development rarely follows a perfectly linear plan. The app did not begin with a complete architecture diagram and then simply fill in the pieces. Instead, each implementation step revealed the next architectural need. A form revealed the need for drafts. External calendars revealed the need for source metadata and sync states. Autosave revealed the need to distinguish loading from editing. Reusable cards revealed the need for stronger model relationships. The architecture emerged through contact with the problem.</p>



<p class="wp-block-paragraph">There is also a broader lesson about building products that are not ready to be fully disclosed. It can be difficult to talk about a project publicly when the most exciting parts are still private. But not every development story has to reveal the final product. It is possible to discuss the engineering process, the technical decisions, the mistakes avoided, and the problems encountered without giving away the complete concept. In some ways, this is more useful for an informatics audience. The real value is not only in what the app will eventually do, but in how the system is being shaped to make that possible.</p>



<p class="wp-block-paragraph">The next steps will likely focus on stability, refinement, and careful expansion. The persistence system will need to remain reliable as the data model grows. Synchronization will need more testing, especially around conflicts and edge cases. The interface will need continued polishing across different device sizes and user scenarios. Some flows that currently exist as early implementations will need to become more robust. And as the app moves closer to a publishable version, privacy, permissions, error handling, and user trust will become even more important.</p>



<p class="wp-block-paragraph">What makes this project interesting is that it sits at the intersection of design, data modeling, and everyday behavior. A planning app is not just a technical container for events and tasks. It is a tool that asks to be invited into a user’s routine. That creates a high bar: the app has to be useful without being noisy, structured without being rigid, and personal without becoming confusing. Reaching that balance takes iteration.</p>



<p class="wp-block-paragraph">So far, the development has already moved beyond a simple prototype. The app has a working SwiftUI foundation, a growing design system, structured models, persistent local data, reusable creation flows, and initial external calendar synchronization. More importantly, it has a clearer sense of direction. The final product is not ready to be revealed yet, but the development process has already shown what kind of challenge this is: not merely building screens, but building a coherent planning environment one careful layer at a time.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/building-a-personal-planning-app-from-interface-idea-to-data-driven-ios-prototype/">Building a Personal Planning App: From Interface Idea to Data-Driven iOS Prototype</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Projekt WS25 &#124; MixMatch (Fortsetzung)</title>
		<link>https://mobile.fhstp.ac.at/allgemein/projekt-ws25-mixmatch-fortsetzung/</link>
		
		<dc:creator><![CDATA[Andreas Kaiser]]></dc:creator>
		<pubDate>Mon, 02 Mar 2026 17:16:26 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Native Development]]></category>
		<category><![CDATA[Projekte]]></category>
		<category><![CDATA[Studium]]></category>
		<guid isPermaLink="false">https://mobile.fhstp.ac.at/?p=15368</guid>

					<description><![CDATA[<p>Um mein Projekt “MixMatch” näher in die Richtung production-ready Zustand zu bringen wurde das 3. Semester von mir genutzt, nahtlos an den zuvor vorhandenen Stand der Entwicklung anzuschließen. Im Semester habe ich mich im wesentlichen darauf fokussiert, Community Features, wie Sterne-Bewertungen und Kommentier- bzw. Antwortfunktion, in meine Nativescript-App zu integrieren und deutlich mehr Zeit in <a class="read-more" href="https://mobile.fhstp.ac.at/allgemein/projekt-ws25-mixmatch-fortsetzung/">[...]</a></p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/projekt-ws25-mixmatch-fortsetzung/">Projekt WS25 | MixMatch (Fortsetzung)</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Um mein Projekt “MixMatch” näher in die Richtung production-ready Zustand zu bringen wurde das 3. Semester von mir genutzt, nahtlos an den zuvor vorhandenen Stand der Entwicklung anzuschließen.</p>



<p class="wp-block-paragraph">Im Semester habe ich mich im wesentlichen darauf fokussiert, Community Features, wie Sterne-Bewertungen und Kommentier- bzw. Antwortfunktion, in meine Nativescript-App zu integrieren und deutlich mehr Zeit in Bugfixing zu investieren. Nebenbei wurde stellenweise das Design weiter optimiert, sowie das Fehlerhandling überarbeitet. Zur Abrundung wurde im Projektabschnitt eine exportierbare Einkaufliste für Zutaten, die der/die User:in braucht, um die Rezepte nachzumachen, integriert.</p>



<h2 class="wp-block-heading"><strong>Neuerungen seit dem letzten Semester</strong></h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/03/Screenshot_20260301_232048.png" alt="" class="wp-image-15782"/></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/03/Screenshot_20260302_003203.png" alt="" class="wp-image-15787"/></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/03/Screenshot_20260301_232236.png" alt="" class="wp-image-15784"/></figure>
</div>
</div>



<ul class="wp-block-list">
<li><strong>Filteroptionen in der Rezeptliste</strong></li>
</ul>



<p class="wp-block-paragraph">Der größte strukturelle Umbau fand in der Rezeptliste statt. Bisher wurden die Rezepte einfach als rohe Liste angezeigt, ohne Möglichkeit zur Filterung. Jetzt gibt es eine eigene Suchleiste mit Autocomplete-Vorschlägen der gefilterten Drinks, eine alphabetische Sortierung mit Drink-Nummer pro Karte, sowie einen Toggle-Filter für Signature Drinks.</p>



<ul class="wp-block-list">
<li><strong>Kommentar-Sektion bei den Recipes</strong></li>
</ul>



<p class="wp-block-paragraph">Das Herzstück des Projektes war die Erweiterung, dass jedes Rezept nun eine vollwertige Community-Section hat. User:innen können Kommentare mit Sternebewertung hinterlassen, auf Kommentare anderer antworten und eigene Kommentare wieder löschen. Die Bewertungen fließen in eine Durchschnittsbewertung ein, die prominent im Header der Rezeptkarte angezeigt wird. Die gesamte Kommunikation läuft in Echtzeit über WebSockets.</p>



<ul class="wp-block-list">
<li><strong>Benötigte Zutaten einer Einkaufsliste hinzufügen</strong></li>
</ul>



<p class="wp-block-paragraph">Bisher wurden alle nicht angegebenen Zutaten bei der Hauptsuche automatisch gesammelt und global in meinem Code zwischengespeichert. Das wurde grundlegend über ein Einkaufslisten-Feature überarbeitet: Pro Rezept kann der/die Nutzer:in nun selbst entscheiden, ob er/sie die fehlenden Zutaten zur Einkaufsliste hinzufügen möchte. Fehlende Zutaten werden in der Zutatenliste farblich in Orange hervorgehoben. Ein einzelner Button pro Rezept überträgt dann gezielt nur die fehlenden Zutaten in die Einkaufsliste. Die Einkaufsliste selbst unterstützt den Export als PDF über den nativen Android PrintManager sowie im CSV-Format.</p>



<p class="wp-block-paragraph">Neben diesen Änderungen konnten viele Bugs behoben werden. Speziell einige Bugs, die die Funktionalität rund um die Erstellung von “Signature Drinks” (anlegen/löschen) als auch den Umgang mit gekennzeichneten Favoriten-Rezepten, eingeschränkt haben, existieren nicht mehr.</p>



<h2 class="wp-block-heading"><strong>Technischer Part</strong></h2>



<p class="wp-block-paragraph">Der Großteil an Technologien ist gleich geblieben. Im Frontend setze ich weiterhin auf NativeScript Angular bzw. Tailwind für das Design und im Backend SpringBoot. Das Backend habe ich dieses Semester erstmalig über den Online-Service Railway gehostet. Dazugekommen sind vor allem die WebSocket-Bibliothek <code>@stomp/stompjs</code> im Frontend sowie die entsprechende Spring WebSocket-Abhängigkeit im Backend.</p>



<p class="wp-block-paragraph">Für die Kommentar-Funktion war von Anfang an klar, dass klassische HTTP-Requests nicht ausreichen , sonst wäre ein ständiges manuelles Aktualisieren notwendig gewesen, um neue Kommentare zu sehen. Die Wahl fiel auf WebSockets mit STOMP, einem einfachen Messaging-Protokoll das auf WebSocket aufsetzt und ein Publish-Subscribe-Modell ermöglicht. Im Spring Boot Backend wird dabei ein Message-Broker konfiguriert der eingehende Nachrichten an alle Subscriber des jeweiligen Rezept-Topics weiterleitet. Jedes Rezept hat seinen eigenen Kanal, zum Beispiel <code>/topic/recipe/42/comments</code>. Nur Nutzer:innen die gerade dieses Rezept offen haben, empfangen die Kommentar, die zu diesem Rezept abgegeben wurden. Der Controller nimmt neue Kommentare entgegen, speichert sie und schickt sie sofort an alle aktiven Subscriber:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: java; title: ; notranslate">
@MessageMapping(&quot;/comments/create&quot;)
public void createComment(CreateCommentRequestDto request, Principal principal) {
    User author = userRepository.findByUsername(principal.getName())...;
    CommentResponseDto saved = commentService.createComment(request, author);
    messagingTemplate.convertAndSend(
        &quot;/topic/recipe/&quot; + request.getRecipeId() + &quot;/comments&quot;, 
        saved
    );
}
</pre></div>


<p class="wp-block-paragraph">Im Frontend übernimmt ein dedizierter <code>CommentWebSocketService</code> die gesamte Verbindungslogik. Er verwendet die <code>@stomp/stompjs</code> Bibliothek, schickt den JWT-Token beim Verbindungsaufbau mit und subscribt auf den zum Rezept passenden Topic-Kanal:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: java; title: ; notranslate">
this.stompClient = new Client({
    webSocketFactory: () =&gt; new WebSocket(`wss://...railway.app/ws`),
    connectHeaders: { Authorization: `Bearer ${this.authToken}` },
    reconnectDelay: 5000,
});
 
this.stompClient.onConnect = () =&gt; {
    this.stompClient.subscribe(
        `/topic/recipe/${this.recipeId}/comments`,
        (message) =&gt; {
            const comment = JSON.parse(message.body);
            onCommentReceived(comment);
        }
    );
};
</pre></div>


<h2 class="wp-block-heading"><strong>Herausforderungen &amp; Learning(s) daraus</strong></h2>



<p class="wp-block-paragraph">Die größte Herausforderung des Semesters waren eindeutig die WebSockets. Theoretisch klingt es simpel (Verbindung öffnen und Nachrichten empfangen), jedoch in der Praxis gab es einige Hürden. Zuerst die Authentifizierung: anders als bei normalen HTTP-Requests musste der JWT-Token über die STOMP <code>connectHeaders</code> mitgeschickt und im Backend extra validiert werden. Dann das Problem mit inaktiven Verbindungen, denn ohne der sogenannten “Heartbeat-Konfiguration” auf beiden Seiten kamen Nachrichten irgendwann einfach nicht mehr an (Fehlermeldungen wurden nicht geworfen). Zu guter Letzt das Aufräumen der Verbindungen, denn ohne explizites <code>disconnect()</code> blieben im Hintergrund immer mehr aktive Verbindungen offen.</p>



<p class="wp-block-paragraph">Was das Aufräumen so schwierig gemacht hat, war der Pager, mit dem man zwischen den Rezepten swipen kann. NativeScript’s Pager-Komponente erstellt alle Rezept-Items gleichzeitig beim Laden, bei 14 Rezepten also sofort 14 WebSocket-Verbindungen und 14 HTTP-Requests, obwohl der/die Nutzer:in nur ein einziges Rezept sieht. Noch dazu wird <code>ngOnDestroy</code> beim Wischen zwischen den Items nicht zuverlässig aufgerufen, weil der Pager seine Views intern recycelt statt sie wirklich zu zerstören. Dadurch hat die Cleanup-Logik nie gefeuert.</p>



<p class="wp-block-paragraph">Die Lösung war letztlich ein Umdenken: statt auf <code>ngOnDestroy</code> zu vertrauen, steuert jetzt ein Flag, ob die Kommentar-Sektion überhaupt gerendert wird. Sobald der Nutzer wegwischt, verschwindet die Komponente via <code>*ngIf</code> aus dem DOM, damit das Cleanup zuverlässig durchläuft. Das wesentliche Learning dabei, war ein gut ausgebautes Debugging zu haben.</p>



<h2 class="wp-block-heading"><strong>Fazit und zukünftige TODOs</strong></h2>



<p class="wp-block-paragraph">In diesem Projektabschnitt konnte ich mich erstmalig herausfordernd damit befassen, wie WebSockets technisch umgesetzt werden. Das war gleichzeitig eines der interessantesten und aufwändigsten Themen des Semesters. Die vielen Debugging-Sessions haben mich aber auch am meisten weitergebracht. Rückblickend bin ich sehr zufrieden damit, was ich mir vorgenommen hatte auch tatsächlich vollständig umgesetzt zu haben. Gerade das Bugfixing war diesmal besonders aufwändig, hat aber mit großem Erfolg geendet und manche Features meiner App brauchbarer gemacht.</p>



<p class="wp-block-paragraph">An diesen Dingen sollte nun weitergearbeitet werden: Performance-seitig gibt es noch Luft nach oben, sowohl beim initialen Laden als auch bei den Screen-Wechseln merkt man stellenweise noch Verzögerungen die es zu optimieren gilt. Auch das Design ist noch nicht wirklich durchgängig einheitlich, was ich in einem nächsten Schritt konsequenter angehen möchte. Dazu kommt, dass ich bisher kaum echtes User-Feedback eingeholt habe, das soll sich ändern. Auf technischer Seite fehlen noch automatisierte Tests, die zur noch besseren Stabilität der App beitragen sollen. Schlussendlich wäre das große Ziel die App veröffentlichen zu können.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph"><strong>Beitragsbild</strong>: dieses wurde mittels Einsatz von KI erzeugt</p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/projekt-ws25-mixmatch-fortsetzung/">Projekt WS25 | MixMatch (Fortsetzung)</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Projekt &#124; Timelapse Android App</title>
		<link>https://mobile.fhstp.ac.at/development/projekt-timelapse-android-app/</link>
		
		<dc:creator><![CDATA[David Grünberger]]></dc:creator>
		<pubDate>Thu, 26 Feb 2026 10:19:46 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Native Development]]></category>
		<category><![CDATA[Projekte]]></category>
		<category><![CDATA[Studium]]></category>
		<category><![CDATA[Usability]]></category>
		<category><![CDATA[User Experience]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[Jetpack Compose]]></category>
		<category><![CDATA[Material Design]]></category>
		<category><![CDATA[mobile]]></category>
		<guid isPermaLink="false">https://mobile.fhstp.ac.at/?p=15691</guid>

					<description><![CDATA[<p>Im Rahmen des Semesterprojekts im 3. Semester der Masterklasse Mobile habe ich die Android-App &#8220;Timelapse&#8221; entwickelt. Die Idee dahinter: Aus vielen einzelnen Momentaufnahmen wird automatisch ein visueller Zeitstrahl – als Video, das langfristige Veränderungen sichtbar macht. Egal ob Bartwachstum, Fitness-Progress oder das Wachstum einer Zimmerpflanze: Timelapse hilft dabei, diese Reise festzuhalten. Ziel des Projekts war ein alltagstauglicher MVP, der den kompletten Ablauf abdeckt: vom Erstellen <a class="read-more" href="https://mobile.fhstp.ac.at/development/projekt-timelapse-android-app/">[...]</a></p>
<p>The post <a href="https://mobile.fhstp.ac.at/development/projekt-timelapse-android-app/">Projekt | Timelapse Android App</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Im Rahmen des Semesterprojekts im 3. Semester der Masterklasse Mobile habe ich die Android-App &#8220;Timelapse&#8221; entwickelt. Die Idee dahinter: Aus vielen einzelnen Momentaufnahmen wird automatisch ein visueller Zeitstrahl – als Video, das langfristige Veränderungen sichtbar macht. Egal ob Bartwachstum, Fitness-Progress oder das Wachstum einer Zimmerpflanze: Timelapse hilft dabei, diese Reise festzuhalten. Ziel des Projekts war ein alltagstauglicher MVP, der den kompletten Ablauf abdeckt: vom Erstellen eines Projekts über tägliche Erinnerungen und eine übersichtliche Tage-Ansicht bis hin zur fertigen Video-Montage, die sich teilen oder archivieren lässt.</p>



<h2 class="wp-block-heading">Worum es in der&nbsp;App geht</h2>



<p class="wp-block-paragraph">Im Kern funktioniert Timelapse wie ein&nbsp;visuelles&nbsp;Tagebuch:</p>



<ul class="wp-block-list">
<li>Nutzer:innen legen Projekte an (z. B. „Daily Selfie“ oder „Zimmerpflanze“).</li>



<li>Die App erinnert regelmäßig daran, neue Fotos aufzunehmen.</li>



<li>Alle Bilder werden zeitlich geordnet, mit Datum und optionalen Kommentaren gespeichert.</li>



<li>Auf einer kompakten Übersicht sieht man, wann man etwas aufgenommen hat, welche Tage besonders wichtig waren und wie sich das Projekt über Wochen und Monate entwickelt.</li>



<li>Aus dieser Sammlung erzeugt Timelapse auf Knopfdruck ein Zeitraffervideo, das die Geschichte des Projekts in wenigen Sekunden erzählt.</li>
</ul>



<h2 class="wp-block-heading">Zentrale&nbsp;Funktionen&nbsp;in&nbsp;Kürze</h2>



<ul class="wp-block-list">
<li><strong>Projektorganisation:</strong> Mehrere parallele Timelapse-Projekte mit Titel, Beschreibung und individuellen Einstellungen.</li>



<li><strong>Visuelle Timeline:</strong> Eine kombinierte Wochen- und Listenansicht zeigt, welche Tage schon dokumentiert sind und welche Lücken es noch gibt.</li>



<li><strong>Erinnerungen:</strong> Konfigurierbare Benachrichtigungen, damit die tägliche Aufnahme zur Routine wird und die Timeline nicht abreißt.</li>



<li>F<strong>avoriten &amp; Notizen:</strong> Einzelne Tage können hervorgehoben und mit kurzen Kommentaren versehen werden – etwa um Meilensteine oder besondere Momente zu markieren.</li>



<li><strong>Automatische Video-Montage:</strong> Die App erstellt aus den Bildern ein Video, berücksichtigt Ausrichtung, Datum, optionales Wasserzeichen und Kommentare und bietet Konfigurationsmöglichkeiten für Qualität oder Textstil.</li>
</ul>



<figure class="wp-block-gallery has-nested-images columns-4 wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-full"><img decoding="async" data-id="15763" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/Screenshot_20260226_122653_Timelapse.jpg" alt="" class="wp-image-15763"/></figure>



<figure class="wp-block-image size-full"><img decoding="async" data-id="15765" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/Screenshot_20260226_122559_Timelapse.jpg" alt="" class="wp-image-15765"/></figure>



<figure class="wp-block-image size-full"><img decoding="async" data-id="15764" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/Screenshot_20260226_122619_Timelapse.jpg" alt="" class="wp-image-15764"/></figure>



<figure class="wp-block-image size-full"><img decoding="async" data-id="15766" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/Screenshot_20260226_122700_Timelapse.jpg" alt="" class="wp-image-15766"/></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Abb. 1: App Screenshots</strong></figcaption></figure>



<h2 class="wp-block-heading">Technologie-Stack</h2>



<p class="wp-block-paragraph">Die&nbsp;App ist&nbsp;als&nbsp;native&nbsp;Android-Anwendung mit Jetpack Compose&nbsp;umgesetzt und&nbsp;nutzt&nbsp;moderne&nbsp;Android-APIs sowie eine&nbsp;klare&nbsp;Schichtung&nbsp;zwischen&nbsp;UI, Logik&nbsp;und Medienverarbeitung.</p>



<ul class="wp-block-list">
<li><strong>UI: Jetpack Compose + Material 3</strong>
<ul class="wp-block-list">
<li>Alle Screens (Projektliste, Details, Dialoge, Konfiguration) sind vollständig in Compose realisiert.</li>



<li>Material-3-Komponenten wie LargeFlexibleTopAppBar, Listen, Cards und Dialoge sorgen für ein konsistentes Look &amp; Feel.</li>



<li>Ein zentrales TimelapseTheme verwaltet App-Theme (Light/Dark), Seed-Farbe und den Palette-Stil.</li>
</ul>
</li>



<li><strong>Material 3 Expressive &amp; Material You</strong>
<ul class="wp-block-list">
<li>Die App nutzt Material 3 Expressive, um eine lebendige, charakterstarke Farb- und Typografie-Sprache umzusetzen – passend zu einer App, die persönliche Veränderungen dokumentiert.</li>



<li>Über einen integrierten Color Picker wählen Nutzer:innen eine eigene Akzentfarbe.</li>



<li>Aus dieser Seed-Farbe wird mit Material You ein komplettes Farbschema generiert, das sich konsistent über die gesamte App zieht.</li>



<li>Dadurch wirkt Timelapse auf jedem Gerät persönlich, bleibt aber immer klar strukturiert und gut lesbar – unabhängig davon, ob Light- oder Dark-Mode aktiv ist.</li>
</ul>
</li>



<li><strong>Video-Montage &amp; Bildverarbeitung</strong>
<ul class="wp-block-list">
<li>Die Montage-Logik läuft in einem eigenen Modul:</li>



<li>Bilder werden anhand ihrer EXIF-Daten korrekt ausgerichtet.</li>



<li>Ein Canvas setzt die Frames zusammen, ergänzt Texte (Datum, Kommentare, Wasserzeichen) und kümmert sich um Hintergrundfarbe und Zensurflächen.</li>



<li>Ein Muxer erzeugt daraus ein MP4-Video, gesteuert über Kotlin Flows, die den Fortschritt an die UI melden.</li>



<li>So entsteht aus einer losen Sammlung von Bildern ein stimmiges Timelapse-Video, ohne dass externe Tools nötig sind.</li>
</ul>
</li>
</ul>



<h2 class="wp-block-heading">Material&nbsp;3 Expressive&nbsp;&amp; Material You im&nbsp;Einsatz</h2>



<p class="wp-block-paragraph">Ein großer&nbsp;Fokus&nbsp;des&nbsp;Projekts lag&nbsp;auf einer&nbsp;modernen, adaptiven&nbsp;UI, die sich&nbsp;nach&nbsp;den&nbsp;Nutzer:innen richtet:</p>



<ul class="wp-block-list">
<li><strong>Dynamische Farbwelten</strong>
<ul class="wp-block-list">
<li>Durch den Seed-Color-Ansatz wirkt die App nicht wie ein statisch durchgestyltes Produkt, sondern wie ein Rahmen, der sich der jeweiligen Geschichte anpasst.</li>



<li>Projekte mit emotionalem Kontext (z. B. Schwangerschaft, Beziehungen, erste Wohnung) profitieren von den kräftigen, expressiven Paletten, die Material 3 bereitstellt.</li>
</ul>
</li>



<li><strong>Einheitliche, weiche Formsprache</strong>
<ul class="wp-block-list">
<li>Großzügige Rundungen (z. B. extra große Card-Shapes in Dialogen), expressive Typografie und bewusst eingesetzte Icons unterstützen den Tagebuch-Charakter der App.</li>



<li>Die App verzichtet auf überladene UI-Elemente und setzt auf Ruhe, Klarheit und Fokus – wichtig, wenn man sich über Monate immer wieder mit derselben Oberfläche beschäftigt.</li>
</ul>
</li>



<li><strong>Konsistentes Theming</strong>
<ul class="wp-block-list">
<li>Alle Bausteine – von Erfassungsdialogs über Erinnerungs-Settings bis hin zu Fortschrittsanzeigen bei Scans und Montage – hängen am gleichen Theme.</li>



<li>Änderungen an Theme, Palette oder Seed-Farbe greifen sofort durch, ohne dass einzelne Screens speziell angepasst werden müssen.</li>
</ul>
</li>
</ul>



<h2 class="wp-block-heading">Herausforderungen&nbsp;&amp; Learnings</h2>



<ul class="wp-block-list">
<li><strong>Balance zwischen Einfachheit und Kontrolle</strong>
<ul class="wp-block-list">
<li>Nutzer:innen sollen einerseits „einfach nur“ ein Timelapse-Video erstellen können, andererseits genug Einstellmöglichkeiten für Qualität, Text, Stabilisierung und Privatsphäre haben.</li>



<li>Ein wichtiger Lernpunkt war, die Komplexität hinter klaren Dialogen und wenigen, gut erklärten Optionen zu verstecken.</li>
</ul>
</li>



<li><strong>Stabile, angenehme Timelapse-Videos</strong>
<ul class="wp-block-list">
<li>Kleine Fehler bei Skalierung oder Matrix-Transformation führen schnell zu Sprüngen oder abgeschnittenen Bereichen – hier war viel Feintuning nötig.</li>
</ul>
</li>



<li><strong>Durchgängiges Theming mit Material 3 / Material You</strong>
<ul class="wp-block-list">
<li>Seed-Farben, dynamische Paletten und unterschiedliche Palettenstile (z. B. Expressive) lassen sich mächtig kombinieren – aber nur, wenn das Theming-Konzept von Anfang an klar definiert ist.</li>



<li>Die wichtigste Erkenntnis: Ein zentrales Theme-Objekt vereinfacht langfristig alle Entscheidungen rund um Farben, Typografie und Component-Styling.</li>
</ul>
</li>
</ul>



<h2 class="wp-block-heading">Fazit</h2>



<p class="wp-block-paragraph">Timelapse war für mich ein Projekt, in dem sich User Experience, modernes Android-Design und Medienverarbeitung sehr gut ergänzt haben. Die App zeigt, wie sich mit Jetpack Compose, Material 3 Expressive und Material You eine Oberfläche bauen lässt, die technisch anspruchsvolle Abläufe (zB. Videomontage) hinter einem einfachen, persönlichen Nutzungserlebnis versteckt. Besonders spannend war zu sehen, wie stark sich die Wahrnehmung der App ändert, wenn man eine eigene Farbwelt definieren kann und diese sich konsequent durch alle Screens zieht. In zukünftigen Schritten möchte ich die App um Themen wie Musik, Export-Presets und umfangreichere Sharing-Optionen erweitern – immer mit dem Ziel, den Kern beizubehalten: Veränderung sichtbar machen, ohne dass sich der Aufwand danach anfühlt.</p>
<p>The post <a href="https://mobile.fhstp.ac.at/development/projekt-timelapse-android-app/">Projekt | Timelapse Android App</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Projekt &#124; bHere &#8211; not on your phone</title>
		<link>https://mobile.fhstp.ac.at/studium/blog-bhere-not-on-your-phone/</link>
		
		<dc:creator><![CDATA[David Grünberger]]></dc:creator>
		<pubDate>Mon, 23 Feb 2026 23:13:34 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Native Development]]></category>
		<category><![CDATA[Projekte]]></category>
		<category><![CDATA[Studium]]></category>
		<category><![CDATA[Workshop]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[BLE]]></category>
		<category><![CDATA[Bluetooth Low Energy]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[Semesterprojekt]]></category>
		<guid isPermaLink="false">https://mobile.fhstp.ac.at/?p=15451</guid>

					<description><![CDATA[<p>Im 3. Semester des Master-Studiengangs Interactive Technologies (Masterklasse Mobile) stand das große Semestergruppenprojekt an. Als ganze Masterklasse (insgesamt 8 Personen) entwickelten wir über das ganze Semester hinweg bHere, eine soziale App, die Gruppen durch spielerische Mechaniken und Konsequenzen motiviert, beim gemeinsamen Ausgehen das Handy wegzulegen. Aufgabenstellung Die einzige Vorgabe war, dass das Projekt eine technische <a class="read-more" href="https://mobile.fhstp.ac.at/studium/blog-bhere-not-on-your-phone/">[...]</a></p>
<p>The post <a href="https://mobile.fhstp.ac.at/studium/blog-bhere-not-on-your-phone/">Projekt | bHere &#8211; not on your phone</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Im 3. Semester des Master-Studiengangs Interactive Technologies (Masterklasse Mobile) stand das große Semestergruppenprojekt an. Als ganze Masterklasse (insgesamt 8 Personen) entwickelten wir über das ganze Semester hinweg bHere, eine soziale App, die Gruppen durch spielerische Mechaniken und Konsequenzen motiviert, beim gemeinsamen Ausgehen das Handy wegzulegen.</p>



<h2 class="wp-block-heading">Aufgabenstellung</h2>



<p class="wp-block-paragraph">Die einzige Vorgabe war, dass das Projekt eine technische Herausforderung beinhalten muss. Nachdem wir drei Ideen gesammelt hatten, entschieden wir uns dazu, eine App zu entwickeln, die Menschen motiviert, die Zeit wieder mehr mit ihren Liebsten zu genießen, ohne auf das Handy zu schauen. Die technische Herausforderung lag hierbei vor allem in der Verbindung zwischen den Geräten durch Bluetooth, aber auch in der Erstellung einer nativen, production-ready App.</p>



<h3 class="wp-block-heading">MVP</h3>



<p class="wp-block-paragraph">Anfangs wurde von uns selbst ein MVP mit den grundlegendsten Funktionen definiert. Dazu zählte, sich als User einloggen bzw. registrieren und Sessions starten zu können, in der das Handy nicht verwendet werden sollte. Weiters ist es wichtig, die Verbindung zwischen den Geräten (egal ob NFC, BLE etc.) herzustellen und aufrechtzuerhalten. Die App sollte erkennen, wenn jemand das Handy verwendet, und das den anderen Geräten mitteilen. Wir hatten die Idee, dass die User*innen mehrere Modi spielen können, wobei nur der “Bill Splitter” zum MVP zählt. Dabei sollte man einen Rechnungsbetrag eingeben können und die App zeigt an, wer wie viel schlussendlich zahlen muss, basierend darauf, wie oft jeweils das Handy verwendet wurde. Die App sollte grundsätzlich nativ auf beiden Plattformen (iOS und Android) entwickelt werden und am Ende production-ready sein. Zudem hatten wir uns vorgenommen, eine CI/CD-Pipeline fürs Backend aufzusetzen und eine Website mit rechtlichen Informationen und Links zur App zu entwickeln. Die Website spielt vor allem für die Releases in den Stores eine Rolle.</p>



<h3 class="wp-block-heading">Nice To Have</h3>



<p class="wp-block-paragraph">Als “Nice To Have” hatten wir uns primär die Erweiterung um 2 zusätzliche Modi vorgenommen: Beim Modus “Pot Lock” legen die Spieler*innen anfänglich fest, welcher Betrag pro Person in einen virtuellen Topf gegeben werden soll, und ab welcher Handynutzungsgrenze (“Threshold”) man seinen Einsatz verliert. Am Schluss teilen sich die Personen, die den Threshold nicht überschritten haben, den Topf. Beim Modus “Group Jar” legt die Gruppe einen Preis pro Handynutzung fest und am Ende der Session sagt die App, wer wie viel insgesamt in die Gruppenkasse zahlen muss.</p>



<p class="wp-block-paragraph">Weitere Nice-To-Haves waren die Möglichkeit, dass Spieler*innen auch mittendrin einer Session beitreten können (falls jemand sich z. B. verspätet), dass sie sich nach einem Disconnect wieder reconnecten können und dass sie auch früher die Session verlassen können (falls jemand z. B. schon früher nach Hause gehen muss). Abgesehen davon wollten wir, dass die App auch komplett offline funktioniert, damit man sie auch in einer Keller-Bar mit schlechtem Empfang nutzen kann. Zusätzlich wollten wir die App zumindest in einem Store veröffentlichen.</p>



<p class="wp-block-paragraph">Weiters hatten wir viele coole Ideen, die App durch Features wie Mini Games, einer KI-Funktion zum Rechnung-Scannen (für den Modus “Bill Splitter”), einer History, einem Streak, einer Realtime Abstimmung (für Notfallsituationen, in denen die Handynutzung toleriert wird), einem SSO-Login und einer direkten Zahlung in der App bzw. einer Importfunktion in eine App wie Splitwise oder Tricount zu erweitern, allerdings blieb dafür leider keine Zeit mehr &#8211; zumindest in diesem Semester. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<h2 class="wp-block-heading">Verwendete Technologien</h2>



<h3 class="wp-block-heading">App</h3>



<p class="wp-block-paragraph">Die Apps wurden jeweils nativ, in Swift für iOS und Kotlin für Android geschrieben, um Erfahrung im Bereich der nativen App-Entwicklung zu sammeln.</p>



<h3 class="wp-block-heading">Website</h3>



<p class="wp-block-paragraph">Die Website wurde mit Next.js, einem React-basierten Framework entwickelt. Für das Styling wurde Tailwind CSS verwendet und für eine typsichere Entwicklung haben wir uns für TypeScript entschieden. Wir verwenden eine klassisch komponentenbasierte Architektur mit wiederverwendbaren Komponenten. Für die Containerisierung verwenden wir Docker.</p>



<h2 class="wp-block-heading">Projektablauf</h2>



<p class="wp-block-paragraph">Die Arbeit am Semesterprojekt wurde in Sprints aufgeteilt (die an Masterklassen-Einheiten orientiert waren, wobei wir intern auch hin und wieder Meetings dazwischen hatten, wenn der Abstand zwischen den Masterklassen Einheiten zu lange war).</p>



<p class="wp-block-paragraph">Als Projektmanagement-Tool und zum Verwalten des Backlogs wurde die Software &#8220;Linear&#8221; verwendet: vor jedem Sprint definierten wir, wer an welchen Tickets arbeitet und teilten diese im Team auf.</p>



<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/bhere6.jpg" alt="" class="wp-image-15471"/><figcaption class="wp-element-caption"><strong>Abb. 1: Beispielsprint in unserem Linear-Setup</strong></figcaption></figure>



<h2 class="wp-block-heading">Das Endprodukt</h2>



<p class="wp-block-paragraph">Wir haben die MVP Funktionalitäten und Nice-To-Haves wie beschrieben umgesetzt, mit den folgenden kleinen Anpassungen: Unsere App funktioniert komplett offline mit Bluetooth LE und die Session ID wird über das Scannen eines QR-Codes unter den Spieler*innen ausgetauscht. Das Backend hatten wir zwar anfänglich aufgesetzt, es wird momentan allerdings nicht benötigt. Zusätzlich zu den 3 Modi gibt es nun auch einen vierten Modus, das “Basic Leaderboard”, mit welchem sich User*innen ihre eigenen Regeln festlegen können. Weiters implementierten wir einen Solomodus, sodass die App nicht nur in der Gruppe, sondern auch alleine (z. B. beim Lernen) verwendet werden kann. Außerdem setzten wir eine coole CI/CD Pipeline für unsere Website auf und veröffentlichten unsere App in nicht nur einem, sondern in beiden Stores (Apple App Store, Google Play Store).</p>



<p class="has-medium-font-size wp-block-paragraph"><a href="https://apps.apple.com/de/app/bhere-not-on-your-phone/id6757390918" target="_blank" rel="noreferrer noopener">Link zum iOS App Store-Eintrag</a></p>



<p class="has-medium-font-size wp-block-paragraph"><a href="https://play.google.com/store/apps/details?id=app.bhere" target="_blank" rel="noreferrer noopener">Link zum Android App Store-Eintrag</a></p>



<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/bhere1.jpg" alt="" class="wp-image-15457"/><figcaption class="wp-element-caption"><strong>Abb. 2: Produkt-Teaser aus dem App Store</strong></figcaption></figure>



<p class="wp-block-paragraph">Beim ersten Öffnen der App wird man durch ein Onboarding geleitet, in welchem die App kurz vorgestellt wird. Hierbei muss man auch einen Namen angeben, welcher innerhalb der App für einen selbst verwendet werden soll. Schließlich landet man am Homescreen (Abb. 3, links). Über das Profil-Icon rechts oben hat man die Möglichkeit, den festgelegten Namen zu ändern. Über den Button “Start New Session” startet man eine neue Gruppensession und kommt zum New Session Screen (Abb. 3, Mitte) &#8211; diesen Button muss allerdings nur eine Person in der Gruppe klicken. Alle anderen klicken auf “Join Session”, welcher die Kamera öffnet, um den generierten QR-Code zu scannen, welcher einen dann auch zum New Session Screen weiterleitet. Der Button “Go Solo” führt zum Solomodus, welcher weiter unten genauer beschrieben wird.</p>



<p class="wp-block-paragraph">Am New Session Screen werden die aktuellen Teilnehmer*innen und der ausgewählte Modus angezeigt. Der “Host” der Session (die Person, die auf “Start New Session” geklickt hat) hat zudem die Möglichkeit, den Modus auszuwählen (Abb. 3, rechts) und notwendige Einstellungen vorzunehmen (z. B. den Preis pro Nutzung für den Group Jar Modus zu setzen).</p>



<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/bhere2.jpg" alt="" class="wp-image-15459"/><figcaption class="wp-element-caption"><strong>Abb. 3: iOS App Screenshots</strong></figcaption></figure>



<p class="wp-block-paragraph">Wie angekündigt, gibt es auch einen Solomodus. Mit dem Klick auf “Go Solo” am Home Screen kommt man zum New Focus Session Screen (Abb. 4, links), welcher wie ein Group Jar Modus funktioniert &#8211; man muss also den Preis pro Nutzung festlegen. Während der Session (Focus Session Screen, Abb. 4, rechts) sieht man die gesamte Nutzungsanzahl und wie viel Geld man sich in das eigene Sparschwein zahlen müssen wird.</p>



<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/bhere3.jpg" alt="" class="wp-image-15460"/><figcaption class="wp-element-caption"><strong>Abb. 4: iOS App Screenshots</strong></figcaption></figure>



<p class="wp-block-paragraph">Ein weiteres spannendes Feature ist das Starten des Solomodus durch das Ablegen des Handys auf eine bestimmte Stelle. Wir stellen uns den Ablauf für User*innen folgendermaßen vor:</p>



<ol class="wp-block-list">
<li>User*in setzt sich an den Schreibtisch.</li>



<li>User*in legt das Handy auf eine Ablagefläche.</li>



<li>bHere App öffnet sich und Solomodus wird automatisch gestartet.</li>
</ol>



<figure class="wp-block-video"><video controls src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/NFC-Solomodus.mp4"></video><figcaption class="wp-element-caption"><strong>Video. 1: Starten des Solomodus per NFC-Tag</strong></figcaption></figure>



<p class="wp-block-paragraph">Technisch funktioniert es mit einem NFC-Tag, der einen Deeplink enthält. Für die Projektevernissage 2026 haben wir 100 Tags (NTAG215) bestellt, beschrieben und hergeschenkt. Das Feature haben wir aktuell nur für Android implementiert.</p>



<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/bhere4.jpg" alt="" class="wp-image-15467"/><figcaption class="wp-element-caption"><strong>Abb. 5: Android App Screenshots (Dark Mode Support)</strong></figcaption></figure>



<p class="wp-block-paragraph">Die App ist auf beiden Plattformen sowohl in Light Mode, als auch in Dark Mode, verfügbar. </p>



<h2 class="wp-block-heading">Feedback und Verbesserungsmöglichkeiten</h2>



<p class="wp-block-paragraph">Durch die Teilnahme an der Projektvernissage konnten wir wertvolles User-Feedback für potentielle zukünftige Erweiterungen und Verbesserungen der App sammeln: der wohl größte Pain Point der aktuellen Lösung ist die Latenz des implementierten BLE-Mesh. Vor allem bei Sessions mit einer hohen Teilnehmeranzahl kann es aktuell bis zu mehreren Minuten dauern, bis alle Daten zwischen allen Geräten ausgetauscht wurden. Sollte das Projekt noch weiterentwickelt werden, wäre das wohl eines der ersten Tickets, das umgesetzt werden müsste.</p>



<figure class="wp-block-image size-full"><img decoding="async" src="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/bhere5.jpg" alt="" class="wp-image-15469"/><figcaption class="wp-element-caption"><strong>Abb. 6: Einer unserer beiden Stände auf der Projektvernissage</strong></figcaption></figure>



<h2 class="wp-block-heading">Fazit aller Teilnehmer*innen</h2>



<p class="wp-block-paragraph">Im folgenden Abschnitt möchten wir noch unser Fazit und unsere Erfahrungen teilen, die wir im Zuge dieses Semestergruppenprojekts sammeln konnten.</p>



<h3 class="wp-block-heading">David</h3>



<p class="wp-block-paragraph">Die Arbeit an bHere war für mich eine intensive und lehrreiche Erfahrung. Technisch gesehen war die Implementierung der Bluetooth-Low-Energy-Kommunikation sicherlich die größte Hürde. Es hat einige Iterationen und Research gebraucht, weshalb ich umso stolzer bin, dass wir schlussendlich ein funktionierendes BLE-Mesh auf die Beine stellen konnten.</p>



<p class="wp-block-paragraph">Eine wichtige Lektion war für mich die Herausforderung der parallelen Entwicklung auf zwei Plattformen. Es zeigte sich schnell: Wenn Details nicht vorab klar definiert sind, kann es passieren, dass sie auf iOS und Android unterschiedlich umgesetzt werden. Das führte dazu, dass die letzten Wochen und Tage vor der Abgabe besonders intensiv waren, da wir viele dieser kleinen Unstimmigkeiten noch angleichen mussten. Auch den Prozess der Veröffentlichung hatte ich unterschätzt – insbesondere die Vorbereitung des Store-Eintrags und die zwingende 14-tägige Testphase im Google Play Store benötigen mehr Vorlaufzeit, als man zu Beginn vermuten würde.</p>



<h3 class="wp-block-heading">Andreas</h3>



<p class="wp-block-paragraph">Anders als bei der einwöchigen WildWeek, die wir im Laufe des 2. Semesters hatten, wurden wir in diesem Semester in einem technisch herausfordernden Semesterprojekt auf die Probe gestellt. Dies forderte ein hohes Maß an Eigenverantwortung und gute Teamkommunikation. Insbesondere, weil wir die App bhere auf zwei Plattformen (Android und iOS) gleichzeitig aufgebaut haben. Die Tatsache, dass wir uns nicht wie in der WildWeek auf direkten Wege schnell austauschen konnten, machte das Projekt umso lehrreicher, weil es auch nebenbei die heutige Home Office Arbeitsweise gut widerspiegelt hat.<br><br>Persönlich habe ich mich speziell im Bereich der Android App Entwicklung einbringen können. Dort war ich mit einigen Kollegen an der Gestaltung der UI unserer Screens dran. Neben dem Entwicklungs-Part habe ich auch an der Gestaltung unseres App-Logos beigetragen und fürs Marketing die Bierdeckeln designed, welche wir dann als Merch an die Interessent:innen verteilt haben. Ich fand die Tasks sehr abwechslungsreich und konnte mich erstmals tiefgründig mit der Programmiersprache Kotlin auseinandersetzen. Erste wirkliche Programmiererfahrung von Apps hatte ich damals noch mit Java.<br><br>Rückblickend betrachtet bin ich froh gewesen, mit unserem Team eine solch innovative App auf die Beine gestellt zu haben. Die Umsetzung hat gezeigt, dass wir mit vielen guten Ideen und einigem Kreativitätsvermögen, auch technisch herausfordernde Anforderungen meistern können. Nichtsdestotrotz ist zu erwähnen, dass der Weg dorthin nicht immer einfach war. Durch die Parallelentwicklung (Android/iOS) mussten wir ständig so up-to-date sein, dass auf beiden Systemen ein ziemlich einheitliches Design und verhalten aufgebaut werden konnte. Außerdem waren zahlreiche Testläufe nötig, bis wir eine stabile, plattformübergreifende Lösung gefunden hatten, um zwischen allen Endgeräten ein BLE-Mesh aufzubauen, über das die Kommunikation schließlich zuverlässig stattfinden konnte.</p>



<h3 class="wp-block-heading">Felix</h3>



<p class="wp-block-paragraph">Dieses Gruppen-Semesterprojekt war für mich eine sehr spannende Herausforderung, weil wir in einem großen Team eine komplette App für iOS und Android entwickelt haben und diese dann auch veröffentlicht haben. Im Vergleich zu der Wild Programming Week war es finde ich viel anstrengender, weil wir einfach über einen langen Zeitraum uns immer koordinieren mussten. Aber im Großen und Ganzen hat alles gut funktioniert.</p>



<p class="wp-block-paragraph">Zum Start haben wir zuerst ein Backend entwickelt für User Anmeldung, was dann aber nicht verwendet wurde, weil wir die App offline verwenden wollten, das war anfangs frustrierend, weil dabei Zeit draufgegangen ist, aber natürlich verständlich. Dann habe ich auch ziemlich am Anfang eine Website entwickelt, die, wie ich finde, sehr gut geworden ist. Ansonsten habe ich im Android Team und beim Merch für die Projekte Vernissage geholfen. Das fand ich am spannendsten, da ich Kotlin besser kennenlernen konnte.</p>



<p class="wp-block-paragraph">Das gesamte Projekt hat mir sehr gut gefallen, vor allem weil es ein sehr reales und arbeitsnahes Projekt war. Wir waren ein gutes Team und haben gute Meetings abgehalten. Es hat alles gut funktioniert, wenn auch nicht ohne Schwierigkeiten.</p>



<h3 class="wp-block-heading">Caroline</h3>



<p class="wp-block-paragraph">Im Vergleich zur Extreme Programming Week im 2. Semester hatten wir diesmal mehr Zeit fürs Einarbeiten und Ausprobieren, wobei die Entwicklung einer nativen App auf zwei unterschiedlichen Plattformen dafür neue Herausforderungen mit sich brachte. Ich habe hauptsächlich gemeinsam mit Katharina an der iOS-Version gearbeitet, was im Pair-Programming sehr gut funktioniert hat. Besonders hilfreich war dabei, dass sie bereits mehr Erfahrung mit Swift hatte, wodurch ich mich trotz anfänglicher Unsicherheiten schnell einarbeiten konnte.</p>



<p class="wp-block-paragraph">Eine der größten Herausforderungen war die Umsetzung der Bluetooth-Low-Energy-Kommunikation. Da es auf iOS im Wesentlichen nur Core Bluetooth gibt, mussten wir viele Teile selbst erarbeiten. Zunächst mussten wir die Grundlagen von BLE verstehen und verschiedene Ansätze ausprobieren, bis wir zu einer funktionierenden Lösung kamen, die über mehrere Geräte hinweg funktioniert. Besonders hilfreich war Davids erste Umsetzung eines Meshs auf Android, auch wenn die Übertragung der Logik auf iOS aufgrund plattformspezifischer Unterschiede nicht einfach war.</p>



<p class="wp-block-paragraph">Zusätzlich haben wir die iOS-Version der App im App Store veröffentlicht, was für mich eine neue und spannende Erfahrung war. Dabei habe ich einen Einblick in den Release-Prozess sowie die dafür notwendigen Schritte und Anforderungen von Apple bekommen.</p>



<p class="wp-block-paragraph">Insgesamt hat mir das Projekt nochmal gezeigt, wie wichtig klare Absprachen im Team sowie ein möglichst fertiges und einheitliches Design zu Beginn eines Projekts sind. Fehlende oder unklare Definitionen führen später nämlich zu Mehraufwand und Inkonsistenzen zwischen den Plattformen. Rückblickend war das Projekt jedoch sehr lehrreich und hat mir sowohl technisch als auch im Bereich Teamarbeit wertvolle Erfahrungen gebracht.</p>



<h3 class="wp-block-heading">Sebastian</h3>



<p class="wp-block-paragraph">Auch wenn wir im zweiten Semester die Wild Programming Week hatten, in welcher wir eine App innerhalb einer Woche entwickeln mussten, war dieses Semesterprojekt doch noch einmal ein ganz neues Erlebnis. Eine App über ein ganzes Semester zu entwickeln hat das Ganze leichter wie auch schwerer gestaltet.&nbsp;</p>



<p class="wp-block-paragraph">Durch den Zeitrahmen hatten wir um einiges mehr Zeit zu entwickeln, nicht wie in der Wild Week. Aber auch durch diesen größeren Zeitrahmen wurde es schwerer, da es zum einen natürlich den Projektumfang erhöht hat. Gleichzeitig war es auch um einiges schwerer, den nötigen Arbeitsaufwand einzuschätzen, wodurch man zum Beispiel zu Beginn die Arbeitspakete zu klein für einen Sprint geschätzt hat.&nbsp;</p>



<p class="wp-block-paragraph">Ich war vor allem mit UI Umsetzungen auf Android, zusammen mit der Implementierung der Nutzungserkennung zuständig. Bei der UI Umsetzung der die größte Herausforderung die Abstimmung zwischen Android und iOS. Zwar sind beide Plattformen von der gleichen Basis ausgegangen, auf beiden gab es aber kleiner Änderungen, welche dafür gesorgt haben, dass das Design nicht komplett einstimmig waren und hat extra Arbeit gegen Ende benötigt zum Anpassen.</p>



<p class="wp-block-paragraph">Im Ganzen war es aber wieder ein gutes Erlebnis, mit einem tollen Team. Das ganze kam mit wichtigen Erfahrungen, wie Programmiersprachen Erlernung, App Store Einrichtungen und auch das erneute zeigen, wie wichtig gute Kommunikation und Abstimmung ist, um extra Arbeit zu vermeiden. Ich wäre jederzeit bereit, noch ein Projekt mit diesem Team zu machen.</p>



<h3 class="wp-block-heading">Jan</h3>



<p class="wp-block-paragraph">Es war sehr nett, mit allen Studienkollegen der Masterklasse ein Projekt umzusetzen. Spannend war, wie sich jeder mit seinen Stärken eingebracht hat. Gerne hätte ich noch mehr an diesem Projekt gearbeitet und dafür auf andere Lehrveranstaltungen verzichtet.</p>



<p class="wp-block-paragraph">Das Projekt hat sich im Laufe der Zeit in eine spannende Richtung entwickelt. Features, die wir anfangs eingeplant hatten, haben sich bei genauerer Betrachtung als nicht wichtig oder sogar als störend erwiesen und wurden daher gestrichen. Ich bin beispielsweise sehr froh über die Entscheidung, dass die App zu 100% offline funktioniert und es kein Backend gibt. Das ermöglicht den flexiblen Einsatz der App, zum Beispiel in Kellerlokalen ohne Mobilfunk, und hat den Vorteil, dass User ohne lästige Account-Erstellung direkt loslegen können. Für uns als Team bedeutet der Wegfall eines Backends, dass wir die Applikation kostengünstiger und mit weniger Aufwand betreiben können.</p>



<p class="wp-block-paragraph">Wir können stolz darauf sein, dass wir eine neuartige und wirklich nützliche Applikation geschaffen haben, die ausreichend stabil läuft. Natürlich sehen wir noch einige offene Themen, die wir gerne angehen würden. Schade, dass wir vorerst kaum Zeit mehr für bHere haben werden, da wir uns nun auf unsere Masterarbeiten konzentrieren müssen.</p>



<h3 class="wp-block-heading">Katharina</h3>



<p class="wp-block-paragraph">Das Projekt &#8220;bHere&#8221; war für mich eine besonders wertvolle Erfahrung. In meiner Rolle als Projektleiterin übernahm ich die Moderation der Sprint-Meetings, repräsentierte die Gruppe gegenüber unseren Stakeholdern (unseren Masterklassenleitern) und behielt den Gesamtüberblick über Fortschritt und offene Punkte.</p>



<p class="wp-block-paragraph">Zusätzlich war ich gemeinsam mit Caro für die iOS-Entwicklung verantwortlich. Wir haben intensiv im Pair Programming gearbeitet und unsere Zusammenarbeit war dabei sehr produktiv. Besonders spannend war die Integration der BLE-Funktionalität – dank Davids Vorarbeit konnten wir diese auch erfolgreich in die iOS-App einbinden. Die BLE-Kommunikation auch plattformübergreifend zwischen iOS und Android zum Laufen zu bringen, stellte allerdings eine technische Herausforderung dar, da sich insbesondere das Debugging der Verbindung als nicht so trivial erwies.</p>



<p class="wp-block-paragraph">Zeitlich war das Semester ebenfalls herausfordernder als gedacht. Im Vergleich zur &#8220;Wild Week&#8221; erforderte dieses Semestergruppenprojekt ein noch höheres Maß an Eigenorganisation und Abstimmung. Gerade gegen Ende kamen viele Aufgaben parallel zusammen, wodurch Priorisierung und Struktur besonders wichtig wurden. Für zukünftige Projekte nehme ich außerdem mit, wie wichtig ein frühzeitig klar definiertes und abgestimmtes Design ist, um plattformübergreifend noch effizienter arbeiten zu können.</p>



<p class="wp-block-paragraph">Ein besonderes Highlight war für mich die erstmalige App-Einreichung im App Store. Der umfangreiche Einreichungsprozess hat uns auf der Zielgeraden noch einmal intensiv gefordert, ich konnte daraus aber auch sehr viel lernen.</p>



<p class="wp-block-paragraph">Insgesamt habe ich sehr viel Arbeit und Engagement in dieses Projekt investiert – sowohl in die technische Umsetzung der iOS-App, als auch in die Organisation und Qualitätssicherung des ganzen Projekts. Umso stolzer bin ich auf das entstandene Produkt und auf das, was wir als Team erreicht haben.&nbsp;</p>



<h3 class="wp-block-heading has-medium-font-size">Matthias</h3>



<p class="wp-block-paragraph">Das Projekt hat mir einen spannenden Einblick gegeben, wie komplex die Entwicklung einer mobilen App werden kann, wenn sie plattformübergreifend funktionieren und gleichzeitig komplett offline nutzbar sein soll. Besonders spannend fand ich es, gemeinsam im Team einen Lösungsansatz für unser Bluetooth-Mesh-System zu suchen. Vor allem, weil es für unseren Anwendungsfall keine bestehende Lösung gab. Ich habe dabei auch versucht, ein eigenes Konzept zu entwickeln. Dieses war zwar noch nicht vollständig ausgereift und hätte mehr Zeit benötigt, hat mir aber geholfen, die technischen Herausforderungen besser zu verstehen und viel von den Lösungsansätzen der anderen im Team zu lernen.</p>



<p class="wp-block-paragraph">Intensiver beschäftigt habe ich mich mit der Umsetzung der Nutzungserkennung unter iOS. Ziel war es, das Verhalten der App während der Nutzung besser nachvollziehen und entsprechende Daten erfassen zu können. Dabei musste darauf geachtet werden, dass möglichst ähnliche Informationen wie auf Android gesammelt werden, damit die Nutzung plattformübergreifend vergleichbar bleibt. Dadurch konnte ich mich stärker mit dem Verhalten von Apps unter iOS auseinandersetzen und besser verstehen, wie Apps im Hintergrund arbeiten und wie solche Informationen sinnvoll für die Weiterentwicklung eines Produkts genutzt werden können.</p>



<p class="wp-block-paragraph">Außerdem hatte ich die Möglichkeit, den Prozess rund um die Veröffentlichung der App im Apple App Store aus nächster Nähe mitzuerleben. Es war spannend zu sehen, welche Schritte und Anforderungen notwendig sind, bis eine App tatsächlich eingereicht und veröffentlicht werden kann.</p>



<p class="wp-block-paragraph">Mit am meisten Spaß haben mir die Diskussionen rund um die User Experience der App gemacht. In den Meetings habe ich gemerkt, dass mich dieser Bereich besonders interessiert und ich mich dort auch am besten einbringen kann. Die Kombination aus technischen Überlegungen und der Frage, wie sich eine App für Nutzer:innen möglichst intuitiv bedienen lässt, fand ich besonders spannend.</p>



<p class="wp-block-paragraph">Während des Projekts bin ich mit vielen neuen Themen in Berührung gekommen und konnte dabei viel lernen, sowohl technisch als auch in der Zusammenarbeit im Team. Gleichzeitig war die Arbeit durch die vielen gemeinsamen Lösungsansätze sehr motivierend, wodurch das Lernen fast nebenbei passiert ist.</p>
<p>The post <a href="https://mobile.fhstp.ac.at/studium/blog-bhere-not-on-your-phone/">Projekt | bHere &#8211; not on your phone</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></content:encoded>
					
		
		<enclosure url="https://mobile.fhstp.ac.at/wp-content/uploads/2026/02/NFC-Solomodus.mp4" length="3462174" type="video/mp4" />

			</item>
		<item>
		<title>Projekt SS25 &#124; MixMatch (Fortsetzung)</title>
		<link>https://mobile.fhstp.ac.at/allgemein/projekt-ss25-mixmatch-fortsetzung/</link>
		
		<dc:creator><![CDATA[Andreas Kaiser]]></dc:creator>
		<pubDate>Thu, 16 Oct 2025 08:54:14 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Native Development]]></category>
		<category><![CDATA[Projekte]]></category>
		<category><![CDATA[Studium]]></category>
		<guid isPermaLink="false">https://mobile.fhstp.ac.at/?p=14351</guid>

					<description><![CDATA[<p>Nach der ersten Umsetzungsphase habe ich entschlossen mit dem Projekt: Mix &#38; Match nun in die zweite Runde zu gehen. Aufbauend auf dem bisherigen Konzept und dem erhaltenen Feedback der Lektoren wurde die App gezielt weiterentwickelt und technisch auf ein neues Niveau gehoben. Der Fokus lag dabei auf der nativen Umsetzung, der Optimierung bestehender Funktionen <a class="read-more" href="https://mobile.fhstp.ac.at/allgemein/projekt-ss25-mixmatch-fortsetzung/">[...]</a></p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/projekt-ss25-mixmatch-fortsetzung/">Projekt SS25 | MixMatch (Fortsetzung)</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Nach der ersten Umsetzungsphase habe ich entschlossen mit dem Projekt: Mix &amp; Match nun in die zweite Runde zu gehen. Aufbauend auf dem bisherigen Konzept und dem erhaltenen Feedback der Lektoren wurde die App gezielt weiterentwickelt und technisch auf ein neues Niveau gehoben. Der Fokus lag dabei auf der nativen Umsetzung, der Optimierung bestehender Funktionen sowie der vollständigen Integration teils nur angedachter Features aus dem ersten Prototypen. Ergänzt wurde die Anwendung um ein neues, zentrales Feature: die sogenannten &#8220;Signature Drinks&#8221; – individuell erstellbare Rezepturen, die das Angebot an bestehenden Anleitungen zur Getränkemischung erweitert. Bevor ich näher auf die Fortschritte eingehe, möchte ich in aller Kürze das für die native Entwicklung genutzte Framework beschreiben &#8211; und zwar:</p>



<h2 class="wp-block-heading">Nativescript &#8211; eine kurze Vorstellung</h2>



<figure class="wp-block-image size-large"><img decoding="async" src="data:image/jpeg;base64,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" alt=""/></figure>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">Mit <strong>NativeScript</strong>, das 2015 auf den Markt kam, wurde ein Framework präsentiert, das plattformübergreifende mobile Apps ermöglicht und zugleich direkten Zugriff auf die nativen Funktionen von Android und iOS bietet. Seit dessen Einführung wird es kontinuierlich weiterentwickelt und hat sich zu einer stabilen Lösung für die App-Entwicklung etabliert. NativeScript erlaubt es, Apps mit bekannten Web-Technologien wie JavaScript, TypeScript, Angular oder Vue.js zu erstellen, wodurch der Einstieg für Web-Entwickler besonders einfach ist. Gleichzeitig werden die UI-Komponenten direkt nativ gerendert, was eine nahezu maximale Leistung gewährleistet. Ein Großteil des Codes kann zwischen Android und iOS weiterverwendet werden, wodurch Entwicklungszeit und -kosten reduziert werden. Damit kombiniert NativeScript die Vorteile plattformübergreifender Entwicklung mit direktem API-Zugriff und nativer Performance.</p>



<p class="wp-block-paragraph">Mit diesem Wissen im Gepäck gelang es mir meine Erstentwicklung als reine WebApp in Angular ohne grundlegenden Neuanfang im nativen Bereich durchführen. Das hat mir die Möglichkeit eröffnet viele Teile der ursprünglichen Angular-Programmierung in eine App zu übertragen, auch wenn dies mit einer gewissen Komplexität einherging.</p>



<h2 class="wp-block-heading">Von einer Web-App zu einer nativen App</h2>



<p class="wp-block-paragraph">Der Wechsel von der ursprünglichen Angular Web-App zu einer nativen App mit NativeScript stellte einen entscheidenden Entwicklungsschritt dar. Während die erste Version ausschließlich im Browser funktionierte und auf klassische Web-Komponenten ausgelegt war, verlagerte sich der Fokus nun auf die mobile Nutzung, native Performance und eine tiefere Integration in die jeweilige Plattform. Diese Veränderung erforderte nicht nur technisches Know-how, sondern auch ein grundlegendes Umdenken im App-Design. Die Benutzeroberfläche musste für mobile Endgeräte neu konzipiert werden, denn alleine responsive Layouts reichten nicht mehr aus. Stattdessen waren eine intuitive Navigation, Gestensteuerung und schnelle Ladezeiten gefragt. Technisch konnten viele Angular-Module und Services zwar grundsätzlich übernommen werden, jedoch waren umfangreiche Anpassungen notwendig, insbesondere beim Routing, der Datenbindung und dem Zugriff auf systemnahe Funktionen wie dem Dateispeicher.</p>



<h2 class="wp-block-heading">Optimierter Suchalgorithmus</h2>



<p class="wp-block-paragraph">Der ursprüngliche Suchalgorithmus stellte einen klaren Engpass dar, da jede Filteranfrage direkt an die externe API gesendet wurde. Das führte zu langen Ladezeiten und unnötiger Netzwerklast. </p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.34%">
<figure data-wp-context="{&quot;imageId&quot;:&quot;6ad66f2aa5ad9&quot;}" data-wp-interactive="core/image" data-wp-key="6ad66f2aa5ad9" class="wp-block-image size-full wp-lightbox-container"><img decoding="async" width="1180" height="1536" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://mobile.fhstp.ac.at/wp-content/uploads/2025/10/image-1.jpg" alt="" class="wp-image-14823"/><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">Alter (langsamer) Suchalgo. direkt über die API</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure data-wp-context="{&quot;imageId&quot;:&quot;6ad66f2aa5f86&quot;}" data-wp-interactive="core/image" data-wp-key="6ad66f2aa5f86" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1324" height="1531" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://mobile.fhstp.ac.at/wp-content/uploads/2025/10/api_data_loader_into_db.png" alt="" class="wp-image-14832"/><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">Methode, um die Drink-Daten der API in eine lokale DB zu übertragen</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure data-wp-context="{&quot;imageId&quot;:&quot;6ad66f2aa63b5&quot;}" data-wp-interactive="core/image" data-wp-key="6ad66f2aa63b5" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="1398" height="2010" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://mobile.fhstp.ac.at/wp-content/uploads/2025/10/filter_method_recipes_over_db-2.jpg" alt="" class="wp-image-14834" srcset="https://mobile.fhstp.ac.at/wp-content/uploads/2025/10/filter_method_recipes_over_db-2.jpg 1398w, https://mobile.fhstp.ac.at/wp-content/uploads/2025/10/filter_method_recipes_over_db-2-1068x1536.jpg 1068w" sizes="auto, (max-width: 1398px) 100vw, 1398px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">Logik des neuen Suchalgo. (Daten werden direkt aus lok. DB geholt)</figcaption></figure>
</div>
</div>



<p class="wp-block-paragraph">Mit dem neuen, optimierten Ansatz werden die Rezeptdaten nun beim Start der Anwendung automatisch aus der API geladen und in die lokale Datenbank geschrieben. Die Suche erfolgt anschließend vollständig serverseitig über diese Datenbank, wodurch Abfragen deutlich schneller und stabiler laufen. Gleichzeitig bleibt die Filterlogik flexibel: Nutzer:innen können nach Kategorie, Alkoholgehalt und Zutaten suchen, ohne dass alle Kriterien zwingend gesetzt sein müssen.</p>



<h2 class="wp-block-heading">Die &#8220;Signature&#8221;-Drinks</h2>



<p class="wp-block-paragraph">Dieses neue Feature bietet dem/der User:in die Möglichkeit, sich seinen/ihren Drink (Cocktail) selbst nach eigenen Wünschen zusammenzustellen. Diese Drinks in Form von Rezepten werden entsprechend gekennzeichnet und sind mit dem/der Ersteller:in verknüpft. Die Datenspeicherung erfolgt in der selben Tabelle, wo die Drink Rezepte der API abgespeichert sind. Nach dem Anlegen eines &#8220;Signature&#8221;-Drinks stehen dem/der Ersteller:in die Möglichkeit zur Verfügung den Drink . Hier ein Blick in die App (Bild folgt):</p>



<p class="wp-block-paragraph"></p>



<h2 class="wp-block-heading">Weitere Features/Verbesserungen</h2>



<h3 class="wp-block-heading">Redesign</h3>



<p class="wp-block-paragraph">Mit dem Wechsel von einer WebApp auf eine native Mobile-App wurde das bestehende Design nicht vollständig neu entwickelt, sondern gezielt modernisiert und an mobile Nutzungsszenarien angepasst. Der Fokus lag dabei auf einer klareren visuellen Struktur, einer verbesserten Bedienbarkeit sowie einem konsistenten Erscheinungsbild über alle Ansichten hinweg.</p>



<p class="wp-block-paragraph">Ein wesentlicher Schritt war die Umstellung des UI-Designs auf das Utility-First-Framework <strong>Tailwind CSS</strong>, das eine deutlich effizientere Gestaltung und höhere Flexibilität bei der Komponentenentwicklung ermöglicht. Dadurch konnten wiederkehrende Layouts vereinheitlicht, Abstände und Typografie präzise gesteuert und das gesamte Erscheinungsbild minimalistischer und moderner gestaltet werden.</p>



<h3 class="wp-block-heading">Zutaten Suchfunktion</h3>



<p class="wp-block-paragraph">Bisher musst man sich durch eine lange Liste von Zutaten &#8220;quälen&#8221; &#8211; mit einem neuen Suchfeld bei den Zutaten wurde dies nun deutlich vereinfacht.</p>



<h2 class="wp-block-heading">Herausforderungen im Entwicklungsprozess</h2>



<p class="wp-block-paragraph">Anbei zwei wesentliche Herausforderungen, die sich während der Entwicklungsarbeit zunehmend herauskristallisiert haben:<br><br>Die mobile Umsetzung von MixMatch mit NativeScript und Angular brachte einige technische und strukturelle Herausforderungen mit sich. Der Umstieg von der bisherigen Angular-Webumgebung auf NativeScript erforderte eine Einarbeitung in neue Syntaxkonzepte und native UI-Komponenten. Viele Layouts und Funktionen ließen sich nicht direkt übernehmen, sondern mussten gezielt an mobile Anforderungen angepasst werden. Zudem NativeScript auch noch ein sehr &#8220;junges&#8221; natives Framework ist und die Dokumentation derzeit noch nicht als allzu ausgereift erscheint.<br><br>Gleichzeitig wurde deutlich, dass Teile des bestehenden Codes, insbesondere im Hinblick auf Wartbarkeit und Performance auf mobilen Geräten, zu komplex aufgebaut waren. Um dem entgegenzuwirken, habe ich z. B. Übersetzungen in separate Lokalisations-Files ausgelagert, anstatt sie direkt im Code zu halten. Auch die Datenverarbeitung wurde angepasst: Statt verschachtelter Aufrufe zu der thecocktaildb API erfolgt die Filterung nun über eine lokale Datenbank, nachdem alle Daten der externen API einmalig gespeichert wurden.</p>



<h2 class="wp-block-heading">Resümee mit Ausblick</h2>



<p class="wp-block-paragraph">Das Projekt stellte in mehrfacher Hinsicht eine wertvolle Lernerfahrung dar – insbesondere durch den mutigen Schritt, mit NativeScript in ein für mich völlig neues Framework einzusteigen. Dieser Ansatz bot die Gelegenheit, die Stärken und Schwächen der Plattform unmittelbar kennenzulernen: Auf der einen Seite die hohe Flexibilität, die tiefe Integration nativer Funktionen und die Möglichkeit, moderne Frontend-Technologien wie Angular und Tailwind zu kombinieren; auf der anderen Seite aber auch die typischen Herausforderungen eines jungen Frameworks – etwa eingeschränkte Dokumentation, komplexe Build-Prozesse und teils unerwartete Kompatibilitätsprobleme.</p>



<p class="wp-block-paragraph">Gerade dieser direkte Umgang mit den Schattenseiten und positiven Aspekten des Frameworks hat jedoch den größten Lerneffekt gebracht. Es entstand ein tieferes Verständnis für mobile App-Architekturen, die Bedeutung einer sauberen Code-Struktur.</p>



<p class="wp-block-paragraph">Auch nach Abschluss dieser Entwicklungsphase bleiben einige naheliegende Erweiterungen vorstellbar, die sich mittelfristig umsetzen lassen. Dazu zählt etwa eine Kommentarfunktion, mit der Nutzer:innen direkt Rückmeldung zu einzelnen Drinks geben oder sich austauschen können. Ebenso wäre es denkbar, die „Signature Drinks“ für andere sichtbar zu machen. Mit der individuellen Möglichkeit, eigene Rezepturen anderen Usern/Userinnen sichtbar zu machen, können persönliche Rezeptideen einem weiten Publikum präsentiert werden.<br>Ergänzend ließe sich eine einfache Einkaufsliste integrieren, in der fehlende Zutaten gesammelt und als Liste exportiert werden könnte. Diese funktionalen Erweiterungen würden, wie üblich, Hand in Hand mit weiterem Bugfixing, Code-Optimierungen und kleineren UI-Verbesserungen erfolgen, um die App sowohl technisch als auch inhaltlich weiter zu stabilisieren, der Benutzerfreundlichkeit hin zu optimieren und auszubauen.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph"><strong>Beitragsbild</strong>: dieses wurde mittels Einsatz von KI erzeugt</p>
<p>The post <a href="https://mobile.fhstp.ac.at/allgemein/projekt-ss25-mixmatch-fortsetzung/">Projekt SS25 | MixMatch (Fortsetzung)</a> appeared first on <a href="https://mobile.fhstp.ac.at">Mobile USTP MKL</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
