Localizable strings shows key instead of value every other time - ios

I have a weird inconsistency with my localizable strings where sometimes the value is shown and sometimes thee key is shown. It's about every other time I restart the app. I use a lot of localizable strings in the project and they all work fine except for the ones I use in conjunction with this enum, which of course makes me think there is something wrong with it:
enum ParameterDescription {
case parameterDescription(id: Int)
var description: String {
switch self {
case .parameterDescription(let id):
return "parameterDescription_\(id)"
}
}
}
This returns the string I expect, like parameterDescription_0 or parameterDescription_3. And then I hook on localized:
print(ParameterDescription.parameterDescription(id: 0).description.localized())
extension String {
func localized() -> String {
return NSLocalizedString(self, comment: "")
}
}
And sometimes I get the correct associated value, but if I build and run the code again without changing anything, I (might) get the key printed as a string instead, which I guess means that Xcode couldn't find the string.
Is there something wrong with the enum or some reason it can't be used like this? Or is it something else? I have checked through the project folders so that there aren't any conflicting files, and as I previously said, I only experience this problem using this enum and/or this key. And there are thousands of localized strings in the project.
I have checked similar threads, but there doesn't seem to be any answer that is relevant for this.

Related

iOS project - Strange characters in localized strings

The localized strings are loaded with strange characters in my iOS project.
Like instead of "Home" it gets "H̀o̥m̧ë"
I set up 3 .strings files to store the strings used in the project and used swiftgen to generate the enum for using them. (I don't know if this info is relevant, just wanted to give the most comprehensive picture.)
I have my strings like:
"backHomeNavButton" = "Home";
The code that was generated by swiftgen is:
{
…
private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
let format = BundleToken.bundle.localizedString(forKey: key, value: nil, table: table)
return String(format: format, locale: Locale.current, arguments: args)
}
…
}
private final class BundleToken {
static let bundle: Bundle = {
Bundle(for: BundleToken.self)
}()
}
The string is already the above mentioned result at let format inside tr(_:_:_:)
Did anyone experienced something similar?
I already tried adding a new .strings file, the results is the same.
I checked it in another project without swiftgen, it worked there. Then I compared those Build Settings that contained Localiz and the projects had the same settings.
I am using Xcode 12.5 at the moment, but when I opened the project in Xcode 12.4 I got the same results.
Thanks for your help in advance.
I figured it out since, so I leave the solution here if anyone gets in the same situation.
You can change the App Language in the Build Scheme. and if you set it to Accented Pseudolanguage, then it will produce these strange characters.

RxJava2 order of sequence called with compleatable andThen operator

I am trying to migrate from RxJava1 to RxJava2. I am replacing all code parts where I previously had Observable<Void> to Compleatable. However I ran into one problem with order of stream calls. When I previously was dealing with Observables and using maps and flatMaps the code worked 'as expected'. However the andthen() operator seems to work a little bit differently. Here is a sample code to simplify the problem itself.
public Single<String> getString() {
Log.d("Starting flow..")
return getCompletable().andThen(getSingle());
}
public Completable getCompletable() {
Log.d("calling getCompletable");
return Completable.create(e -> {
Log.d("doing actuall completable work");
e.onComplete();
}
);
}
public Single<String> getSingle() {
Log.d("calling getSingle");
if(conditionBasedOnActualCompletableWork) {
return getSingleA();
}else{
return getSingleB();
}
}
What I see in the logs in the end is :
1-> Log.d("Starting flow..")
2-> Log.d("calling getCompletable");
3-> Log.d("calling getSingle");
4-> Log.d("doing actuall completable work");
And as you can probably figure out I would expect line 4 to be called before line 3 (afterwards the name of andthen() operator suggest that the code would be called 'after' Completable finishes it's job). Previously I was creating the Observable<Void> using the Async.toAsync() operator and the method which is now called getSingle was in flatMap stream - it worked like I expected it to, so Log 4 would appear before 3. Now I tried changing the way the Compleatable is created - like using fromAction or fromCallable but it behaves the same. I also couldn't find any other operator to replace andthen(). To underline - the method must be a Completable since it doesn't have any thing meaning full to return - it changes the app preferences and other settings (and is used like that globally mostly working 'as expected') and those changes are needed later in the stream. I also tried to wrap getSingle() method to somehow create a Single and move the if statement inside the create block but I don't know how to use getSingleA/B() methods inside there. And I need to use them as they have their complexity of their own and it doesn't make sense to duplicate the code. Any one have any idea how to modify this in RxJava2 so it behaves the same? There are multiple places where I rely on a Compleatable job to finish before moving forward with the stream (like refreshing session token, updating db, preferences etc. - no problem in RxJava1 using flatMap).
You can use defer:
getCompletable().andThen(Single.defer(() -> getSingle()))
That way, you don't execute the contents of getSingle() immediately but only when the Completablecompletes and andThen switches to the Single.

How do I get an optional from decodeIntegerForKey instead of 0?

My app saves settings to a file on an iOS device by archiving instances of a class with properties. The class uses the NSCoding protocol, and therefore, I encode these properties using encodeWithCoder. I then try to read these files back into memory using a command such as tempInt = decoder.decodeIntegerForKey("profileFlags") as Int
This has worked well so far, but now I need to be able to store additional properties and retrieve them. In essence, the structure of this archived object is being changed, but users will already have files with the old structure (which has fewer properties). If the user has a file with the new structure (additional properties), then I want to read them. If not, I want to be able to execute code to handle that and provide default values.
I tried using a nonexistent key tempInt = decoder.decodeIntegerForKey("nonExistentKey") as Int, expecting to get a nil value, but it returned a zero. Unfortunately, this is one place where I really need an optional, because 0 is a valid value.
The closest help article I can find is Swift: decodeObjectForKey crashes if key doesn't exist but that doesn't seem to apply here. It seems like decodeObjectForKey returns an optional and decodeIntegerForKey returns an Integer.
Any ideas on how to do this?
You can check using decoder.containsValueForKey("nonExistentKey") wether or not there is an actual value present and only if it is extract it with decodeIntegerForKey:
if decoder.containsValueForKey("nonExistentKey") {
let tempInt = decoder.decodeIntegerForKey("nonExistentKey")
}
You can use decodeObjectForKey that returns nil instead of zero. You just need to downcast to Int as follow:
decoder.decodeObjectForKey("profileFlags") as? Int
#luk2302 gave you the answer, but I wanted to adjust the syntax slightly and expand on it:
var tempInt: Int?
let key = "profileFlags"
let hasValue = decoder.containsValueForKey(key)
tempInt = hasValue ? decoder.decodeIntegerForKey(key) : nil
The last statement is using the "tertiary operator", which has the same effect as:
if hasValue
{
tempInt = decoder.decodeIntegerForKey(key)
}
else
{
tempInt = nil
}
...but all in 1 line. It's a little odd-looking until you get used to it, but it is a very concise way to express this sort of thing.

Function with Enumeration Parameter (and Default value)

I wanted to create an extension method function called toCurrencyString that has one parameter that is actually an integer enumeration of type CurrencyFormatType. The enum and code would be:
enum CurrencyFormatType: Int {
/// Formats a standard currency string (localized) such as $45.35 or *45,00
case Standard = 1,
///Rounded currency format (rounds up last decimals and does not return any decimals in string)
Rounded,
///Will not include the thousands separator (, or .) in a string. Retains localized currency symbol.
WithoutThousandsSeparator
}
func toCurrencyString(currencyFormat: CurrencyFormatType = 1) -> String
{
..my switch code w/ default to the first option if nothing passed in
}
I'd like to do this so that I can call .toCurrencyString on NSDecimalNumbers without having to pass in the optional parameter (most of the time I won't need it), but on the few times I do, i'd like to use an enum to select other options.
It complains at me telling me that my enum doesn't conform to IntegerLiteralConvertible. I did a little reading on how this would work but couldn't figure out the code.
Could anyone shed some light on how to get this working?
thanks for your help!
My apologies, no sooner did I ask it than I figured it out. I should have waited a bit longer.
In hopes of helping others out, just a reminder about the 'Raw' aspect of enums:
Adding this to my function signature helped:
CurrencyFormatType.Raw = 1
Thanks!
alternately you could have done:
func toCurrencyString(currencyFormat: CurrencyFormatType = CurrencyFormatType.Standard) -> String
{
...

Swift Framework does not include symbols from extensions to generic structs

I am having trouble linking my framework with code that takes advantage of that framework. Specifically, the linker isn't able to find the symbols for extensions for generics structs.
This is what one of the extensions looks like for Optional:
extension Optional {
/// Unwrap the value returning 'defaultValue' if the value is currently nil
func or(defaultValue: T) -> T {
switch(self) {
case .None:
return defaultValue
case .Some(let value):
return value
}
}
}
This method works great in a playground or in an app if the code is compiled within the main part of the app. However, when I try to compile this into a Framework, apps (and even the tests for the framework) produce the following linker error:
Undefined symbols for architecture i386: "__TFSq2orU__fGSqQ__FQQ",
referenced from:
__TFC18SwiftPlusPlusTests27Optional_SwiftPlusPlusTests13testOrWithNilfS0_FT_T_
in Optional+SwiftPlusPlusTests.o
Similar methods like the one following, link fine (notice, it is not on a generic)
extension String {
/// Returns a string by repeating it 'times' times
func repeat(times: Int) -> String {
var result = ""
for i in 0..times {
result += self
}
return result
}
}
There are two other extensions within my repository on github: SwiftPlusPlus that also do not link (both on generic strucs). You will reproduce the errors if you pull the latest commit, build the framework, and then try to run the unit tests.
So far I have tried to run "strings" on the outputted framework and intermediate files and I do not see the symbols for these extensions but I do see the symbols for the repeat method extension on String. So it doesn't even seem to be compiling them into the library.
Does anyone know why the symbols are not defined in the framework?
Edit
Here is a link to my Optional Extension
Here is a link to the test file that causes the linker error when trying to compile the test target
I posted on the Apple Developer forums and an Apple employee responded that this is a known bug.
It looks like the compiler gets the mangled symbol names of methods in generic extensions wrong when they live in a different framework.
In case you are looking for a temporary fix, you can wrap the extension in a class method:
// In your framework
public class OptionalOperator {
public class func or<T>(optional:Optional<T>,defaultValue:T) ->T {
return optional.or(defaultValue)
}
}
// Outside the framework
var maybeText:String?
let text = OptionalOperator.or(maybeText, defaultValue: "Apple, please fix this")
Of course, this is not ideal and defeats the purpose of extensions. So if you plan on calling this method frequently, we could overload/define an operator.
// In your framework
infix operator ||| {}
public func |||<T>(left:Optional<T>, right:T) -> T {
return left.or(right)
}
// Outside the framework
var maybeText:String?
let text = maybeText ||| "Apple, please fix this"
In my case, I have multiple applications using the framework, so I'd like to keep the method implementation inside the framework. However, overloading an operator (or just using a global function) would be awkward, so I have to go with the first option until that bug is fixed.
Hope this helps.
UPDATE
Funny thing is that Swift already has an operator for that (??).
var maybeText:String?
let text = maybeText ?? "Nice!"
It's called - Nil Coalescing Operator

Resources