Should I set Build Configuration in Xcode - ios

When distribution app on app store should I also set Build Configuration in Xcode from edit scheme like this

I would recommend to set up two schemas.
First one: Development -> setup with Debug build configuration.
You can use this while you are developing your app. This will give you logging, easy debugging, etc..
Second one: Distribution -> setup with Release build configuration.
Logging will not happen on this schema, also debugging will be unavailable, because the build is not optimizaed for that.
When you are preparing your submittal to the App Store, archive the Distribution schema using the Release build configuration.
You can find some more detailed description here about the difference between Debug and Release build configurations.

This will cover your question in depth. I have used build configuration environment so that if you make a build in release Configuration. your automatic values will be se according to release version. you can also download the sample code from the link below to see actually what happens when you change the scheme.
First step.
Add the variable "Configuration" in your info.plist and add value "$(CONFIGURATION)" there.
Make a Config.swift file and copy paste the below code there.
`
import Foundation
private let configManagerSharedInstance = ConfigManager()
class Config {
class var sharedInstance: ConfigManager {
return configManagerSharedInstance
}
}
// You can put as much Environment as you need but you make sure you also put these environment in the config.plist file.
enum Environment: String {
case Debug = "Debug"
case Production = "Release"
}
class ConfigManager: NSObject {
private var environment: Environment?
var config: NSDictionary?
override init() {
super.init()
// Retrieve the current evironment from the main bundle
if let currentEnvironment = Bundle.main.infoDictionary?["Configuration"] as? String {
// Store the current environment for later use
environment = Environment(rawValue: currentEnvironment)
if let projectConfigPath = Bundle.main.path(forResource: "Config", ofType: "plist") {
if let projectConfigContents = NSDictionary(contentsOfFile: projectConfigPath) as? Dictionary<String, AnyObject> {
config = projectConfigContents[currentEnvironment] as? Dictionary<String, AnyObject> as NSDictionary?
}
} else {
print("config file not found")
}
}
}
func getCurrentEnvironment() -> Environment? {
return self.environment
}
func configForKey(key: String) -> String {
return config?[key] as! String
}
//It will use to get sub dictionary and their values.
func configForCategory(category: String, andKey: String) -> String {
let configuration = config?.value(forKeyPath: category) as! NSDictionary
return configuration.value(forKeyPath: andKey) as! String
}
}
`
I have also made a file Constants.swift in which i have set the varibles using the above code.
`
//
// Constants.swift
// BuildConfiguration
//
// Created by Ourangzaib khan on 4/6/17.
// Copyright © 2017 Ourangzaib khan. All rights reserved.
//
let kBASE_URL : String = {
print(Config.sharedInstance.configForKey(key: "kBASE_URL"));
return Config.sharedInstance.configForKey(key: "kBASE_URL")
}()
let STRIPEKEY : String = {
return Config.sharedInstance.configForCategory(category: "Stripe", andKey: "Publishable Key")
}()
let PUBNUBKEYSUBSCRIBE : String = {
return Config.sharedInstance.configForCategory(category: "PubNub", andKey: "Publish Key")
}()
let PUBNUBKEYPUBLISH : String = {
return Config.sharedInstance.configForCategory(category: "PubNub", andKey: "Subscribe Key")
}()
let WOWZAKEY : String = {
return Config.sharedInstance.configForKey(key: "Wowza");
}()
`
Now you just have to select the environment using edit sceme go into the edit scheme and chose Build Configuration Now when you will run the project you will see this output WRT build configuration in below images.
https://github.com/ourangzeb/Build-Configuration-for-IOS

Related

iOS user with language of "en", no region code, translation lookup fails

We have a bug reported where a user has a device with an en language and nil region code, and thus all NSLocalizedString lookups in are failing, meaning our string key is what is rendered onscreen. Thus, if we had this in our en.lproj/Localizable.strings file:
"some_key" = "Some string.";
It would render some_key instead of Some string. in our UI.
First question: how do I replicate this scenario locally? This question on Stack seems to almost describe the issue, but does not describe how one enters this state.
Second question: why would iOS not fall back to English in the event the region code was nil?
Second question: why would iOS not fall back to English in the event
the region code was nil?
The cause can be "There is no base development language that is enabled". Or it is iOS logic.
Here is my solution for Localization. I just want to share with you an alternative solution if it can help you to solve the issue.
public enum AppResourceLang: String {
case en
case vi
}
public class AppResManager {
public static let shared = AppResManager()
public var lang = "en"
var textBundle: Bundle!
public var mainBundle: Bundle!
private init() {
let mainBundleId = Bundle.main.bundleIdentifier!
// we use mainBundle's bundleIdentifier because normally your running target will contains "lproj" in Copy Bundle Resource
// If your text/image is located on different project/target or framework, you need to enter that target's bundleId.
mainBundle = Bundle(identifier: mainBundle)!
let path = mainBundle.path(forResource: lang, ofType: "lproj")
textBundle = Bundle(path: path!)
}
public func changeLang(code: String) {
if let path = mainBundle.path(forResource: code, ofType: "lproj") {
lang = code; textBundle = Bundle(path: path)
} else {
// fallback to English
lang = "en"
let path = mainBundle.path(forResource: lang, ofType: "lproj")
textBundle = Bundle(path: path!)
}
}
}
Then we can use above textBundle like below:
public extension String {
var localText: String {
guard let bundle = AppResManager.shared.textBundle else { return self }
return NSLocalizedString(self, bundle: bundle, comment: "")
}
var lTextUpcased: String {
guard let bundle = AppResManager.shared.textBundle else { return self.uppercased() }
return NSLocalizedString(self, bundle: bundle, comment: "").uppercased()
}
}
Here is my AppResource (like a framework). We can see I have Localizable.strings and it is localized for EN, VI.
Here is the real file/folder structure, you can see if you check on English in Localization for *.string, *.storyboard,... file. It will be cloned and saved in this folder (ex: en.lproj). You can base on this to point to the corresponding resource file.
Then, how to use my above codes. It is just a way to allow you to completely control the Localization.
public enum AppLanguage: String {
case en
case vi
}
// MARK: - Device info
public static func getDeviceLang() -> AppLanguage {
guard let languageCode = Locale.current.languageCode else { return .en }
let appLang = AppLanguage(rawValue: languageCode.lowercased()) ?? .en
return appLang
}
// Then this will switch the language of your resource based on device language.
AppResManager.shared.changeLang(code: YourGlobalClass.getDeviceLang().rawValue)
// Then use the above string extension to load the corresponding text
// Anywhere in your project.
let text = "your_key".localText

What are the imagePath and apiRootUrl values when deploying a micro service using edgeSDK wrapper on iOS and how do I set them?

I'm working on an iOS application that uses a duktape javascript based micro service and will deploy it using edgeSDK's iOS wrapper. In order to do that I am asked to specify an imagePath and apiRootUrl values. What are they and how do I set them?
This is my deployment function:
func deployMicroService() -> Void {
let deployConfig = MMKMicroserviceDeploymentConfig.init(name: "micro-service", apiRootUrl: "???", imagePath: "???", envVariables: [:])
let edgeOpsWrapper = edgeSDK_iOS_app_ops()
edgeOpsWrapper.deployMicroservice(edgeAccessToken: "MY-DEPLOYMENT-TOKEN", config: deployConfig) { (result) in
if let checkedError = result.error {
print("micro service deployment ended with error: \(checkedError.localizedDescription)")
}
else {
print("micro service deployment result: \(result.status.debugDescription)")
}
}
}
The answers are:
apiRootUrl is an arbitrary value that your micro service will be deployed under. For example you can use something like:
"/micro-service/v1"
and this would result for your micro service to be accessible at the following location:
http://localhost:8087/client-id-value/micro-service/v1
imagePath is a file system path to the micro service image tar file. Usually located in the application's bundle. For example you can use something like:
class func microServiceBundleStoragePath(serviceName: String) -> String? {
let microServiceFileName = "\(serviceName)-v1"
let microServiceBundlePath = Bundle.main.path(forResource: microServiceFileName, ofType: ".tar")
return microServiceBundlePath
}
resulting in a file system path of:
"/private/var/containers/Bundle/Application/DF2F6617-9153-4AEC-999D-BC7699A80FA0/example.app/micro-service-v1.tar"

Use AWSMobileClient without `awsconfiguration.json`in iOS

I'd like to authenticate an iOS device to use AppSync/S3 services via Cognito user pools. The AWSMobileClient provides some nice conveniences but the initialization requires that you're bundle have an awsconfiguration.json file -- which our application will define dynamically. Is there a way to configure that manually?
The current solution is to use the multi-environment workflow from the CLI.
https://aws-amplify.github.io/docs/cli/multienv?sdk=ios
Edit
If the multi-environment workflow from the Amplify team doesn't work for you, what you can do is create debug and prod versions of your config, and then create a build phase that copies the correct one based on your build settings (debug vs release, etc). This is working extremely well for one of my projects.
#export; #Prints list of all xcode variables with values
printf "$CONFIGURATION\n";
if [ "$CONFIGURATION" = "Debug" ]; then
printf "creating debug configuration";
cp -r "$PROJECT_DIR/awsconfiguration-debug.json" "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app/awsconfiguration.json"
else
printf "creating production configuration";
cp -r "$PROJECT_DIR/awsconfiguration-prod.json" "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app/awsconfiguration.json"
fi
As of AWS iOS SDK 2.11.0 (9th September 2019) it is now possible to configure without an awsconfiguration.json file.
It's even documented in the amplify documentation here
See also my answer to a related question
Here's a specific solution:
extension AWSMobileClient {
convenience init?(configuration url: URL) {
guard let data = try? Data(contentsOf: url) else { return nil }
guard let dict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] else { return nil }
self.init(configuration: dict)
}
convenience init?(configuration name: String) {
guard let url = Bundle.main.url(forResource: name, withExtension: "json") else {
return nil
}
print("INITIALIZING AWSMobileClient [\(name)]")
self.init(configuration: url)
}
}
To use it, you can have as many different awsconfiguration-XXX.json files as you need, and at runtime you initialize with the one you want by:
let mobileClient = AWSMobileClient(configuration: "awsconfiguration-XXX")
mobileClient.initialize { (userState, error) in ... }

Changing language at runtime using SwiftGen

My app is supposed to support language change at runtime. I'm using SwiftGen 5.0. ViewControllers subscribe to language change notification and I've checked that the localisation function fires correctly. My overriden tr function looks like this:
fileprivate static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
guard let bundle = LanguageManager.shared.bundle else {
fatalError("Cannot find bundle!")
}
let format = NSLocalizedString(key, tableName: table, bundle: bundle, comment: "")
let locale = Locale(identifier: LanguageManager.shared.currentLanguageKey!)
return String(format: format, locale: locale, arguments: args)
}
The bundle is set like so:
if let path = Bundle.main.path(forResource: currentLanguageKey, ofType: "lproj") {
bundle = Bundle(path: path)
}
However, the tr function returns mostly previous language strings. Only one out of all labels currently in memory refreshes. Setting a breakpoint inside the function and printing bundle returns
NSBundle </var/containers/Bundle/Application/ED5A6C7D-1807-4319-8817-45E693BC45E2/MyApp.app/en_US.lproj> (not yet loaded)
which is the correct new language. After app restarts the language is set correctly. Am I doing something wrong?
Okay, I found the problem. The stencil was generating static variables:
static let label = L10n.tr("Localizable", "registration_verify.pin_code.label")
Changing stencil to generate computed properties fixed the behaviour:
static var label: String {
return L10n.tr("Localizable", "registration_verify.pin_code.label")
}
Now you can config lookupFunction params in swiftgen.yml file
strings:
inputs:
- en.lproj
outputs:
- templateName: structured-swift5
params:
lookupFunction: Localize_Swift_bridge(forKey:table:fallbackValue:)
output: L10n-Constants.swift
in your project you just need implement lookupFunction,
your can use use Localize_Swift library
import Localize_Swift;
func Localize_Swift_bridge(forKey:String,table:String,fallbackValue:String)->String {
return forKey.localized(using: table);
}
generated code may like this:
internal enum Localizable {
internal static var baseConfig: String { return
L10n.tr("Localizable", "base config", fallback: #"Base Config"#) }}
extension L10n {
private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String {
let format = Localize_Swift_bridge(forKey:table:fallbackValue:)(key, table, value)
return String(format: format, locale: Locale.current, arguments: args)
}
}
https://github.com/SwiftGen/SwiftGen/blob/stable/Documentation/templates/strings/structured-swift5.md
https://github.com/marmelroy/Localize-Swift

How can I take out a value as a string from RealmSwift?

I am developing an iOS app with RealmSwift by referring here. https://realm.io/docs/swift/latest/#in-memory-realms
And, what I don't understand is, how can I indicate the location(record and column) of the data in the realm file.
I've saved a realm file that named "DicData.realm" on the main folder where the same location as ViewController.swift is saved.
The data of DicData.realm is something like this:
1,face,423
2,rain,435
3,airplane,555
If I run the code below, it only printed like this: "results: Results ( )". It seems the filter method is just neglected. When I want to take out the word "airplane" and store in a variable as a string, how should I modify my code?
override func didMoveToView(view: SKView) {
func test()->Int {
let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "DicData"))
let results = realm.objects(DBData).filter("id == 3")
print("results: \(results)")
}
class DBData: Object {
dynamic var id = 0
dynamic var name = ""
dynamic var code = ""
}
You're referring here to the chapter of in-memory Realms and have setup your configuration to use those. An in-memory Realm is not a file. It lives exclusively in memory and is not actually written to disk.
If you've a prepared file, you want to bundle with your app, you need to make sure, that it is part of the Copy-Files Build Phase of the corresponding target of your Xcode project.
You can then copy the file from the app bundle initially via NSFileManager to a path, where the copy can be modified.
let defaultURL = Realm.Configuration.defaultConfiguration.fileURL!
let bundleURL = NSBundle.mainBundle().URLForResource("DicData", withExtension: "realm")!
do {
try NSFileManager.defaultManager().copyItemAtURL(bundleURL, toURL: defaultURL)
} catch {}
}

Resources