SDK Setup Guides
...
Tutorials in Swift
UIKit: Task App Quickstart
this quickstart provides step by step instructions for using uikit to create a task app in xcode, which consists of the following /#creating a new app in xcode /#adding permissions to the infoplist /#creating ui elements /#configuring the storyboard /#connecting the ui /#setting up the taskstableviewcontroller /#adding a task /#configuring the uitableview to display task list /#editing tasks prerequisites ditto account and access credentials ios 13 (or later) macos 11 (or later) https //apps apple com/us/app/xcode/id497799835 15 (or later) for instructions on creating your account and obtaining your credentials, see docid\ jqjyl9gbvsgi9vlw3ywvc creating a new app in xcode click file , and then select new project in the choose a template for your new project modal, select app and then click next in the choose options for your new project modal, enter information as appropriate and then click next the following are merely suggestions; enter any information in the form that you desire the following steps provide suggested values for the form; however, you can enter any information you desire for product name , type "tasks" for team , select none for organization identifier , type "live ditto" for interface , select storyboard for life cycle , select appkit app delegate for language , select swift adding permissions to the info plist for ditto to access available networking capabilities, such as bluetooth low energy, configure your project's user control and privacy permissions by doing the following ios restricts access ios restricts access to some ditto functionality by default for the sake of user control and privacy as of the release of ios 13 and xcode 11 to ensure access to all platform capabilities, you must configure your app to request all the permissions it needs to sync these requests occur once at the initiation of the sync process by default, ditto activates bluetooth, triggering an automatic permission prompt for your end users ditto attempts to use your device's available networking capabilities to locate and sync with other app users this includes standard wi fi, also referred to as local area network ( lan ), and peer to peer functionality such as apple wireless direct link (awdl) and bluetooth low energy for more information, see ditto basics > docid\ hfwwxtojepjkcxqkp4pvr in addition, since ios 14, apple requires ios device end users grant permissions to use the lan to discover devices privacy — local network usage description privacy — bluetooth peripheral usage description privacy — bluetooth always usage description a bonjour service from xcode, add a new custom ios target properties entry from the left navigator area, click your project in the editor that appears, click info tab right click any row in the list, and then select add row from the menu for instructions on configuring permissions for your app, see the docid\ ahoag3kgvwhox12xkzds0 > docid\ ahoag3kgvwhox12xkzds0 topic from your project's info plist file, add the following key value pairs, and then, if desired, modify each string value once implemented, the following string values display to your end users as dismissable prompts explaining why the app requires certain permissions if desired, replace the default values for instance, if your end users prefer a language other than english, replace with their language equivalents key nsbluetoothalwaysusagedescription type string value uses bluetooth to connect and sync with nearby devices key nslocalnetworkusagedescription type string value uses wifi to connect and sync with nearby devices key nsbonjourservices type string value http alt tcp \<key>nsbluetoothalwaysusagedescription\</key> \<string>uses bluetooth to connect and sync with nearby devices\</string> \<key>nslocalnetworkusagedescription\</key> \<string>uses wifi to connect and sync with nearby devices\</string> \<key>nsbonjourservices\</key> \<array> \<string> http alt tcp \</string> \</array> from xcode, ensure your app continues to sync while it runs in the background by enabling bluetooth background modes once enabled, your app continuously syncs in the background, even while the device is locked from the left navigator area, click your project click signing & capabilities from targets , select your app from the list from background modes , click to select uses bluetooth le accessories from the list creating ui elements create elements for your interface, including a top navigation bar, a task list, and an element for entering the task name click file > new and then select file from the list from the choose a template for your new file modal, select cocoa touch class and then click next from the choose options for your new file modal, enter the following for class , type "tasktableviewcontroller" for subclass of , select uitableviewcontroller for language , select swift delete your project's default view controller file by right clicking viewcontroller swift from the left navigator area and selecting delete verify that your project structure appears as follows configuring the storyboard once you've created your ui elements, open your project's main storyboard file located in the left navigator area and then do the following to set up your storyboard in the storyboard editor that appears, right click the viewcontroller swift file and select delete from the list add a uinavigationcontroller into the storyboard click the circle with a square in it icon in the top right to display your list of ui elements and search for uinavigationcontroller click to add it to the storyboard click on the "root view controller scene" and navigate the right hand menu to declare the uitableviewcontroller as a custom class of taskstableviewcontroller this created a uinavigationcontroller and a root view controller based on uitableviewcontroller , but it needs to be configured to work with our taskstableviewcontroller swift file click on the "root view controller scene" and navigate to the menu on the right to declare the uitableviewcontroller as a custom class of taskstableviewcontroller ensure that the "navigation controller scene" is the initial view controller for our app, so click on it and navigate to the menu on the right to set this property now we need to customize the uitableviewcontroller to include the add task button click on the "root view controller scene" and then click the "circle with square" icon in the top right to add a uibarbuttonitem , search for "bar button item" and drag it onto the top right corner of the uinavigationbar finally, configure the bar item to use the "system item" "add" in the right hand menu the last configuration is to adjust the name in the navigation bar to "tasks", click on the "root view controller" text and navigate to the menu on the right to adjust the name connecting the ui declare a function that will called when the user clicks on the add icon in the navigation bar to do so, click the "show assistant editor" button in the top right so that our storyboard and tasktableviewcontroller swift file are both displayed now, right click on the add button and drag it into the class implementation of taskstableviewcontroller just above the numberofsections() function name the function didclickaddtask and adjust the type to uibarbuttonitem and click connect you should now have a function that is wired up to be called whenever that button is clicked finally, we will need to configure uitableview in taskstableviewcontroller and as part of this, we need to provide a "table view cell reuse identifier", so let's configure our storyboard cell to include an identifier click on the "prototype cells" area in your view and navigate the right hand menu to display the configuration for the "table view cell" and insert taskcell as the identifier setting up the taskstableviewcontroller first, we need to add some variables that will be created on viewdidload of the taskstableviewcontroller so adjust the class to match this code // remember to import dittoswift! import dittoswift class tasktableviewcontroller uitableviewcontroller { // these hold references to ditto for easy access var ditto ditto! var store dittostore! var livequery dittolivequery? var subscription dittosubscription? var collection dittocollection! // this is the uitableview data source var tasks \[dittodocument] = \[] override func viewdidload() { super viewdidload() // create an instance of ditto ditto = ditto(identity onlineplayground(appid "your app id here", token "your token here")) // this starts ditto's background synchronization ditto startsync() // create some helper variables for easy access store = ditto store // we will store data in the "tasks" collection // ditto stores data as collections of documents collection = store collection("tasks") // subscribe to changes with a live query subscription = collection find("!isdeleted") subscribe() // this function will create a "live query" that will update // our uitableview setuptasklist() } func setuptasklist() { // query for all tasks // observe changes and update the uitableview when anything changes livequery = collection find("!isdeleted") observelocal { \[weak self] docs, event in guard let `self` = self else { return } switch event { case update(let changes) guard changes insertions count > 0 || changes deletions count > 0 || changes updates count > 0 || changes moves count > 0 else { return } dispatchqueue main async { self tableview\ beginupdates() self tableview\ performbatchupdates({ let deletionindexpaths = changes deletions map { idx > indexpath in return indexpath(row idx, section 0) } self tableview\ deleterows(at deletionindexpaths, with automatic) let insertionindexpaths = changes insertions map { idx > indexpath in return indexpath(row idx, section 0) } self tableview\ insertrows(at insertionindexpaths, with automatic) let updateindexpaths = changes updates map { idx > indexpath in return indexpath(row idx, section 0) } self tableview\ reloadrows(at updateindexpaths, with automatic) for move in changes moves { let from = indexpath(row move from, section 0) let to = indexpath(row move to, section 0) self tableview\ moverow(at from, to to) } }) { in } // set the tasks array backing the uitableview to the new documents self tasks = docs self tableview\ endupdates() } case initial // set the tasks array backing the uitableview to the new documents self tasks = docs dispatchqueue main async { self tableview\ reloaddata() } default break } } } // remaining tasktableviewcontroller code } let's break down what this code does first, we create the variables needed and then initialize them in viewdidload() to enable background synchronization, we need to call startsync() which allows you to control when synchronization occurs for this application we want it to run the entire time the app is in use // these hold references to ditto for easy access var ditto ditto! var store dittostore! var livequery dittolivequery? var collection dittocollection! var subscription dittosubscription? // this is the uitableview data source var tasks \[dittodocument] = \[] override func viewdidload() { super viewdidload() // create an instance of ditto ditto = ditto(identity onlineplayground(appid "your app id here", token "your token here")) // this starts ditto's background synchronization ditto startsync() // create some helper variables for easy access store = ditto store // we will store data in the "tasks" collection // ditto stores data as collections of documents collection = store collection("tasks") // subscribe to changes with a live query subscription = collection find("!isdeleted") subscribe() // this function will create a "live query" that will update // our uitableview setuptasklist() } after setting up the variables and starting ditto, we then use ditto's key api to observe changes to the database by creating a live query in the setuptasklist() function this allows us to set the initial state of the uitableview after the query is immediately run and then subsequently get callbacks for any new data changes that occur locally or that were synced from other devices note, that we are using the observe api in ditto this api performs two functions first, it sets up a local observer for data changes in the database that match the query and second it creates a subscription for the same query that will be used to request this data from other devices for simplicity, we are using this combined api, but you can also call them independently to learn more, see the section in the documentation func setuptasklist() { livequery = collection find("!isdeleted") observelocal { \[weak self] docs, event in guard let `self` = self else { return } switch event { case update(let changes) guard changes insertions count > 0 || changes deletions count > 0 || changes updates count > 0 || changes moves count > 0 else { return } dispatchqueue main async { self tableview\ beginupdates() self tableview\ performbatchupdates({ let deletionindexpaths = changes deletions map { idx > indexpath in return indexpath(row idx, section 0) } self tableview\ deleterows(at deletionindexpaths, with automatic) let insertionindexpaths = changes insertions map { idx > indexpath in return indexpath(row idx, section 0) } self tableview\ insertrows(at insertionindexpaths, with automatic) let updateindexpaths = changes updates map { idx > indexpath in return indexpath(row idx, section 0) } self tableview\ reloadrows(at updateindexpaths, with automatic) for move in changes moves { let from = indexpath(row move from, section 0) let to = indexpath(row move to, section 0) self tableview\ moverow(at from, to to) } }) { in } // set the tasks array backing the uitableview to the new documents self tasks = docs self tableview\ endupdates() } case initial // set the tasks array backing the uitableview to the new documents self tasks = docs dispatchqueue main async { self tableview\ reloaddata() } default break } } collection find("isdeleted == true") evict() } adding a task to allow the user to create a task we want to display an alert view in response to clicking the add bar item add the following code to the didclickaddtask() function we added earlier @ibaction func didclickaddtask( sender uibarbuttonitem) { // create an alert let alert = uialertcontroller( title "add new task", message nil, preferredstyle alert) // add a text field to the alert for the new task text alert addtextfield(configurationhandler nil) alert addaction(uialertaction(title "cancel", style cancel, handler nil)) // add a "ok" button to the alert alert addaction(uialertaction(title "ok", style default, handler { \[weak self] ( ) in guard let self = self else { return } if let body = alert textfields?\[0] text { // insert the data into ditto = try! self collection upsert(\[ "body" body, "iscompleted" false ]) } })) // present the alert to the user present(alert, animated true, completion nil) } take note that this logic is using the ditto insert() api to create a task document ditto's api is designed around json compatible documents which are organized into collections = try! self collection upsert(\[ "body" body, "iscompleted" false ]) configuring the uitableview to display task list to ensure the uitableview can display the tasks, we need to configure it adjust your taskstableviewcontroller to include the following code (these functions were already created when the file was generated by xcode) // mark table view data source override func numberofsections(in tableview uitableview) > int { return 1 } override func tableview( tableview uitableview, numberofrowsinsection section int) > int { return tasks count } override func tableview( tableview uitableview, cellforrowat indexpath indexpath) > uitableviewcell { let cell = tableview\ dequeuereusablecell(withidentifier "taskcell", for indexpath) // configure the cell let task = tasks\[indexpath row] cell textlabel? text = task\["body"] stringvalue let taskcomplete = task\["iscompleted"] boolvalue if taskcomplete { cell accessorytype = checkmark } else { cell accessorytype = none } return cell } earlier, we created the tasks array which is the data source to the uitableview this code configures the uitableview to use this array and then configures the table view cell to display the task text and a checkmark on whether it is complete or not editing tasks select the task to complete when the user selects the task in the table view, we want to mark the task completed adjust your taskstableviewcontroller to include the following code (these functions were already created when the file was generated by xcode) override func tableview( tableview uitableview, didselectrowat indexpath indexpath) { // deselect the row so it is not highlighted tableview\ deselectrow(at indexpath, animated true) // retrieve the task at the row selected let task = tasks\[indexpath row] // update the task to mark completed collection findbyid(task id) update({ (newtask) in newtask?\["iscompleted"] set(!task\["iscompleted"] boolvalue) }) } override func tableview( tableview uitableview, caneditrowat indexpath indexpath) > bool { // return false if you do not want the specified item to be editable return true } this action makes use of ditto's update method where we are able to find the existing task and set the iscompleted value to the opposite of its current value swipe to delete task finally, we want to allow the user to delete a task by swiping the row in the table view adjust your taskstableviewcontroller to include the following code (this function was already created when the file was generated by xcode) // override to support editing the table view override func tableview( tableview uitableview, commit editingstyle uitableviewcell editingstyle, forrowat indexpath indexpath) { if editingstyle == delete { // retrieve the task at the row swiped let task = tasks\[indexpath row] // delete the task from ditto ditto store\["tasks"] findbyid( id) update { doc in doc?\["isdeleted"] set(true) } } } build and run! you now have a fully functioning to do app build and run it on the simulator or devices and observe the automatic data sync provided by ditto