My app is crashing on this method:
func loadData() {
timelineData.removeAllObjects()
var findTimelineData:PFQuery = PFQuery(className:"AllTweets")
findTimelineData.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]!, error:NSError!)-> Void in
if (error == nil) {
for object:AnyObject in objects {
self.timelineData.addObject(object as PFObject)
}
let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = array as NSMutableArray
self.tableView.reloadData()
}
}
}
The error that it throws is:
ECX_BREAKPOINT(code=EXC_I386_BPT,subcode=0x0)
I am calling this method in my viewDidAppear method.
Does anybody have any ideas why this is happening?
The reverseObjectEnumerator().allObjects will return a NSArray, no matter the type of the original array is Mutable or not. (https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSEnumerator_Class/index.html#//apple_ref/occ/instp/NSEnumerator/allObjects)
In swift, if you need a mutable array, then use
var mutableArray = array.reverseObjectEnumerator().allObjects
var is for mutable and let is the opposite.
Related
I'm having an issue while using reload data to refresh my UITableView.
As a quick description:
This class received an NSNotification contain the object with data that need to added to the list and device must vibrate once that indicate that the data is received. The device itself vibrate but the list does not updated, so no new data is added to the list.
Code when notification is received:
dispatch_async(dispatch_get_main_queue(), {
let object = notification.object as! Attendee
self.handshakesArray.append(object)
self.tableViewMain.reloadData()
AudioServicesPlayAlertSound(UInt32(kSystemSoundID_Vibrate))
})
I don't know if this is an iOS issue or my implementation is wrong.
Edit:
class Shakes: UITableViewController {
private var handshakesArray = [Attendee]()
override func viewDidLoad() {
super.viewDidLoad()
}
func executeTask(notification:NSNotification) {
dispatch_async(dispatch_get_main_queue(), {
let object = notification.object as! Attendee
self.handshakesArray.append(object)
self.tableViewMain.reloadData()
AudioServicesPlayAlertSound(UInt32(kSystemSoundID_Vibrate))
})
}
// ........
}
Thanks in advance
I think you haven't initialized self.handshakesArray. Please initialise it by
var self.handshakesArray = [objectType]()
Make sure you are setting datasource and delegate for self.tableViewMain in you viewDidLoad func or from the storyboard .
self.tableViewMain.delegate = self
self.tableViewMain.dataSource = self
I have two classes Place and BeenHere. BeenHere has pointers called "toPlace" and "fromUser"with target to Class Place and User accordingly. Place, in its turn, has title and image (PFFile) that I want to retrieve and show in ViewController. In the code below I have reached that pointer with objectId, but don't know how I can now retrieve title and image related to specific place this pointer leads to. Appreciate your help and suggestions.
class UserBeenHereViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let user = PFUser.currentUser()?.username
if user != nil {
let query = PFQuery(className: "BeenHere")
query.includeKey("toPlace")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
for object in objects! {
print(object["toPlace"].objectId)
}
}
else {
print("There is error")
}
}
}
}
First you need to get the "toPlace" into a PFObject and then access it. So in your case it should look something like:
var toPlace = comment["toPlace"] as? PFObject
print (toPlace["title"])
Answer is below, image is here:
I was searching how to do this for a couple of days and was only able to find people who stored UILocalNotificaations in NSUserDefaults. Saving these in NSUserDefaults seemed wrong to me because it is supposed to be used for small flags. I just now finally figured out how to store notifications in CoreData. This is Using Xcode 7.3.1 and Swift 2.2
First off you need to create a new entity in your CoreDataModel
and then add a single attribute to it. the attribute should be of type Binary Data I named my table/entity "ManagedFiredNotifications" and my attribute "notification". it should look like this:
Image linked in Question above.
Next you need to add an extension to UILocalNotification it should go like this:
extension UILocalNotification {
func save() -> Bool {
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
let firedNotificationEntity = NSEntityDescription.insertNewObjectForEntityForName("ManagedFiredNotifications", inManagedObjectContext: appDelegate!.managedObjectContext)
guard appDelegate != nil else {
return false
}
let data = NSKeyedArchiver.archivedDataWithRootObject(self)
firedNotificationEntity.setValue(data, forKey: "notification")
do {
try appDelegate!.managedObjectContext.save()
return true
} catch {
return false
}
}
}
Now for saving a notification all you need to do is call
UILocalNotification.save()
On the notification you would like to save. my notifications were named 'notification' so I would call notification.save()
To retrieve a notification you need a method like this
func getLocalFiredNotifications() -> [UILocalNotification]? {
let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)!.managedObjectContext
let firedNotificationFetchRequest = NSFetchRequest(entityName: "ManagedFiredNotifications")
firedNotificationFetchRequest.includesPendingChanges = false
do {
let fetchedFiredNotifications = try managedObjectContext.executeFetchRequest(firedNotificationFetchRequest)
guard fetchedFiredNotifications.count > 0 else {
return nil
}
var firedNotificationsToReturn = [UILocalNotification]()
for managedFiredNotification in fetchedFiredNotifications {
let notificationData = managedFiredNotification.valueForKey("notification") as! NSData
let notificationToAdd = NSKeyedUnarchiver.unarchiveObjectWithData(notificationData) as! UILocalNotification
firedNotificationsToReturn.append(notificationToAdd)
}
return firedNotificationsToReturn
} catch {
return nil
}
}
Note that this returns an array of UILocalNotifications.
When retrieving these if you plan on removing a few of them and then storing the list again you should remove them when you get them something like this works:
func loadFiredNotifications() {
let notifications = StudyHelper().getLocalFiredNotifications()
if notifications != nil {
firedNotifications = notifications!
} else {
// throw an error or log it
}
classThatRemoveMethodIsIn().removeFiredLocalNotifications()
}
I hope this helps someone who had the same problems that I did trying to implement this.
I use method with for loop inside:
func filter (array: NSArray) -> NSMutableArray {
var filteredArray: NSMutableArray = NSMutableArray()
for objects in array as [MyObject] { // this line crash only on release mode
// TODO
}
return filteredArray
}
when it is debug mode it works fine, but when I change to release mode it's crashed on line:
for objects in array as [MyObject]{
When I change method to this one (without casting inside loop) it wont crash on debug also on release mode:
func filter (array: [MyObject]) -> NSMutableArray {
var filteredArray: NSMutableArray = NSMutableArray()
for objects in array {
// TODO
}
return filteredArray
}
Can some explain why?
Hard to say without knowing what's actually inside the NSArray. I suggest setting a breakpoint and inspecting the content of the array variable.
However, the reason is that the as operator fails doing the cast, because at least one element in array is not an instance of (a subclass of) MyObject. I would protect that code by using optional cast, although that would skip the entire for loop if cast fails.
func filter (array: NSArray) -> NSMutableArray {
var filteredArray: NSMutableArray = NSMutableArray()
if let array = array as? [MyObject] {
for objects in array as [MyObject] { // this line crash only on release mode
// TODO
}
}
return filteredArray
}
If you are sure that the array contains MyObject instances, then I would solve the problem in the code that calls this function, using a swift array instead of NSArray, so avoiding cast problems, but of course that depends from your actual code - so this is not a solution that may work in all cases.
Update This solution could also better solve your problem, if it happens that you have an array with elements of mixed types, but you are interested in processing only the ones having MyObject type:
func filter (array: NSArray) -> NSMutableArray {
var filteredArray: NSMutableArray = NSMutableArray()
for element in array {
if let element = element as? MyObject {
// TODO
}
}
return filteredArray
}
The difference is that instead of trying to cast the entire array, the cast is attempted for each element.
Original Question (see solution below):
I am trying to use the AddressBook.framework in my Swift App, but can't figure out how to implement the ABAddressBookRegisterExternalChangeCallback function.
In Objective-C, I just implement the callback as a C function and pass its pointer:
// somewhere in the initializer of the MyAddressBook class:
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(nil, nil);
ABAddressBookRegisterExternalChangeCallback(addressBook, externalChangeCallback, (__bridge void *)(self));
// somewhere else in the MyAddressBook class:
void externalChangeCallback(ABAddressBookRef reference, CFDictionaryRef info, void *context)
{
[(__bridge MyAddressBook *)context addressBookDidChangeExternally];
}
- (void)addressBookDidChangeExternally
{
// good old Obj-C from here on!
}
In Swift, it is proving very difficult for me to handle C functions. I found that Apple added the ability to pass C function pointers around in beta 3, but how do I declare such a function? It would be good to use Swift's closure syntax, but is that even possible here?
This is where I create the ABAddressBookRef:
var addressBookRef: ABAddressBookRef = {
let addressBookRef: ABAddressBookRef = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()
// TODO: how do I make this work?
let externalChangeCallback: ABExternalChangeCallback = {
println("Address book changed externally!")
}
ABAddressBookRegisterExternalChangeCallback(addressBookRef, externalChangeCallback, nil)
return addressBookRef
}()
So how can I implement this in Swift?
Solution (with flaws):
As suggested by pNre, this is how I implemented it now:
In Objective-C:
AddressBookExternalChangeCallback.h:
#import <AddressBook/AddressBook.h>
void registerExternalChangeCallbackForAddressBook(ABAddressBookRef addressBookRef);
AddressBookExternalChangeCallback.m:
#import "AddressBookExternalChangeCallback.h"
void addressBookExternalChangeCallback(ABAddressBookRef addressBookRef, CFDictionaryRef info, void *context)
{
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"AddressBookDidChangeExternallyNotification" object:nil];
});
}
void registerExternalChangeCallbackForAddressBook(ABAddressBookRef addressBookRef)
{
ABAddressBookRegisterExternalChangeCallback(addressBookRef, addressBookExternalChangeCallback, nil);
}
In Swift:
after importing bridging header:
registerExternalChangeCallbackForAddressBook(addressBookRef)
A notification is posted whenever the address book changes. Only #objc classes can register for notifications, though, so is there a way to call a Swift function or method instead?
ABExternalChangeCallback is defined as
typealias ABExternalChangeCallback = CFunctionPointer<((ABAddressBookRef!, CFDictionary!, UnsafeMutablePointer<()>) -> Void)>
From the Xcode release notes:
However, you cannot call a C function pointer (CFunctionPointer) or
convert a closure to C function pointer type.
This means you can't assign a block the way you're doing.
However you can bypass this limitation calling ABAddressBookRegisterExternalChangeCallback in an objc function and calling it from your swift code.
Swift 2.0
if let addressBook = ABAddressBookCreateWithOptions(nil, nil) {
let ref = addressBook.takeRetainedValue()
let callback: #convention(c) (addressBookRef: ABAddressBookRef!, info: CFDictionaryRef!, context: UnsafeMutablePointer<Void>) -> Void = {
(addressBookRef, info, context) in
// do the things you want
}
let addressBookChangeCallback = unsafeBitCast(callback, ABExternalChangeCallback.self)
ABAddressBookRegisterExternalChangeCallback(ref, addressBookChangeCallback, nil)
}
in swift 4.0
class ABAddressBookManager: NSObject {
static let shared = ABAddressBookManager()
private var addressBook: ABAddressBook?
override init() {
super.init()
let addressBookForObserving = ABAddressBookCreate().takeRetainedValue()
let context = Unmanaged.passUnretained(self).toOpaque()
DispatchQueue.main.async {
ABAddressBookRegisterExternalChangeCallback(addressBookForObserving, { (addressBook, info, context) in
guard let context = context else {
return
}
let manager = Unmanaged<ABAddressBookManager>.fromOpaque(context).takeUnretainedValue()
//call manager's method
}
}
self.addressBook = addressBookForObserving
}
}
I got the same issue. But I have cleared the cache by calling ABAddressBookRevert() in addressbook object.