Why are the results of PDFKit .beginFindStrings nil? - ios

Self-teaching novice here.
My end goal:
iOS/Mac app that loads a directory of PDFs, searches each for an array of strings, and lists which PDFs contain which strings where.
Problem in prototyping for only one PDF:
I receive a perplexing nil from loading a chosen PDF, running .beginFindStrings(["and", "the"], withOptions: .caseInsensitive) and waiting for the Notification .PDFDocumentDidEndFind to check [PDFSelection] .
That shouldn't be. Memory shows the PDF is loaded. Am I doing something wrong with threads? I think I've followed the async advice here: PDFKit background search
Code
import UIKit
import MobileCoreServices
import PDFKit
class ViewController: UIViewController, UIDocumentPickerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
var matchesFound: PDFSelection?
#IBOutlet weak var resultsLabel: UILabel!
#IBAction func importPDF(_ sender: Any) {
let picker = UIDocumentPickerViewController(documentTypes: [kUTTypePDF as String], in: .import)
picker.delegate = self
picker.allowsMultipleSelection = false
self.present(picker, animated: true)
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard urls.count == 1 else {return}
let data = try! Data(contentsOf: urls[0])
let subjectPDF = PDFDocument.init(data: data)
guard subjectPDF!.isLocked == false else {return}
subjectPDF!.beginFindStrings(["the", "and"], withOptions: .caseInsensitive)
NotificationCenter.default.addObserver(self, selector: #selector(onDidFindMatch(_:)), name: Notification.Name.PDFDocumentDidEndFind, object: nil)
}
#objc func onDidFindMatch(_ notification: Notification) {
resultsLabel.text = "\(String(describing: matchesFound?.string))"
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
dismiss(animated: true, completion: nil)
}
}
Code with Markup
import UIKit
import MobileCoreServices
import PDFKit
class ViewController: UIViewController, UIDocumentPickerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
//Array of PDFSelection search results
var matchesFound: PDFSelection?
//Temporary display for search result strings
#IBOutlet weak var resultsLabel: UILabel!
//Choose a PDF to import, temporarily limited to one
#IBAction func importPDF(_ sender: Any) {
let picker = UIDocumentPickerViewController(documentTypes: [kUTTypePDF as String], in: .import)
picker.delegate = self
picker.allowsMultipleSelection = false
self.present(picker, animated: true)
}
//Load the picked PDF as subjectPDF, if unlocked
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard urls.count == 1 else {return}
let data = try! Data(contentsOf: urls[0])
let subjectPDF = PDFDocument.init(data: data)
guard subjectPDF!.isLocked == false else {return}
//Find temporary array of strings
subjectPDF!.beginFindStrings(["the", "and"], withOptions: .caseInsensitive)
//Trigger results readout upon search competion
NotificationCenter.default.addObserver(self, selector: #selector(onDidFindMatch(_:)), name: Notification.Name.PDFDocumentDidEndFind, object: nil)
}
//Readout found strings to temporary label
#objc func onDidFindMatch(_ notification: Notification) {
resultsLabel.text = "\(String(describing: matchesFound?.string))"
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
dismiss(animated: true, completion: nil)
}
}

The question was asked 7 months ago though, I hope you already found the solution.
Anyway the solution for your problem:
1- Move the line below to the viewDidLoad(), because you are adding an observer after triggering the beginFindString() method.
NotificationCenter.default.addObserver(self, selector: #selector(onDidFindMatch(_:)), name: Notification.Name.PDFDocumentDidEndFind, object: nil)
2- You are never assigning any value to matchesFound variable, so it's always nil.
3- To get the matches from beginFindString method, you need to add an observer for PDFDocumentDidFindMatch and get the data from userInfo instead of PDFDocumentDidEndFind.
PDFDocumentDidEndFind observer will be called when searching has been finished, you can use this observer for removing you loading view for instance.
Here is a sample code of the correct implementation:
var matchesFound = [PDFSelection]()
// MARK: Life cycle
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(didFindMatch(_:)), name: NSNotification.Name.PDFDocumentDidFindMatch, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// This method will get called every-time a match has been found.
#objc private func didFindMatch(_ sender: Notification) {
guard let selection = sender.userInfo?["PDFDocumentFoundSelection"] as? PDFSelection else { return }
self.matchesFound.append(selection)
}

Related

NSNotification not observing or posting data

I am trying to learn how to use NSNotification for a project I am working on, and since I have never used it before, I am first trying to learn how to use it first, however; every time I try to follow a youtube tutorial or a tutorial found online my code doesn't seem to be working. Also, when trying to debug the issue, it is showing the observer part of the code isn't going inside the #obj c function. Below is my code showing how it is being used to post and observe a notification.
extension Notification.Name {
static let notifyId = Notification.Name("NotifyTest")
}
ViewController.swift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
var test: ObserverObj = ObserverObj(observerLblText: "Observing")
#IBAction func notifyObserver(_ sender: Any) {
let vc = storyboard?.instantiateViewController(identifier: "ObserverVC")
vc?.modalPresentationStyle = .fullScreen
guard let vcL = vc else {
return
}
NotificationCenter.default.post(name: .notifyId, object: nil)
self.navigationController?.pushViewController(vcL, animated: true)
}
}
NotificationTestViewController.swift
import UIKit
class NotificationTestViewController: UIViewController {
#IBOutlet weak var observerLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(observingFunc(notification:)), name: .notifyId, object: nil)
// Do any additional setup after loading the view.
}
#objc func observingFunc(notification: Notification) {
observerLbl.text = "notifying"//text.test.observerLblText
}
Can someone help me and let me know where I am going wrong as I've been trying for 2 days.
The notification is sent before the observer is added, that means viewDidLoad in the destination controller is executed after the post line in the source view controller.
Possible solutions are :
Override init(coder in the destination controller and add the observer there.
required init?(coder: NSCoder) {
super.init(coder: coder)
NotificationCenter.default.addObserver(self, selector: #selector(observingFunc), name: .notifyId, object: nil)
}
If init(coder is not called override init()
Add the observer before posting the notification (this solution is only for education purpose)
#IBAction func notifyObserver(_ sender: Any) {
guard let vcL = storyboard?.instantiateViewController(identifier: "ObserverVC") as? NotificationTestViewController else { return }
vcL.modalPresentationStyle = .fullScreen
NotificationCenter.default.addObserver(vcL, selector: #selector(NotificationTestViewController.observingFunc), name: .notifyId, object: nil)
self.navigationController?.pushViewController(vcL, animated: true)
NotificationCenter.default.post(name: .notifyId, object: nil)
}
However in practice you are strongly discouraged from sending a notification to a destination you have the reference to.
The reason is that when you send out a notification, your NotificationTestViewController has not yet called the viewdidload method
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
var test: ObserverObj = ObserverObj(observerLblText: "Observing")
#IBAction func notifyObserver(_ sender: Any) {
let vc = storyboard?.instantiateViewController(identifier: "ObserverVC")
vc?.modalPresentationStyle = .fullScreen
guard let vcL = vc else {
return
}
self.navigationController?.pushViewController(vcL, animated: true)
NotificationCenter.default.post(name: .notifyId, object: nil)
}
}

Refresh Storyboard viewcontroller using swift iOS

Im having button in all viewcontrollers to change language
LanguageViewController.swift
class LanguageViewController: UIViewController {
#IBAction func actionChange(_ sender: Any) {
L102Language.currentAppleLanguage()
L102Language.setAppleLAnguageTo(lang: "en")
// below code to refresh storyboard
self.viewDidLoad()
}
}
L102Language.swift
class func currentAppleLanguage() -> String{
let userdef = UserDefaults.standard
let langArray = userdef.object(forKey: APPLE_LANGUAGE_KEY) as! NSArray
let current = langArray.firstObject as! String
let endIndex = current.startIndex
let currentWithoutLocale = current.substring(to: current.index(endIndex, offsetBy: 2))
return currentWithoutLocale
}
/// set #lang to be the first in Applelanguages list
class func setAppleLAnguageTo(lang: String) {
let userdef = UserDefaults.standard
userdef.set([lang,currentAppleLanguage()], forKey: APPLE_LANGUAGE_KEY)
userdef.synchronize()
}
I inherited LanguageViewController in all my FirstViewCOntroller, SecondController as below
class FirstViewController: LanguageViewController {
}
class SecondController: LanguageViewController {
}
If I call self.viewDidLoad() it fails to change language from view defined in storyboard. How to reload storyboard, so that the language should change in all viewcontroller,if any button from any viewcontroller is clicked? Thanks!
You can use NotificationCenter for reloading the view controllers content, this will also reload the content of view controllers that are not visible.
extension Notification.Name {
static let didChangeLanguage = Notification.Name("didChangeLanguage")
}
override func viewDidLoad() {
//Add a listener
NotificationCenter.default.addObserver(self, selector: #selector(onDidChangeLanguage(_:)), name: .didChangeLanguage, object: nil)
}
#IBAction func actionChange(_ sender: Any) {
L102Language.currentAppleLanguage()
L102Language.setAppleLAnguageTo(lang: "en")
// Notify about the change.
NotificationCenter.default.post(name: .didChangeLanguage, object: self, userInfo: nil)
}
#objc func onDidChangeLanguage(_ notification:Notification) {
// reload content using selected language.
}
Correct me if I'm wrong. but I think you don't need to reload all view controllers. you just need to update them when they get displayed, view controllers are behind the presented one are not visible for the user.
for doing that you can do something like this:
var currentLanguage = ""
override func viewDidLoad() {
currentLanguage = currentAppleLanguage()
loadContentForLanguage(currentLanguage)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// this will be executed every time this sceen gets display
if currentLanguage != currentAppleLanguage() {
currentLanguage = currentAppleLanguage()
loadContentForLanguage(currentLanguage)
}
}
func loadContentForLanguage(_ currentLanguage: String) {
//here it goes whatever you currently have in viewDidLoad
}
My apologies if this does not compile, my swift is really rusty.

NotificationCenter to pass data in Swift

I am working on a test project in Swift 3. I am trying to pass textField string from one class to another class using NotificationCenter. I am trying to workout the answer from this link: pass NSString variable to other class with NSNotification and how to pass multiple values with a notification in swift
I tried few answers from the above link but nothing worked.
My code:
//First VC
import UIKit
extension Notification.Name {
public static let myNotificationKey = Notification.Name(rawValue: "myNotificationKey")
}
class ViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sendData(_ sender: AnyObject) {
let userInfo = [ "text" : textView.text ]
NotificationCenter.default.post(name: .myNotificationKey, object: nil, userInfo: userInfo)
}
}
//SecondVC
import Foundation
import UIKit
class viewTwo: UIViewController {
#IBOutlet weak var result: UILabel!
override func viewDidLoad() {
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.notificationReceived(_:)), name: .myNotificationKey, object: nil)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: .myNotificationKey, object: nil)
}
func notificationReceived(_ notification: Notification) {
guard let text = notification.userInfo?["text"] as? String else { return }
print ("text: \(text)")
result.text = text
}
}
I am not sure whats wrong with the code. Above, code originally marked as a answered which, I found from the first link. Code been converted to Swift.
Don't use object parameter to pass data. It is meant to filter notifications with the same name, but from a particular object. So if you pass some object when you post a notification and another object when you addObserver, you won't receive it. If you pass nil, you basically turn off this filter.
You should use userInfo parameter instead.
First, it is better to define notification's name as extension for Notification.Name. This approach is much safer and more readable:
extension Notification.Name {
public static let myNotificationKey = Notification.Name(rawValue: "myNotificationKey")
}
Post notification:
let userInfo = [ "text" : text ]
NotificationCenter.default.post(name: .myNotificationKey, object: nil, userInfo: userInfo)
Subscribe:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.notificationReceived(_:)), name: .myNotificationKey, object: nil)
}
Unsubscribe:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: .myNotificationKey, object: nil)
}
Method to be called:
func notificationReceived(_ notification: Notification) {
guard let text = notification.userInfo?["text"] as? String else { return }
print ("text: \(text)")
}
Pass text using userInfo which is a optional Dictionary of type [AnyHashable:Any]? in Swift 3.0 and it is [NSObject : AnyObject]? in swift 2.0
#IBAction func sendData(_ sender: UIButton) {
// post a notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: ["text": textValue.text])
print(textValue) // textValue printing
}
in viewDidLoad
// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(self. incomingNotification(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
and in incomingNotification
func incomingNotification(_ notification: Notification) {
if let text = notification.userInfo?["text"] as? String {
print(text)
// do something with your text
}
}
In your sendData method pass textField.text into Notification object and in your incomingNotification do this:
guard let theString = notification.object as? String else {
print("something went wrong")
return
}
resultLabel.text = theString
You can also use blocks to pass data between controllers.
Use dispatchQueue because your notification is posting before your view load. Therefore just give delay in your notification Post.
#IBAction func sendData(_ sender: AnyObject) {
let userInfo = [ "text" : textView.text ]
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
NotificationCenter.default.post(name: .myNotificationKey, object: nil, userInfo: userInfo) }
}
Just use NotificationCenter to send and receive the notification that state changed. Pass the data through some a data model such as an ObservableObject (particularly if you're bridging between SwiftUI and UIKit). Here's are a couple of extension that make it pretty simple for lightweight inter-component signaling without the cumbersome forgettable semantics of NotificationCenter. (Of course you define your own Notification.Name constants to be meaningful to your purpose).
extension Notification.Name {
static let startEditingTitle = Notification.Name("startEditingTitle")
static let stopEditingTitle = Notification.Name("stopEditingTitle")
}
extension NotificationCenter {
static func wait(_ name : Notification.Name) async {
for await _ in NotificationCenter.default.notifications(named: name) {
break;
}
}
static func post(_ name : Notification.Name) {
NotificationCenter.default.post(name: name, object: nil)
}
#discardableResult static func postProcessing(_ name: Notification.Name, using block: #escaping (Notification) -> Void) -> NSObjectProtocol {
NotificationCenter.default.addObserver(forName: name, object: nil, queue: OperationQueue.main, using: block)
}
}
To post a notification is as simple as:
NotificationCenter.post(.startEditingTitle)
And to receive the notification elsewhere:
NotificationCenter.postProcessing(.startEditingTitle) (_ in {
print("Started editing title")
}
Or to just wait for the notification instead of asynchronously handling it:
NotificationCenter.wait(.startEditingTitle)

detect up volume change swift

I'm having problems detecting when someone presses up or down volume button. For the moment I just play a file but I want to know when the user presses the button to show an alert when the volume changes. I'm developing in Swift and I'm using AVFoundation to create this player. For the moment I can't find something that works in Swift. I'm very new to this language.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var backgroundMusicPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
playBackgroundMusic("IronBacon.mp3")
}
func playBackgroundMusic(filename:String){
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
print(url)
guard let newUrl = url else{
print("couldn't find file: \(filename)")
return
}
do{
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newUrl)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
}catch let error as NSError{
print(error.description)
}
}
#IBAction func playPauseAction(sender: UIButton) {
sender.selected = !sender.selected
if sender.selected {
backgroundMusicPlayer.play()
} else {
backgroundMusicPlayer.pause()
}
}
func ShowAlert(title: String, message: String, dismiss: String) {
let alertController = UIAlertController(title: title, message:
message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: dismiss, style: UIAlertActionStyle.Default,handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
}
func volumeUp(){
ShowAlert( "example", message: "example", dismiss: "close")
}
func volumeDown(){
ShowAlert( "example", message: "example", dismiss: "close")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This should do the trick.
class ViewController: UIViewController {
// MARK: Properties
let notificationCenter = NSNotificationCenter.defaultCenter()
// MARK: Lifecycle
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
notificationCenter.addObserver(self,
selector: #selector(systemVolumeDidChange),
name: "AVSystemController_SystemVolumeDidChangeNotification",
object: nil
)
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
notificationCenter.removeObserver(self)
}
// MARK: AVSystemPlayer - Notifications
func systemVolumeDidChange(notification: NSNotification) {
print(notification.userInfo?["AVSystemController_AudioVolumeNotificationParameter"] as? Float)
}
}
For some reason, the accepted answer does not work. Here is how you can do it in latest iOS versions -
func addObserver() {
NotificationCenter.default.addObserver(self,
selector: #selector(systemVolumeDidChange),
name: Notification.Name("SystemVolumeDidChange"),
object: nil)
}
func systemVolumeDidChange(notification: NSNotification) {
Log.msg("New Volume = \(notification.userInfo?["Volume"] as? Float)")
}
There are a few more fields in user info that can determine the volume change reason etc.

Add initial note

I am looking at adding an inital note to the note page within my app. this is so that when people click to the notes part there will be some detail on how to use it rather than just a big empty screen. I have no idea where to implement this though. Could you please help, below is the page where it talks about the dictionaries.
import UIKit
import MessageUI
class DetailViewController: UIViewController, MFMailComposeViewControllerDelegate, UITextViewDelegate {
#IBOutlet weak var tView: UITextView!
#IBAction func BarButton(sender: UIBarButtonItem) {
let textToShare = ""
if let myWebsite = NSURL(string: "")
{
let objectsToShare = [textToShare, myWebsite]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
}
OpenMail()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tView.text = (allNotes[currentNoteIndex] as Note).note
tView.becomeFirstResponder()
// Set controller as swipe gesture recogniser, to allow keyboard dismissal for text box
var swipe: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "dismissKeyboard")
swipe.direction = UISwipeGestureRecognizerDirection.Down
self.view.addGestureRecognizer(swipe)
self.tView.delegate = self
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if tView.text == "" {
allNotes.removeAtIndex(currentNoteIndex)
}
else {
(allNotes[currentNoteIndex] as Note).note = tView.text
}
Note.saveNotes()
noteTable?.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
// Open mail controller on screen and prepare with preset values.
let mailComposerVC = MFMailComposeViewController()
var MessageText: String!
MessageText = tView.text
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setToRecipients([""])
mailComposerVC.setSubject("")
mailComposerVC.setMessageBody(MessageText, isHTML: false)
return mailComposerVC
}
func showSendMailErrorAlert() {
// Alert user to email error
let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail. Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
sendMailErrorAlert.show()
}
// MARK: MFMailComposeViewControllerDelegate Method
func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
func OpenMail() {
//Function to open mail composer on screen
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
func dismissKeyboard() {
// Dismiss keyboard for textfield
self.tView.resignFirstResponder()
}
}
note.swift
import UIKit
var allNotes:[Note] = []
var currentNoteIndex:NSInteger = -1
var noteTable:UITableView?
let KAllNotes:String = "notes"
class Note: NSObject {
var date:String
var note:String
override init() {
date = NSDate().description
note = ""
}
func dictionary() -> NSDictionary {
return ["note":note, "date":date]
}
class func saveNotes() {
var aDictionaries:[NSDictionary] = []
for (var i:NSInteger = 0; i < allNotes.count; i++) {
aDictionaries.append(allNotes[i].dictionary())
}
NSUserDefaults.standardUserDefaults().setObject(aDictionaries, forKey: KAllNotes)
// aDictionaries.writeToFile(filePath(), atomically: true)
}
class func loadnotes() {
allNotes.removeAll(keepCapacity: true)
var defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var savedData:[NSDictionary]? = defaults.objectForKey(KAllNotes) as? [NSDictionary]
// var savedData:NSArray? = NSArray(contentsOfFile: filePath())
if let data:[NSDictionary] = savedData {
for (var i:NSInteger = 0; i < data.count; i++) {
var n:Note = Note()
n.setValuesForKeysWithDictionary(data[i] as [NSObject : AnyObject])
allNotes.append(n)
}
}
}
class func filePath() -> String {
var d:[String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if let directories:[String] = d {
var docsDirectory:String = directories[0]
var path:String = docsDirectory.stringByAppendingPathComponent("\(KAllNotes).notes")
return path;
}
return ""
}
}
Thanks in advance
Sam
Add an NSUserDefault boolean that stores whether or not the initial note should be shown, e.g. that the app has been launched for the first time. Then load an initial note accordingly. When a note is added or the initial note is deleted, then change the boolean accordingly so the initial note doesn't show up next time.
You could also initialize your database with an initial note. Not clear from your code how the notes are saved, but this approach would probably rely on the NSUserDefault approach above, except it could be done in the AppDelegate or something.
example:
let InitialSetupComplete = "InitialSetupComplete" // Note: I would define this at the top of a file
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.boolForKey(InitialSetupComplete) {
// Show initial note
}
// Later on when the note is deleted, or modified (or immediately after initial note loaded into the database, see below)
defaults.setBool(true, forKey: InitialSetupComplete)
Would be easier/cleaner just to initialize your database with the initial note in the app delegate (e.g. call within applicationDidFinishLaunching), so your view controller doesn't have to figure this out. Similar code, except you would use setBool right away after the initial note has been saved to the database. I don't know anything about your database from the question, so can't really provide a more detailed example than this. Hope this helps.

Resources