iOS project - Strange characters in localized strings - ios

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.

Related

What is `self` in swift?

I found this code in one of the old projects:
guard let `self` = self else {
return .empty()
}
static let `default`: LayoutParameters = { ..some code.. }
I assume `` was used in older versions of the language. But I would like to know why it is used/was used. Also, I would like to know if there are any problems if do not change the code and "leave it as is" in the latest versions of Swift and Xcode. Does it work correctly? Should I replace this with
guard let self = self else ......
self is a keyword and normally you cannot use keywords and reserved words in places outside their context. However, that sometimes creates problems. For that reason there is a special syntax to make the keyword to be a normal identifier, e.g.:
enum MyEnum: String {
case `default`
}
(also see Swift variable name with ` (backtick))
Historically self was not allowed as as a constant name inside guard-let-else and therefore backticks were commonly (ab)used.
They are no longer needed since Swift 4.2.
The code will still work correctly since you can wrap any identifier in backticks and it will just be a normal identifier.
The Xcode IDE suggestion you using `` to help to use the same default key in Foundation SDK.
Example: default is a constant name in Foundation, if you want using default to create new variable name is default you need add ``.
But you using SwiftLint with default rules, Using a default contants name is a code smell.

Swift - Localizable file working properly in all except one iPad device

I am currently working on an Xcode project that has several different targets, each one with their own strings defined for specific parts of the app. That is why we are using different string files, which we call depending on the target we are running.
I came across a strange problem which I still could not find a way to reproduce. One of the devices of a client is showing the name of the key instead of showing the string defined for that target or the default string in the file "default.string". The problem is that all other test devices (simulator and physical devices) we used show the strings correctly. The device that is not showing strings properly (and just showing the key) is an iPad 7th Generation, with iPadOS 14.
I am using the following code to show the string on the view (note that I am using "section2HomeTitle" as an example here):
FactoryView.sectionHeaderView(with: Strings.localizedString(forKey: Strings.LocalizedStringKey.section2HomeTitle), icon: Constants.Config.useIconSectionsHomeTitle ? #imageLiteral(resourceName: "imbatibles"): nil )
The key section2HomeTitle is defined in a file for Constants the following way:
struct Strings {
struct LocalizedStringKey {
static let section2HomeTitle = "section2HomeTitle"
}}
Also in the Constants file, I have the following function defined, which looks for the definition of key "section2HomeTitle" in the localized files:
static func localizedString(forKey key: String) -> String {
var result = Bundle.main.localizedString(forKey: key, value: nil, table: Constants.Config.store)
if result == key {
result = Bundle.main.localizedString(forKey: key, value: nil, table: "default")
}
return result
}
That function looks for the current target's definition for that particular key using Constants.Config.store. If it does not find the key in that target's file, it will look for it in the default file.
In this case I am dealing with Target store "xyz", and therefore I define its keys in file xyz.string which has Localization language set to Spanish and Target Membership set to "xyz" target. The key is defined in that file with the following string:
"section2HomeTitle" = "REBAJAS";
In case the file is not found, I have the "default" file defined as default.string which has Localization language set to Spanish and Target Membershipt set to "xyz" target and all other targets in the project. The key definition here is:
"section2HomeTitle" = "Ofertas";
As I mentioned before, the output shows up as "REBAJAS" in all devices we tested for that target, except in that one iPad 7th Gen from the client, where it shows:section2HomeTitle
My questions are:
Why is this key showing up correctly in all devices we test it, except on the client's iPad 7th Gen
What can I do to correct this issue?
Thank you.

Localizable strings shows key instead of value every other time

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.

How to find source file and line number from os_log()

The Logging Apple reference for the new logging system in iOS 10 and macOS Sierra explicitly say not to include line number and source file info, because it is automatically captured.
Don’t include symbolication information or source file line numbers in messages. The system automatically captures this information.
But I have yet to be able to find any way of viewing these supposedly captured values. In the Console app I can see subsystem, category, process ID, etc, but nothing about file and line.
And the command line tool similarly appears to lack any options for displaying this information (unless I'm missing something).
Anyone figured it out yet?
I don't think it's available in Swift yet, although you can see file and line number in C / C++ in Terminal. See Apple forum here.
I tried something similar to what's inside the forum by creating a simple command-line tool Xcode project:
import Foundation
import os.log
os_log("rrrr")
and type the following in Terminal: log stream --source --predicate 'eventMessage contains "rrrr"', and I got this:
Timestamp Thread Type Activity PID
2017-02-18 17:58:46.012381+0700 0x5067d Default 0x0 5637 <testLogSwift`_swift_os_log> rrrr
in contrast to what I got in C/C++ version, which shows the file and line number:
Timestamp Thread Type Activity PID
2017-02-18 17:55:05.056103+0700 0x4aa01 Default 0x0 5218 <testLogging`main (main.cpp:13)> qqq
Till apple fixes this issue,
I've created a simple extension
import os
extension Logger {
init(subsystem: String = Bundle.main.bundleIdentifier ?? "", file: String = #file, function: String = #function, line: Int = #line, context: String) {
let category = "\(file):\(line):\(function), \(context)"
self.init(subsystem: subsystem, category: category )
}
}
Usages:
Logger(context: "LoginFLow").debug("Hello World")
We can even remove param name to make it more cleaner:
import os
extension Logger {
init(subsystem: String = Bundle.main.bundleIdentifier ?? "", file: String = #file, function: String = #function, line: Int = #line, _ context: String) {
let category = "\(file):\(line):\(function), \(context)"
self.init(subsystem: subsystem, category: category )
}
}
Usages:
Logger("LoginFLow").debug("Hello World")
Note: If you wish to use the original Logger, just remove the context param and it will use its original init provided by apple.
Logger().debug("Hello World")

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