Can't get access to string localizations in UI Test (Xcode 7) - ios

So I have a situation in which I have a few textFields that are validated. I'm trying to run a UI test, and when they fail they will get an alert to pop up with an error message (potentially a different message depending on what fields are invalid and in what way).
I'd like to test that not only that an alert appeared, but that the correct message is displayed. The problem I'm having is that I need to get the localized text for comparison (if I run tests in another language other than english), but when I call NSLocalizedString in the UITest it can't gather the correct localized string (just returns the key [default])
I've tried adding the localizeable.strings files to the UITest target, but to no avail. Does anyone know if this is possible?
edit as a side note: I also tried setting an accessibility identifier on the UIAlertView but when I query with that accessibility identifier it doesn't exist, I can only query it using the title of the alert which seems backwards.

In the UI tests, the main bundle seems to be a random launcher app. That's why the .strings file doesn't appear: even though you added it to your test bundle, NSLocalizedString is checking the wrong bundle. To get around this, you need an invocation like this:
NSLocalizedString("LOCALIZATION_KEY", bundle: NSBundle(forClass: AClassInYourUITests.self), comment: "")
Which you might want to pull into a helper method.

Here is my solution:
In your UI Tests target -> Build Phases -> Copy Bundle Resources, add the needed localization files (e.g. Localizable.strings).
Add a function similar to the following:
func localizedString(key:String) -> String {
/*1*/ let localizationBundle = NSBundle(path: NSBundle(forClass: /*2 UITestsClass*/.self).pathForResource(deviceLanguage, ofType: "lproj")!)
/*3*/ let result = NSLocalizedString(key, bundle:localizationBundle!, comment: "") //
return result
}
/*1 Gets correct bundle for the localization file, see here: http://stackoverflow.com/questions/33086266/cant-get-access-to-string-localizations-in-ui-test-xcode-7 */
/*2 Replace this with a class from your UI Tests*/
/*3 Gets the localized string from the bundle */
Use the function like this: app.buttons[localizedString("localized.string.key")]

I have another approach written here: https://stackoverflow.com/a/75178860/7715250. Copy paste for when the question is deleted/closed:
Different approach without calling methods on your String
Prerequisites
You are using NSLocalizedString.
Step 1
Make sure you add the translations to your test targets (Go to your
Localizable file and on the right side you can tap your UI test
targets).
Step 2
Add this somewhere in your main target
#if DEBUG
// Can be changed by UI tests to get access to localized content
var bundleForLocalizedTexts = Bundle.main
#else
let bundleForLocalizedTexts = Bundle.main
#endif
Step 3
Add this value to the parameter bundle in all your
NSLocalizedStrings, like this:
NSLocalizedString(
"localized",
bundle: bundleForLocalizedTexts,
comment: ""
)
Step 4
Override the method setUp in your XCTestCase subclass and add this
line:
bundleForLocalizedTexts = Bundle(for: MySubclass.self)
Step 5
Everything should work! All languages should work, no extra methods to call.

Related

Xcode 10 code completion placeholders — what is the "T"?

In Xcode 10 code completion, the text underlying the placeholder tokens has an extra #T# before it (to see that this is so, copy and paste the inserted code template into a different text editor):
let alert = UIAlertController(
title: <#T##String?#>, message: <#T##String?#>,
preferredStyle: <#T##UIAlertController.Style#>)
What is that? Does "T" mean Type? What difference does it make in my usage of the placeholder?
The syntax <#T##_A_##_B_#> is used to specify a default piece of code for the placeholder. The placeholder will be displayed as _A_, but when the user presses the enter key the placeholder will be replaced with _B_.
Placeholder:
After pressing enter:
It'd a useful feature when presenting something to an audience, because as opposed to snippets, I would't need to remember the name of each snippet, I'd just select a placeholder and press enter to get the right piece of code.
EDIT:
Answering your question, indeed it seems that the T refers to type. If you try to replace the placeholder with an expression, like <#T##Example1##let i = 3#>, the placeholder it's not replaced with let i = 3 as you would expect. It is instead replaced with <<error type>>.
Furthermore, this placeholder <#T##transform: (Error) throws -> U?##(Error) throws -> U?#> is replaced with:
{ (<#Error#>) -> U? in
<#code#>
}
My guess is that when you prepend the T you are telling Xcode that you will provide a type, then Xcode finds an appropriate default value for that type.

How change app language in real time?

I am working on an app which runs in 2 languages, English and Persian (farsi). So user can select his desired language and app is displayed in that language. What should I do?
Depends on what are your trying to achieve, there is an approach that might be useful to your case, which is:
Declare a global variable which should represents the key of the current used language in the app, call it appLanguageKey String -for example-:
var appLanguageKey = "english"
Create a datastore for storing both languages desired caption for each term, it might looks like (contains):
term_key term_english term_farsi
greeting_key Hello Hoi
bye_key Bye Doei
For now, you could get the desired value if you tried to do:
desiredTerm = "select term_\(appLanguageKey) where term_key = 'greeting_key'"
Consider it as pseudo-code, the condition should be similar to it.
By implementing such a function that returns a string (I will assume that it is: getDesiredCaption(_ termKey: String) -> String in the following code snippet), you will be able to automatically set the desired caption for any UI component, just call it in viewWillAppear method the view controller:
override func viewWillAppear(_ animated: Bool) {
.
.
.
label.text = getDesiredCaption("greeting_key")
// Since that 'appLanguageKey' global viriable is "english",
// the output should be "Hello"
.
.
.
}
All you have to do for changing the language of the app is to change the value of appLanguageKey to "farsi".
For another approach which related to the Internationalization and Localization, you might want to check this answer, it should affect the app to let its navigation to be from right to left.
Hope this helped.
First you should use realm, magical records or an API to store what the user picks. Then you could create your own type of localizeable string handeler, by having an array with 2 dictionaries 1 with english and 1 with persian that all the texts in your application will read from just as you do with a normal localizeable.

how do you iterate through elements in UI testing in Swift/iOS?

I understand how I could, for example look at one element in a table, but what's the correct way to simply iterate through all the elements, however many are found, to for example tap on them?
let indexFromTheQuery = tables.staticTexts.elementBoundByIndex(2)
Okay I succeeded in figuring out the simple syntax. I have just started working with Swift and so it took me sleeping on it to think of the answer.
This code works:
var elementLabels = [String]()
for i in 0..<tables.staticTexts.count {
elementLabels.append (tables.staticTexts.elementBoundByIndex(i).label)
}
print (elementLabels)
My guess is the answer is there is no way to do so.
The reason is because of my experience with one of the critical iOS components while making a number of UI tests: UIDatePicker.
If you record a test where you get the page up and then you spin the picker, you will notice that the commands are all non-specific and are screen related. In the case of the picker, however, community requests resulted in the addition of a method for doing tests: How to select a picker view item in an iOS UI test in Xcode?.
Maybe you can add a helper method to whatever controller contains this table. Also, keep in mind that you can easily add methods without polluting the class interface by defining them as extensions that are in test scope only.
For Xcode 11.2.1, SwiftUI and swift 5 based app, the following code works for testing a list, each element in this case appears as a button in the test code. The table is set up like this (for each row) :
NavigationLink(destination: TopicDetail(name: "Topic name", longDesc: "A description")) {
TopicRow(thisTopic: top).accessibility(identifier: "newTopicRow_\(top.name!)")
}
Then I catch the members of the table by getting the buttons into an array:
let myTable = app.tables.matching(identifier: "newTopicTable")
var elementLabels = [String]()
for i in 0..<myTable.buttons.count {
elementLabels.append (tablesQuery.buttons.element(boundBy: i).label)
}
print (elementLabels)
Finally, I deleted each member of the table by selecting the detail view where I have a delete button, again with
.accessibility(identifier: "deleteTopic"
I wanted to delete all members of the table:
for topicLabel in elementLabels {
let myButton = app.buttons[topicLabel]
myButton.firstMatch.tap()
app.buttons["deleteTopic"].tap()
}

Objective-C iOS parse string according to the definition

I want create an iOS app for my school.
This App will show Week schledule and when I tap on Cell with Subject, it will show me detail info about subject...
My problem:
Our teachers use shotcuts for their names, but I want show their full name... I created the file "ucitele.h" with definitions of their names, but I don't know, how to use it 😕.
This is how that file looks:
//
// ucitele.h
//
#define Li #"RNDr. Dan---vá"
#define He #"Mgr. Ja---hl"
#define Sm #"Ing. Mich---rek"
#define Ks #"Mgr. Svat---á"
I get the shortcut of Teacher from previous view from "self.ucitel" and I maybe want compare the contents of the "self.ucitel" with definitions and set the "ucitelFull" string from the definitions? I don't know how to say it 😕.
when the content of the self.ucitel will be #"Sm", than I want parse "ucitelFull" as #"Ing. Mich---rek"
Answers in Objective-C only please
Okay, sounds like your trying to map a short identifier to a full name:
-(NSString*)fullNameFromShortName:(NSString*)short {
NSDictionary * names = #{#"Li" : #"RNDr. Dan---vá",
#"He" : #"Mgr. Ja---hl", ... };
return [names objectForKey:short];
}
Use like:
self.ucitelFull = [self fullNameFromShortName:self.ucitel];
This is a dictionary that has the short name as a key and the full name as the value.
Some further suggestions:
try using lowercase keys and comparing lowercaseString's, incase the user doesn't enter the value with the correct case.
You can move the dictionary definition into a json file and read it from your bundle, to eliminate the hardcoding

How to internationalize the UITextField placeholder property value string in interface builder itself?

How can we internationalize the placeholder property's value of a UITextField in interface builder-attribute inspector?
You can easily change it without writing any code.
here is the example in your localized strings file set your placeholder as follows
"itw-s8-fkt.placeholder" = "YOUR TEXT";
You do the same localization as usual:
myTextField.placeholder = NSLocalizedString(#"emptyUsername",
#"userNameTextFieldPlaceholder");
Then you enter the according string in your localizable strings file.
The question has been discussed here, too:
How to use NSLocalizedString in IB [iPhone SDK]?
Describing an alternative, using several xib files.
What I've done, to minimise the amount of localisation boilerplate, is follow the model outlined here (GitHub here): create Interface Builder-friendly helper extensions (#IBInspectable vars, in a single file for convenience) for all the types of controls I want localised, including one for UITextField placeholders. I can then centralise all localisations to a single .strings file for each language and hook them up via IB. The only downside is remembering to add the keys when I create the controls. Localisation debugging (set by editing the Run scheme; I run the app in a non-base language by default) helps spot these early.
I have a single Excel spreadsheet containing keys and translations that gets passed to local translators to update, and a script that (re)generates the .strings files from the .csv prior to release.
With reference to the linked tutorial, my UITextField extension looks like this:
extension UITextField: XIBLocalizable {
#IBInspectable var xibLocKey: String? {
get { return nil }
set(key) {
text = key?.localized
}
}
#IBInspectable var xibPlaceholder: String? {
get { return nil }
set(key) {
placeholder = key?.localized
}
}
}
(The linked GitHub has an almost identical version I've only just spotted). Worth noting that when adding a new localisation I have to manually delete the contents of the XCode autogenerated .strings file, while leaving the file in place, to ensure the extensions get called.

Resources