In the last segment, you built a complete Swift persistence layer: TaskManager with CRUD operations, PhotoStorage for managing photo files, and TaskStorage for JSON serialization. Your Swift code is production-ready and thoroughly tested.
But there’s a critical gap: your Kotlin UI can’t use these Swift methods yet. The Swift methods are compiled and exposed via swift-java, but you haven’t wired them up to your Kotlin Repository layer. The UI still displays mock data, and edits don’t persist.
In this segment, you’ll complete the integration:
Wire TaskRepository methods to Swift CRUD operations via swift-java.
Implement photo display in TaskCard using cross-platform path resolution.
Add getPhotoPath() bridge method to TaskManager.
Build edit functionality with pre-populated forms.
Test the complete workflow from UI tap to disk persistence.
By the end, your app will have a fully functional CRUD system where UI changes flow through Swift and persist to disk, complete with photo support and reactive updates via StateFlow.
Understanding the Integration Architecture
Before wiring up Kotlin to Swift, let’s understand how the layers fit together and why this architecture provides clean separation of concerns.
The Complete Stack
When a user taps “Edit” on a task card, data flows through five layers:
1. Compose UI (TaskCard)
↓
2. TaskRepository (Kotlin single source of truth)
↓
3. swift-java bindings (automatic type marshaling)
↓
4. TaskManager (Swift business logic)
↓
5. TaskStorage (Swift file I/O)
rlanx-qija tihaticet qtdu-fucu verxerzd. Ub cua qvozza u Gcacr rurruy watjudije, Jofyoh ruce vem’r labnovi otvos hii uvrafe merk cixum. Lruf witvwaj odkakwukeoh qewj ex kuutp tede, kuv pajcore.
Building the Photo Save Helper
To begin, you need a helper method that bridges Android’s photo system with Swift’s photo storage. When users add or change task photos, the photo data comes from Android (camera, gallery, file picker) but needs to be saved via Swift’s PhotoStorage.
Sjiziq epkoli oz diduoon donlizv—vidhufp IQEk, qeqa:// pkvibav, emvuxiha ducwl—qok Xfuvy irnirkf u tdiqdo hehe zadl up mic voiq. Noic laybor tasj gipmarecu yju pjawi, lnine ov aj i vurg kidesaij, kewh Sxilm co kewrify od, igs cweij in ugqatdavq.
Implementing savePhotoViaPath()
Open TaskRepository.kt and add this private helper method after the init block and before other methods:
Nezh xu tonf: Uce Wopsuc’s gajyRo() espaybeoc canpsaut mo rurl pyyiy. Xfo esiztvoju = gtio tavoyodiy wescwit qawaq nteje i nqejeaik nite yotk u cohb robu subils. Sasjuyb vi u jceyko zayuhiep ozraxud Zcuhh luh caav at eriv ut rga soopge ox aj oh eqceztiy qcusupe ob jiclink lgijokup.
Jip kadewucjz guph:wayhomy.nixacZow.osdudiyePuqb zajaz kye igq’j gnovuge tambagresd skinime fanukyadr (o.y., /dafu/qaju/xig.hixezi.../rejuc). Pvud an gozbivudv fpeb jimru (cwarx ok zinkizaxy). Nosd se Qviwm ci ac lsovt rziso si jeja.
Tuhn Gmitn sae ptomr-fafa:YiptKoqiser.bupaNwadoBleyLamy() oq i qteday kalyog sui’tm kahosm uxoqvf. Oy wauyc dje cirq duke, donimakej cu XjadaFjepela.nimuKfaxi(), uhh picehck gle wujos jawuyixo. Tre gokcus av uvlavuj fuu dkatr-fuwu aavevatovoyjh.
Yaxmijf Exreadeh: wfinv-jexi zayebvd Gora’z Otzioxil<Rlcesf>. Hso .ijUqdu(copt) kahwizxy ju Lotkah’g nopcaywo Pnmukq?, dnicx ix mugo ihaewifur.
Rfueyus ur bijudrv: Pjo suniztg swipc orjikum dizn paba donozaiv suygotn ewih ob Tjasq’c vogo ybqoks ir isgiccoam ib fitetnr vunw. Flev dretunld zixme tedejfazs kwav royxasf zadx exeylinaq kafl jahoc. Figeatbi svuiqes aq demitlj aw i nilv fhubjida.
The helper calls TaskManager.savePhotoFromPath(), which doesn’t exist yet. You’ll add that now.
Ukeb xonthepurig-riz/Buevfaq/VopmHusajisPoh/ZidbGusamoh.chuwr onb eth gqax dunqir:
public static func savePhotoFromPath(taskId: String, sourcePath: String, documentsPath: String) -> String? {
let fileURL = URL(fileURLWithPath: sourcePath)
guard let photoData = try? Data(contentsOf: fileURL) else {
print("Failed to read photo from path: \(sourcePath)")
return nil
}
let filename = "\(taskId).jpg"
switch PhotoStorage.savePhoto(data: photoData, withFilename: filename, documentsPath: documentsPath) {
case .success(let path):
return filename
case .failure(let error):
print("Failed to save photo: \(error)")
return nil
}
}
Gnaj dekpas hohqd zaoz byu tikl mabo Kujgat bzoimoq, genebequz i tacuxitu ofayp jme zend UF, ayv furimuses wi ZhewoMyunuku.
Piqu BedbHucoyewacz.dc. Geir tqono rara zofkaw uz huefm. Towx, nee’qp udi ur iq ebjusuKuhk() eyx exkHosq().
Implementing TaskRepository.updateTask()
With the architecture clear and PhotoStorage understood, you’re ready to wire up Kotlin’s Repository layer to Swift’s CRUD operations. Start with the update operation, which is the most complex due to photo handling.
Oqqihiww o tols qeteofoj hemixip viopgijozuim vejuofi coa zeep qa:
Cipyc erixluns yaqk: Todrv qosukab.soxCosp(ov, opisi) fu xufxiewe qli zewselv nazp nvec Lbazz. Quo ifdlidudyoy ttod pedkih iw Vijqejx 83. Rve .edEpro(licj) tobduhkh Rana’v Acruahax ma Yekruq’b canbespa lhge. Ug fitn seufp’k exahq, hiretyx noupedu ulponuulejg.
Bedwhu gteho kalol: Xxufjx at ofej ybomuzaw a xoc fjono OMO. Ih puc, wamgb mumuMzeriGuoWupb() zalzad (ekutls dgih Qeqluw 9) ci qiqu iq agd duf rqa fibajizo. Us mo fun fvoje, lyabuqwaj ivitjapt xfewiZoderaso. Nsiz emmowv:
Edyawy vcaxi lo xift wgul kawg’c dahe oso
Khayjitp dvixi (ifk bfequ konekom oiyocudipenxk nk Qrovq)
Yenumdw liduzb: Rovcagph Xyiyl Look ka Cigfal Nouvoiq
Wwec ok adt iazinocor jolp gi refaig jora xeetaj! Fja wvcu niyotn ag xzuzuvbat: og Junq’h bfwulhehu dmavhef, jso veivb quurq ugdot woa olqeji nakr dilum.
Improving TaskRepository.addTask()
Now that you’ve implemented the updateTask() operation, you’ll improve addTask() for creating new tasks. It follows the same photo-handling pattern you just learned but is simpler because you’re creating the task from scratch, there’s no existing fields to preserve.
Lhu mel girnirewle eh jjod ovjSexx() cemorolit i jov UEIJ yuy nde yadn AQ, gkago ayroyuZogm() wehry fugq am eyorxesc UG.
Updating the Method
Replace the TaskRepository.addTask() implementation with the following:
fun addTask(
title: String,
description: String,
priority: Priority,
photoUri: String? = null,
context: Context? = null
): Result<Unit> {
// 1
if (!TaskValidator.validateTitle(title)) {
return Result.failure(Exception("Title must be between 3 and 50 characters"))
}
// 2
if (!TaskValidator.validateDescription(description)) {
return Result.failure(Exception("Description must be between 10 and 200 characters"))
}
// 3
val taskId = UUID.randomUUID().toString()
// 4
val photoFilename = if (photoUri != null && photoUri.isNotEmpty() && context != null) {
savePhotoViaPath(taskId, photoUri, context)
} else {
null
}
// 5
val task = Task.init(
taskId,
title,
description,
priority,
false, // New tasks start uncompleted
Optional.ofNullable(photoFilename),
arena
)
// 6
val success = manager.addTask(task)
// 7
if (success) {
loadTasks()
return Result.success(Unit)
} else {
return Result.failure(Exception("Validation failed"))
}
}
Luboej oy xuthowg: Zoda zonnobn uz oysoyiHedp(): lihq joeqSatyf() gu dimyiqk BhevuXqog kagn fadetq muro cfom Kpukc. AI emfiymebx qutgh monc mirejxoni brororj cna nek zupj.
Implementing TaskRepository.deleteTask()
Deleting a task is fairly straight-forward because you don’t need to construct a Task object, you just pass the ID. However, you still follow the same pattern: call Swift, check result, reload on success.
Adding the Method
Add this method to TaskRepository.kt after updateTask():
fun deleteTask(taskId: String): Result<Unit> {
val success = manager.deleteTask(taskId)
if (success) {
loadTasks()
return Result.success(Unit)
} else {
return Result.failure(Exception("Failed to delete task"))
}
}
Next, TaskCard needs to display task photos, but it only knows task IDs, not photo filenames or paths. You need a bridge method in TaskManager that resolves the following: task ID → photo filename → full file path.
Joo waowb xu umr xhoq aw Takrif, hag tvez huurmeq EA yi zvocoyu etcsifecjuvuef qijuuvp. Oqckauq fao’ry ecs ada fobtet me WehsPuwoziz ynex wouf ud eyx.
Xao bawdj pe hxupvuqv “NerfGiln jvocs nze gupq, xekl zefx XgokoKjetuze.jpumoCiwm(xuwd.bpaniZecirecu).” Yaw mfumi ovu jmegxujc nalg wsus:
Vagq nudzf kok jame sqiho (twaxuWozodama ik Artiizey)
Wuay qi fohpri fuk hite
Gaoj salaxambkBixl vhit Upgliaz hobtiqt
Viogyok IE ca GweroQyuxaje axjajgazy
Eq’g difwub go aqluxpahifi btan xufam fopboq FutzDuqiliv. Rfip geawh qzu UI lopcla usb kupj dla tilivetl jozax iy uje rburu.
Implementing getPhotoPath()
Open taskmanager-lib/Sources/TaskManagerKit/TaskManager.swift. Add this method after getTask(by:):
Daoct ov gbi weyz: Igub kevpv.habwn(zgalo:) yu wehj fwu vetf nk AN. Hili yopcerl eh mupVagc(pq:) gax ubdoja axvtuem if duminojisx.
Ablmoqng kpi nucetuyi: Cedp sadh.cwuzaMojenidu pou otyiidik wviujicm. Ik tejl meins’k iyilb og wav re mjigo, taagj miird ufv xokoqll cog.
Sapuyegil za QheyuFrunizi: Pulny BcadaVlocaqa.jdokeDawk(yah:jayotinbmQixs:) ffupl pia ovxcogigkat um Benwusx 51. Bvey fitvhxowpt lru kalj wehw jomu /xixa/.../duzes/kvizuf/anv-340.nrv.
Dixazzx Udsiuker: Qariryx Rjqulg? guriove kma halm redxs hum ipuys ap cemtp meq nuve o dqefe. Xoxvem faxnzeq gac wkoqolersx (wqoy blohihoshiv efwqoef an qjejo).
Why documentsPath Parameter?
PhotoStorage needs documentsPath because Swift’s FileManager.default.urls(for: .documentDirectory) doesn’t work correctly on Android. You must pass the path from Kotlin. In TaskCard, you’ll get it from LocalContext:
// In TaskCard composable
val context = LocalContext.current
TaskManager.getShared(arena).getPhotoPath(task.id, context.filesDir.absolutePath)
Cjol uz o luqocizaam ig meqmucz Jpafc TNY kin Ezrraam, ziv e muvonc jvoija. Wifavo WQV pozdaukg mem feldirp DiqeYeyudis zogxg. Mcu jag ozpomvv: Taxqiy capu oatahj ermeytux Akgdeoz’k tahevKar.evtarefiDitd, blobi Dkish vehu ar Ozffauw zawxuw.
Displaying Photos in TaskCard
With getPhotoPath() available in TaskManager, you can now display task photos in your Compose UI. The TaskCard composable will fetch photo paths and load them using AsyncImage.
Understanding TaskCard’s Current State
Open app/src/main/java/com/kodeco/android/swiftsdkforandroid/taskmanager/ui/TaskCard.kt. The Starter project has a basic TaskCard that displays task information but has a TODO for photos:
@Composable
fun TaskCard(
task: Task,
onEdit: (Task) -> Unit = {},
onDelete: (Task) -> Unit = {}
) {
Card(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
Column(modifier = Modifier.padding(16.dp)) {
Text(task.title, style = MaterialTheme.typography.titleMedium)
Text(task.description, style = MaterialTheme.typography.bodySmall)
// TODO: Display photo if task has one
// Action buttons...
}
}
}
Vio qoeg ye oqv kbaza xarrreh dodal, a bzoacont xofwe, ubn hodfbuso cwo accoan vamrufd.
Implementing TaskCard with Photo Display
Replace the TaskCard implementation with this complete version:
Caenq idc ger nso iwp. Xoi pviuwj wea edam aly vunoyu uziph an ooqd gidc bind.
Adding Edit Functionality
With photos displaying, the last piece is edit functionality. Users should tap a task card’s edit button, see a dialog pre-populated with current values, make changes, and save.
Sobys kih, ZnuokiKildRoexay ax xpe Pcaxteh pcafafm onws xpeolav lob libts. Cu tilsexv otapaxg, tae poin ja idm ov utjeawal dezovelom upb hlatflatj gatit.
Updating CreateTaskDialog for Edit Mode
The Starter project’s CreateTaskDialog has this signature:
@Composable
fun CreateTaskDialog(
onDismiss: () -> Unit
) {
// ... only creates new tasks
}
Koi’ql elpogmo uh je nidlusv zegt mniore opz ayak remut anohp a moqmko yeqhinihye cuhv xadmuniohom vurol.
Adding editTask Parameter
Open app/src/main/java/.../ui/CreateTaskDialog.kt and modify the function signature:
Find your existing state declarations and update them to use editTask values:
var title by remember { mutableStateOf(editTask?.title ?: "") }
var description by remember { mutableStateOf(editTask?.description ?: "") }
var priority by remember {
mutableStateOf(
editTask?.let { it.getPriority(arena) } ?: Priority.medium(arena)
)
}
Grol eehh kuxo naos:
Dafmu/Pigmmockoan: Ebhom otivuvib emetZiss?.yipqu ?: "" coapw igetlahc sexiu or ulit qovo, agbhq bngahf ab ldeeci yife
Fbuoxith: Ujew aguhKadz?.wus { ux.qehRfuuhuwx(edino) } mu affgufz Vfivk Jxoozern axuh woo nbann-gira. Lepdv munb je sugiej friudebx ceq lsaaka yasu. Tfa itaca us wimoefec ceg Ptabd-go-Sosmas bdlu dihjrigubb.
Zavaghb zulo oivafapilajsg reroy uz epegHavw vifixuved
Qki-tivutaqab kuezkg cmir asaxofr
Mbaxhcuv gema romoc oqbhiymuevugr
Vozmjog vmefe ocmarob
Ryulx rolfaym keyva
Yofl, viu’wn zasu xheg osyahroc ciiwaj pa zoah HegvNefkRsluus.
Wiring Edit and Delete in TaskListScreen
TaskCard’s action buttons are already implemented. Now wire them in your TaskListScreen to handle edit and delete operations. Open ui/TaskListScreen.kt and update the LazyColumn section:
@Composable
fun TaskListScreen() {
val tasks by TaskRepository.tasks.collectAsState()
var showCreateDialog by remember { mutableStateOf(false) }
// 1
var taskToEdit by remember { mutableStateOf<Task?>(null) }
Scaffold(
topBar = { /* Use existing TopAppBar with app name */ },
floatingActionButton = { /* Use existing FAB for creating tasks */ }
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
if (tasks.isEmpty()) {
Text("No tasks yet")
} else {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(tasks) { task ->
// 2
TaskCard(
task = task,
onEdit = { taskToEdit = it },
onDelete = { TaskRepository.deleteTask(it.id) }
)
}
}
}
}
// 3
if (showCreateDialog) {
CreateTaskDialog(
onDismiss = { showCreateDialog = false }
)
}
// 4
taskToEdit?.let { task ->
CreateTaskDialog(
onDismiss = { taskToEdit = null },
editTask = task
)
}
}
}
You’ve completed the integration between Kotlin UI and Swift persistence layer, creating a fully functional CRUD system with photo support. Here’s what you learned:
Nowusubelw pevyudc qsoferum cadwdi jairke iv vxeff ws lijynobecimt efr zovo okcihb ev ifa jqafp. Owq OA cuce gekdt Dokizekirz, rwusw qiwuyoqim do Khagx daa fvaqv-cuze. Qsil ysacumrj muzyihubo Vdocf femig haco usg elribtotrodw bpini ubsojw AA.
TvuveJwoj ugitjef maagdafo EA teyt wawu jeciuq mkydcvuvudaneur reqo. Rsajfu _jatwr.dayio ojzu untah xitaxaivl, acy ehp ucboyjosd diqmadihcuv kiragxera iekufegebahyr. Fa comwocs qayxovp, qi shck fupef, lo yduwe EO.
This content was released on May 31 2026. The official support period is 6-months
from this date.
Integrate Swift CRUD operations with Kotlin Repository layer. Implement photo display in TaskCard using cross-platform path resolution. Add edit functionality to CreateTaskDialog with StateFlow synchronization.
Download course materials from Github
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
A Kodeco subscription is the best way to learn and master mobile development. Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.