How do I get the XCode Project URL programmatically? - objective-c

I'm curious if there is a way to programmatically get the location of the .xcodeproj package within an Objective-C (or Swift) class contained within that package. I'd like to make a simple utility that puts files directly into the containing folder based on various app events, but I would rather avoid hard coding the path.
Essentially I want to create a target (and a reusable class) that builds swift files for NSManagedObject subclasses based on the Core Data model present in the app.

I found out the trick here is to add an item to your plist file that contains value ${PROJECT_DIR}, then you can get the location in your code with
var projectPath = NSBundle.mainBundle().infoDictionary.objectForKey("com.myapp.project_dir") as String
This assumes the plist key is "com.myapp.project_dir", of course.

Related

Prevent Xcode from creating NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END on new headers

I've updated Xcode and since updating I've been getting NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END macros around every new header file for every new class that I create.
I know what it does, but I'm not interested in nullability annotations it and it causes unnecessary warnings when I try to set some properties to nil in my app (which are perfectly okay to set to nil).
How do I prevent Xcode from creating these every time I create a new file? (other than the obvious: deleting them individually)
Based on this and this arcticles I was able to create custom template to get rid of NS_ASSUME_NONNULL* macros.
Excerpt from the first arcticle:
Location
These user-defined templates are located in
~/Library/Developer/Xcode/Templates/File Template. If such folder is
missing you can create it yourself and Xcode will be linked to it upon
reopening. Also you can group them into subfolders inside that folder.
For example, ~/Library/Developer/Xcode/Templates/File Template/Custom
Templates and Xcode will render those groups properly when creating a
new file.
Template Setup
Each template is a folder with .xctemplate extension. That folder
contains file templates and resources that will be populated and added
to a project and configuration TemplateInfo.plist.
All template settings and fields that user can fill in during template
creation are defined in TemplateInfo.plist.
create path as follows:
~/Library/Developer/Xcode/Templates/File Templates/Source/My Awesome
Template.xctemplate
go to
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File
Templates/Source/Cocoa Touch Class.xctemplate
copy TemplateIcon.png, TemplateIcon#2x.png and
TemplateInfo.plist to our My Awesome Template.xctemplate folder. You can omit copying icons if you do not need them in browser or use your own.
copy NSObjectObjective-C and NSObjectSwift folders to be able to create both Obj C and Swift files.
edit ___FILEBASENAME___.h, ___FILEBASENAME___.m, ___FILEBASENAME___.swift, for example put your macros or in our case delete surrounding NS_ASSUME_NONNULL* macros
now you are able to find your template in file creation browser on the very top
Profit!
I found a best way:
go to /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates/Source/Cocoa Touch Class.xctemplate/NSObjectObjective-C
copy ___FILEBASENAME___.h to anywhere
open and delete NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END
Finally replace the original file and enter System password

Correct way to update Objective C project for Xcode 8 Core Data NSManagedObject subclass changes

I took a break from coding for a few months and came back and discovered the changes in CoreData with Xcode8/iOS10/macOS Sierra.
I have been trying to get my head around the new NSManagedObject subclass generation in Objective C but there is very little out there on the web. I have a few things I need clarifying before I start butchering my project and messing things up completely but first, some things I have discovered from poking around that might be useful to others out there...
Where things are
Automatically generated files live buried deep in the DerivedData folder. Look in USER->Library->Developer->Xcode->DerivedData->ProjectName-lotsOfRandomLetters->Build then keep opening folders until you find DerivedSources->CoreDataGenerated.
Automatically generated files do not appear in your project folder or navigator, although if there is an error in one Xcode will display the source for you.
Things Xcode generates
There are three codegen settings - manual/none, Class Definition, and Category/Extension.
When an entities codegen is set to manual/none (which was the old behaviour) creating the NSmanagedObject subclass using Editor-> Create NSManagedObject Subclass generates 4 files inside your project...
Entity+CoreDataClass.h and Entity+CoreDataClass.m and
Entity+CoreDataProperties.h and Entity+CoreDataProperties.m
(previous version Xcode 7 generated Entity.h, Entity.m,
Entity+CoreDataProperties.h and Entity+CoreDataProperties.m files)
If the entity's codegen is set to Class Definition, Xcode generates these same 4 files automatically in the derived data folder - not the project, These files are then marked with a comment telling you not to alter them.
Xcode generates 2 files if the entities codegen is set to Category/Extension. These files are marked with a comment telling you not to alter them. These are...
Entity+CoreDataProperties.h and Entity+CoreDataProperties.m
These 2 file are expecting a Entity.h file to be in the project and will show an error in Xcode if absent. This is the one time that you will be able to see the source for one of these files within Xcode.
Whats in these files
The + CoreDataProperties files appear to be the same as those generated previous version of Xcode generated files except for one addition. They contain all the attributes / properties for the entity / NSmanagedObject and the methods to handle entities that have a one to many or many to many relationship. The new addition is a method for fetchRequest subclassing NSmanageObject's new fetchRequest method.
Questions
1) Is Class Definition now the obvious and best choice for codegen when you don't have any extra properties/functionality to add to a NSManagedObject subclass, as it automatically updates the files for you (when you save the project with cmd-s)?
2) The naming of the files with +CoreDataClass follows the convention for a category on a class, which would imply there should be a class for this to be an extension on.
Am I right in assuming that the Entity+CoreDateClass .h/m files are a straight replacement for the old Entity.h/m files? and that its not actually a category, despite the file name?
3) For new NSManagedObject subclasses should I be importing Entity+CoreDataClass.h rather than Entity.h?
4) If I want to uncluttered my project by removing most of my NSManagedObject subclass files, do i just delete the files in Xcode and set the entities codegen to Class Definition or ...
is there magic under the hood that looks for the entity+CoreDataClass when you try to #import entity.h or will I have to go through and find every reference to #import entity.h and change them to #import entity+CoreDataClass.h ?
5) Am I right in assuming that if I want a NSManagedObject subclass where I want to add a property and a method that i should set codegen to Category/Extension?
6) If I choose Category/Extension I have to create my own NSmanagedObject subclass file, its just entity.h not entity+CoreDataClass.h?
7) If entity+CoreDataClass.h is the new accepted naming format for the entity.h file why does the generated Category/Extension file look for a plain entity.h name file instead of a entity+CoreDataClass.h file? Is this just an inconsistency on Apples part and something I should just accept or am I missing something that I should know about?
Thank you.
Okay - quite a few people looked and no answers so i'll try and answer myself.
1) Yes - if you don't need to add extra properties/functionality to a CoreData entity, go with Class Definition. This creates 4 files:
Entity+CoreDataClass.h and Entity+CoreDataClass.m and Entity+CoreDataProperties.h and Entity+CoreDataProperties.m but you'll never see them as they are hidden away from sight deep inside the derived data folder. If you need to check on a attribute name you can look in the core data editor as you won't have access to these files.
2) Entity+CoreDateClass .h/m files are a straight replacement for the old Entity.h/m files. Despite using the file naming convention for a category, they are not categories, don't let Apple's naming system confuse you. Look inside the file and the class is defined as Entity not Entity+CoreDataClass.
3) For new NSManagedObject subclasses (autogenerated with the 'Class Definition' option) import Entity+CoreDataClass.h rather than Entity.h. After all' it's the file you are importing not the class defined inside. When using the class its just Entity not Entity+...
4) If you decided to declutter your project, by deleting your NSManagedObject subclass files then switching entities codegen to 'Class Definition', you will need to go through the project and change all the import statements that refer to them by adding +CoreDataClass to the file name. Fortunately its not that big a deal as Xcode will have flagged them all up as errors anyway so they are easy to find.
5) Yes - if you wish to add properties or functionality to a NSManagedObject subclass use the codegen "Category/Extension" option.
6) If you choose Category/Extension you have to create my own NSmanagedObject subclass file, name it Entity.h. Do NOT name it Entity+CoreDataClass.h because the autogenerated Entity+CoreDataProperty.h is looking to import an Entity.h file.
7) Yes, this is just a naming inconsistency on Apple's part. Don't let it throw you, like it did me.
And finally , don't forget...
if you go down the route of using codegen ->Category/Extension, if you add an additional relationship to the entity, you will need to update your Entity.h file. For example if you added a relationship to a NSManagedObject subclass called Car you would need to add the #Class Car; to Entity.h.

How to intercept reading of plist values in Objective-C code?

We're using the new Urban Airship iOS plugin for PhoneGap.
In the plugin's plist file, we're supposed to enter the app-specific keys needed to enable push notifications.
The problem is we have two versions, free and paid, of the same app, but the plist file only accommodates one version.
Essentially, we need to modify the Objective-C code to read different plist values, depending on whether it's the free or premium version.
We currently manage both versions with the same code base and Xcode project. Unless we change the plugin code, it seems like we need to create a new Xcode project, which we don't want to do.
How do we adjust Urban Airship's Objective-C files to read different values from the plsit file?
Sorry to keep you waiting, I wanted to give you a very detailed answer instead of rushing last night :) So here we go.
First in your project we need to add a new target. Go to your project settings and right click your target. Click duplicate.
You'll get a new target probably named Target-copy. You'll also get a new info.plist file just for that target.
Next we're going to edit our Pro version's Built Settings. Scroll or search and find Apple LLVM compiler 4.0 Preprocessing. Add to both your Debug and Release configurations. I normally just go with the simple PRO=1. You also need to add PRO=0 to your lite version or it will be undefined when you try to build that version.
Now lets look at how to add a custom plist like I'm sure you'll need. First create two folders. Its important these are folders not groups. In each folder we can create a plist with the exact same filename.
Since Now you can add something to each of them. I just added a key property and a value pro string / lite string. Finally to the code. In the sample project I made I simple overrode viewDidLoad but obviously this will work anywhere. Since the plists have the same name you can load them with one line of code. They'll never get mixed up because they are only copied to their respective target. If you need to do code level based logic you can use the PRO preprocessor we made.
- (void)viewDidLoad
{
[super viewDidLoad];
// This will load the proper plist automatically.
NSLog(#"Plist Value: %#",[[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Property List" ofType:#"plist"]] objectForKey:#"property"]);
// Also remember we set up a preprocessor PRO. you can use it as well.
if (PRO) {
NSLog(#"Only Show for Pro");
} else {
NSLog(#"Only Show for Lite");
}
NSLog(#"This will show for both");
}
This is the method I use for all my lite/pro version apps so I can share a common codebase without copying it between projects or other complicated systems. It has worked pretty well for me so far :) Happy Coding!
Source
Figured someone may be able to use the project to look at so here it is on GitHub.

Is there a way to put the .h and .m file in a central location to use in any of my projects?

In one of my projects I have a class that has just some standard code that I use in most off the apps and I thought it would be useful to use this class in some other projects. Is there a way to put the .h and .m file in a central location to use in any of my projects? I could make changes to the class and would not have to worry about having to correct the files in multiple places.
For XCode 4.2 you could use a workspace, which can be comprised of multiple projects. Let's say you want to build a static lib. You can have a dedicated project for a lib, and include this project in multiple workspaces. You will need to properly setup project dependencies, so changes in your lib would be automatically picked up next time you build a product.
I found it was easier to save my .h & .m file in a folder called MyClasses, then drag both files in to the project I want to add them to. Xcode gives you the option to copy the files in to my project or leave them where they are. I left them in my MyClasses folder. I can now edited this class and all my changes show up in all the apps I have this class in.
Yes you can leave the files at a central location and add only refrences of the class to your project by dragging onto xcode into your project. Just do not copy files when xcode asks for it. Like we do for some third party libraries for ex. core-plot,facebook, Three20 etc.

Objective C: Register file extension

I am creating an app which needs to be opened if a user double clicks on a file with a certain extension.. How do i register the file extension with my app? and then read the contents?.
E.G the file could have the extension words.ThisApp and it could be in XML Format.. how could I read that in objective c into an array?
I think you should read the Document-Based Applications Overview.
To register an extension to your application, bring up the Target info window (Project » Edit Current Target "My Target"... at the bottom) and open the "Properties" tab. Fill in the blanks for your document type there. For more info, read Storing document type informations in the Application's Property List, contained inside the above guide.
To read XML data, consider using a NSXMLParser (google it for examples) to drive the results into a NSMutableArray as you see fit; and to get the data into your application, consider using a NSDocument subclass, as suggested (again) in the document-based application overview.
As you might understand, this document is quite a vital read.