Core Data Problems? - xcode

I am trying to use CoreData but I already went through the setup for my project and forgot to check off the box to utilize it.
Is there a way to implement the use of core data when the CoreData box was not checked previously during the setup?
If I start a new project will have to transfer a lot of information and it would be time consuming so I would like to stay on the same project and not create a new one.

To be frank, you did the right think by not checking the Use CoreData box on project creation. I feel that just bloats the project with a bunch of stuff that is easier (and more insightful) to do manually.
To be short, you can implement CoreData the same regardless of what option you selected at project creation.
Here are the steps I usually go through when I want to add CoreData support to my project (manually/programatically):
Define a Data Model
These are just NSManagedObjects which represent your application's data structure. For example, a User, Message, BlogPost, etc. I also make one for my user settings.
Example:
import CoreData
class User : NSManagedObject
{
// #NSManaged is the replacement for #dynamic when using CoreData in Swift
#NSManaged var identifier : String
#NSManaged var firstName : String?
#NSManaged var lastName : String?
// This is called when a new User object is inserted to CoreData
override func awakeFromInsert()
{
super.awakeFromInsert()
self.identifier = NSUUID().UUIDString // generate a random unique ID
}
}
Add Core Data Model
This is another file you add to your project via: File -> New-> iOS-> CoreData -> Data Model. I usually store this same xcmodeldata file in my Models project folder (along with my actual model classes).
Upon selecting this new file, you'll see the CoreData model editor. You will want to see the right-hand side inspector pane is visible (hotkey is ⌥⌘1). For the core data editor, you will also primarily use the third tab (data model inspector) which is switchable with ⌥⌘3.
Now you can add an entity object to this data model (via Add Entity at the bottom). Assuming the example above, add a User entity. With the User entity selected, add the three attributes that are defined in the above class: identifier, firstName, and lastName. They should match the class definition, using String types.
Next step is to tell CoreData that this User entity defined here maps to our actual class file. With the User selected and the data model inspector pane open, set the Name to User and Class to YourAppName.User.
This is the "gotcha" with Swift and CoreData, your classes are prefixed with the module name in order to namespace them (avoiding name collisions). The nice part is that you no longer need to add "XYZ" class prefixes to your objects.
Initialize Core Data Stack
With your data model defined, you need to initialize the CoreData stack itself (database store and context). The most basic example is a global singleton for your NSManagedObjectContext, which will be lazy-loaded when needed.
You can put this in its own Swift file (CoreDataStack.swift):
import CoreData
let managedObjectContext : NSManagedObjectContext =
{
// This is your xcdatamodeld file
let modelURL = NSBundle.mainBundle().URLForResource("MyApp", withExtension: "momd")
let dataModel = NSManagedObjectModel(contentsOfURL: modelURL!)
// This is where you are storing your SQLite database file
let documentsDirectory : NSURL! = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last as? NSURL
let storeURL = documentsDirectory.URLByAppendingPathComponent("MyApp.sqlite")
let psc = NSPersistentStoreCoordinator(managedObjectModel: dataModel!)
var error : NSError?
let store = psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error)
if let error = error
{
println("Uhoh, something happened! \(error), \(error.userInfo)")
}
let context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
context.persistentStoreCoordinator = psc
context.undoManager = nil
return context
}()
Using Core Data
So now that you have a working Core Data stack, some data models defined and mapped... what next?
Let's fetch some objects!
func getUsersByFirstName(firstName: String) -> [User]
{
let fetchRequest = NSFetchRequest(entityName: "User")
// The [c] part indicates case-insensitive, "Bob" == "bob"
fetchRequest.predicate = NSPredicate(format: "firstName ==[c] %#", firstName)
var error : NSError?
let results = context.executeFetchRequest(fetchRequest, error: &error) as [User]
// Handle errors here
return results
}
Oh right, we have nothing to fetch. You can also insert objects...
func insertNewUser() -> User
{
return NSEntityDescription.insertNewObjectForEntityForName("User", inManagedObjectContext: context) as User
}
And of course you can delete objects...
func deleteUser(user: User)
{
context.deleteObject(user)
}
The key is to remember that CoreData contexts (NSManagedObjectContext) keep track of changes in memory. While you can perform these CRUD operations on a context and see the changes instantly (within the same context), they will not persist in the database until you save the changes:
func saveContext() -> Bool
{
var error : NSError?
if context.hasChanges && !context.save(&error)
{
println("Something happened when saving! \(error!), \(error!.userInfo)")
return false
}
return true
}
You can also rollback changes from the last save by using context.rollback().
Feel free to explore CoreData and experiment with the more advanced features like predicates (NSPredicate), sort descriptors (NSSortDescriptor), and setting up object relationships.

Basically all the tick box for Core Data does is add the core data framework (CoreData.framework) to your project and setup your AppDelegate.m with the core data stack, add in a data file and possibly give you a sample view controller (depending on which project type you start with).
If you want your existing project to be setup like the template would set you up, then the quickest way is to just create a new project as an example and select Core Data tick box. Open the new project and review the AppDelegate.m file and grab the code for initializing core data stack. It's about 80 lines and has a comment calling out the Core Data Stack.
Take that over to your existing project and drop it in to your AppDelegate file. Also in your existing project, add the CoreData.framework, then add in a new file (File->New File->CoreData), under Core Data called a "Data Model" file. This file is used to define the equivalent of your data schema. You select it to use the graphical controls.
Then use your sample project to review how you access the core data stack by reviewing the sample ViewController.
Note: some people are not fond of how Apple sets up the Core Data stack in the AppDelegate.m and you'll find many comments about it and how to do it better, if you search for it (I feel compelled to make this disclaimer). There are also some 3rd party libraries on GitHub that can assist you in that regard as well. (MagicalRecord,SLCoreDataStack, etc).
hope that helps!
be well!

Related

I want to addChild the Entity created by the RealityComposer

I'm adding addChild to a custom class that inherits the Entity that I created with RealityComposer, but the Entity is not placed at the tapped position and is displayed in the center of the screen.
I'm using the official sample by Apple of collaborative session creation and implemented so far I've done it. (It doesn't use RealityComposer.)
For example, tapping on an Entity will place it in that location.
However, when I add an Entity to the scene, such as a container that is an addChild of an Entity created by RealityComposer, it always appears in the middle.
My guess is that this is because Entities created with the RealityComposer are not HasModel compliant.
In this code, the Entity is always in the center of the screen
(I've already created a QRScene.rcproject.)
final class QRCardEntity: Entity, HasModel {
let twitterCard = try! QRScene.loadTwitterCard ()
var cardContainer: HasModel {
twitterCard.allChildren().first { $0.name == " CardContainer" }! .children[0] as! HasModel
}
required init() {
super.init()
addChild(twitterCard) // preservingWorldTransform: true does not change this.
}
}
However, this code puts it in the right place.
final class QRCardEntity: Entity, HasModel {
let twitterCard = try! QRScene.loadTwitterCard ()
var cardContainer: HasModel {
twitterCard.allChildren().first { $0.name == "CardContainer" }! .children[0] as! HasModel
}
required init() {
super.init()
model = cardContainer.model
}
}
Extensions used
private extension Entity {
func allChildren() -> [Entity] {
children.reduce([]) { $0 + [$1] + $1.allChildren () }
}
}
I don't think this is the best way.
AddChild is a better way to add a whole Entity while preserving the hierarchical relationship.
This model assignment can only add models from the Top hierarchy, so it doesn't add the other Models needed for display.
How do I get the Entity created by the RealityComposer to appear in the correct position with addChild?
ps 2020/07/03
To be precise, when you tap on Device1, Entity appears in the center and
Device2 shows Entity asynchronously centered no matter where the camera is pointed.
Instead of Entity of created by RealityComposer, using code like this, it works.
addChild(
ModelEntity(
mesh: .generateBox(size: 0.1),
materials: [
SimpleMaterial(color: color ?? .white, isMetallic: false)
]
)
)
If the position is already zero, you could try something like this that may get it looking right, but the positions of your Entities may be a bit back and forth:
twitterCard.position = -twitterCard.visualBounds(relativeTo: nil).center
That may need a little tweaking, but hopefully the intention is clear.
I've had issues understanding the hierarchy that Reality Composer gives to Entities, which is why I have avoided using it.
I solved by myself.
The reason is because I added the scene as child.
This code is to load the scene created by RealityComposer.
TwitterCard was scene name.
let twitterCard = try! QRScene.loadTwitterCard()
Scene name is presented in here.
So, correctly ModelEntity's name is here.
I needed to use this.
This load correctly.
addChild(twitterCard.cardObject!)
Only write this, it could add child Entity created by RealityComposer.
I have problem yet.
I can’t post notification for moving motion created by RealityComposer.
Because of twitterCard is not added to ARView’s scene. If can’t do this, we must write a lot of animation codes.
When needed, I create another post, thanks.

How to store a non-standard persistent attribute in Core Data (ex. NSRect)?

I'm struggling with my first Core Data app and not having the smoothest ride :o(
I have a drawing app with a baseclass called DrawingObject which subclasses NSManagedObject and has two properties:
#NSManaged var frameAsValue: NSValue
var frame: NSRect
DrawingObject has a subclass DrawingRectangle. All have corresponding entities with fully qualified classnames set. The frameAsValue attribute is marked as transformable and frame is marked as Undefined transient. The problem is that I get an unrecognised selector error for the frameAsValue property when creating a DrawingRectangle.
I've seen suggestions to transform NSRect to a string to save it to Core Data but this seems error prone (localization) and hackish (if thats a proper word ;o). Here is the code for DrawingObject:
class DrawingObject: NSManagedObject {
#NSManaged var frameAsValue: NSValue
var frame: NSRect = NSZeroRect {
didSet {
frameAsValue = NSValue(rect: frame)
}
}
override func awakeFromInsert() {
frame = NSMakeRect(0, 0, 0, 0)
}
override func awakeFromFetch() {
frame = frameAsValue.rectValue
}
}
I'm now assuming that you have to declare all of the inherited properties in the class hierarchy in each entity. I don't have time to test this now, but will be back soon.
You have to set parent entity of DrawingRectangle to DrawingObject to be able to inherit the properties from the parent entity (just like in the class-hierarchy). You also have to set parent--child entity relationships if you intend to save different subclasses of a parent-entity/parent-class into the same to-many relationship.
For examble if you want to save different subclasses of DrawingObjects in a #NSManaged var objects: NSSet in a Drawing for example, you have to set the parent relationships on the entities to correspond to your NSManagedObject class-hierarchy. So in this case you would set the parent of the DrawingRectangle entity to DrawingObject.
Otherwise NSRect is saved as in the code shown in the question by setting the frame-property of the DrawingObject-entity to transient and optional and the frameAsValue as transformable. You should not specify a valuetransformer so Core Data will use the default value transformer which works perfectly in this case.

Separation of Concerns: Returning Projected Data between layers From a Linq Query

I'm using Linq and having trouble doing something that I believe should be trivial. I want to return data from one layer so it can be used independently of linq in another layer.
Suppose I have a Data Access Layer. It knows about the entity framework and how to interact with it. But, it doesn't care who accesses it. The one interesting requirement I have is that the queries in the entity framework return projected data that is not part of the Entity Model itself. Please don't ask me to change this part of the requirement and make POCOs for each return type, as it is not the best design given the problem I am trying to solve. Below is an example.
public class ChartData
{
public function <<returnType??>> GetData()
{
MyEntities context = new MyEntities();
var results = from context.vManyColumnsOfData as v
where v.CompanyName = "acme"
select new {Year = v.SalesYear, Income = v.Income};
return ??;
}
}
Then, I would like to have an ASP.Net UI layer be able to call into the Data Access Layer to get the data in order to bind it to a control. The UI layer should have no notion of where the data came from. It should only know that it has the data it needs to bind. Below is an example.
protected void chart_Load(object sender, EventArgs e)
{
// set some chart properties
chart.Skin = "Default";
...
// Set the data source
ChartData dataMgr = new ChartData();
<<returnType?>> data = dataMgr.GetData();
chart.DataSource = data;
chart.DataBind();
}
What is the best way to send linq projected data back to another layer?
If you don't need to use the projected type statically, just return IEnumerable<object>.
Please don't ask me to change this part of the requirement and make
POCOs for each return type, as it is not the best design given the
problem I am trying to solve.
I feel like I should rightly ignore this, as the best thing to do is to return a defined type. Anonymous types are useful when they are wholly contained within the method that creates them. Once you start passing them around, it is time to go ahead and give them the proper class treatment.
However, to live within your imposed limitations, you can return IEnumerable<object> from the method and use that or var at the callsite and rely upon the dynamic binding of the control to get at the data. It's not going to help you if you need to deal with the object programmatically, but it will serve fine for databinding.
You can not return an anonymous type, so basically for this you will need POCO's even though you don't want them.
"not the best design given the problem I am trying to solve"
Could you explain what you are trying to achieve a little more? It might be possible to return some type of list containing a dictionary of items (ie rows and columns). Think something like an untyped dataset (yuck)
Your GetData method can use IEnumerable (the "old" non-generic interface) as its return type.
Any dynamic resolution (e.g. ASP.NET or XAML bindings) should work as expected, which seems to be what you want to do.
However, if you want to use the results in your code, you will probably have to resort to .NET 4's dynamic keyword.
The following example can be run in LINQPad (in "C# Program" mode) and illustrates this:
void Main()
{
var v = GetData();
foreach (dynamic element in v)
{
((string)element.Name).Dump();
}
}
public IEnumerable GetData()
{
return from i in Enumerable.Range(1, 10)
select new
{
Name = "Item " + i,
Value = i
};
}
Keep in mind that, design-wise, coding like this will make most people frown and can affect performance.

How to save and retrieve lists in PhoneApplicationService.Current.State?

I need to store and retrieve lists in PhoneApplicationService.Current.State[] but this is not a list of strings or integers:
public class searchResults
{
public string title { get; set; }
public string description { get; set; }
}
public List<searchResults> resultData = new List<searchResults>()
{
//
};
The values of the result are fetched from internet and when the application is switched this data needs to be saved in isolated storage for multitasking. How do I save this list and retrieve it again?
If the question really is about how to save the data then you just do
PhoneApplicationService.Current.State["SearchResultList"] = resultData;
and to retrieve again you do
List<searchResults> loadedResultData = (List<searchResults>)PhoneApplicationService.Current.State["SearchResultList"];
Here is a complete working sample:
// your list for results
List<searchResults> resultData = new List<searchResults>();
// add some example data to save
resultData.Add(new searchResults() { description = "A description", title = "A title" });
resultData.Add(new searchResults() { description = "Another description", title = "Another title" });
// save list of search results to app state
PhoneApplicationService.Current.State["SearchResultList"] = resultData;
// --------------------->
// your app could now be tombstoned
// <---------------------
// load from app state
List<searchResults> loadedResultData = (List<searchResults>)PhoneApplicationService.Current.State["SearchResultList"];
// check if loading from app state succeeded
foreach (searchResults result in loadedResultData)
{
System.Diagnostics.Debug.WriteLine(result.title);
}
(This might stop working when your data structure gets more complex or contains certain types.)
Sounds like you just want to employ standard serialisation for your list object, see here in the MSDN docs
http://msdn.microsoft.com/en-us/library/ms973893.aspx
Or also XML serialisation if you want something that can be edited outside of the application (you can also use the Isolated Storage exploter to grab the file off and edit later)
http://msdn.microsoft.com/en-us/library/182eeyhh(v=vs.71).aspx
Alternatively i would also suggest trying out the Tombstone Helper project by Matt Lacey which can simplify this for you greatly
http://tombstonehelper.codeplex.com/
The answer by Heinrich already summarizes the main idea here - you can use the PhoneApplicationService.State with Lists like with any objects. Check out the MSDN docs on preserving application state: How to: Preserve and Restore Application State for Windows Phone. There's one important point to notice there:
Any data that you store in the State dictionary must be serializable,
either directly or by using data contracts.
Directly here means that the classes are marked as [Serializable]. Regarding your List<searchResults>, it is serializable if searchResults is serializable. To do this, either searchResults and all types referenced by it must be marked with the [Serializable] OR it must be a suitable Data Contract, see Using Data Contracts and Serializable Types. In short, make sure the class is declared as public and that it has a public, parameterless constructor.

Composite WPF: Showing/Hiding Views?

I am getting up to speed on Composite WPF, building a small demo app to work through the issues. My app has one region, and two modules, Module A and Module B. Each module contains a simple "Hello World" text block. Both modules are set up as load-on-demand, as per this MSDN How-To.
The shell has two buttons, "Load Module A" and "Load Module B". When a button is clicked, the corresponding module is loaded. So, lets say I click "Load Module A", then "Load Module B". Module A, then Module B load as expected. But if I click "Load Module A" again, nothing happens.
I'm stuck at this point. I think my problem is that I need to activate and deactivate the views in the modules, rather than using load-on-demand. But I have no idea how to do that, and I can't find any documentation or blogs that talk about it.
So, here's my question: How to I load/unload (or show/hide) views? If someone can point me to sample code, I'd really appreciate it. Thanks for your help.
I found my answer. Here is the approach: Load all the modules at startup, then activate and deactivate views as you need them. I am going to write this issue up as a CodeProject article, but here is an outline of how to do it:
(1) In the module Initialize() method, add the module, but don't activate it:
public void Initialize()
{
// Get main region
var mainRegion = m_RegionManager.Regions["MainRegion"];
// Load Module B
var newView = new ModuleBView();
mainRegion.Add(newView, "ModuleA.ModuleAView");
}
Note that the Add() method has two parameters. The second parameter is the name of the view, which we set to the value produced by the ToString() method of the view. We'll see why in the next step.
(2) When activating a view, we need to deactivate the previous view. But we may not know the name of the view, so we deactivate all active views:
public static void ClearRegion(IRegion region)
{
// Get existing view names
var oldViewNames = new List<string>();
foreach (var v in region.Views)
{
var s = v.ToString();
oldViewNames.Add(s);
}
// Remove existing views
foreach (var oldViewName in oldViewNames)
{
var oldView = region.GetView(oldViewName);
region.Deactivate(oldView);
}
}
Since we set the name of each view equal to its ToString() value, we can get the names easily without knowing anything about them in advance.
(3) Now we activate the new view. I do it in an MVVM ICommand.Execute() method:
public void Execute(object parameter)
{
// Get main region
var mainRegion = m_ViewModel.RegionManager.Regions["MainRegion"];
// Clear region
ModuleServices.ClearRegion(mainRegion);
// Activate Module A view
var moduleAView = mainRegion.GetView("ModuleA.ModuleAView");
mainRegion.Activate(moduleAView);
}
Hopefully, that will be enough to get you going. Like I said, I plan to do a more complete writeup, with demo code, for CodeProject.
David Veeneman
Foresight Systems

Resources