Can I access a struct inside of an array that is defined in the application delegate from a ViewController?
I get the error:
'Any' does not have a member named 'title' in XCode 6.2
What is the syntax for accessing a structure inside of the array?
//AppDelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
struct TodoItem {
var title: String
}
var todoItem = TodoItem(
title: "Get Milk")
var myArray: [Any] = []
And then in the ViewController
//
// ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
//here I'm adding the struct to the array
let myTodoItem = delegate.myArray.append(delegate.todoItem)
//how do I access the items in the struct?
println(delegate.myArray[0].title)
You can access structures in array same as you would classes.
Your issue is that you explicitly tell that the array contains Any Objects. Type the array to be of type MyStruct and it works fine:
var myArray: [MyStructure] = [];
alternatively, if you can't modify the array declaration, cast:
myValueIWannaRead = (myArray[0] as MyStruct).myValueIWannaRead
You can define the array to store the type you desire :
var myArray: [TodoItem] = [];
Then you can access the instances properly.
The Any type denotes an object of an unknown type (class/primitive type ) and during compilation time there is no way to know which type the instance accessed will be of.
In Swift be always as specific as possible.
If the array contains only items of type TodoItem, declare it respectively.
var myArray: [TodoItem] = []
Any is a kind of placeholder, the compiler has no idea what dynamic type it is.
Why do you have to add the struct TodoItem to AppDelegate? in my point of view, it would be better to create it in the same file that you have your view controller or - even better - create a new Swift file named TodoItem.swift holding the struct.
Then after you have moved your struct to a new file or inside your View Controller file, but outside your ViewController class. You can just call:
let myTodoItem = TodoItem(title: "Get Milk") // declaring TodoItem
var myTodoItemArray : [TodoItem] = [] // declaring TodoItem array
myTodoItemArray.append(myTodoItem) // Appending to the array
// then you can set it by calling the array only member
let todoItem = myTodoItemArray[0] as! TodoItem
let title = todoItem.title
// Or you can just call the toDo Item itself
let title = myTodoItem.title
If you want to comunicate this data between two different classes, I would suggest use Delegation by creating a protocol or Notifications with NSNotifications.
I hopw this helps, happy coding.
EDIT: Fixed some minor error in the code
Solution1: Your struct is defined under AppDelegate class so, you will have to parse it like this;
//** Swift 1.2, xCode 6.3.1**//
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
//here I'm adding the struct to the array
delegate.myArray.append(delegate.todoItem)
//how do I access the items in the struct?
println((delegate.myArray[0] as! AppDelegate.TodoItem).title)
Solution2: Change the data type of your array Any to TodoItem
var myArray: [TodoItem] = []
and then, it would work;
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
//here I'm adding the struct to the array
delegate.myArray.append(delegate.todoItem)
//how do I access the items in the struct?
println(delegate.myArray[0].title)
Related
I am building an iOS app with Xcode and using CoreData. In the data model there are a few entities, let's say: A, B, C, D, E.
In the homeViewController there are five buttons and each button perform the segue to the detailTableViewController for each entity.
Depending on which button is pressed, you should fetch the information for the corresponding entity. For example, if I press button "B" I should get the Data for the "B" entity in the detailTableViewController.
And here comes the question: how can I declare the variable "entitiesArray" to store the fetch request result if I don't know which entity is going to be pushed until the button is pushed? I have no idea of its data type until the button is pushed.
If there were only one entity "A" I would write:
let entitiesArray = [A]()
let request: NSFetchRequest<A> = A.fetchRequest()
entitiesArray = try context.fetch(request)
...
However, I don't know the entity that will be pushed.
And using a switch statement in viewDidLoad doesn't solve the problem because I need the entitiesArray to be a global variable to use it inside other functions like numberOfRowsInSection and cellForRowAt indexPath.
Add this to your context via extension:
func fetchMOs (_ entityName: String, sortBy: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil) throws -> [NSManagedObject] {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
request.returnsObjectsAsFaults = false //as I need to access value
request.predicate = predicate
request.sortDescriptors = sortBy
return try! self.fetch(request) as! [NSManagedObject]
}
then you simply call it like this:
let mos = context.fetchMOs(String(describing: yourClassofAorBorCorD))
The point is using NSFetchRequest's convenience init(:entityName) and struct NSFetchRequestResultType as result type.
An alternative to using the superclass NSManagedObject could be to create a protocol and have your array declared to contain objects of that protocol. This could make sense if want to access data the same way for all entities in your UI like getting a name, identifier etc and preferably if it is immutable
Here is a quick example using the built-in protocol CustomStringConvertible
let entitesArray = [CustomStringConvertible]()
implement the protocol in an extension
extension A: CustomStringConvertible {
var description: String {
return "\(someAttribute), \(someOtherAttribute)"
}
}
You can have a simple protocol then with title and url and let all entities implement this protocol, you do need to have different names in the protocol do for the attributes so that they do not clash with the atributes in the entities. An example
protocol LabelSupport {
var titleLabel: String { get }
var urlLabel: String { get }
}
And let A implement it
extension A: LabelSupport {
var titleLabel: String {
return title
}
var urlLabel: String {
return url
// or perhaps url.path or similar
}
}
I need to read an array which is in ViewController-A from ViewController-B in order to fill the data of an UIPickerView which is in ViewController-B. How could I do it ?
I have tried this answer:
https://stackoverflow.com/a/31574620/7127489
But I don't know why I can't instantiate my ViewController-B as it says in order to pass my filled array to an empty array in the other class.
Any idea ?
Thank you very much!
You can use delegates to pass data (many examples of delegation here on SO) or here
or you can do something like this:
If in navigationController:
if let navController = self.tabBarController?.viewControllers?[1] as? UINavigationController, let yourController = navController.viewControllers[0] as? yourVC{
yourArray = yourController.array
}
If not in navigationController:
if let yourController = self.tabBarController?.viewControllers?[1] as? yourVC{
yourArray = yourController.array
}
if your controller-B is seuged frome controller-A,you shuould declare a property in controller-B,and assign the value you need to controller-B when segueing.
Set up a static variable storing the array in 'ViewControllerA', then you can access it from ViewControllerB using ViewControllerA.array = [1,2,3].
You can set up the static variable as static var array = [Int]()
You would be knowing the the index of ViewControllerB in tab bar.
Declare an array in ViewControlleB. Like
var pickerData = [String]() // use appropriate type depending upon need.
Whenever the data in ViewControllerA get update update the data in ViewControllerB like this
let vcb = self.tabBarController?.viewControllers?[index of ViewControllerB] as? ViewControllerB
vcb?.pickerData = newData
I have the following code
class Test: UIViewController {
var imagesOfChef = [Int : chefImages]()
struct chefImages {
var objidChef: String!
var imageChef: UIImage!
}
}
I fill up this dictionary as soon as the user opens the application.
But i want it to be available also in other Views (swift files)
Lets say in this class
class Test2: UIViewController{
}
How can i create a singleton for this Dictionary so it can be available to other views?
Thanks for your time!
You can use a static property:
static var imagesOfChef = [Int : chefImages]()
and then you use:
Test.imagesOfChef
But I suggest avoiding the static approaches as much as possible, you can use the prepare segue or assign the property from outside if possible if Test has Test2.
There are plenty of examples on StackOverlow about how to create a Singleton.
Anyway, your code should be like this
struct Chef {
let id: String
let image: UIImage
}
final class Singleton {
static let sharedInstance = Singleton()
private init() { }
var dict = [Int: Chef]()
}
Now in any source of your app you can use it
Singleton.sharedInstance.dict[1] = Chef(id: "1", image: UIImage())
class MySingleton{
static let shareInstance = MySingleton()
private init() {}
var myDetail = [Detail]()
}
class DetailTableViewController {
var expense = [Detail]()
override func viewDidLoad() {
super.viewDidLoad()
... put stuff in expense array ....
MySingleton.shareInstance.myDetail = expense //<--- doesn't work
// error is "cannot assign value of type '[Detail]' to type [MySingleton.Detail]"
}
}
How do I copy an array to my MySingleton?
right now i just pass my array around my classes using segue
From your error, it is likely you are defining Detail twice, once locally to the singleton, once globally for the viewController.
I'm presently taking an iOS development course. As part of an assignment, I'm tasked with creating a UISearchController in a note tracking project using Core Data in Swift.
Every example I've found is in Objective-C or is filtering a static array. Apple's "sample" code, updated in December 2014 doesn't compile in Xcode 6.3.
To add a UISearchController, I've got 3 primary tasks to do:
1) Create a view controller to present search results. I'm using a TableViewController.
2) Create a UISearchController, and pass it my search results view controller.
What's "stumping" me is now to get a hold of the objects in the managedObjectsContext. Prior to attempting to add a UISearchController, my app works fine. I can add, edit, and delete items. I'm using the "canned" Core Data code in Xcode 6.3 with the stack in AppDelegate.
class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate
var searchController: UISearchController? = nil
func addSearchBar() {
var resultsController = SearchResultsTableViewController()
resultsController.notes = // stumped what to call the notes. Trying to call an array from the Notes class below
resultsController.delegate = self
searchController = UISearchController(searchResultsController: resultsController)
searchController!.searchResultsUpdater = resultsController
searchController!.searchBar.frame = CGRect(
x: searchController!.searchBar.frame.origin.x,
y: searchController!.searchBar.frame.origin.y, width: searchController!.searchBar.frame.size.width, height: 44.0)
tableView.tableHeaderView = searchController!.searchBar
self.definesPresentationContext = true
}
3) The UISearchController will notify its searchResultsUpdater (a class that conforms to UISearchResultsUpdating) when the search text changes. I'm using my search results view controller implement this protocol so I can update the filtered results.
Below is my Note: NSManagedObject class:
import Foundation
import CoreData
class Note: NSManagedObject {
#NSManaged var dateCreated: NSDate
#NSManaged var dateEdited: NSDate
#NSManaged var noteTitle: String
#NSManaged var noteBody: String
// TODO: adding this to make it easier to handle names
class func notes() -> NSArray {
let whereMyNotesAreStored = // Need syntax for where my MyManagedObjectContext is located
let dataArray = NSArray(contentsOfFile: whereMyNotesAreStored!)
var notesArray = NSMutableArray()
for dictionary in dataArray {
var note = Note()
note.noteTitle = dictionary["noteTitle"] as! String
note.noteBody = dictionary["noteBody"] as! String
note.dateCreated = dictionary["dateCreated"] as! String
note.dateEdited = dictionary["dateEdited"] as! String
notesArray.addObject(note)
}
return NSArray(array: notesArray as! [AnyObject])
}
}
There are two approaches to setting the context:
Calling back to the App Delegate:, like this
let appDelegate : AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context = appDelegate.managedObjectContext!
or passing the context forward from the App Delegate to the Master View Controller, which then passes it on to any subsequent view controllers, etc. Each view controller will need a property defined for the context; when a new VC is instantiated, the context is set before the VC is presented/pushed, eg:
class CustomViewController : UIViewController {
var managedObjectContext : NSManagedObjectContext
...
and, when loading a new view controller,
let newVC = CustomViewController()
newVC.managedObjectContext = self.managedObjectContext
...
To access the objects, use either a NSFetchRequest or NSFetchedResultsController to create an array of results, which you can then pass to the resultsController. eg. for a fetch request:
let fetch = NSFetchRequest(entityName: "Notes")
var error : NSError? = nil
let fetchedResults = managedObjectContext?.executeFetchRequest(fetch, error: &error)
(Your notes() function is on the wrong track - you would not use NSArray(contentsOfFile:) to access CoreData objects. Also, you must use the designated initialiser for NSManagedObject subclasses: so not var note = Notes() but var note = Notes(entity: NSEntityDescription, insertIntoManagedObjectContext: NSManagedObjectContext?)