Selecting a approach for storing knowledge completely is one thing that’s at all times wanted when creating purposes. There are numerous choices we will decide from: To create single information, to make use of CoreData or create a SQLite database. The final possibility consists of some further problem because the database should be created first, and all tables and fields to be already outlined earlier than an app makes use of them. Moreover, and from the programming viewpoint, it’s not that simple to handle a SQLite database when knowledge must be saved, up to date or retrieved.
These issues appear to vanish when utilizing a comparatively new library that was popped on the GitHub referred to as SwiftyDB. It’s a third-party library, which, because the creator says, is a plug-and-play part certainly. SwiftyDB reliefs builders from the effort of making a SQLite database manually, and from defining all required tables and fields. That occurs routinely by the library utilizing the properties of the category (or courses) used as knowledge fashions. In addition to that, all database operations are carried out underneath the hood, so builders can give attention to the implementation of the app logic solely. A easy but highly effective API makes it actually a bit of cake when coping with the database.
It’s essential to say although that you simply shouldn’t anticipate SwiftyDB to make miracles. It’s a dependable library that may do fairly properly what it’s imagined to do, however there are nonetheless some lacking options which might be most likely meant to be added within the (close to or distant) future. Nonetheless, it’s nonetheless remaing an incredible software that deserves some particular consideration, due to this fact on this tutorial we’ll undergo the fundamentals of SwiftyDB.
To your reference, you’ll find its documentation here, which you need to positively see after having gone by way of this textual content. Should you had been at all times keen to work with SQLite databases however hesitated to take action, I imagine that SwiftyDB is an effective begin to do this.
With the above mentioned, let’s get going with the exploration of a brand new, fairly promising software.
Concerning the Demo App
In our publish at present we’re going to create a extremely easy observe taking app able to doing all the essential operations one would anticipate:
- Listing notes
- Create new notes
- Replace current notes
- Delete notes
Apparently, SwiftyDB goes to take cost of managing the info into an SQLite database. All of the listed operations above are ok to exhibit all the things you want so can get began working with SwiftyDB simply.
To make it attainable for us to remain into the purpose, I’ve created a starter project which you need to obtain and start with. Once you get it, open it in Xcode and navigate round so that you get acquainted with it. As you’ll see, all the essential performance is there, with the many of the data-related options to be lacking. It might be nice in the event you would additionally run the challenge not less than as soon as, so that you see what’s all about.
The app is navigation primarily based, and within the first view controller there’s a tableview the place all notes are supposed to be listed.

By tapping on an current observe we’ll be capable of edit it and replace it, and when swiping to the left we’ll be capable of delete it:

Composing a brand new observe is feasible by tapping on the plus (+) button on the navigation bar. With the intention to have a ok working instance, right here’s an inventory of actions that we will carry out when enhancing a observe (new or current one):
- Set title and physique for the observe.
- Change the font identify.
- Change the font measurement.
- Change the textual content colour.
- Import photos to the observe.
- Transfer the pictures round and place them to a special place.
All values matching to the above operations shall be saved to the database. To make it a bit extra clear for the final two elements particularly, the precise photos are going to be saved to the paperwork listing of the app, whereas we’re going to save simply the identify and body for every picture to the database. However not simply that; moreover we’re going to create a brand new class for managing photos (with the small print are coming later).

A final however vital element I’ve to say about is that though you’re downloading a starter challenge to work on, by the tip of the subsequent half you’ll be having a workspace. That’s as a result of we’ll use CocoaPods to obtain the SwiftyDB library, and every other dependencies that come alongside.
Please get going if you’re as much as it, however first, shut the starter challenge when you have already opened it in Xcode.
Putting in SwiftyDB
The very first thing we’ve to do is to obtain the SwiftyDB library and use it in our challenge. Merely getting the library information and including them to the challenge isn’t going to work, so we’ve to carry out the set up utilizing the CocoaPods dependency supervisor. The method is easy, and also you’ll be capable of do it very quickly in any respect, even when you have by no means used CocoaPods earlier than. For you reference nonetheless, have a look to the earlier hyperlink.
Putting in CocoaPods
We’re going to start by putting in CocoaPods to our system. If in case you have put in CocoaPods already please skip this step. If not then go forward and open Terminal. Kind the next command to put in CocoaPods:
sudo gem set up cocoapods |
Press Return, present your password and sit again whereas the obtain course of is happening. Don’t shut Terminal as soon as it’s completed, we’re nonetheless needing it.
Putting in SwiftyDB and Different Dependencies
Navigate to the folder the place the starter challenge exists through the use of the cd command (nonetheless in Terminal):
cd PATH_TO_THE_STARTER_PROJECT_DIRECTORY |
It’s now time to create a Podfile that describes the library (or libraries) we wish to obtain to CocoaPods. The simplest approach to do this is by typing the next command and let CocoaPods create one for us:
A brand new file named Podfile
shall be created to the challenge folder. Open it utilizing a textual content editor (ideally not TextEdit), and modify it in accordance with the subsequent snippet:
goal ‘NotesDB’ do
pod “SwiftyDB”
finish
use_frameworks!
goal ‘NotesDB’ do pod “SwiftyDB” finish |

The road that can really do the entire job is the pod "SwiftyDB"
. CocoaPods will obtain SwiftyDB library and all of its dependencies through the use of that line, and it’ll create some new subfolders together with an Xcode workspace.
When you end enhancing the file, save and shut it. Then, just be sure you’ve closed the starter challenge on Xcode and return to Terminal. Kind the subsequent command:

Wait once more for a number of moments, and you then’re able to go. As a substitute of opening the starter challenge, open the NotesDB.xcworkspace
on Xcode this time.

Starting With SwiftyDB – Our Mannequin
Contained in the NotesDB
challenge there’s a file referred to as Be aware.swift
, however it’s presently empty. That is our entry level at present, as we’re going to create a few courses that can symbolize a observe entity programmatically. In a extra theoretical stage, our upcoming work right here consists of the Mannequin
half within the iOS MVC sample.
What we want initially is to import the SwiftyDB library, in order you guess go to the highest of the file and add this line:
Now, let’s declare our most vital class on this challenge:
class Be aware: NSObject, Storable
|
When working with SwiftyDB there are a number of however particular guidelines to comply with, and within the above class header line you may see two of them:
- A category with properties meant to be saved in a database utilizing SwiftyDB should be a subclass of the
NSObject
class. - A category with properties meant to be saved in a database utilizing SwiftyDB should undertake the
Storable
protocol (it’s a SwiftyDB protocol).
Now we’ve to consider the properties we wish to have on this class, and right here it comes once more a brand new rule from SwiftyDB: The datatypes
of the properties should be any of these listed here so as to have the ability to load whole Be aware
objects, as a substitute of easy knowledge (array with dictionaries) when retrieving knowledge from the database. If there are properties with “incompatible” datatypes, then we’ll need to take further actions so we convert them into the advised ones (we’ll see that in particulars in only a whereas). By default, properties with such datatypes are merely ignored by SwiftyDB when it’s about time to avoid wasting to database, and no respective fields are created to the desk. Additionally, we’ll give particular therapy to every other properties within the class that we don’t actually wish to be saved to the database.
The final rule for now says {that a} class that conforms to the Storable
protocol should essentially implement the init
technique:
override required init()
tremendous.init()
class Be aware: NSObject, Storable
override required init() tremendous.init()
|
Now that we’ve all the data we want, let’s begin declaring the properties of our class. Not all for now, as a few of them require extra dialogue. Nonetheless, listed below are the fundamentals:
…
class Be aware: NSObject, Storable let database: SwiftyDB! = SwiftyDB(databaseName: “notes”) var noteID: NSNumber! var title: String! var textual content: String! var textColor: NSData! var fontName: String! var fontSize: NSNumber! var creationDate: NSDate! var modificationDate: NSDate!
...
|
Unnecessary to remark any of them, aside from the primary one. When that object will get initialised it would create a brand new database (named notes.sqlite
) if it doesn’t exist and it’ll create a desk routinely. The desk fields will match to the properties having a correct datatype. However, if the database already exists, it would simply open.
As you would possibly discover, the above properties describe a observe and all of the attributes we wish to save (title, textual content, textual content colour, font identify and measurement, creation and modification dates), however there’s nothing there concerning the pictures {that a} observe can presumably have. Deliberately, we’re going to create a brand new class for photos, the place we’ll retailer two properties solely: The body and the picture identify.
So, nonetheless being within the Be aware.swift
file, create the next class above or under the prevailing one:
class ImageDescriptor: NSObject, NSCoding var frameData: NSData! var imageName: String!
|
Be aware that the body is represented as a NSData
object on this class, and never as a CGRect
. It’s essential to do it that approach, so we will simply retailer that worth to the database later. You’ll see shortly how we’re going to deal with it, and also you’ll additionally perceive why we undertake the NSCoding
protocol.
Again to the Be aware
class, let’s declare an ImageDescriptor
array as proven subsequent:
var photos: [ImageDescriptor]!
…
class Be aware: NSObject, Storable ...
var photos: [ImageDescriptor]!
...
|
There’s a limitation that now’s the perfect time to say about, and that’s the indisputable fact that SwiftyDB does not retailer collections to the database
. In easy phrases, that signifies that our photos
array won’t ever be saved to the database, so we’ve to determine learn how to cope with this. What we’re allowed to do is to make use of one of many supported datatypes (see the hyperlink I supplied somewhat after the start of this half), and probably the most appropriate datatype is NSData
. So, as a substitute of saving the photos
array to the database, we’ll be saving the next (new) property:
var imagesData: NSData!
…
class Be aware: NSObject, Storable ...
var imagesData: NSData!
...
|
However how are we imagined to go from the photos
array with ImageDescriptor
objects to the imagesData
NSData
object? Properly, the reply is by archiving
the photos
array utilizing the NSKeyedArchiver
class and producing that approach a NSData
object. We’ll see later how that is actually being finished in code, however now that we all know what we’ve to do, we should return to the ImageDescriptor
class and have some additions.
As you recognize, a category might be archived (in different languages often known as serialized
) if solely all of its properties might be serialised too, and in our case that is attainable, because the datatypes (NSData
and String
) of the 2 properties within the ImageDescriptor
class are serialisable. Nonetheless that’s not sufficient, as we additionally need to encode
and decode
them to be able to efficiently archive and unarchive respectively, and that’s why we really need the NSCoding
protocol. By utilizing it we’ll implement the strategies proven subsequent (considered one of them is an init
technique), and we’ll correctly encode and decode our two properties:
required init?(coder aDecoder: NSCoder)
frameData = aDecoder.decodeObjectForKey(“frameData”) as! NSData
imageName = aDecoder.decodeObjectForKey(“imageName”) as! String
func encodeWithCoder(aCoder: NSCoder)
aCoder.encodeObject(frameData, forKey: “frameData”)
aCoder.encodeObject(imageName, forKey: “imageName”)
class ImageDescriptor: NSObject, NSCoding ...
required init?(coder aDecoder: NSCoder) frameData = aDecoder.decodeObjectForKey(“frameData”) as! NSData imageName = aDecoder.decodeObjectForKey(“imageName”) as! String
func encodeWithCoder(aCoder: NSCoder) aCoder.encodeObject(frameData, forKey: “frameData”) aCoder.encodeObject(imageName, forKey: “imageName”)
|
For extra details about the NSCoding
protocol and the NSKeyedArchiver
class have a look here and here, it’s pointless to debate extra about them right here and now.
Along with all of the above, let’s outline a fairly helpful customized init
technique. It’s actually easy, so no have to make any feedback:
init(frameData: NSData!, imageName: String!)
tremendous.init()
self.frameData = frameData
self.imageName = imageName
class ImageDescriptor: NSObject, NSCoding ...
init(frameData: NSData!, imageName: String!) tremendous.init() self.frameData = frameData self.imageName = imageName
|
At this level our first fast assembly with the SwiftyDB library involves its finish. Regardless that we didn’t do a lot SwiftyDB stuff, this half was essential for 3 causes:
- To create a category that shall be used from the SwiftyDB.
- To study some guidelines utilized when utilizing SwiftyDB.
- To see some vital limitations concerning the datatypes that may be saved within the database utilizing SwiftyDB.
Be aware
: Should you’re seeing some errors proper now on Xcode, then construct the challenge as soon as (Command-B) to do away with them.
Main Keys and Ignored Properties
It’s at all times really helpful to make use of major keys
when coping with databases, as such keys can assist you uniquely establish information within the database tables and carry out numerous operations through the use of them (for instance, replace a particular report). You could find here an excellent definition about what a major secret’s.
It’s simple to specify a number of properties of a category as the first key (or keys) for the respective desk within the database in SwiftyDB. The library supplies the PrimaryKeys
protocol, which needs to be carried out by all courses that the respective tables ought to have a major key so their objects to be uniquely recognized. The best way to do this is kind of easy and commonplace, so let’s get into the purpose right away:
Within the NotesDB
challenge you’ll discover a file named Extensions.swift
. Click on it on the Challenge Navigator so that you open it. Add the next strains there:
extension Be aware: PrimaryKeys class func primaryKeys() –> Set<String> return [“noteID”]
|
In our demo, we wish the noteID
property to be the one major key within the respective desk within the sqlite database. Nonetheless, if extra major keys are required, you then simply have to jot down them within the row separated by comma (for instance, return ["key1", "key2", "key3"]
).
In addition to that, not all properties of a category ought to at all times be saved to the database, and you need to explicitly order SwiftyDB to not embody them. For instance, within the Be aware
class we’ve two properties that aren’t going to be saved to the database (both as a result of they can not or we don’t need so): The photos
array and the database
object. How can we explicitly exclude these two? By implementing one other protocol that the SwiftyDB library supplies referred to as IgnoredProperties
:
extension Be aware: IgnoredProperties class func ignoredProperties() –> Set<String> return [“images”, “database”]
|
If there have been extra properties we wouldn’t prefer to need to the database they need to be added above too. For instance, let’s say that we’ve the next property:
… and that we don’t need it to be saved to the database. In that case, we should always add it to the IgnoredProperties
protocol implementation too:
extension Be aware: IgnoredProperties class func ignoredProperties() –> Set<String> return [“images”, “database”, “noteAuthor”]
|
Be aware: Import the MARKDOWN_HASH6211c316cc840902a4df44c828a26fbeMARKDOWN_HASH
library to the MARKDOWN_HASH1dbda56f2122b1744ebf59bb64bbffdfMARKDOWN_HASH
file in the event you see any errors.
Saving a New Be aware
Having finished the naked minimal implementation within the Be aware
class, it’s time to show to the functionalities of the demo app. We nonetheless haven’t added any strategies to our new class; we’ll accomplish that by following the implementation move of all of the lacking functionalities.
So, the very first thing we want is having notes, due to this fact the app should be instructed learn how to save them correctly utilizing the SwiftyDB and our two new courses. That is going to happen largely within the EditNoteViewController
class, so it’s about time to open the respective file utilizing the Challenge Navigator. Earlier than we write the primary line of code right here, I think about fairly vital to spotlight the many of the properties discovered there:
imageViews
: This array holds all of the picture view objects that comprise photos added to a observe. Please don’t overlook that this array exists; it’ll develop into helpful shortly.currentFontName
: It holds the identify of the presently utilized font to the textview.currentFontSize
: It’s the font measurement of the textual content within the textview.editedNoteID
: ThenoteID
worth (major key) of a observe that’s about to be up to date. We’ll use it later.
For the reason that normal performance already exists within the starter challenge, what we’ve to do is to implement the lacking logic within the saveNote()
technique. We’ll start by doing two issues: First we received’t enable saving if there is no such thing as a textual content within the title or the physique of the observe. Second, we’ll dismiss the keyboard if it’s appeared by the point of saving:
func saveNote() |
We’ll proceed now by initializing a brand new Be aware
object, and by assigning the suitable values to the correct properties. The pictures want particular therapy, and we’ll do it proper after.
let observe = Be aware()
observe.noteID = Int(NSDate().timeIntervalSince1970)
observe.creationDate = NSDate()
observe.title = txtTitle.textual content
observe.textual content = tvNote.textual content!
observe.textColor = NSKeyedArchiver.archivedDataWithRootObject(tvNote.textColor!)
observe.fontName = tvNote.font?.fontName
observe.fontSize = tvNote.font?.pointSize
observe.modificationDate = NSDate()
func saveNote() ...
let observe = Be aware() observe.noteID = Int(NSDate().timeIntervalSince1970) observe.creationDate = NSDate() observe.title = txtTitle.textual content observe.textual content = tvNote.textual content! observe.textColor = NSKeyedArchiver.archivedDataWithRootObject(tvNote.textColor!) observe.fontName = tvNote.font?.fontName observe.fontSize = tvNote.font?.pointSize observe.modificationDate = NSDate()
|
A number of feedback now:
- The
noteID
property expects any Int quantity that can work as the first key. You’ll be able to create or generate any worth you need so long as it’s distinctive. On this case we set the integer half of the present timestamp as our major key, however typically this isn’t a good suggestion in actual world purposes because the timestamp incorporates too many digits. Nonetheless for our demo software it’s simply fantastic, because it additionally consists of the best possibility for having a singular int worth. - As we save a observe for first time, we set the present timestamp (expressed as a
NSDate
object) to each creation and modification date properties. - The one particular motion we positively need to take is to transform the textual content colour of the textview right into a
NSData
object. That is achieved by archiving the colour object utilizing theNSKeyedArchiver
class.
Let’s give attention to learn how to save the pictures now. We are going to create a brand new technique which shall be fed with the picture views array. We’ll carry out two issues in it: We’ll save the precise picture of every picture view to the paperwork listing of the app, and we’ll create an ImageDescriptor
object for every one. Every such object shall be appended to the photos
array.
With the intention to create this new technique we’re going to make a small detour, and to return to the Be aware.swift
file once more. Let’s see the implementation first, after which we’ll talk about about it.
<
pre lang=”swift”>
func storeNoteImagesFromImageViews(imageViews: [PanningImageView])
if imageViews.rely > 0
if photos == nil
photos = ImageDescriptor
else
photos.removeAll()
photos.append(ImageDescriptor(frameData: imageView.body.toNSData(), imageName: imageName))
Helper.saveImage(imageView.picture!, withName: imageName)
imagesData = NSKeyedArchiver.archivedDataWithRootObject(photos)
else
imagesData = NSKeyedArchiver.archivedDataWithRootObject(NSNull())
for i in 0..<imageViews.rely let imageView = imageViews[i] let imageName = “img_(Int(NSDate().timeIntervalSince1970))_(i)“
photos.append(ImageDescriptor(frameData: imageView.body.toNSData(), imageName: imageName))
Helper.saveImage(imageView.picture!, withName: imageName)
imagesData = NSKeyedArchiver.archivedDataWithRootObject(photos)
else imagesData = NSKeyedArchiver.archivedDataWithRootObject(NSNull())
|
}
Right here’s what’s happening within the above technique:
- For starters we examine if the
photos
array is initialised or not. If it’s nil we initialise it, if not, we simply take away any current knowledge from it. The second will develop into helpful later, after we’ll be updating an current observe. - Then for every picture view we create a singular identify for its picture. Every identify shall be much like this: “img_12345679_1”.
- We initialise a brand new
ImageDescriptor
object through the use of our customizedinit
technique and by offering the picture view body and the picture identify as parameters. ThetoNSData()
technique has been carried out as an extension of theCGRect
and you’ll find it within theExtensions.swift
file. Its goal is to transform a body to aNSData
object. As soon as the brand newImageDescriptor
object is prepared, it’s appended to thephotos
array. - We save the precise picture to the paperwork listing. The
saveImage(_: withName:)
class technique might be discovered within theHelper.swift
file, together with couple extra helpful class strategies. - Lastly, when all picture views have been processed, we convert the
photos
array to aNSData
object by archiving it and we assign it to theimagesData
property. The final line above is the precise motive that theNSCoding
protocol and the implementation of its strategies are required within theImageDescriptor
class.
The else
case above might sound reduntant, however it’s required. By default, the imagesData
property is nil and it’ll stay like that if no photos get added to the observe. Nonetheless, “nil” isn’t acknowledged by SQLite. What SQLite understands is the equal of NSNull
, and that’s what we offer transformed to a NSData
object.
Again to the EditNoteViewController.swift
file once more to make use of what we simply created:
observe.storeNoteImagesFromImageViews(imageViews)
func saveNote() ...
observe.storeNoteImagesFromImageViews(imageViews)
|
Let’s return now to the Be aware.swift
file, and let’s implement the strategy that can carry out the precise saving to the database. There’s one thing vital you need to know at this level: SwiftyDB supplies the choice to carry out any database-related operation synchronously or asynchronously. Which technique you need to choose will depend on the character of the app you construct. Nonetheless, I’d recommend to make use of the asynchronous technique, as this received’t block the primary thread whereas a database operation is in progress, and it received’t have an effect on the consumer expertise by freezing (even immediately) the UI. However I’m saying once more, it’s completely as much as you.
We’ll use the asynchronous approach to save our knowledge in right here. As you’ll see, the respective SwiftyDB technique incorporates a closure that returns the results of the operation. You’ll be able to learn particulars about that consequence object here, and really I’m recommending to take action now.
Let’s implement now our new technique so we will talk about extra about it:
else
completionHandler(success: true)
func saveNote(shouldUpdate: Bool = false, completionHandler: (success: Bool) –> Void) database.asyncAddObject(self, replace: shouldUpdate) (consequence) –> Void in if let error = consequence.error print(error) completionHandler(success: false)
else completionHandler(success: true)
|
It’s simple to know from the above implementation that we’re going to make use of the identical technique for updating notes too. We ensure that to set the shouldUpdate
Bool worth as a parameter to the strategy prematurely, after which relying on its worth the asyncDataObject(...)
will both create a brand new report or it’ll replace an current one.
Moreover, you see that the second parameter in our technique is a completion handler. We name it with the correct parameter worth, relying on whether or not the saving is profitable or not. I’d recommend you to at all times use completion handlers when you might have duties working asynchronously on the background. That approach, you’ll be capable of notify the caller strategies when the background job has completed, and switch any attainable outcomes or knowledge again.
What you see taking place above is what you’ll see taking place in different database-related strategies too. We’ll at all times be checking for an error within the consequence, and we’ll proceed accordingly relying on whether or not there’s any or not. Within the above case if there’s an error, we name our completion handler passing the false
worth, that means that the saving was failed. If the other case, we cross true
to point a profitable operation.
Again to the EditNoteViewController
class as soon as once more, let’s get completed with the saveNote()
technique. We’ll name the one created proper above, and if the observe saving has been profitable we’ll simply pop the view controller. If there’s an error, then we’ll show a message.
let shouldUpdate = (editedNoteID == nil) ? false : true
observe.saveNote(shouldUpdate) (success) -> Void in
dispatch_async(dispatch_get_main_queue(), () -> Void in
if success
self.navigationController?.popViewControllerAnimated(true)
else
let alertController = UIAlertController(title: “NotesDB”, message: “An error occurred and the observe couldn’t be saved.”, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: “OK”, model: UIAlertActionStyle.Default, handler: (motion) -> Void in
))
self.presentViewController(alertController, animated: true, completion: nil)
)
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
func saveNote() { ...
let shouldUpdate = (editedNoteID == nil) ? false : true
observe.saveNote(shouldUpdate) (success) –> Void in dispatch_async(dispatch_get_main_queue(), () –> Void in if success self.navigationController?.popViewControllerAnimated(true)
else let alertController = UIAlertController(title: “NotesDB”, message: “An error occurred and the observe couldn’t be saved.”, preferredStyle: UIAlertControllerStyle.Alert) alertController.addAction(UIAlertAction(title: “OK”, model: UIAlertActionStyle.Default, handler: (motion) –> Void in
)) self.presentViewController(alertController, animated: true, completion: nil)
)
} |
Discover the shouldUpdate
variable within the above implementation. It will get the correct worth relying on whether or not the editedNoteID
property is nil or not, that means whether or not the observe is being up to date or not.
At this level you may run the app and attempt to save new notes. Should you went step-by-step all alongside the best way up up to now, you then’ll be capable of save your notes with none issues.
Loading and Itemizing Notes
With the creation and saving of latest notes being functioning, we will transfer on and make our app able to loading saved notes from the database. The loaded notes are supposed to be listed within the NoteListViewController
class. Nonetheless, earlier than we begin working on this class, let’s create first a brand new technique within the Be aware.swift
file for loading our knowledge.
if let error = consequence.error
print(error)
completionHandler(notes: nil)
func loadAllNotes(completionHandler: (notes: [Note]!) –> Void) database.asyncObjectsForType(Be aware.self) (consequence) –> Void in if let notes = consequence.worth completionHandler(notes: notes)
if let error = consequence.error print(error) completionHandler(notes: nil)
|
The SwiftyDB technique that performs the precise loading is the asyncObjectsForType(...)
, and it really works asynchronously. The consequence will comprise both an error, or a set with observe objects (an array) loaded from the database. Within the first case, we name the completion handler passing the nil worth in order to point to the caller that there was an issue whereas loading the info. Within the second case, we cross the Be aware
objects to the completion handler so we will use them out of this technique.
Let’s head to the NoteListViewController.swift
file now. Initially, we should declare an array that can comprise Be aware
objects (these loaded from the database). It’s going to be the datasource of our tableview (clearly). So, on the high of the category add the next line:
In addition to that, initialize a brand new Be aware
object as properly, so we will use the loadAllNotes(...)
technique we created earlier:
Time to jot down a extremely easy new technique that can name the one above and cargo all saved objects from the database to the notes
array:
)
func loadNotes() observe.loadAllNotes (notes) –> Void in dispatch_async(dispatch_get_main_queue(), () –> Void in if notes != nil self.notes = notes self.tblNotes.reloadData()
)
|
Discover that in any case notes get loaded we use the primary thread to reload the tableview. Previous to that after all we maintain them to the notes
array.
The above two strategies are all we want for getting the saved notes from the database. That straightforward! Don’t overlook although that the loadNotes()
should be referred to as someplace, and it will occur within the viewDidLoad()
technique:
loadNotes()
override func viewDidLoad() ...
loadNotes()
|
Loading the notes isn’t sufficient, as we should use them as soon as they’re fetched. So, let’s begin updating the tableview strategies, beginning by the whole variety of rows:
func tableView(tableView: UITableView, numberOfRowsInSection part: Int) –> Int return notes.rely
|
Subsequent, let’s show some observe knowledge to the tableview. To be particular, we’ll show the title, and the creation and modification dates for every observe:
let currentNote = notes[indexPath.row]
cell.lblTitle.textual content = currentNote.title!
cell.lblCreatedDate.textual content = “Created: (Helper.convertTimestampToDateString(currentNote.creationDate!))”
cell.lblModifiedDate.textual content = “Modified: (Helper.convertTimestampToDateString(currentNote.modificationDate!))”
return cell
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) –> UITableViewCell let cell = tableView.dequeueReusableCellWithIdentifier(“idCellNote”, forIndexPath: indexPath) as! NoteCell
let currentNote = notes[indexPath.row]
cell.lblTitle.textual content = currentNote.title! cell.lblCreatedDate.textual content = “Created: (Helper.convertTimestampToDateString(currentNote.creationDate!))” cell.lblModifiedDate.textual content = “Modified: (Helper.convertTimestampToDateString(currentNote.modificationDate!))”
return cell
|
Should you run the app now, all notes that you’ve got created to date shall be listed to the tableview.
An Different Approach To Fetch Information
Only a bit earlier we used the asyncObjectsForType(...)
technique of the SwiftyDB library to load our notes from the database. This technique returns an array of objects (in our case Be aware
objects) as you’ve seen, and I think about this to be fairly helpful. Nonetheless, it’s not at all times that helpful to retrieve objects from the database; there are instances the place fetching an array with the precise knowledge values could be extra handy.
SwiftyDB can assist you on that, because it supplies another approach for retrieving knowledge. There’s a technique referred to as asyncDataForType(...)
(or dataForType(...)
if you wish to make synchronous operations), and it returns a colletion of dictionaries on this type: [[String: SQLiteValue]]
(the place SQLiteValue
is any of the allowed datatypes).
You could find extra here and here. I depart it for you as an train to counterpoint the Be aware
class and cargo easy knowledge as properly, as a substitute of objects solely.
Updating A Be aware
One of many options we wish our demo app to have is the aptitude to edit and replace an current observe. In different phrases, we have to current the EditNoteViewController
with the small print of a observe that’s being chosen just by tapping to the respective cell, and retailer to the database its modified knowledge as soon as it will get saved once more.
Beginning within the NoteListViewController.swift
file, we want a brand new property for storing the ID of the chosen observe, so go to the highest of the category and add the next line:
Now, let’s implement the subsequent UITableViewDelegate
technique, the place we discover the noteID
worth primarily based on the chosen row, after which we carry out the segue to point out the EditNoteViewController
:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) idOfNoteToEdit = notes[indexPath.row].noteID as Int performSegueWithIdentifier(“idSegueEditNote”, sender: self)
|
Within the prepareForSegue(...)
technique let’s cross the worth of the idOfNoteToEdit
to the subsequent view controller:
if idOfNoteToEdit != nil
editNoteViewController.editedNoteID = idOfNoteToEdit
idOfNoteToEdit = nil
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) if let identifier = segue.identifier if identifier == “idSegueEditNote” let editNoteViewController = segue.destinationViewController as! EditNoteViewController
if idOfNoteToEdit != nil editNoteViewController.editedNoteID = idOfNoteToEdit idOfNoteToEdit = nil
|
The half job has been finished now. Earlier than we swap to the EditNoteViewController
class and proceed there, let’s make one other fast detour by paying a go to to the Be aware
class to be able to implement a easy new technique that can retrieve only a single observe utilizing the ID worth that’s being given with. Right here’s the implementation:
if singleNote.imagesData != nil
singleNote.photos = NSKeyedUnarchiver.unarchiveObjectWithData(singleNote.imagesData) as? [ImageDescriptor]
completionHandler(observe: singleNote)
if let error = consequence.error
print(error)
completionHandler(observe: nil)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func loadSingleNoteWithID(id: Int, completionHandler: (observe: Be aware!) –> Void) database.asyncObjectsForType(Be aware.self, matchingFilter: Filter.equal(“noteID”, worth: id)) (consequence) –> Void in if let notes = consequence.worth let singleNote = notes[0]
if singleNote.imagesData != nil singleNote.photos = NSKeyedUnarchiver.unarchiveObjectWithData(singleNote.imagesData) as? [ImageDescriptor]
completionHandler(observe: singleNote)
if let error = consequence.error print(error) completionHandler(observe: nil)
|
The brand new factor right here is that for first time we use a filter
to be able to apply limitations to the outcomes that shall be returned. Utilizing the equal(...)
class technique of the Filter class is only a approach to set the filter we wish. Don’t miss to go to this hyperlink to see extra methods for making use of filters when fetching knowledge or objects from the database.
By utilizing the filter within the style proven above, we really ask from SwiftyDB to load solely these information the place the noteID
equals to the worth given as a parameter to the strategy. In fact, only one report shall be returned as a result of we all know that this area is the first key and there’s no approach to have multiple information with the identical key.
The discovered outcomes shall be returned as an array of Be aware
objects, so it’s essential to get the primary (and solely) merchandise from that assortment. After that, we positively need to convert the picture knowledge (if exists) to an array of ImageDescriptor
objects, and assign it to the photos
property. That’s vital, as a result of if we skip it any photos initially added to the loaded observe received’t be proven.
On the finish we name the completion handler in accordance with whether or not the observe fetching was profitable or not. Within the first case we cross the fetched object to the completion handler so it may be utilized by the caller, whereas within the second case we simply cross nil as there’s no object.
Now we will head to the EditNoteViewController.swift
file, and declare and initialize on the similar time a brand new Be aware
property to the category:
This object shall be used firstly for calling the brand new technique we carried out above, after which to comprise the loaded knowledge from the database.
We’re nearly to load the observe specified by the editedNoteID
property utilizing the loadSingleNote(...)
technique. For our goal, we’re going to outline the viewWillAppear(_:)
technique, and in there we’ll develop our logic.
As you will notice within the following code snippet, all values shall be populated correctly as soon as the loadSingleNoteWithID(...)
technique returns the fetched observe by way of the completion handler. That signifies that we begin setting the observe title, physique, textual content colour, font, and so forth, however not solely. If there are photos included to the observe, we’ll be creating photos views for every one, utilizing after all the frames specified within the ImageDescriptor
objects.
if editedNoteID != nil {
editedNote.loadSingleNoteWithID(editedNoteID, completionHandler: { (observe) -> Void in
dispatch_async(dispatch_get_main_queue(), () -> Void in
if observe != nil
self.txtTitle.textual content = observe.title!
self.tvNote.textual content = observe.textual content!
self.tvNote.textColor = NSKeyedUnarchiver.unarchiveObjectWithData(observe.textColor!) as? UIColor
self.tvNote.font = UIFont(identify: observe.fontName!, measurement: observe.fontSize as CGFloat)
if let photos = observe.photos
for picture in photos
let imageView = PanningImageView(body: picture.frameData.toCGRect())
imageView.picture = Helper.loadNoteImageWithName(picture.imageName)
imageView.delegate = self
self.tvNote.addSubview(imageView)
self.imageViews.append(imageView)
self.setExclusionPathForImageView(imageView)
self.editedNote = observe
self.currentFontName = observe.fontName!
self.currentFontSize = observe.fontSize as CGFloat
)
})
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
override func viewWillAppear(animated: Bool) { tremendous.viewWillAppear(animated)
if editedNoteID != nil { editedNote.loadSingleNoteWithID(editedNoteID, completionHandler: { (observe) –> Void in dispatch_async(dispatch_get_main_queue(), () –> Void in if observe != nil self.txtTitle.textual content = observe.title! self.tvNote.textual content = observe.textual content! self.tvNote.textColor = NSKeyedUnarchiver.unarchiveObjectWithData(observe.textColor!) as? UIColor self.tvNote.font = UIFont(identify: observe.fontName!, measurement: observe.fontSize as CGFloat)
if let photos = observe.photos for picture in photos let imageView = PanningImageView(body: picture.frameData.toCGRect()) imageView.picture = Helper.loadNoteImageWithName(picture.imageName) imageView.delegate = self self.tvNote.addSubview(imageView) self.imageViews.append(imageView) self.setExclusionPathForImageView(imageView)
self.editedNote = observe
self.currentFontName = observe.fontName! self.currentFontSize = observe.fontSize as CGFloat
) }) } } |
Don’t miss that after having populated all values, we assign the observe
to the editedNote
object, so we will use it afterward.
There’s one final step required: To replace the saveNote()
technique, so when a observe is being up to date to keep away from creating a brand new Be aware
object, and to not set a brand new major key and creation date.
So, discover these three strains (contained in the saveNote()
technique):
let observe = Be aware() observe.noteID = Int(NSDate().timeIntervalSince1970) observe.creationDate = NSDate() |
… and exchange them with the next snippet:
if editedNoteID == nil
observe.noteID = Int(NSDate().timeIntervalSince1970)
observe.creationDate = NSDate()
let observe = (editedNoteID == nil) ? Be aware() : editedNote
if editedNoteID == nil observe.noteID = Int(NSDate().timeIntervalSince1970) observe.creationDate = NSDate()
|
The remaining a part of the strategy stays as is (not less than for now).
Updating the Notes Listing
Should you examined the app up up to now, you then would have positively realized that the notes record isn’t up to date if you create a brand new observe or if you replace an current one. That’s affordable to occur as a result of the app isn’t able to that but, nonetheless on this half we’re about to repair this undesirable behaviour.
As you might guess, we’re going to use the Delegation sample
to inform the NoteListViewController
class about adjustments made to notes within the EditNoteViewController
. Our place to begin is to create a brand new protocol within the EditNoteViewController
with two required strategies, these proven under:
func didUpdateNote(noteID: Int)
protocol EditNoteViewControllerDelegate func didCreateNewNote(noteID: Int)
func didUpdateNote(noteID: Int)
|
In each instances we offer to the delegate strategies the ID worth of the brand new or edited observe. Now, go to EditNoteViewController
class and add the next property:
var delegate: EditNoteViewControllerDelegate! |
Lastly, let’s revisit one final time the saveNote()
technique. At first find the subsequent line contained in the completion handler block:
self.navigationController?.popViewControllerAnimated(true) |
Exchange that single line with the next bunch of code:
else
self.delegate.didUpdateNote(self.editedNoteID)
self.navigationController?.popViewControllerAnimated(true)
if self.delegate != nil if !shouldUpdate self.delegate.didCreateNewNote(observe.noteID as Int)
else self.delegate.didUpdateNote(self.editedNoteID)
self.navigationController?.popViewControllerAnimated(true) |
The right delegate perform shall be referred to as each time {that a} new observe is created or an current one is being up to date any longer. However what we simply did consists of the half job solely. Let’s swap to the NoteListViewController.swift
file, and initially let’s undertake the brand new protocol to the header line of the category:
class NoteListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, EditNoteViewControllerDelegate ...
|
Subsequent, within the prepareForSegue(...)
technique let’s make this class the delegate of the EditNoteViewController
. Proper under the let editNoteViewController = segue.destinationViewController as! EditNoteViewController
line add the subsequent one as proven to this snippet:
editNoteViewController.delegate = self // Add this line.
…
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) if let identifier = segue.identifier if identifier == “idSegueEditNote” let editNoteViewController = segue.destinationViewController as! EditNoteViewController
editNoteViewController.delegate = self // Add this line.
...
|
Fairly good, because the many of the job has been finished. What we’re nonetheless lacking is the implementation of the 2 delegate strategies. First, let’s deal with the scenario the place a brand new observe has been created:
)
func didCreateNewNote(noteID: Int) observe.loadSingleNoteWithID(noteID) (observe) –> Void in dispatch_async(dispatch_get_main_queue(), () –> Void in if observe != nil self.notes.append(observe) self.tblNotes.reloadData()
)
|
As you see, we simply fetch from the database the thing specified by the noteID
parameter worth, and (if exists) we append it to the notes
array and reload the tableview.
Let’s see now the subsequent one:
for i in 0..<notes.rely
if notes[i].noteID == noteID
indexOfEditedNote = i
break
if indexOfEditedNote != nil
observe.loadSingleNoteWithID(noteID, completionHandler: (observe) -> Void in
if observe != nil
self.notes[indexOfEditedNote] = observe
self.tblNotes.reloadData()
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
func didUpdateNote(noteID: Int) var indexOfEditedNote: Int!
for i in 0..<notes.rely if notes[i].noteID == noteID indexOfEditedNote = i break
if indexOfEditedNote != nil observe.loadSingleNoteWithID(noteID, completionHandler: (observe) –> Void in if observe != nil self.notes[indexOfEditedNote] = observe self.tblNotes.reloadData()
)
|
On this case we first discover the index of the up to date observe within the notes
assortment. As soon as that occurs, we load the up to date observe from the database and we exchange the previous object with the brand new one. By refreshing the tableview, the brand new modification date of the up to date observe shall be proven immediately.
Deleting Information
The final main function that’s nonetheless lacking from our demo app is the observe deletion. It’s simple to know that we want one final technique carried out within the Be aware
class that shall be referred to as evey time we wish to delete a observe, so open the Be aware.swift
file.
The one new factor on this half is the SwiftyDB technique that performs the precise deletion from the database, as you will notice within the following implementation. Like earlier than, that is another operation executed asynchronously, and we’ve a completion handler to name as soon as the execution is over once more. Lastly, there’s a filter to specify the row that needs to be deleted from the database.
database.asyncDeleteObjectsForType(Be aware.self, matchingFilter: filter) (consequence) -> Void in
if let deleteOK = consequence.worth
completionHandler(success: deleteOK)
if let error = consequence.error
print(error)
completionHandler(success: false)
func deleteNote(completionHandler: (success: Bool) –> Void) let filter = Filter.equal(“noteID”, worth: noteID)
database.asyncDeleteObjectsForType(Be aware.self, matchingFilter: filter) (consequence) –> Void in if let deleteOK = consequence.worth completionHandler(success: deleteOK)
if let error = consequence.error print(error) completionHandler(success: false)
|
Let’s open the NoteListViewController.swift
now, and let’s outline the subsequent UITableViewDataSource
technique:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) if editingStyle == UITableViewCellEditingStyle.Delete
|
By having added the above technique to our code, every time you swipe in direction of left on a observe cell the default Delete
button will seem to the suitable facet. Furthermore, the code that shall be executed when the Delete button is tapped is the one which shall be outlined within the physique of the if
assertion above. Let’s accomplish that:
noteToDelete.deleteNote( (success) -> Void in
dispatch_async(dispatch_get_main_queue(), () -> Void in
if success
self.notes.removeAtIndex(indexPath.row)
self.tblNotes.reloadData()
)
)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { if editingStyle == UITableViewCellEditingStyle.Delete let noteToDelete = notes[indexPath.row]
noteToDelete.deleteNote( (success) –> Void in dispatch_async(dispatch_get_main_queue(), () –> Void in if success self.notes.removeAtIndex(indexPath.row) self.tblNotes.reloadData()
) )
} |
At first, we discover the correct observe object matching to the chosen cell within the notes assortment. Subsequent, we name our new technique within the Be aware
class to delete it, and if that operation is profitable we take away the Be aware
object from the notes
array and reload the tableview so we replace the UI.
It was simply that!
And What About Sorting?
Probably you’re questioning how we will kind our knowledge whereas fetching them from the database. Sorting is kind of helpful, as it may be primarily based on a number of fields, to be carried out in ascending or descending order and finally change the order of the returned knowledge. For instance, we might kind our notes in a approach that the newest modified notes seem to the highest.
Sadly, SwiftyDB doesn’t assist knowledge sorting on the time of penning this tutorial. This can be a drawback certainly, however there’s an answer: To manually kind the info if you want so. To exhibit this, let’s create one final technique within the NoteListViewController.swift
file referred to as sortNotes()
. On this one we’ll use the Swift’s default kind()
perform:
return modificationDate1 > modificationDate2
)
func sortNotes() notes = notes.kind( (note1, note2) –> Bool in let modificationDate1 = note1.modificationDate.timeIntervalSinceReferenceDate let modificationDate2 = note2.modificationDate.timeIntervalSinceReferenceDate
return modificationDate1 > modificationDate2 )
|
Since NSDate
objects can’t be in contrast immediately, we convert them to timestamp values (double values) first. Then we carry out the comparability and we return the consequence. The above code results in observe sorting the place the newest modified notes are within the first positions of the notes
array.
The above technique should be referred to as each time the notes
array will get modified. First, let’s replace the loadNotes
technique as proven subsequent:
self.sortNotes() // Add this line to kind notes.
self.tblNotes.reloadData()
)
func loadNotes() observe.loadAllNotes (notes) –> Void in dispatch_async(dispatch_get_main_queue(), () –> Void in if notes != nil self.notes = notes
self.sortNotes() // Add this line to kind notes.
self.tblNotes.reloadData()
)
|
Then, we should do the identical to the 2 following delegate strategies:
self.sortNotes() // Add this line to kind notes.
self.tblNotes.reloadData()
)
func didCreateNewNote(noteID: Int) observe.loadSingleNoteWithID(noteID) (observe) –> Void in dispatch_async(dispatch_get_main_queue(), () –> Void in if observe != nil self.notes.append(observe)
self.sortNotes() // Add this line to kind notes.
self.tblNotes.reloadData()
)
|
if indexOfEditedNote != nil
observe.loadSingleNoteWithID(noteID, completionHandler: (observe) -> Void in
if observe != nil
self.notes[indexOfEditedNote] = observe
self.sortNotes() // Add this line to kind notes.
self.tblNotes.reloadData()
)
func didUpdateNote(noteID: Int) ...
if indexOfEditedNote != nil observe.loadSingleNoteWithID(noteID, completionHandler: (observe) –> Void in if observe != nil self.notes[indexOfEditedNote] = observe
self.sortNotes() // Add this line to kind notes.
self.tblNotes.reloadData()
)
|
By working the app once more now, you’ll see all notes listed on the tableview primarily based on their modification date.
Abstract
Undoubtably, SwiftyDB is a superb software that can be utilized in a wide range of purposes with out a lot effort. It’s actually quick and dependable to what’s made to do, and we will all agree that may cowl a number of wants when a database should be utilized in our apps. On this demo tutorial we went by way of the fundamentals of this library, however that is what you just about have to know. In fact, there’s at all times the official documentation you may confer with for additional help. In our instance at present, and for the sake of the tutorial, we created a database with one desk solely matching to the Be aware
class. In real-world apps although, you may have as many tables as you need, so long as you create the respective fashions in code (let’s say the respective courses). Personally, I’d positively use SwiftyDB in my initiatives, and as a matter of truth, I’m planning to take action. In any case, now you recognize about it, you’ve seen the way it works and the way it may be used. It’s as much as you to determine if that is another software in your toolbox or not. Anyway, I hope the time you spent studying isn’t wasted, and also you’ve realized one other new factor, or two. And till our subsequent tutorial comes, have all a beautiful time!
To your reference, you may download the full project on GitHub.