Azure DevOps Extension Placeholders - azure-devops-extensions

I want to be able to use a place holder in the extension code that takes its actual value from the extension manifest upon packaging (or build), such as the version.
So for example in the vss-extension.json there's a version property set to 1.2.3, so in the build task code inside that extension I want to be able to use something like that:
// index.ts
var agentId = "myext-azurepipelines-${extension.version}";
that would translate to:
// index.ts
var agentId = "myext-azurepipelines-1.2.3";
when packaging or building, but without changing the original file (a template of a sort).
Is something like this available?

Unfortunately, your requirement is not able to achieve currently. You have to modify the original file.

Related

Storing Global/Environment Variables in iOS Swift Apps

I'm a JavaScript web app developer that has just inherited a legacy iOS app. The app is full of hardcoded URLs that point to our production API.
I'd like to factor out the Base URL into a constants file, or an environment variables file, or a config file, so that a different Base URL can be used in different environments (dev, staging, production).
What's the best practice in the land of the Swift?
Is there something similar to a .env file I can use?
What I always do is probably a more flexible approach than checking constants in your code and littering different values of the same variable per environment everywhere.
It is not necessary to create .xcconfig files either.
Setting up the whole thing is a little more work but you will see that it is very easy to use afterwards.
In my example you see how I set different REST endpoints for production/UITest/development builds in my Toeppersee iOS app.
Changes to your Info.plist
First, just define a new constant and select a name that identifies your content, in my case it is TS_WEBSERVICE_URL:
Make sure to set its value to the name you chose, enclosed in $() (brackets, not braces!), in my case it's $(TS_WEBSERVICE_URL).
Build settings
In your build settings tab, add custom "user defined" build parameters, again with the same name:
You see that you can define values for Debug, UITests and Release. And that I use this mechanism for a whole lot of things that are different in the environments.
Adding more environments
If you need more environments, add them on your master project node's Info tab (like I probably did here with UITests):
Use the values in code
Now for the easy part: use your newly defined values in your code. It all boils down to getting an NSBundle instance and reading the value defined in your Info.plist which has been substituted automatically during build process.
Objective C:
NSString *webserviceURL = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"TS_WEBSERVICE_URL"];
Swift:
let webserviceURL = Bundle.main.object(forInfoDictionaryKey: "TS_WEBSERVICE_URL") as! String
Just one line of code per environment setting.
One thing I have not shown, but which is also fairly easy: just define a category to NSBundle and supply new methods called webserviceUrl and similar methods. They can then be called like this: Bundle.main.webserviceUrl() which even more simplifies the source code.
Edit: write extension to Bundle object (optional)
To achieve the aforementioned simplified access to the webservice URL, a new Swift file is created and called (for example): Bundle+AppAdditions.swift
import Foundation
// An extension (or category in Objective C) extends any types functionality,
// in our case the NSBundle foundation class.
extension Bundle {
// get the webservice URL from an NSBundle instance
public func webserviceUrl() -> String {
// get an object for info dictionary key and cast it as string
return self.object(forInfoDictionaryKey: "TS_WEBSERVICE_URL") as! String
}
}
The extension works directly on any given Bundle instance, not necessarily only the main bundle. Example:
let url = Bundle.main.webserviceUrl()
or any other bundle, e.g. for an App Clip Extension containing an InitialViewController:
let url = Bundle(for: InitialViewController.self).webserviceUrl()
I would actually add another static extension method to get the app clip extension bundle. But that's optional as well ;-)
Some recommend creating a config file (.xcconfig) for every environment and then you can set up your different base URL there.
Where I used to work, we used a constants file where we set up different values, urls,ports and so on depending on the environment like this:
#if DEBUG
let a = 2
#if PRODUCTION
let a = 4
I think this is the best thing to do.

C# folder.FolderPath (and many other dynamic properties)

I logon to an RDOSession, then:
foreach(RDOStore store in rdoSession.Stores)
{
RDOFolder folder = store.GetDefaultFolder(rdoDefaultFolders.olFolderInbox);
Debug.WriteLine(folder.FolderPath);
}
Visual Studio marks FolderPath as an error: RDOFolder does not contain a definition for FolderPath...
If I omit that line (so the code will run), then break right after getting the folder, I can add folder to the watch list and expand it. It shows no properties at all unless I expand the Dynamic View node. I can then see all the properties referred to in the Redemption docs.
My question is: how do I refer to Redemption properties like FolderPath in code? Some properties are fine -- e.g. folder.Items will compile just fine. But many are not -- like folder.FolderPath.
The FolderPath property is implemented by the IRDOFolder2 interface and derived from RDOFolder. Try to cast an instance of the RDOFolder
class to IRDOFolder2 and get the property value.
Also you may try using the Outlook object model which provides the MAPIFolder.FolderPath property which returns a string (string in C#) that indicates the path of the current folder.

How to configure Simple Injector depending on build configuration

I want to be able, to configure Simple Injector differently for each developer (for prototyping purposes for example).
The default configuration should be hardcoded, of course.
I have used Unity before, and there I was able to overwrite the hardcoded registrations by an XML configuration file. This config file was not under source control, and so other developers could overwrite the hardcoded registrations with their custom registrations without interfering with others.
The developers should not need to submit their configuration to source control.
Is such a scenario supported by Simple Injector ?
Is there any best practice for such a scenario ?
Does this make sense at all, or is there a better way to achieve what I want?
One of the design decisions for Simple Injector is to not support XML based configuration out-of-the-box. This decision is described here but can be summarizes as:
XML based configuration is brittle, error prone and always provides a
subset of what you can achieve with code based configuration. General
consensus is to use code based configuration as much as possible and
only fall back to file based configuration for the parts of the
configuration that really need to be customizable after deployment.
These are normally just a few registrations since the majority of
changes would still require developer interaction (write unit tests or
recompile for instance). Even for those few lines that do need to be
configurable, it’s a bad idea to require the fully qualified type name
in a configuration file. A configuration switch (true/false or simple
enum) is more than enough. You can read the configured value in your
code based configuration, this allows you to keep the type names in
your code. This allows you to refactor easily, gives you compile-time
support and is much more friendly to the person having to change this
configuration file.
This however doesn't completely satisfy your requirements, since you don't want to "customizable after deployment". Instead, you want to customize per developer.
For this particular case, you shouldn't fall back on XML based configuration IMO. Just as you can exclude xml files using .gitignore, you can do the same with code based configuration files that developers can change, and that will compile with the rest of the application. Here's an example:
// Global.cs
public void Application_Start() {
var container = new Container();
// Default configuration here
container.Options.AllowOverridingRegistrations = true;
DeveloperOverrides.ApplyOverrides(container);
container.Options.AllowOverridingRegistrations = false;
DependencyResolver.Current = new SimpleInjectorDependencyResolver(container);
}
// DeveloperOverrides.cs
public static class DeveloperOverrides {
public static void ApplyOverrides(Container container) {
}
}
These two files can be checked in, where the DeveloperOverrides.ApplyOverrides method is left blank. After that you add the exclusion of the DeveloperOverrides.cs to your .gitignore file.
After this, developers can add their own overrides that are checked by the compiler, but are never checked in into source control:
// DeveloperOverrides.cs
public static class DeveloperOverrides {
public static void ApplyOverrides(Container container) {
container.Register<IMailSender, FakeMailSender>(Lifestyle.Singleton);
}
}

How to load new generated private API headers in iOS?

I'm dummy in iOS especially in private API.
I have application for testing and now I need to use private API (this application not for App Store).
I downloaded generated headers from iOS-Runtime-Headers and what next?
Under /System/Library/ I have list of libraries that contain Frameworks, ... ,PrivateFrameworks as well.
Do I need to replace original framework with ones I copied from iOS-Runtime-Headers?
Because I have other applications that use Public API only and I don't want to damage them.
From posted link they tell to validate library for example by:
NSBundle *b = [NSBundle
bundleWithPath:#"/System/Library/PrivateFrameworks/GAIA.framework"];
BOOL success = [b load];
But here the path points to original path.
Or I miss something,
Thank you
First of all, don't replace any headers which are provided by Apple.
Generally, it's done one of two ways:
1) You can copy some of these headers to your project and just include these files the same way as you include any other headers
#import "SomeHeader.h"
2) Sometimes you have to sanitize them (edit them) a little bit. Quite often, these headers has something like in in them:
#import "NSObject.h"
And compliller won't be able to find it, because NSObject is built-in class. So, you need to remove this like.
3) If you just need couple of methods out of it, then Tuukka Nori solution is right.
On top of these, you will need to link (statically or dynamically) against appropriate private framework (just including headers isn't enough).
Don't replace any files. Instead, write a header file with the symbol that you intend to use. If you need an Objective-C method, add a category with a unique name, e.g.
#interface NSString (MyOwnPrivateCategory)
- (void) privateMethodDeclaredInRuntimeHeaders;
#end
Import it and use the method as you like.
The sample code given shows how to load a framework at runtime in case you don't want to link to it. Since some frameworks are private, they might not be available in all versions of iOS.

How can I add a custom extension tag to the GPXFramework code?

I found the excellent resource of the open source iOS GPX Framework (http://gpxframework.com/) which allows me to create and read GPX files. However I would like to add a custom extension to store data specific to my app. I can see that the GPX framework implements a file GPXExtensions.h and .m, but I am not sure how to go about adding say a tag for storing the speed data at a particular coordinate in the GPX.
I am guessing I would have to add the data I would like to add as an extension to the GPXExtensions class as a property and then somehow modify the code in this method:
- (void)addChildTagToGpx:(NSMutableString *)gpx indentationLevel:(NSInteger)indentationLevel
{
[super addChildTagToGpx:gpx indentationLevel:indentationLevel];
}
But I am not sure what this method is supposed to do, any ideas?
That's basically the right idea. Jaime Machuca's fork contains a commit where he added heart rate, etc. It should give you a pretty good template from which to create your own modifications. Note that if you want something more sophisticated, you may need to create your own subclass of GPXElement so that you can take advantage of the tree structure.

Resources