I have the following code which copies data from the iPhone's contacts into an NSMutableDictionary. I'm using optional binding as discussed in this answer.
The firstName and lastName fields copy properly (as well as phone and email fields not shown here), but I have never been able to access the kABPersonNoteProperty, nor have I seen the println statement in the log. I've tried to copy the note as both String and NSString without success. Any ideas what could be causing this problem?
var contactInfoDict:NSMutableDictionary!
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson person: ABRecord!) {
self.contactInfoDict = ["firstName":"", "lastName":"", "notes":""]
if let firstName:String = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String {
contactInfoDict.setObject(firstName, forKey: "firstName")
println(firstName)
}
if let lastName:String = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String {
contactInfoDict.setObject(lastName, forKey: "lastName")
println(lastName)
}
if let notes:String = ABRecordCopyValue(person, kABPersonNoteProperty)?.takeRetainedValue() as? String {
contactInfoDict.setObject(notes, forKey: "notes")
println("Note: \(notes)")
}
}
Edit Here is the full class:
import UIKit
import AddressBookUI
class ContactsVC: UIViewController, ABPeoplePickerNavigationControllerDelegate {
#IBOutlet weak var done_Btn: UIBarButtonItem!
#IBAction func dismissContacts(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
println("ContactsVC dismissed")
}
#IBOutlet weak var viewContainer: UIView!
var object:PFObject = PFObject(className: "Contact")
let personPicker: ABPeoplePickerNavigationController
var contactInfoDict:NSMutableDictionary!
required init(coder aDecoder: NSCoder) {
personPicker = ABPeoplePickerNavigationController()
super.init(coder: aDecoder)
personPicker.peoplePickerDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
done_Btn.setTitleTextAttributes([NSFontAttributeName: UIFont(name: "Avenir Next Medium", size: 16)!], forState: UIControlState.Normal)
}
#IBAction func addContact() {
let actionSheetController: UIAlertController = UIAlertController(title: nil, message: "Would you like to select a contact from your iPhone or create a new contact directly in the app?", preferredStyle: .ActionSheet)
let selectContactAction: UIAlertAction = UIAlertAction(title: "Select from iPhone", style: .Default) { action -> Void in
self.performPickPerson(UIAlertAction)
}
actionSheetController.addAction(selectContactAction)
let createContactAction: UIAlertAction = UIAlertAction(title: "Create App Contact", style: .Default) { action -> Void in
self.performSegueWithIdentifier("AddContact", sender: self)
}
actionSheetController.addAction(createContactAction)
let cancelAction:UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) {
action -> Void in
}
actionSheetController.addAction(cancelAction)
self.presentViewController(actionSheetController, animated: true, completion: nil)
}
func performPickPerson(sender : AnyObject) {
self.presentViewController(personPicker, animated: true, completion: nil)
}
func peoplePickerNavigationControllerDidCancel(peoplePicker: ABPeoplePickerNavigationController!) {
personPicker.dismissViewControllerAnimated(true, completion: nil)
}
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson person: ABRecord!) {
self.contactInfoDict = ["firstName":"", "lastName":"", "company":"", "mobilePhone":"", "homePhone":"", "workPhone":"", "personalEmail":"", "workEmail":"", "street":"", "city":"", "state":"", "zipCode":"", "notes":""]
if let firstName:String = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String {
contactInfoDict.setObject(firstName, forKey: "firstName")
println(firstName)
}
if let lastName:String = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String {
contactInfoDict.setObject(lastName, forKey: "lastName")
println(lastName)
}
if let company:String = ABRecordCopyValue(person, kABPersonOrganizationProperty)?.takeRetainedValue() as? String {
contactInfoDict.setObject(company, forKey: "company")
println(company)
}
if let phonesRef:ABMultiValueRef = ABRecordCopyValue(person, kABPersonPhoneProperty)?.takeRetainedValue() as ABMultiValueRef? {
for (var i = 0; i < ABMultiValueGetCount(phonesRef); i++ ) {
var currentPhoneLabel:CFStringRef = ABMultiValueCopyLabelAtIndex(phonesRef, i).takeRetainedValue()
var currentPhoneValue:CFStringRef = ABMultiValueCopyValueAtIndex(phonesRef, i)?.takeRetainedValue() as! CFStringRef
if let mobileResult = CFStringCompare(currentPhoneLabel, kABPersonPhoneMobileLabel, CFStringCompareFlags.CompareCaseInsensitive) as CFComparisonResult? {
if mobileResult == CFComparisonResult.CompareEqualTo {
contactInfoDict.setObject(currentPhoneValue, forKey: "mobilePhone")
}
}
if let homeResult = CFStringCompare(currentPhoneLabel, kABHomeLabel, CFStringCompareFlags.CompareCaseInsensitive) as CFComparisonResult? {
if homeResult == CFComparisonResult.CompareEqualTo {
contactInfoDict.setObject(currentPhoneValue, forKey: "homePhone")
}
}
if let workResult = CFStringCompare(currentPhoneLabel, kABWorkLabel, CFStringCompareFlags.CompareCaseInsensitive) as CFComparisonResult? {
if workResult == CFComparisonResult.CompareEqualTo {
contactInfoDict.setObject(currentPhoneValue, forKey: "workPhone")
}
}
}
}
if let emailsRef:ABMultiValueRef = ABRecordCopyValue(person, kABPersonEmailProperty)?.takeRetainedValue() as ABMultiValueRef? {
for (var i = 0; i < ABMultiValueGetCount(emailsRef); i++ ) {
var currentEmailLabel:CFStringRef = ABMultiValueCopyLabelAtIndex(emailsRef, i).takeRetainedValue()
var currentEmailValue:CFStringRef = ABMultiValueCopyValueAtIndex(emailsRef, i)?.takeRetainedValue() as! CFStringRef
if let email = kABHomeLabel as CFStringRef? {
if let homeResult = CFStringCompare(currentEmailLabel, kABHomeLabel, CFStringCompareFlags.CompareCaseInsensitive) as CFComparisonResult? {
if homeResult == CFComparisonResult.CompareEqualTo {
contactInfoDict.setObject(currentEmailValue as String, forKey: "personalEmail")
}
}
}
if let workResult = CFStringCompare(currentEmailLabel, kABWorkLabel, CFStringCompareFlags.CompareCaseInsensitive) as CFComparisonResult? {
if workResult == CFComparisonResult.CompareEqualTo {
contactInfoDict.setObject(currentEmailValue as String, forKey: "workEmail")
}
}
}
}
if let addressRef:ABMultiValueRef = ABRecordCopyValue(person, kABPersonAddressProperty)?.takeRetainedValue() as ABMultiValueRef? {
if ABMultiValueGetCount(addressRef) > 0 {
var addressDict:NSDictionary = ABMultiValueCopyValueAtIndex(addressRef, 0)?.takeRetainedValue() as! NSDictionary
if let street = addressDict.objectForKey(kABPersonAddressStreetKey) as? String {
contactInfoDict.setObject(street as NSString, forKey: "street")
}
if let city = addressDict.objectForKey(kABPersonAddressCityKey) as? String {
contactInfoDict.setObject(city as NSString, forKey: "city")
}
if let state = addressDict.objectForKey(kABPersonAddressStateKey) as? String {
contactInfoDict.setObject(state as NSString, forKey: "state")
}
if let zipCode = addressDict.objectForKey(kABPersonAddressZIPKey) as? String {
contactInfoDict.setObject(zipCode as NSString, forKey: "zipCode")
}
}
}
// Notes is not currently accessible
if let note:String = ABRecordCopyValue(person, kABPersonNoteProperty)?.takeRetainedValue() as? String {
println("12")
contactInfoDict.setObject(note, forKey: "notes")
println("Note: \(note)")
}
saveToContact()
}
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, shouldContinueAfterSelectingPerson person: ABRecord!, property: ABPropertyID, identifier: ABMultiValueIdentifier) -> Bool {
return false
}
func saveToContact(){
self.object["username"] = PFUser.currentUser()!
if let firstName = contactInfoDict["firstName"] as? String{
self.object["contactFirstName"] = firstName
}
if let lastName = contactInfoDict["lastName"] as? String{
self.object["contactLastName"] = lastName
}
if let company = contactInfoDict["company"] as? String{
self.object["contactCompany"] = company
}
if let mobilePhone = contactInfoDict["mobilePhone"] as? String{
self.object["contactMobilePhone"] = mobilePhone
}
if let homePhone = contactInfoDict["homePhone"] as? String{
self.object["contactHomePhone"] = homePhone
}
if let workPhone = contactInfoDict["workPhone"] as? String{
self.object["contactWorkPhone"] = workPhone
}
if let personalEmail = contactInfoDict["personalEmail"] as? String{
self.object["contactPersonalEmail"] = personalEmail
}
if let workEmail = contactInfoDict["workEmail"] as? String{
self.object["contactWorkEmail"] = workEmail
}
if let street = contactInfoDict["street"] as? String{
self.object["contactStreet"] = street
}
if let city = contactInfoDict["city"] as? String{
self.object["contactCity"] = city
}
if let state = contactInfoDict["state"] as? String{
self.object["contactState"] = state
}
if let zipCode = contactInfoDict["zipCode"] as? String{
self.object["contactZipCode"] = zipCode
}
if let notes = contactInfoDict["notes"] as? String{
self.object["contactNotes"] = notes
}
self.object["contactType"] = "Lead"
self.object["contactIsActive"] = true
var addrObject = self.object
self.object.pinInBackgroundWithName("Contacts")
self.object.saveEventually{ (success, error) -> Void in
if (error == nil) {
println("saved in background")
} else {
println(error!.userInfo)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I just tested it and it works fine, just as you have it. This is effectively my whole code (except for getting authorization, of course):
#IBAction func doPeoplePicker (sender:AnyObject!) {
let picker = ABPeoplePickerNavigationController()
picker.peoplePickerDelegate = self
self.presentViewController(picker, animated:true, completion:nil)
}
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!,
didSelectPerson person: ABRecord!) {
if let note = ABRecordCopyValue(person, kABPersonNoteProperty)?.takeRetainedValue() as? String {
println(note)
} else {
println("no note")
}
}
When my person has a note, I see it; when my person has no note, I see "no note".
EDIT You and I played around with this for a while, sending each other actual projects, and I observed the following difference between your implementation and mine: mine, for which fetching the note works, obtains authorization to access the address book (you'll notice that I did mention that, in the first paragraph of my original answer); yours, for which fetching the note doesn't work, doesn't obtain authorization. Hence, I suggest that this is the missing piece of the puzzle.
Here's my theory. Prior to iOS 8, you needed authorization in order to use ABPeoplePickerNavigationController. Thus, my code, which goes way back, still obtains it. The way this supposedly works in iOS 8 is that, in the absence of authorization, the people picker fetches a copy of the address book data. Well, I think that this copy is faulty (and that you should file a bug report with Apple about this). But because I have authorization, I'm accessing the actual address book data, and so my code can see the note.
Related
I'm currently developing an app without using a UIStoryboard. I have a simple form with 4 UITextField objects to get input from users (first name, last name etc.).
I want to get the values and store them in my model. However, it always seems to be empty. How can I get the values without using a UIStoryBoard? Here is my model:
class Records: NSObject, NSCoding {
//MARK: Properties
var firstName: String
var lastName:String
var occupation:String
var placeMeet: String
var notes:String
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("records")
//MARK: Types
struct PropertyKey {
static let firstName = "firstName"
static let lastName = "lastName"
static let occupation = "occupation"
static let placeMeet = "placeMeet"
static let notes = "notes"
}
//MARK: Initialization
init?(firstName: String, lastName:String, occupation: String, placeMeet: String, notes: String) {
// The name must not be empty
guard !firstName.isEmpty else {
return nil
}
guard !lastName.isEmpty else {
return nil
}
guard !occupation.isEmpty else {
return nil
}
guard !placeMeet.isEmpty else {
return nil
}
guard !notes.isEmpty else {
return nil
}
// Initialization should fail if there is no name or if the rating is negative.
if firstName.isEmpty || lastName.isEmpty {
return nil
}
// Initialize stored properties.
self.firstName = firstName
self.lastName = lastName
self.occupation = occupation
self.placeMeet = placeMeet
self.notes = notes
}
//MARK: NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(firstName, forKey: PropertyKey.firstName)
aCoder.encode(lastName, forKey: PropertyKey.lastName)
aCoder.encode(occupation, forKey: PropertyKey.occupation)
aCoder.encode(placeMeet, forKey: PropertyKey.placeMeet)
aCoder.encode(notes, forKey: PropertyKey.notes)
}
required convenience init?(coder aDecoder: NSCoder) {
// The name is required. If we cannot decode a name string, the initializer should fail.
guard let firstName = aDecoder.decodeObject(forKey: PropertyKey.firstName) as? String else {
os_log("Unable to decode the name for a Record object.", log: OSLog.default, type: .debug)
return nil
}
guard let lastName = aDecoder.decodeObject(forKey: PropertyKey.lastName) as? String else {
os_log("Unable to decode the name for a Record object.", log: OSLog.default, type: .debug)
return nil
}
// Because photo is an optional property of Meal, just use conditional cast.
let occupation = aDecoder.decodeObject(forKey: PropertyKey.occupation) as? String
let placeMeet = aDecoder.decodeObject(forKey: PropertyKey.placeMeet) as? String
let notes = aDecoder.decodeObject(forKey: PropertyKey.notes) as? String
// Must call designated initializer.
self.init(firstName:firstName , lastName: lastName, occupation: occupation!, placeMeet: placeMeet! , notes:notes!)
}
}
And here is what Im trying to do
func save() {
let homepage = HomepageController()
let navController = UINavigationController(rootViewController: homepage) // Creating a navigation controller with VC1 at the root of the navigation stack
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId") as! AddContactCell
let note = tableView.dequeueReusableCell(withIdentifier: "note") as! Note
var occu = ""
var fname = ""
var lname = ""
var place = ""
var noteString = ""
self.navigationController?.present(navController, animated: true)
if myCell.nameLabel.text == items[0] {
occu = myCell.input_field.text!
}
else if myCell.nameLabel.text == items[1] {
fname = myCell.input_field.text!
}
else if myCell.nameLabel.text == items[2] {
lname = myCell.input_field.text!
}
else if myCell.nameLabel.text == "Place" {
place = myCell.input_field.text!
}
else {
noteString = note.input_field.text!
}
record = Records(firstName:fname , lastName: lname, occupation: occu, placeMeet:place, notes: noteString)
}
EDITED:
cellForRow override function
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId") as! AddContactCell
let note = tableView.dequeueReusableCell(withIdentifier: "note") as! Note
myCell.input_field.delegate = self
note.input_field.delegate = self as? UITextViewDelegate
if indexPath.section == 0 {
myCell.nameLabel.text = items[indexPath.row]
if (myCell.nameLabel.text == "Occupation")
{
myCell.input_field.attributedPlaceholder = NSAttributedString(string: "Occupation",
attributes: [NSForegroundColorAttributeName:UIColor(red:0.81, green:0.81, blue:0.83, alpha:1.0)])
}
else if (myCell.nameLabel.text == "First Name"){
myCell.input_field.attributedPlaceholder = NSAttributedString(string: "First Name",
attributes: [NSForegroundColorAttributeName:UIColor(red:0.81, green:0.81, blue:0.83, alpha:1.0)])
}
else {
myCell.input_field.attributedPlaceholder = NSAttributedString(string: "Last Name",
attributes: [NSForegroundColorAttributeName:UIColor(red:0.81, green:0.81, blue:0.83, alpha:1.0)])
}
myCell.myContactController = self
return myCell
}
else if indexPath.section == 1 {
myCell.nameLabel.text = "Place"
myCell.input_field.attributedPlaceholder = NSAttributedString(string: "Place",
attributes: [NSForegroundColorAttributeName:UIColor(red:0.81, green:0.81, blue:0.83, alpha:1.0)])
return myCell
}
else {
return note
}
}
In func save()
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId") as! AddContactCell
is wrong...
Use below code
let myCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! AddContactCell
I am currently trying to load data from Parse (which I realize will soon be obselete but I have to stay on it for now) and I have a custom class that is to handle all the data from the backend like so:
import Foundation
import Parse
class FeedContent: PFObject {
#NSManaged var address: String
#NSManaged var content: PFFile
#NSManaged var isVideo: Int
#NSManaged var attendeeObjectId: String
#NSManaged var created: NSDate?
#NSManaged var objId: String
init(address: String, content: PFFile, isVideo: Int, attendeeObjectId: String, created: NSDate?, objId: String) {
super.init()
self.address = address
self.content = content
self.isVideo = isVideo
self.attendeeObjectId = attendeeObjectId
self.created = createdAt
self.objId = objId
}
override init() {
super.init()
}
}
extension FeedContent: PFSubclassing {
class func parseClassName() -> String {
return "FeedContent"
}
override class func initialize() {
var onceToken: dispatch_once_t = 0
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
}
and in my Display View Controller I am creating an array to house these items then calling a function to display the data , like here:
func addPFObjectInFeedContent(object : PFObject) {
let newItem = FeedContent()
newItem.address = (object["address"] as? String)!
newItem.isVideo = (object["isVideo"] as? Int)!
newItem.attendeeObjectId = (object["attendeeObjectId"] as? String)!
//newItem.eventObjectId = (object["eventObjectId"] as? String)!
newItem.content = (object["content"] as? PFFile)!
newItem.created = object.createdAt
newItem.objId = object.objectId!
self.feedContentItems.append(newItem)
}
Here is the array as well:
var feedContentItems = [FeedContent]()
On this line specifically is where the EXC_Breakpoint error occurs:
newItem.isVideo = (object["isVideo"] as? Int)!
so i was curious if anyone had any intimation of what may be happening?
func loadParseFeed() {
let contentQuery = PFQuery(className: "FeedContent")
contentQuery.whereKey("address", equalTo: defaults.objectForKey("newLocation")! as! String)
//contentQuery.whereKey("flagged", notEqualTo: true)
contentQuery.orderByDescending("createdAt")
if blacklist.count != 0 {
for var item = 0; item <= blacklist.count; item += 1 {
contentQuery.whereKey("attendeeObjectId", equalTo: blacklist[item])
}
}
contentQuery.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?,error: NSError?) -> Void in
if error == nil {
if let object = objects as [PFObject]? {
if objects!.count > 0 {
for thing in objects! {
// let newItem = FeedContent()
// newItem.isVideo = (thing["isVideo"] as? Int)!
// newItem.attendeeObjectId = (thing["attendeeObjectId"] as? String)!
// newItem.eventObjectId = (thing["eventObjectId"] as? String)!
// newItem.content = (thing["content"] as? PFFile)!
// newItem.created = thing.createdAt
// newItem.objId = thing.objectId!
// self.feedContentItems.append(newItem)
self.addPFObjectInFeedContent(thing)
}
self.advanceFeed()
// self.overviewTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "advanceFeed", userInfo: nil, repeats: true)
// self.overviewTimer.fire()
}
}
}
})
}
I want to save the response from JSON in a file and fetch from it when the network is not available. However on trying to fetch idea by disabling the wifi, the app always crashes. Are there any other ways for offline fetching in swift except saving in database??
This is the error I am getting : Could not cast value of type 'Swift._NSContiguousString' (0x109e22320) to 'NSArray'
This is what I have done so far:
Create a model
class Directory : NSObject, NSCoding {
var data : [AnyObject]
var tid : String
var vid : String
var name : String
var imgThumbnail : String
var imgMedium : String
var imgLarge : String
var child : String
// MARK: Archiving Paths
init(data:[AnyObject],tid:String,vid:String,name:String,imgThumbnail:String,imgMedium:String,imgLarge:String,child:String) {
self.data = data ?? []
self.tid = tid ?? ""
self.vid = vid ?? ""
self.name = name ?? ""
self.imgThumbnail = imgThumbnail ?? ""
self.imgMedium = imgMedium ?? ""
self.imgLarge = imgLarge ?? ""
self.child = child ?? ""
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(data, forKey:Constants.kData)
aCoder.encodeObject(name, forKey:Constants.Directory.kName )
aCoder.encodeObject(tid, forKey: Constants.Directory.tid)
aCoder.encodeObject(vid, forKey: Constants.Directory.vid)
aCoder.encodeObject(imgThumbnail, forKey:Constants.Directory.kImageThumbnail)
aCoder.encodeObject(imgMedium, forKey: Constants.Directory.kImageMedium)
aCoder.encodeObject(imgLarge, forKey: Constants.Directory.kImageLarge)
aCoder.encodeObject(child, forKey: Constants.Directory.kChild)
}
required convenience init?(coder aDecoder: NSCoder) {
let data = aDecoder.decodeObjectForKey(Constants.kData) as! [AnyObject]
let name = aDecoder.decodeObjectForKey(Constants.Directory.kName) as! String
let tid = aDecoder.decodeObjectForKey(Constants.Directory.tid) as! String
let vid = aDecoder.decodeObjectForKey(Constants.Directory.vid) as! String
let imgThumbnail = aDecoder.decodeObjectForKey(Constants.Directory.kImageThumbnail) as! String
let imgMedium = aDecoder.decodeObjectForKey(Constants.Directory.kImageMedium) as! String
let imgLarge = aDecoder.decodeObjectForKey(Constants.Directory.kImageLarge) as! String
let child = aDecoder.decodeObjectForKey(Constants.Directory.kChild) as! String
// Must call designated initializer.
self.init(data:data,tid:tid,vid:vid,name:name,imgThumbnail:imgThumbnail,imgMedium: imgMedium,imgLarge: imgLarge, child: child)
}
}
Code for saving and loading the data from file
class func loadSavedFile(fileName: String) -> AnyObject? {
let pathString: String = Utility.fetchFilePathString(fileName)
print("Here the pathString is \(pathString)")
if NSFileManager.defaultManager().fileExistsAtPath(pathString) {
return NSKeyedUnarchiver.unarchiveObjectWithFile(pathString)!
} else {
return "File doesn't exist"
}
return ""
}
class func saveObject(object: AnyObject, toFile fileName: String) {
let pathString: String = Utility.fetchFilePathString(fileName)
NSKeyedArchiver.archiveRootObject(object, toFile: pathString)
}
class func fetchFilePathString(fileName: String) -> String {
let pathAray: [AnyObject] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.AllDomainsMask, true)
let pathString = pathAray.last!
return NSURL(fileURLWithPath: pathString as! String).URLByAppendingPathComponent(fileName).absoluteString
}
Checking for network connection in the view controller
var directoryArr = [Directory]()
override func viewDidLoad() {
super.viewDidLoad()
if Utility.isNetworkReachable() {
Utility.saveObject([], toFile: Constants.File.kDirectory)
self.serviceCallDirectory()
} else {
self.directorie = (Utility.loadSavedFile(Constants.File.kDirectory) as? [Directory])!
self.tableView.reloadData()
}
Service Call
func serviceCallDirectory() -> Void {
let stringUrl = Constants.baseUrl + Constants.kDirectoryUrl
WebService.getRequestAPI(stringUrl, withSuccess: {(responseDic, Statusflag,error) in
if Statusflag {
self.tableView.backgroundColor = UIColor.clearColor()
self.tableView.hidden = false
let tempInfo = responseDic![Constants.kData] as! [AnyObject]
var imgthumbnail : String = ""
var imgmedium : String = ""
var imglarge : String = ""
var name : String = ""
var child : String = ""
if tempInfo.count != 0 {
for info in tempInfo {
let tid = info[Constants.Directory.tid] as! String
let vid = info[Constants.Directory.vid] as! String
if let names = info[Constants.Directory.kName] as? String {
name = names
}
if let childs = info[Constants.Directory.kChild] as? String {
child = childs
}
if let imgthumb = info[Constants.Directory.kImageThumbnail] as? String {
imgthumbnail = imgthumb
} else {
imgthumbnail = ""
}
if let imgmediumd = info[Constants.Directory.kImageMedium] as? String {
imgmedium = imgmediumd
} else {
imgmedium = ""
}
if let imglarges = info[Constants.Directory.kImageLarge] as? String {
imglarge = imglarges
}
let myModel = Directory(
data: tempInfo,
tid: tid,
vid: vid,
name: name,
imgThumbnail: imgthumbnail,
imgMedium: imgmedium,
imgLarge: "",
child: child
)
self.directorie.append(myModel)
}
I don't know that this is the only issue, but this code
class func loadSavedFile(fileName: String) -> AnyObject? {
let pathString: String = Utility.fetchFilePathString(fileName)
print("Here the pathString is \(pathString)")
if NSFileManager.defaultManager().fileExistsAtPath(pathString) {
return NSKeyedUnarchiver.unarchiveObjectWithFile(pathString)!
} else {
return "File doesn't exist"
}
return ""
}
Either returns an object or a string. That's not very sensible. It should return a success flag or a tuple or use a completion block. When you call this function your code expects to get back an array of directory, which in a number of cases won't happen
self.directorie = (Utility.loadSavedFile(Constants.File.kDirectory) as? [Directory])!
The error in your question indicates a different kind of data mismatch. You should try not to use AnyObject, let swift help you by type checking what you're doing...
I am trying to get an array of strings from JSON and I'm trying to figure out how to deal with it if the returned array is empty. In some cases, the returned value is [] and for other cases, the array contains string values. It is crashing because of unexpectedly finding a nil value.
For clarification, the gyms array is passed from another class and everything here works without the code for the images.
Here is my relevant code:
var gyms = [AnyObject]()
var imageArrays = [[String]?]()
In viewDidLoad()
getGymImages()
Methods to get JSON data:
func getGymImages() {
var index = 0
for dictionary in gyms {
let id = dictionary["id"] as! String
let urlString = String("https://gyminyapp.azurewebsites.net/api/GymImage/\(id)")
let url = NSURL(string: urlString)
let data = NSData(contentsOfURL: url!)
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
for imageArray in json as! [AnyObject] {
imageArrays.append((imageArray as? [String])!)
}
} catch {
print("Error")
}
index += 1
}
addImagesToGyms()
}
func addImagesToGyms() {
var index = 0;
for array in imageArrays {
var dictionary = gyms[index] as! [String:AnyObject]
dictionary["images"] = array
gyms[index] = dictionary
index += 1
}
}
In cellForRowAtIndexPath()
let gymImages = dictionary["images"] as! [String]
if gymImages.count > 0 {
let firstImageURL = gymImages[0] as String
cell.cellImageView.sd_setImageWithURL(NSURL(string: firstImageURL))
}
EDIT: I was asked to show more of the file, so here it is.
import UIKit
class GymListTableViewController: UITableViewController {
var gyms = [AnyObject]()
var gymName: String?
var gymAddress: String?
var gymPhoneNumber: String?
var gymWebsite: String?
var gymID: String?
var gymLatitude: String?
var gymLongitude: String?
var maxDistance: Double?
var myLocation: CLLocation?
var milesArray = [Double]()
var imageArrays = [[String]?]()
var segmentedControl: UISegmentedControl?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Gyms"
tableView.registerNib(UINib(nibName: "GymListTableViewCell", bundle: nil), forCellReuseIdentifier: "gymCell")
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "waypoint_map"), style: .Done, target: self, action: #selector(showMapView))
self.navigationItem.rightBarButtonItem?.tintColor = BarItems.greenTintColor
segmentedControl = UISegmentedControl(items: ["A-Z", "Z-A", "Rating", "Distance"])
segmentedControl?.sizeToFit()
segmentedControl?.selectedSegmentIndex = 0
segmentedControl!.setTitleTextAttributes([NSFontAttributeName: UIFont(name:"Helvetica-Light", size: 15)!],
forState: UIControlState.Normal)
segmentedControl?.addTarget(self, action: #selector(changeSelectedSegmentIndex), forControlEvents: .ValueChanged)
self.navigationItem.titleView = segmentedControl
sortAlphabetically()
let backgroundImage = UIImage(named: "gray_background")
let backgroundImageView = UIImageView(image: backgroundImage)
tableView.backgroundView = backgroundImageView
addDistancesToGyms()
getGymImages()
// geocodeAddresses()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func addDistancesToGyms() {
var index = 0
for distance in milesArray {
var dictionary = gyms[index] as! [String:AnyObject]
dictionary["distance"] = distance
gyms[index] = dictionary
index += 1
}
}
func getGymImages() {
var index = 0
for dictionary in gyms {
let id = dictionary["id"] as! String
let urlString = String("https://gyminyapp.azurewebsites.net/api/GymImage/\(id)")
let url = NSURL(string: urlString)
let data = NSData(contentsOfURL: url!)
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
for imageArray in json as! [AnyObject] {
imageArrays.append((imageArray as? [String])!)
}
} catch {
print("Error")
}
index += 1
}
addImagesToGyms()
}
func addImagesToGyms() {
var index = 0;
for array in imageArrays {
var dictionary = gyms[index] as! [String:AnyObject]
dictionary["images"] = array
gyms[index] = dictionary
index += 1
}
}
func changeSelectedSegmentIndex() {
let segmentTouched = segmentedControl?.selectedSegmentIndex
if segmentTouched == 0 {
sortAlphabetically()
} else if segmentTouched == 1 {
sortReverseAlphabetically()
} else if segmentTouched == 2 {
sortByRatingAscending()
} else {
sortByDistanceAscending()
}
}
func sortAlphabetically() {
gyms.sortInPlace{
(($0 as! Dictionary<String, AnyObject>)["name"] as? String) < (($1 as! Dictionary<String, AnyObject>)["name"] as? String)
}
tableView.reloadData()
}
func sortReverseAlphabetically() {
gyms.sortInPlace{
(($0 as! Dictionary<String, AnyObject>)["name"] as? String) > (($1 as! Dictionary<String, AnyObject>)["name"] as? String)
}
tableView.reloadData()
}
func sortByRatingAscending() {
// TODO: Sort by rating
}
func sortByDistanceAscending() {
gyms.sortInPlace{
(($0 as! Dictionary<String, AnyObject>)["distance"] as? Double) < (($1 as! Dictionary<String, AnyObject>)["distance"] as? Double)
}
tableView.reloadData()
}
func showMapView() {
self.performSegueWithIdentifier("displayMapSegue", sender: self.navigationController)
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return gyms.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 131
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("gymCell", forIndexPath: indexPath) as! GymListTableViewCell
let dictionary = gyms[indexPath.row]
let addressDictionary = dictionary["address"]
let street = addressDictionary!!["streetAddress"] as! String
let city = addressDictionary!!["city"] as! String
let state = addressDictionary!!["state"] as! String
let zipInt = addressDictionary!!["zipCode"] as! Int
let zipCode = String(zipInt)
let addressString = String("\(street) " + "\(city), " + "\(state) " + "\(zipCode)")
cell.backgroundColor = UIColor.clearColor()
cell.gymNameLabel.text = dictionary["name"] as? String
cell.gymAddressLabel.text = addressString
let miles = dictionary["distance"] as! Double
let milesString = String(format: "%.1f miles", miles)
let milesLabelString = milesString
cell.milesLabel.text = milesLabelString
let gymImages = dictionary["images"] as! [String]
if gymImages.count > 0 {
let firstImageURL = gymImages[0] as String
cell.cellImageView.sd_setImageWithURL(NSURL(string: firstImageURL))
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let dictionary = gyms[indexPath.row]
if dictionary["name"] as? String != nil {
self.gymName = dictionary["name"] as? String
}
let cell = tableView.cellForRowAtIndexPath(indexPath) as! GymListTableViewCell
self.gymAddress = cell.gymAddressLabel.text
if dictionary["phone"] as? String != nil {
self.gymPhoneNumber = dictionary["phone"] as? String
}
if dictionary["website"] as? String != nil {
self.gymWebsite = dictionary["website"] as? String
}
if dictionary["id"] as? String != nil {
self.gymID = dictionary["id"] as? String
}
if dictionary["latitude"] as? String != nil {
self.gymLatitude = dictionary["latitude"] as? String
}
if dictionary["longitude"] as? String != nil {
self.gymLongitude = dictionary["longitude"] as? String
}
self.performSegueWithIdentifier("detailFromListSegue", sender: self.navigationController)
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detailFromListSegue" {
let gymDetailVC = segue.destinationViewController as! DetailTableViewController
if self.gymName != nil {
gymDetailVC.gymName = self.gymName
} else {
gymDetailVC.gymName = nil
}
if self.gymAddress != nil {
gymDetailVC.gymAddress = self.gymAddress
} else {
gymDetailVC.gymAddress = nil
}
if self.gymPhoneNumber != nil {
gymDetailVC.gymPhoneNumber = self.gymPhoneNumber
} else {
gymDetailVC.gymPhoneNumber = nil
}
if self.gymWebsite != nil {
gymDetailVC.gymWebsite = self.gymWebsite
} else {
gymDetailVC.gymWebsite = nil
}
if self.gymID != nil {
gymDetailVC.gymID = self.gymID!
} else {
// gymDetailVC.gymID = nil
}
if self.gymLatitude != nil {
gymDetailVC.gymLatitude = self.gymLatitude!
}
if self.gymLongitude != nil {
gymDetailVC.gymLongitude = self.gymLongitude!
}
}
}
}
numberOfRowsInSection should be returning gymImages.count if it isn't already.
Then as a safeguard you can always do
if indexPath.row < gymImages.count {
}
before accessing the content in cellForRowAtIndexPath
I am trying to create a custom table view cell and use it in my project.Unfortunately I get an error in the delegate method cellForRowAtIndexPath and I really don't understand why
The person class
import Foundation
class Person {
var name = "name"
var imageName = "blank"
init(name:String , image :String){
self.name = name
self.imageName = image
}
}
the custom cell class looks like this :
import UIKit
class personTableViewCell: UITableViewCell {
#IBOutlet var label: UILabel!
#IBOutlet var iamge: UIImageView!
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
And I am using it here:
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson person: ABRecordRef!) {
var arrayItems = ["firstName","lastName","mobileNumber","homeNumber","homeEmail","workEmail","address","city","zip","image"]
var valuesArray = ["","","","","","","","","",""]
var dictionary :NSMutableDictionary = NSMutableDictionary(objects: valuesArray, forKeys: arrayItems )
var firstName: NSString! = Unmanaged<CFString>.fromOpaque(ABRecordCopyValue(person, kABPersonFirstNameProperty).toOpaque()).takeUnretainedValue().__conversion()
var lastName = Unmanaged<CFString>.fromOpaque(ABRecordCopyValue(person, kABPersonLastNameProperty).toOpaque()).takeRetainedValue().__conversion()
dictionary.setObject(firstName, forKey: "firstName")
if lastName == "" {
}
else{
dictionary.setObject(lastName, forKey: "lastName")
}
// println("\(firstName) \(lastName)")
// Unmanaged<CFString>.fromOpaque(ABRecordCopyValue(person, kABPersonPhoneProperty).toOpaque()).takeRetainedValue().__conversion()
let unmanagedPhones = ABRecordCopyValue(person, kABPersonPhoneProperty)
let phones: ABMultiValueRef =
Unmanaged.fromOpaque(unmanagedPhones.toOpaque()).takeUnretainedValue()
as NSObject as ABMultiValueRef
let countOfPhones = ABMultiValueGetCount(phones)
var ct2 = 1
for index in 0..<countOfPhones{
let unmanagedPhone = ABMultiValueCopyValueAtIndex(phones, index)
let phone: String = Unmanaged.fromOpaque(
unmanagedPhone.toOpaque()).takeUnretainedValue() as NSObject as String
if ct2 == 1 {
dictionary.setObject(phone, forKey: "mobileNumber")
ct2 = ct2 + 1
}
else{
dictionary.setObject(phone, forKey: "homeNumber")
}
// println(phone)
}
let unmanangedEmail = ABRecordCopyValue(person, kABPersonEmailProperty)
let emails :ABMultiValueRef = Unmanaged.fromOpaque(unmanangedEmail.toOpaque()).takeUnretainedValue() as NSObject as ABMultiValueRef
//var oneEmail = ABMultiValueCopyValueAtIndex(emails, 0)
//var secEmail = ABMultiValueCopyValueAtIndex(emails, 1)
//var email :String = Unmanaged.fromOpaque(oneEmail.toOpaque()).takeUnretainedValue() as NSObject as String
var workEmail :String = String()
let countOfEmail = ABMultiValueGetCount(emails)
var ct = 1
for index2 in 0..<countOfEmail {
var oneEmail = ABMultiValueCopyValueAtIndex(emails, index2)
var email :String = Unmanaged.fromOpaque(oneEmail.toOpaque()).takeUnretainedValue() as NSObject as String
if ct==1 {
dictionary.setObject(email ,forKey: "homeEmail")
ct++
}
else {
dictionary.setObject(email,forKey:"workEmail")
}
// print("\(email) is the email")
}
if ABPersonHasImageData(person) {
// var imageData : CFDataRef = Unmanaged.fromOpaque(ABPersonCopyImageData(person).toOpaque()).takeUnretainedValue() as CFDataRef
// self.image = UIImage(data: imageData)
// var contactImage :CFData = ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail).takeRetainedValue()
// dictionary.setObject(imageData, forKey: "image")
}
// dictionary.setObject(workEmail,forKey:"workEmail")
var unamangedAddressDictionary = ABRecordCopyValue(person, kABPersonAddressProperty)
let addresses : ABMultiValueRef = Unmanaged.fromOpaque(unamangedAddressDictionary.toOpaque()).takeUnretainedValue() as NSObject as ABMultiValueRef
// var addressDictionary : NSDictionary = Unmanaged.fromOpaque(ABMultiValueCopyValueAtIndex(unamangedAddressDictionary, 0).toOpaque())
var addressDictionary = ABMultiValueCopyValueAtIndex(addresses, 0).takeUnretainedValue() as NSDictionary
if addressDictionary.objectForKey(kABPersonAddressCityKey) == nil {
}
else{
var smth: AnyObject? = addressDictionary.objectForKey(kABPersonAddressCityKey)
// dictionary.setObject(addressDictionary.objectForKey(kABPersonAddressCityKey) , forKey: "city")
// dictionary.setObject(smth, forKey: "city")
dictionary.setValue(addressDictionary.objectForKey(kABPersonAddressCityKey), forKey: "city")
}
if addressDictionary.objectForKey(kABPersonAddressZIPKey) == nil {
}
else {
// dictionary.setObject(addressDictionary.objectForKey(kABPersonAddressZIPKey), forKey: "zip")
dictionary.setValue(addressDictionary.objectForKey(kABPersonAddressZIPKey), forKey: "zip")
}
if addressDictionary.objectForKey(kABPersonAddressStreetKey) == nil {
}
else{
// dictionary.setObject(addressDictionary.objectForKey(kABPersonAddressStreetKey), forKey: "address")
dictionary.setValue(addressDictionary.objectForKey(kABPersonAddressStreetKey), forKey: "address")
}
contactsArray.addObject(dictionary)
// println(contactsArray)
var fullName = firstName + " " + lastName
self.empty = true
var pers : Person = Person(name: fullName, image: "")
self.arrayOfPerson.append(pers)
self.objects.addObject(fullName)
self.tableView?.reloadData()
self.addressBookController.dismissViewControllerAnimated(true, completion: nil)
//println("\(dictionary)")
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell : personTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as personTableViewCell
self.ceva = "mere"
// Configure the cell...
//cell.textLabel.text = self.objects[indexPath.row] as NSString
let person :Person = self.arrayOfPerson[indexPath.row] as Person
cell.label.text = person.name
if self.image != nil {
cell.iamge.image = self.image
}
else {
cell.iamge.image = UIImage(named: "nopicture.png")
}
return cell
}
I get the error when dequeueing it with identifier :
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[UILabel isEqualToString:]:
unrecognized selector sent to instance 0x79e51190'
I set up the identifier correctly and the array from which i get the person is containing items in it
Can you guys help me and tell me where I did wrong?
Any type of advice will be greatly appreciated