I'm working on the app that has share extension.
Share attachment from Mail App with Share extension is not working.
But sharing attachments(pdf, doc, image etc.) from gmail app working fine.
My swift code
for attachment in contents{
if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { data, error in
//Do action for image
}
} else {
if let item = (filePicker!.types as [String]).first(where: { (item) -> Bool in attachment.hasItemConformingToTypeIdentifier(item)}){
attachment.loadItem(forTypeIdentifier: item, options: nil) { data, error in
//Do action for file
}
}
}
}
My NSExtensionActivationRule:
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsAttachmentsWithMaxCount</key>
<integer>1</integer>
</dict>
I never work with the sharing extensions before, so any knowledge that can be shared will be a great resource for me.
Thanks in advance!
Related
wonder if anyone can help me. I have an app and I'm trying to move some files into iCloud so they'll show up in "Files" and cloud to other devices. I've been going through lots of resources online researching what's wrong, and nothing seems to help.
In my app project, I have turned on iCloud Documents in capabilities.
In my plist file, I have this:
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.mypublishername.myappname</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>myappname</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
In my entitlements file I have:
<dict>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.mypublishername.myappname</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>iCloud.com.mypublishername.myappname</string>
</array>
</dict>
in ObjC, I'm fetching the iCloud folder like so:
NSURL *rootDirectory = [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]URLByAppendingPathComponent:#"Documents"];
if (rootDirectory)
{
if (![[NSFileManager defaultManager] fileExistsAtPath:rootDirectory.path isDirectory:nil]) [[NSFileManager defaultManager] createDirectoryAtURL:rootDirectory withIntermediateDirectories:YES attributes:nil error:nil];
gCloudFolder=rootDirectory;
}
Then, when I save a file, I do so locally, and move it into the cloud folder like this:
//
// theFilename is a file in the app's documents folder...
//
int aFile=creat(theFilename,S_IREAD|S_IWRITE);close(aFile);
aFile=open(theFilename,O_BINARY|O_RDWR);
if (aFile)
{
write(aFile,theDataPtr,theLen);
close(aFile);
if (gCloudFolder)
{
NSURL *aLocalStr=[NSURL fileURLWithPath:[NSString stringWithUTF8String:theFilename]];
NSURL *aCloudStr=[gCloudFolder URLByAppendingPathComponent:#"testing_file.txt"];
NSError *error;
if (![[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:aLocalStr destinationURL:aCloudStr error:&error]) NSLog(#"iCloud Error occurred: %#", error);
}
So... what happens. This file DOES get created. If I run this twice, it tells me it can't move to testing_file.txt because it already exists. Also, if I try to setUbiquitous:NO on the file, it tells me I can't set it to no when the file hasn't been synced.
Any idea why my app's folder and this file don't show up in my FILES folder under iCloud?
I have increased the bundle version, which is something I've seen elsewhere. Did nothing.
What am I doing wrong?
This completely stunned me; I had no idea it was possible. I'll just describe my test app in full. It's going to look a lot like yours!
Here is the bundle identifier:
Here is the entitlement:
Here is the entitlement text:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.neuburg.matt.SaveIntoFilesApp</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>iCloud.com.neuburg.matt.SaveIntoFilesApp</string>
</array>
</dict>
</plist>
Here is the entry in the Info.plist:
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.neuburg.matt.SaveIntoFilesApp</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>MyApp</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
Here is the app delegate:
var ubiq : URL!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DispatchQueue.global(qos:.default).async {
let fm = FileManager.default
let ubiq = fm.url(forUbiquityContainerIdentifier:nil)
print("ubiq: \(ubiq as Any)")
DispatchQueue.main.async {
self.ubiq = ubiq
}
}
return true
}
Here is the button I tap:
#IBAction func doButton (_ sender:Any) {
if let del = UIApplication.shared.delegate as? AppDelegate {
if let ubiq = del.ubiq {
do {
let fm = FileManager.default
let docs = ubiq.appendingPathComponent("Documents")
try? fm.createDirectory(at: docs, withIntermediateDirectories: false, attributes: nil)
let url = docs.appendingPathComponent("test.txt")
print("here we go")
try? fm.removeItem(at: url)
try "howdy \(Date())".write(to: url, atomically: true, encoding: .utf8)
print("saved")
} catch {
print(error)
}
}
}
}
I did have to increment the bundle version (from 1 to 2) and I did have to kill and restart the Files app. And then I saw my file (and can open and examine it):
I have a working share extension written in swift. When I test a share operation from Safari, I always only get the URL type (kUTTypeURL).
What I want is to get some form of rendered version of what the user is looking at (PDF or HTML?). Using the URL and opening it in a webview is not workable due to authentication issues, etc.
I've tried many different activation rules with no change. Here is the current one I am using:
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>20</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>0</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
My controller looks like this - when run from Safari, it always only has one attachment type - the URL:
override func didSelectPost() {
if let item = extensionContext?.inputItems.first as? NSExtensionItem {
if let attachments = item.attachments {
for attachment: NSItemProvider in attachments {
if attachment.hasItemConformingToTypeIdentifier(kUTTypePropertyList as String) {
attachment.loadItem(forTypeIdentifier: kUTTypePropertyList as String, options: nil, completionHandler: { (data, error) in
// Do stuff with this content now
self.extensionContext?.completeRequest(returningItems: [], completionHandler:nil)
})
}
if attachment.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
attachment.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil, completionHandler: { (url, error) in
if let shareURL = url as? NSURL {
// Do stuff with your URL now.
}
self.extensionContext?.completeRequest(returningItems: [], completionHandler:nil)
})
}
}
}
}
}
Other approaches I've seen use a javascript file to walk the DOM but have seen no good examples and I'm not clear on if this would help me in any case.
The key is in using this line in your info.plist file:
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionJavaScriptPreprocessingFile</key>
<string>Action</string>
<key>NSExtensionActivationRule</key>
...your should be fine...
</dict>
At NSExtensionJavaScriptPreprocessingFile you specify the name of the .js file that contains javascript file that must contain a global object named ExtensionPreprocessingJS. You should then search for the items conforming to kUTTypePropertyList in your code (and it seems that you do already that looking at your code).
This is a short list of what you should do, ask if you need something more and it is easy to find more data on the internet if you start from this too.
I'm trying to implement Facebook login in my Xamarin.iOS application. I have tried to use Xamarin.Auth package, but sometimes user cannot processed and there are some issues with this package. I found the best way is to implement manual login flow to my application (using web-view). Currently, I have created an app in Faceboook developer portal, and I can access the link from my iOS application.
So, user will click normal button which will be forwarded to Facebook login page. My question is, how can I get the result from Facebook login page? and how can I get user id, email, ... ?
Here is the source code so far:
partial void BtnFacebookLogin_TouchUpInside(UIButton sender)
{
NSUrl apiRequest =
new NSUrl("https://www.facebook.com/dialog/oauth?client_id="
+ SharedResources.fbClientId
+ "&response_type=token&redirect_uri="
+ SharedResources.fbRedirectUrl);
UIApplication.SharedApplication.OpenUrl(apiRequest);
}
I found the answer by using Facebook SDK, which is currently considered as the best existing solution, after your download the official SDK (Xamarin.Facebook.iOS), here are the steps:
In Info.plist file, add these codes:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>facebook.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
<key>fbcdn.net</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
<key>akamaihd.net</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fbapi</string>
<string>fbapi20130214</string>
<string>fbapi20130410</string>
<string>fbapi20130702</string>
<string>fbapi20131010</string>
<string>fbapi20131219</string>
<string>fbapi20140410</string>
<string>fbapi20140116</string>
<string>fbapi20150313</string>
<string>fbapi20150629</string>
<string>fbauth</string>
<string>fbauth2</string>
<string>fb-messenger-api20140430</string>
<string>fb-messenger-api</string>
<string>fbauth2</string>
<string>fbshareextension</string>
</array>
Also in Info.plist file, the original view, Advanced tab, URL Types, add your facebook app ID like this way "fb1112222......". Keep 'fb' at the beginning.
Pic: Facebook app ID in Info.plist
In AppDelegate.cs, override these methods:
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
Profile.EnableUpdatesOnAccessTokenChange(true);
Settings.AppID = "fb app id";
Settings.DisplayName = "fb app name";
// This method verifies if you have been logged into the app before, and keep you logged in after you reopen or kill your app.
return ApplicationDelegate.SharedInstance.FinishedLaunching(application, launchOptions);
//return true;
}
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
// We need to handle URLs by passing them to their own OpenUrl in order to make the SSO authentication works.
return ApplicationDelegate.SharedInstance.OpenUrl(application, url, sourceApplication, annotation);
}
Lastly, this is the function for Facebook, you can use their own button, or use your button. In my way, I used my button with the name (BtnFacebookLogin).
//Here is the facebook function
private void SetupFacebookLoginButton()
{
List readPermissions = new List { "public_profile", "email" };
////Set Up Button Design
var fbBtnText = new NSAttributedString("login with facebook", new
UIStringAttributes()
{
ForegroundColor = UIColor.White
});
BtnFacebookLogin.SetAttributedTitle(fbBtnText, UIControlState.Normal);
BtnFacebookLogin.SetBackgroundImage(
UIImage.FromBundle("btn_long_blue.png"), UIControlState.Normal);
//Strat Login Functions
Profile.Notifications.ObserveDidChange((sender, e) =>
{
if (e.NewProfile == null)
return;
if (AccessToken.CurrentAccessToken != null)
{
var request = new GraphRequest("/me?fields=name,email", null, AccessToken.CurrentAccessToken.TokenString, null, "GET");
request.Start((connection, result, error) =>
{
// Handle if something went wrong with the request
if (error != null)
{
showAlert("Error", error.Description);
return;
}
fbReponseFromSDK facebookSDKLoginItem = new fbReponseFromSDK();
// Get your profile name
var userInfo = result as NSDictionary;
if(userInfo["id"] != null)
{
Console.WriteLine("id is: " + userInfo["id"].ToString());
}
if (userInfo["name"] != null)
{
Console.WriteLine("name is: " + userInfo["name"].ToString());
}
if (userInfo["email"] != null)
{
Console.WriteLine("email is: " + userInfo["email"].ToString());
}
//(Success) Do what you want next :
doneFacbookLogin();
});
}
});
// Handle actions once the user is logged in
BtnFacebookLogin.ReadPermissions = readPermissions.ToArray();
BtnFacebookLogin.Completed += (sender, e) =>
{
if (e.Error != null)
{
// Handle if there was an error
showAlert("Facebook Login", e.Error.Description);
return;
}
if (e.Result.IsCancelled)
{
// Handle if the user cancelled the login request
//showAlert("Facebook Login", "Login Cancelled");
return;
}
showAlert("Facebook Login", "Login Successfull");
};
// Handle actions once the user is logged out
BtnFacebookLogin.LoggedOut += (sender, e) =>
{
// Handle your logout
};
// If you have been logged into the app before, ask for the your profile name
if (AccessToken.CurrentAccessToken != null)
{
var request = new GraphRequest("/me?fields=name,email", null, AccessToken.CurrentAccessToken.TokenString, null, "GET");
request.Start((connection, result, error) =>
{
// Handle if something went wrong with the request
if (error != null)
{
showAlert("Error", error.Description);
return;
}
// Get your profile name
});
}
}
For more info, refere to their samples in GitHub (Link)
Can you please help me in the matter of supporting apple news sharing ,
My Share Extension info.plist contains :
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsAttachmentsWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>10</integer>
</dict>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
How can i see my share extension while share some thing from apple news ?
OK I sorted this out. You need to configure your Extension to allow content for both public.plain-text and public.url types. Apple News sends an ItemProvider with two attachments, first a plain-text piece with the article summary, and second a Web URL to the article itself. You must accept and process both.
Try these extension attributes. They use a predicate to find the required URL type attachment (assuming that's what you want):
<key>NSExtensionActivationDictionaryVersion</key>
<integer>2</integer>
<key>NSExtensionActivationUsesStrictMatching</key>
<integer>2</integer>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<string>SUBQUERY(extensionItems, $e, (
SUBQUERY($e.attachments, $a, ANY $a.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url").#count == 1
)).#count == 1
</string>
<key>RequestsOpenAccess</key>
<true/>
</dict>
And code along these lines to find the proper URL attachment, again, assuming that's the bit you want:
NSExtensionItem *inputItem = self.extensionContext.inputItems.firstObject;
NSItemProvider *itemProvider;
for (itemProvider in [inputItem.userInfo valueForKey:NSExtensionItemAttachmentsKey]) {
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *) kUTTypeURL]) {
break;
}
}
if (!itemProvider) {
// Handle error here
return;
}
[itemProvider loadItemForTypeIdentifier:(NSString *) kUTTypeURL options:nil completionHandler:^(NSURL *url, NSError *error) {
// Handle the URL here
}];
Here's my rough Swift 4 version I'm using together with your magic PLIST. Seems to work in both News and Safari.
func getUrl(callback: #escaping ((URL?) -> ())) {
guard let items = extensionContext?.inputItems,
let item = items.first as? NSExtensionItem,
let attachments = item.attachments else {
callback(nil)
return
}
var found = false
for attachment in attachments {
if let provider = attachment as? NSItemProvider {
if provider.hasItemConformingToTypeIdentifier("public.url") {
found = true
provider.loadItem(forTypeIdentifier: "public.url", options: nil) { (url, error) in
if let shareURL = url as? URL {
callback(shareURL)
} else {
print("error getting url: \(error)")
callback(nil)
}
}
}
}
}
if !found {
callback(nil)
return
}
}
Our REST based application can be used for testing on multiple internal environments each with a different REST end point. Is there a simple way to set up environment level configuration within an iOS (Swift 3) app? I've seen a few approaches but they all seem pretty involved.
This is my approach of doing things when we have multiple end points. I used to make a ConfigurationManager class something like this
Swift 3.0 code
import Foundation
import UIKit
let kEnvironmentsPlist:NSString? = "Environments"
let kConfigurationKey:NSString? = "ActiveConfiguration"
let kAPIEndpointKey:NSString? = "APIEndPoint"
let kLoggingEnabledKey:NSString? = "LoggingEnabled"
let kAnalyticsTrackingEnabled:NSString? = "AnalyticsTrackingEnabled"
class ConfigurationManager:NSObject {
var environment : NSDictionary?
//Singleton Method
static let sharedInstance: ConfigurationManager = {
let instance = ConfigurationManager()
// setup code
return instance
}()
override init() {
super.init()
initialize()
}
// Private method
func initialize () {
var environments: NSDictionary?
if let envsPlistPath = Bundle.main.path(forResource: "Environments", ofType: "plist") {
environments = NSDictionary(contentsOfFile: envsPlistPath)
}
self.environment = environments!.object(forKey: currentConfiguration()) as? NSDictionary
if self.environment == nil {
assertionFailure(NSLocalizedString("Unable to load application configuration", comment: "Unable to load application configuration"))
}
}
// CurrentConfiguration
func currentConfiguration () -> String {
let configuration = Bundle.main.infoDictionary?[kConfigurationKey! as String] as? String
return configuration!
}
// APIEndpoint
func APIEndpoint () -> String {
let configuration = self.environment![kAPIEndpointKey!]
return (configuration)! as! String
}
// isLoggingEnabled
func isLoggingEnabled () -> Bool {
let configuration = self.environment![kLoggingEnabledKey!]
return (configuration)! as! Bool
}
// isAnalyticsTrackingEnabled
func isAnalyticsTrackingEnabled () -> String {
let configuration = self.environment![kAnalyticsTrackingEnabled!]
return (configuration)! as! String
}
func applicationName()->String{
let bundleDict = Bundle.main.infoDictionary! as NSDictionary
return bundleDict.object(forKey: "CFBundleName") as! String
}
}
In Project--> Info Add some new configurations as per your need.
I have added Staging and QA as extra endpoints.Generally I use to make Staging as Release config and QA as Debug. So it will look like:
Now go to Targets -> Build Settings and add a User Defined Setting
Give the name of the user defined like ACTIVE_CONFIGURATION.
Add a key named ActiveConfiguration in info.plist with a variable name as $(ACTIVE_CONFIGURATION) same as given in User Defined Settings with a $ in the beginning. We gave the name of key as ActiveConfiguration because we are using the same name in our ConfigurationManager.swift class for kConfigurationKey.
let kConfigurationKey:NSString? = "ActiveConfiguration"
You can define as per your naming convention.
It will look like:
Now in the ConfigurationManager class I am getting a path for Environments.plist file.
I will just make a Environments.plist file like this:
The actual description source of this file is
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Development</key>
<dict>
<key>APIEndPoint</key>
<string>https://dev</string>
<key>LoggingEnabled</key>
<true/>
<key>AnalyticsTrackingEnabled</key>
<true/>
<key>Flurry</key>
<dict>
<key>FlurryApplicationID</key>
<string></string>
<key>FlurryApplicationSecret</key>
<string></string>
</dict>
<key>Facebook</key>
<dict>
<key>FacebookAppID</key>
<string></string>
<key>FacebookAppSecret</key>
<string></string>
</dict>
</dict>
<key>QA</key>
<dict>
<key>APIEndPoint</key>
<string>https://qa</string>
<key>LoggingEnabled</key>
<true/>
<key>AnalyticsTrackingEnabled</key>
<true/>
<key>Flurry</key>
<dict>
<key>FlurryApplicationID</key>
<string></string>
<key>FlurryApplicationSecret</key>
<string></string>
</dict>
<key>Facebook</key>
<dict>
<key>FacebookAppID</key>
<string></string>
<key>FacebookAppSecret</key>
<string></string>
</dict>
</dict>
<key>Staging</key>
<dict>
<key>APIEndPoint</key>
<string>https://staging</string>
<key>LoggingEnabled</key>
<false/>
<key>AnalyticsTrackingEnabled</key>
<true/>
<key>Flurry</key>
<dict>
<key>FlurryApplicationID</key>
<string></string>
<key>FlurryApplicationSecret</key>
<string></string>
</dict>
<key>Facebook</key>
<dict>
<key>FacebookAppID</key>
<string>840474532726958</string>
<key>FacebookAppSecret</key>
<string></string>
</dict>
</dict>
<key>Production</key>
<dict>
<key>APIEndPoint</key>
<string>https://production</string>
<key>LoggingEnabled</key>
<true/>
<key>AnalyticsTrackingEnabled</key>
<true/>
<key>Flurry</key>
<dict>
<key>FlurryApplicationID</key>
<string></string>
<key>FlurryApplicationSecret</key>
<string></string>
</dict>
<key>Facebook</key>
<dict>
<key>FacebookAppID</key>
<string></string>
<key>FacebookAppSecret</key>
<string></string>
</dict>
</dict>
</dict>
</plist>
We are now good to go. Now you have to just call
ConfigurationManager.sharedInstance.APIEndpoint()
for your respective end points.
Now you just have to change the schemes from Edit Schemes and you are done and change the Build Configuration in info.
This not only manages API End Points but also other things like whether to enable analytics or tracking for the respective end point or different ids of Facebook for different end points.
As Zac Kwan suggested, you can use different schemes to accomplish this, but you don't necessarily have to create a different configuration as well. Each scheme can specify unique environment variables. Then, access them from Swift:
let prodURL = "http://api.com"
let baseURL = ProcessInfo.processInfo.environment["BASE_URL"] ?? prodURL
I found that creating different Scheme and Configuration for your project works best. My setup is as follow:
I usually have 3 different scheme, MyApp-dev, MyApp-staging and MyApp.
Each of the scheme i created User-Defined-Attribute to have different string appending to my Bundle Display Name. So it can concurrently appear on my iOS device as MyApp-d, MyApp-s and MyApp. Each also have its own Bundle ID Then I create custom flags for each of them.
So in my Routes.swift files i have something like this at the top:
#if PRODUCTION
static let hostName = "http://production.com/api/v1/"
#elseif STAGING
static let hostName = "http://staging.com/api/v1/"
#else
static let hostName = "http://development.com/api/v1/"
#endif
There is quite a few ways in how to update different hostname. But ultimately creating different Scheme and Configuration is always the first step.
Here is a few links that might help you get started:
https://medium.com/#danielgalasko/change-your-api-endpoint-environment-using-xcode-configurations-in-swift-c1ad2722200e#.o6nhic3pf
http://limlab.io/swift/2016/02/22/xcode-working-with-multiple-environments.html
I ended up using https://github.com/theappbusiness/ConfigGenerator:
A command line tool to auto-generate configuration file code, for use
in Xcode projects. The configen tool is used to auto-generate
configuration code from a property list. It is intended to create the
kind of configuration needed for external URLs or API keys used by
your app. Currently supports both Swift and Objective-C code
generation.