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

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.

Related

Xcode / AppleScript / Objective-C (AppDelegate.applescript)

I am having a lot of issues following this video tutorial. I'm getting hung up on the AppDelegate.applescript file. AppleScript Facelift
Here are the steps I'm taking:
1. Create a new project
2. Then I choose "App"
3.1 Options for interface
3.2 Options for Language
Save to file location - can't attach an image due to limit
5. "Template" files created
6. I can add frameworks and libraries (not sure if this is necessary? im doing things blindly...)
7. Then these are the AppDelegate files. (note- I can change the identity and type in the top right fields. if I select AppleScript Uncompiled Source, it does not change the extension)
I've tried creating a new "blank file" and saving it as AppDelegate.applescript, but I don't think this is working. Can someone point me in the right direction???
I've been working on an AppleScript project which works with my Apple Music. Someone mentioned I should look into Objective-C AppleScript, but this is making no sense. I think my end goal is having a window that allows more flexibility with input...
Thanks in advance!
The answer: I was taking a very dated approach to this and asking the wrong question.#has's suggestion to look at his project Swift-AppleScriptObjC is exactly what I needed.

Documentation for Xcode Source Editor Extension

I'm looking for some documentation of the new Xcode Source Editor Extensions in Xcode 8.
As far as I can see there is only the "documentation" found in the header file for XcodeKit. Would be great to get something that's more detailed and more official.
Very preliminary XcodeKit reference documentation is now available.
Our WWDC 2016 presentation introducing Xcode Source Editor Extensions remains the best walkthrough.
The very shortest version, however, is: Because App Extensions need to be embedded in an application, you need to first create a new macOS Cocoa Application, and then add a new Xcode Source Editor Extension to that application. Then the XcodeKit reference should help some in implementing that.
Not really a documentation but a good reference also
https://developer.apple.com/videos/play/wwdc2016/414/
Extensions, at the moment, are poorly documented. There are a lot of assumptions made (for example, did you know that you can execute the container app? Yup, it’s really nice for settings GUI - see this How To Execute Container App - Second Answer)
At the moment, there are a lot of things missing: for example, there isn’t a structure that shows the corresponding lines with the data object - though this is quickly created with the following code:
var matches: [NSTextCheckingResult] = []
do {
let regex = try NSRegularExpression(pattern: "\n", options: [])
matches = regex.matches(in: completeBuffer,
options: [],
range: NSMakeRange(0, completeBuffer.count))
}
catch {
}
This gives you the location of all the \n’s - you should be able to fill out the rest to give you starting and ending positions which should match up to the lines.
All in all, there is a lot to like about the extension, but there are quite a few things missing as well.
Currently the only available documentation is in the headers; there's nothing "unofficial" about them. If you have specific questions, please ask.

Xcode cannot find ProductModuleName-Swift.h

I'm attempting to import my "-Swift.h" file into one of my Objective-C .h files but xcode keeps telling me that the file doesn't exist
#import "Aesculus-Swift.h"
If I command click on the file name it will take me to the generated header file so I know it exists. Why is xcode not able to find it?
This seems like just another issue with Xcode and it's complex tool chain of static analysers and compilers.
Openradar lists radar://21362856 - Swift to Objective-C bridging is unreliable. I am sure there are more but I stopped looking after finding one for this example.
The author imarcelv notes in the description:
I asked a Swift engineer at WWDC in a lab and even he didn't know how to fix this issue.
Steps to Reproduce:
Add a ramdom Swift class to an Objective-C project
Add the #import "ModuleName-Swift.h" file that Xcode generates automatically
Try to use it or just try to compile the project
From time to time it simply doesn't work
It's probably best to file a radar on this issue as it seems that others are already calling it out.
One other thing you could try...
Historically, it was possible for Xcode to completely lose it's syntax highlighting and you could always find out what files the static analyser was giving up on by increasing log level of clang.
I'm not sure if it's still relevant but if I was in your position I'd be trying this command:
defaults write com.apple.dt.Xcode IDEIndexingClangInvocationLogLevel 3
This generates logs you can search with using Console.app for just xcode to highlight the messages. You'll want to trash the derived data of your project to force it to re-compile things.
Although not the same issue as what you're seeing, I have had this post on the syntax highlighting issue bookmarked for years for the above defaults write command to try in times like these.
I solved this recently by adding the following entry to my .xcconfig (you could add it in Xcode's Build Settings > User Header Search Paths if you prefer).
USER_HEADER_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR)/MyFramework.framework/Headers
This tells the compiler to search for headers in the build output directory, which is where Xcode puts the generated header (at least in the case of this framework).
In my case this is a directory like ~/Library/Developer/Xcode/DerivedData/MyProject-LongCode/Build/Products/Debug-iphonesimulator/MyFramework.framework/Headers/MyFramework. You might find your generated header in there too.
Xcode's header and dependency management is a hot mess, and it's not surprising that it doesn't work for you.
I had trouble with this stuff & found that your -Swift file is the Product name of your Target ( not just the name of your Target ) . I found the details here helpful: http://ericasadun.com/2014/08/21/swift-calling-swift-functions-from-objective-c/
When you encounter such situation, just find your kinda "ProductName-Swift.h" file by just cmnd+click on it (even if xcode shows warning about it is not found, the #import "Aesculus-Swift.h" string is still clickable) and then in opened code editor window choose context menu and "Show in Finder" item, then explicitly add it to your project.

Compile time check for valid file references in Xcode

Is it possible to force the Xcode complier to verify that files referenced in code are valid?
There are multiple points in Cocoa development when you naturally reference a file programmatically via an NSString:
[UINib nibWithNibName:#"MyNib" bundle:nil];
[UIImage imageNamed:#"MyImage"];
[[UIViewController alloc] initWithNibName:#"MyNib" bundle:nil];
Is there any way at compile time to check is these file references are valid?
Often times after using above methods, I end up changing the name of the referenced file but forget to change the name in code. Everything complies without a problem and it is only when you happen to go to the portion of the app that accesses this file that the bug will reveal itself.
Is there another approach or technique that people use to avoid this sort of error?
Referencing a file name via a string feels very fragile.
Warning: This answer is mostly outdated. The general idea is fine but better solutions exist now (e.g. Image assets with a SwiftGen script to generate an enum).
Nibs usually have a class with the same name as the file, e.g.
[[MyViewController alloc] initWithNibName:NSStringFromClassName([MyViewController class]) bundle:nil];
I usually hide it into the controller's init method as [self class].
For image loading, compile-time checks are difficult. Help yourself with macros, first replace the loading method by a simple macro, e.g.
#define LOAD_IMAGE(__IMAGE_NAME__) [UIImage imageNamed:__IMAGE_NAME__]
First thing you should do is to put an assert into this macro and always check that the image was successfully loaded. It's not a compile-time check but it helps to find missing resources.
The second thing is to write a ruby/python/shell/(any scripting language) script that will search your source files for LOAD_IMAGE and check if the file (between parenthesis) exists. As a shell script, it will be very simple (e.g. using grep). You can add this script into your xcode project to be run when compiling.
Don't forget to check images referenced by xibs.
However, often you have to create the image name dynamically, e.g. NSString* imageName = [NSString stringWithFormat:#"image_%i", index]. There's no way how you can check this at compile time.
Also don't forget to do the reverse check - don't include image files which are not used anywhere.
AutoComplete for [UIImage imageNamed:] by Kent Sutherland.
This provides code completion support within Xcode - a brilliant piece of code. This is working for me in Xcode 4.6:
Currently this project does not have support for strings other than imageNamed:. To support those, I will try to write a compile time script. Or maybe I will become bold and try to extend Mr. Sutherland's spectacular work.
Xcode doesn't support this, but if this problem is really biting you then you could use the following hack:
Give every in-bundle file a unique prefix (e.g. app__)
When you add a file to your project, make sure you first rename it to add this prefix.
Your compile time (pre-distribution) check then has two parts: 1) Search through all .m files and enumerate strings that begin with the prefix. You shouldn't have to check if the string is quoted since your prefix is unique. 2) grep project.pbxproj for each string to check if it is included in the bundle.
With some effort, this process can be mostly automated and also optimized, but the above recipe ought to work.
here is a bash script that we use that lists all images on disk but NOT referenced in code.
https://gist.github.com/3750087
it would likely be easy to reverse this to check for non-exting images and xibs.
Anyways, the script should be a good starting point

Multiple apps from one project

I have a little problem.. which I know there is a fix for, I just don't know what it is.
The problem is the following. A few weeks ago (2 or so) I had to remove 4 apps from the appstore due to a data problem on my server side. I decided to upgrade all the apps to the latest version at the same time giving them some new features. (I have 6 of the same apps out there, targeting different airports). The difference between these versions are the following:
A set of 50-80 or so images that combine the map of each airport. The filenames are the same in each app. (How do I solve that?)
The name of the app
The Default.png (and those for iPad and retina of course)
The App Icon
The content of a details page (which exists in a .plist file)
The content of the "About" page where the page refers back to the app
Some localize content, refering back to the airport the app targets.
The provisioning profiles, of course.
Keeping track of these things are just a pain in the ass, so I want to have 1 project with 1 code base and just add the images and details (mentioned above) and new versions appear. When I "Archive", I want all of the apps to be build and ready to be send of to apple (which I will have to do manually).
How can I achieve this?
I have done this before using multiple targets and conditional compilation. You need one target per deliverable. You can configure the name, icons etc for each target in the usual way.
A set of 50-80 or so images that combine the map of each airport. The filenames are the same in each app. (How do I solve that?)
Keep the images in different directories and for each target only add the images for that app. This technique will also work for the contents of the about page if you can load that from a file.
I also use conditional compilation so that I can define different values for my constants for each app.
To do this add a setting to Other C Flags and Other C++ flags to identify your app. Something like:
-DAPP_VARIANT=1
In your code you can then use the following to implement any app specific behaviour:
- (id)init
{
#if APP_VARIANT == 1
self->server_url = [[NSURL URLWithString:#"http://app1.example.com"] retain];
#elif APP_VARIANT == 2
self->server_url = [[NSURL URLWithString:#"http://app2.example.com"] retain];
#endif
}
Can't you use one version control branch for all the main code, then fork it six times (once for each airport) where you fill in the data? Once you make new code changes, just push the changes from the main branch to the forks and you're done.