NSFetchedResultsController and Swift3 - nsfetchedresultscontroller

So before when I was in swift 2.2 I had me code basically built like this:
var frc: NSFetchedResultsController = NSFetchedResultsController()
func fetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Lists")
let sortDesc1 = NSSortDescriptor(key: "favorite", ascending: false)
let sortDesc2 = NSSortDescriptor(key: "order", ascending: true)
let sortDesc3 = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDesc1, sortDesc2, sortDesc3]
return fetchRequest
}
func getFRC() -> NSFetchedResultsController {
frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: doc, sectionNameKeyPath: "favorite", cacheName: nil)
return frc
}
I think that is how it used to look. But now with Swift 3, it looks similar but slightly different
var frc: NSFetchedResultsController<Lists>?
func fetchRequest() -> NSFetchRequest<Lists> {
let fetchRequest: NSFetchRequest<Lists> = Lists.fetchRequest() as! NSFetchRequest<Lists>
let sortDesc1 = NSSortDescriptor(key: "favorite", ascending: false)
let sortDesc2 = NSSortDescriptor(key: "order", ascending: true)
let sortDesc3 = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDesc1, sortDesc2, sortDesc3]
return fetchRequest
}
func getFRC() -> NSFetchedResultsController<Lists> {
frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "favorite" , cacheName: nil)
return frc!
}
From what I understand, a lot of these changes were to get away from using strings to identify your entity when you already have a class made for that entity. But I'm wondering if I should be changing my code in the getFRC function since it is identifying the section Key with a string still. Is this still correct, or is there a newer swift 3 way of doing this too? And I guess that question would apply to how I'm identifying my NSSortDescriptors too, those are still strings as well.

Related

call child data from firebase

Good evening,
I am trying to call data to post onto a card very similar to tinder. When I run my code, everything works and I see the print statement in the console. However the card view shows a default image with default text.
I was wondering if anyone has encountered this issue and could possibly help explain what I am doing wrong.
fileprivate func fetchUsersFromDatabase() {
Database.database().reference().child("JobPost").observeSingleEvent(of: .value, with: {(Snapshot) in
if let eachDict = Snapshot.value as? NSDictionary{
for each in eachDict{
//I think the issue is caused by the let post = poster
let post = Poster(dictionary: Snapshot.value as! [String : Any])
self.cardViewModels.append(post.toCardViewModel())
print(each.value )
}
}
self.setupDummyCards()
}, withCancel: {(Err) in
})
}
// the struct is in an extension file.
struct Poster: ProducesCardViewModel{
var jobName : String?
var price: Int?
var postName: String?
var ImageUrl1: String?
var uid: String?
init(dictionary: [String: Any]) {
self.price = dictionary["cost"] as? Int
self.jobName = dictionary["category"] as? String
self.postName = dictionary["description"] as? String ?? ""
self.ImageUrl1 = dictionary["JobImageUrl"] as? String ?? ""
self.uid = dictionary["fromId"] as? String ?? ""
}
func toCardViewModel() -> CardViewModel {
let attributedText = NSMutableAttributedString(string: jobName ?? "", attributes: [.font: UIFont.systemFont(ofSize: 32, weight: .heavy)])
let priceString = price != nil ? "\(price!)" : "N\\A"
attributedText.append(NSAttributedString(string: " \(priceString)", attributes: [.font: UIFont.systemFont(ofSize: 24, weight: .regular)]))
let jobString = jobName != nil ? jobName! : "Not available"
attributedText.append(NSAttributedString(string: "\n\(jobString)", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .regular)]))
return CardViewModel(imageNames: [ImageUrl1 ?? "" ], attributedString: attributedText, textAlignment: .left)
}
}
Example
// toCardViewModel
import UIKit
protocol ProducesCardViewModel {
func toCardViewModel() -> CardViewModel
}
class CardViewModel {
let JobimageName: [String]
let attributedString: NSAttributedString
let textAlignment: NSTextAlignment
init(imageNames: [String], attributedString: NSAttributedString, textAlignment: NSTextAlignment) {
self.JobimageName = imageNames
self.attributedString = attributedString
self.textAlignment = textAlignment
}
fileprivate var imageIndex = 0 {
didSet {
let imageName = JobimageName[imageIndex]
let image = UIImage(named: imageName)
imageIndexObserver?(imageIndex, image)
}
}
var imageIndexObserver: ((Int, UIImage?) -> ())?
func advanceToNextPhoto() {
imageIndex = min(imageIndex + 1, JobimageName.count - 1)
}
func goToPreviousPhoto() {
imageIndex = max(0, imageIndex - 1)
}
}
Thank you in advance!
// P.S I previously posted this question without lack of sufficient detail. I decided to just re post it with the quality material. I really appreciate your time.
I have figured out an answer and it was quite obvious to me. I noticed that
print(each.value )
would print the value, so I just substituted
let post = Poster(dictionary: Snapshot.value as! [String : Any])
to
let post = Poster(dictionary: each.value as! [String : Any])
and everything started to work just fine!

How to create a cached NSNumberFormatter in Swift 3?

I am wondering how to create a cached NSNumberFormatter (NumberFormatter) in Swift 3?
This is what I found via searching Gists;
extension NumberFormatter {
private static var cache:[String:NumberFormatter] = [:]
static func numberFormatterForKey(key:String, initialize:((NumberFormatter)->())? = nil) -> NumberFormatter {
if let formatter = NumberFormatter.cache[key] {
return formatter
} else {
let formatter = NumberFormatter()
NumberFormatter.cache[key] = formatter
initialize?(formatter)
return formatter
}
}
}
But I'm not sure how to use it; or if there is a better solution.
Thus, How to create a cached NSNumberFormatter in Swift 3?
I've since resolved the issue;
I create my cached nsnumberformatter like this;
struct ObjectCache {
static let currencyRateFormatter: NumberFormatter = {
var numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "en_US")
numberFormatter.numberStyle = .currency
numberFormatter.minimumFractionDigits = 0
numberFormatter.maximumFractionDigits = 0
numberFormatter.alwaysShowsDecimalSeparator = false
return numberFormatter
}()
}
Then use it like this;
self.myCashLbl.text = ObjectCache.currencyRateFormatter.string(from: NSNumber(integerLiteral: player.cash))
Thanks.

Swift 3.0 NSFetchRequest error [duplicate]

In Swift 2 the following code was working:
let request = NSFetchRequest(entityName: String)
but in Swift 3 it gives error:
Generic parameter "ResultType" could not be inferred
because NSFetchRequest is now a generic type. In their documents they wrote this:
let request: NSFetchRequest<Animal> = Animal.fetchRequest
so if my result class is for example Level how should I request correctly?
Because this not working:
let request: NSFetchRequest<Level> = Level.fetchRequest
let request: NSFetchRequest<NSFetchRequestResult> = Level.fetchRequest()
or
let request: NSFetchRequest<Level> = Level.fetchRequest()
depending which version you want.
You have to specify the generic type because otherwise the method call is ambiguous.
The first version is defined for NSManagedObject, the second version is generated automatically for every object using an extension, e.g:
extension Level {
#nonobjc class func fetchRequest() -> NSFetchRequest<Level> {
return NSFetchRequest<Level>(entityName: "Level");
}
#NSManaged var timeStamp: NSDate?
}
The whole point is to remove the usage of String constants.
I think i got it working by doing this:
let request:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Level")
at least it saves and loads data from DataBase.
But it feels like it is not a proper solution, but it works for now.
The simplest structure I found that works in 3.0 is as follows:
let request = NSFetchRequest<Country>(entityName: "Country")
where the data entity Type is Country.
When trying to create a Core Data BatchDeleteRequest, however, I found that this definition does not work and it seems that you'll need to go with the form:
let request: NSFetchRequest<NSFetchRequestResult> = Country.fetchRequest()
even though the ManagedObject and FetchRequestResult formats are supposed to be equivalent.
Here are some generic CoreData methods that might answer your question:
import Foundation
import Cocoa
func addRecord<T: NSManagedObject>(_ type : T.Type) -> T
{
let entityName = T.description()
let context = app.managedObjectContext
let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
let record = T(entity: entity!, insertInto: context)
return record
}
func recordsInTable<T: NSManagedObject>(_ type : T.Type) -> Int
{
let recs = allRecords(T.self)
return recs.count
}
func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T]
{
let context = app.managedObjectContext
let request = T.fetchRequest()
do
{
let results = try context.fetch(request)
return results as! [T]
}
catch
{
print("Error with request: \(error)")
return []
}
}
func query<T: NSManagedObject>(_ type : T.Type, search: NSPredicate?, sort: NSSortDescriptor? = nil, multiSort: [NSSortDescriptor]? = nil) -> [T]
{
let context = app.managedObjectContext
let request = T.fetchRequest()
if let predicate = search
{
request.predicate = predicate
}
if let sortDescriptors = multiSort
{
request.sortDescriptors = sortDescriptors
}
else if let sortDescriptor = sort
{
request.sortDescriptors = [sortDescriptor]
}
do
{
let results = try context.fetch(request)
return results as! [T]
}
catch
{
print("Error with request: \(error)")
return []
}
}
func deleteRecord(_ object: NSManagedObject)
{
let context = app.managedObjectContext
context.delete(object)
}
func deleteRecords<T: NSManagedObject>(_ type : T.Type, search: NSPredicate? = nil)
{
let context = app.managedObjectContext
let results = query(T.self, search: search)
for record in results
{
context.delete(record)
}
}
func saveDatabase()
{
let context = app.managedObjectContext
do
{
try context.save()
}
catch
{
print("Error saving database: \(error)")
}
}
Assuming that there is a NSManagedObject setup for Contact like this:
class Contact: NSManagedObject
{
#NSManaged var contactNo: Int
#NSManaged var contactName: String
}
These methods can be used in the following way:
let name = "John Appleseed"
let newContact = addRecord(Contact.self)
newContact.contactNo = 1
newContact.contactName = name
let contacts = query(Contact.self, search: NSPredicate(format: "contactName == %#", name))
for contact in contacts
{
print ("Contact name = \(contact.contactName), no = \(contact.contactNo)")
}
deleteRecords(Contact.self, search: NSPredicate(format: "contactName == %#", name))
recs = recordsInTable(Contact.self)
print ("Contacts table has \(recs) records")
saveDatabase()
This is the simplest way to migrate to Swift 3.0, just add <Country>
(tested and worked)
let request = NSFetchRequest<Country>(entityName: "Country")
Swift 3.0 This should work.
let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
I also had "ResultType" could not be inferred errors. They cleared once I rebuilt the data model setting each entity's Codegen to "Class Definition". I did a brief writeup with step by step instructions here:
Looking for a clear tutorial on the revised NSPersistentContainer in Xcode 8 with Swift 3
By "rebuilt" I mean that I created a new model file with new entries and attributes. A little tedious, but it worked!
What worked best for me so far was:
let request = Level.fetchRequest() as! NSFetchRequest<Level>
I had the same issue and I solved it with the following steps:
Select your xcdatamodeld file and go to the Data Model Inspector
Select your first Entity and go to Section class
Make sure that Codegen "Class Definition" is selected.
Remove all your generated Entity files. You don't need them anymore.
After doing that I had to remove/rewrite all occurences of fetchRequest as XCode seem to somehow mix up with the codegenerated version.
HTH
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func loadItemsCategory() {
let request: NSFetchRequest<Category> = Category.fetchRequest()
do {
categoryArray = try context.fetch(request)
} catch {
print(error)
}
tableView.reloadData()
}

How to get filename last photo in Swift 2.0 iOS9?

I want to get file_name (example = IMG_xxxx.jpg)
func lastphoto() {
var fetchOptions: PHFetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
var fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)
if (fetchResult.lastObject != nil) {
var lastAsset: PHAsset = fetchResult.lastObject as! PHAsset
let options:PHImageRequestOptions = PHImageRequestOptions()
options.version = PHImageRequestOptionsVersion.Current
PHImageManager.defaultManager().requestImageForAsset(lastAsset,
targetSize: self.imageView.bounds.size,
contentMode: PHImageContentMode.AspectFill,
options: PHImageRequestOptions(),
resultHandler: { (result, info) -> Void in
self.imageView.image = result
let manager = PHImageManager.defaultManager()
let assets = PHAsset.fetchAssetsWithMediaType(.Image, options: nil)
})
}
}
As of iOS 9.0, you can get the original file name using the following:
var filename: String?
if #available(iOS 9.0, *) {
let resources = PHAssetResource.assetResourcesForAsset(lastAsset)
if let resource = resources.first {
filename = resource.originalFilename
}
}
If you're supporting pre-iOS 9.0 versions, you can also try using an undocumented workaround that still works as of iOS 9.1:
filename = lastAsset.valueForKey("filename") as? String

Append FMDB SQLite results to Swift array

I'm trying to append results from a FMDB SQLite query to a Swift array. The error I'm getting in XCode is 'value of option type 'String?' not unwrapped.'
Swapping out the line below in the while loop, the FMDB results can be printed to console OK, so no problems there.
println(results_lab_test?.stringForColumn("lab_test"))
New to XCode, so be kind...
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tests_label: UILabel!
var databasePath = NSString()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var arrayData:[String] = []
let filemgr = NSFileManager.defaultManager()
let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docsDir = dirPaths[0] as! String
databasePath = docsDir.stringByAppendingPathComponent("vmd_db.db")
let myDatabase = FMDatabase(path: databasePath as String)
if myDatabase.open(){
let query_lab_test = "SELECT lab_test FROM lab_test"
let results_lab_test:FMResultSet? = myDatabase.executeQuery(query_lab_test, withArgumentsInArray: nil)
while results_lab_test?.next() == true {
if let resultString = results_lab_test?.stringForColumn("lab_test"){
arrayData.append(resultString)
var multiLineString = join("\n", arrayData)
tests_label.text = multiLineString
tests_label.numberOfLines = 0
tests_label.lineBreakMode = NSLineBreakMode.ByWordWrapping
tests_label.sizeToFit()
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Working Code thanks to #skypecakes:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tests_label: UILabel!
var databasePath = NSString()
override func viewDidLoad() {
super.viewDidLoad()
let filemgr = NSFileManager.defaultManager()
let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docsDir = dirPaths[0] as! String
databasePath = docsDir.stringByAppendingPathComponent("vmd_db.db")
let myDatabase = FMDatabase(path: databasePath as String)
if myDatabase.open(){
var arrayData:[String] = []
let query_lab_test = "SELECT lab_test FROM lab_test"
let results_lab_test:FMResultSet? = myDatabase.executeQuery(query_lab_test, withArgumentsInArray: nil)
while results_lab_test?.next() == true {
if let resultString = results_lab_test?.stringForColumn("lab_test"){
arrayData.append(resultString)
}
}
println(arrayData)
myDatabase.close()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
It seems like
results_lab_test?.stringForColumn("lab_test")
returns a String?, which is an optional. Your array is defined as an array of "String" items, so you can't put a "String?" inside it.
Try this:
if let resultString = results_lab_test?.stringForColumn("lab_test")
arrayData.append(resultString)
Note that in general it is good practice to unwrap all of your optionals with "if let" instead of assuming that they are populated. So everywhere you have a question mark (e.g. results_lab_test?.stringForColumn), you can use "if let".
If you're using XCode 7 with Swift 2.0, this would be a good case for the "guard let" statement, which provides a convenient syntax to abort your code if an optional returns null:
guard let queryResults = results_lab_test else return
while queryResults.next() == true {
if let resultString = queryResults.stringForColumn("lab_test")
arrayData.append(resultString)
}
This test worked for me (table has 2 rows, and printing the array prints 2 rows), in case it helps you:
import UIKit
import FMDB
class ViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
if let myDatabase = makeSqlDB()
{
var arrayData:[String] = []
let query_lab_test = "SELECT lab_test FROM lab_test"
let results_lab_test:FMResultSet? = myDatabase.executeQuery(query_lab_test, withArgumentsInArray: nil)
while results_lab_test?.next() == true
{
if let resultString = results_lab_test?.stringForColumn("lab_test")
{
arrayData.append(resultString)
}
}
println(arrayData)
myDatabase.close()
}
}
private func makeSqlDB()->FMDatabase?
{
let database = FMDatabase(path: String())
if !database.open() {
println("Unable to open database")
return nil
}
if !database.executeUpdate("create table lab_test(lab_test text)", withArgumentsInArray: nil) {
println("create table failed: \(database.lastErrorMessage())")
}
if !database.executeUpdate("insert into lab_test (lab_test) values (?)", withArgumentsInArray: ["test1"]) {
println("insert 1 table failed: \(database.lastErrorMessage())")
}
if !database.executeUpdate("insert into lab_test (lab_test) values (?)", withArgumentsInArray: ["test2"]) {
println("insert 2 table failed: \(database.lastErrorMessage())")
}
return database
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Resources