Static string constant in swift evaluating as empty string "" - swift

Why does my string constant kAppCacheKey evaluate to an empty string the first time I access it? It shows up as the string "myAccount" on subsequent calls to it.
Am I declaring static constants in the right way?
let kAppCacheKey = "myAccount"
class LoggedUser {
class var sharedInstance: LoggedUser {
struct Static {
static let instance = LoggedUser.loadFromCache()
}
return Static.instance
}
override private init() {
super.init()
}
private class func loadFromCache() -> LoggedUser {
//kAppCacheKey is evaluated as an empty string here for some reason
if let data = Locksmith.loadDataForUserAccount(kAppCacheKey) as? [String:String]
{
}
}

Related

How to call async methods in an Array map or forEach?

I'm making my first attempts with Swift's async/await features and Task.
My first goal is working with NSItemProvider. I have a custom class that extends NSObject and conforms to NSSecureCoding. I have working code that converts my class to and from NSItemProvider.
What I'm now trying to do is convert an array of NSItemProvider objects back into an array of MyClass objects. Each conversion requires a call to an async function.
I'm able to do this using a plain for in loop. The trouble begins when I try to use compactMap or forEach to create the new array from the array of NSItemProvider.
Here is some code that can be copied into a Swift Playground or project that demonstrates the issue.
import Foundation
class MyClass: NSObject, NSSecureCoding {
var name: String
override var description: String {
return name
}
init(name: String) {
self.name = name
}
static var supportsSecureCoding = true
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
}
required init?(coder: NSCoder) {
if let name = coder.decodeObject(of: NSString.self, forKey: "name") as? String {
self.name = name
} else {
return nil
}
}
}
extension MyClass {
static let identifier = "mySpecialId"
func asItemProvider() -> NSItemProvider {
return NSItemProvider(item: self, typeIdentifier: Self.identifier)
}
static func create(withItemProvider itemProvider: NSItemProvider) async throws -> Self? {
let res = try await itemProvider.loadItem(forTypeIdentifier: identifier)
return res as? Self
}
}
func example() {
Task {
let objs = [ MyClass(name: "A"), MyClass(name: "B"), MyClass(name: "C"), MyClass(name: "D") ]
let providers: [NSItemProvider] = objs.map { $0.asItemProvider() }
do {
// This basic `for in` loop works
var newObjs = [MyClass]()
for provider in providers {
if let obj = try await MyClass.create(withItemProvider: provider) {
newObjs.append(obj)
}
}
print("newObjs: \(newObjs)")
// Attempt to use `forEach`
// Error: Cannot pass function of type '(NSItemProvider) async -> MyClass?' to parameter expecting synchronous function type
/*
providers.forEach { provider in
if let obj = try await MyClass.create(withItemProvider: provider) {
newObjs.append(obj)
}
}
*/
// Attempt to use `compactMap`
// Error: Cannot pass function of type '(NSItemProvider) async -> MyClass?' to parameter expecting synchronous function type
//let newObjs2 = providers.compactMap { await MyClass.create(withItemProvider: $0) }
} catch {
}
}
}
I was looking at another question but the solutions are far more complicated than just using a simple for in loop.
Is there a simpler way to use compactMap, for example, to directly convert the array of NSItemProvider into an array of MyClass?

Swift Generic parameter 'T' could not be inferred

I have a generic dictionary, and built a generic function to access that dictionary (to prevent concurrency access problems).
My (singleton) data class looks like:
class AppData {
static let genericDict: Dictionary<String, Any> = [:]
static func genericGet<T: Any>(_ objID: String) -> T {
let retVal: T
mySemaphore.wait()
retVal = genericDict[objID] as! T
mySemaphore.signal()
return retVal
}
}
However, when I call my function like so:
class SomeClass {
let product: SomeObj = AppData.genericGet(objID) as! SomeObj
}
I get the error:
Generic parameter 'T' could not be inferred
I both explicitly and implicitly cast the type to my desired value. Not sure what else I can do to fix this issue.
I've tried restarting XCode, does not help.
The real code:
public class AppData: ObservableObject {
static private var dataDirectory: Dictionary<String, Any> = Dictionary(minimumCapacity: 10000)
static let dataGetLock = DispatchSemaphore(value: 1)
static func get<T: Any>(_ objID: String) -> T? {
let retVal: T
dataGetLock.wait()
retVal = dataDirectory[objID] as! T
dataGetLock.signal()
return retVal
}
}
I get the error on the following two lines:
class StoreViewEnvironment: ObservableObject {
func initProductLoad(storeID: String) {
...
let liveStore: Store = AppData.get(storeID) as! Store
let liveMenu: Menu = AppData.get(liveStore.menuID) as! Menu
...
}
}
I don't approve of the way you're doing this (on the general grounds that Any is just about always a bad smell), but here's a version that compiles (just delete the Any constraint):
class AppData {
static let genericDict: Dictionary<String, Any> = [:]
static func get<T>(_ objID: String) -> T {
let retVal: T
retVal = genericDict[objID] as! T
return retVal
}
}
class SomeClass {
let product : String = AppData.get("yoho")
}

swift - UserDefaults wrapper

I want to make userDefaults easier to use. I write this code.
But I don't want to create a allKeys enum(cases voipToken, userId, userName, displayName,...) as the key path for my property.
If I want add a var accessToken to struct LoginInfo, I must add a case accessToken to enum AllKeys. I use CoreStore.key(.accessToken) get a keyPath for CoreStore.LoginInfo.accessToken.
import Foundation
struct CoreStore {
// static let sharedInstance = CoreStore()
// private init(){}
private static let keyPrefix = "_shared_store_"
enum allKeys: String {
case accessToken
}
static func key(_ key: allKeys) -> String {
return keyPrefix + key.rawValue
}
struct LoginInfo: CoreStoreSettable,CoreStoreGettable { }
}
extension CoreStore.LoginInfo {
static var accessToken: String? {
set {
set(value: newValue, forKey: CoreStore.key(.accessToken))
}
get {
return getString(forKey: CoreStore.key(.accessToken))
}
}
// ...
}
protocol CoreStoreSettable {}
extension CoreStoreSettable {
static func set(value: String?, forKey key: String) {
UserDefaults.standard.set(value, forKey: key)
}
}
protocol CoreStoreGettable {}
extension CoreStoreGettable {
static func getString(forKey key: String) -> String? {
return UserDefaults.standard.string(forKey: key)
}
}
Questions:
Is there a way to remove "enum allKeys"?
Should I use sharedInstance? Why?
I tried :
static var accessToken: String? {
set {
let keyPath = \CoreStore.Loginfo.accessToken
let keyPathString = keyPath.string
set(value: newValue, forKey: keyPathString)
}
get {
return getString(forKey: CoreStore.key(.accessToken))
}
}
I get error "Type of expression is ambiguous without more context"

Use of unresolved identifier 'Singleton' Swift 3

Here is my below code:
private static var __once: () = {
Singleton.instance = RtccManager()
if let instance = Singleton.instance {
instance.connectionParameters = instance.flattenConnectionParametersOverride(nil)
instance.currentStat = .sta_notConnected
DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.background).async {
instance.rtccConnect([:])
}
}
}()
var connectionParameters: [String : Any] = [:]
var currentStat: AppStatus = AppStatus.sta_notConnected
class var sharedInstance: RtccManager {
struct Singleton {
static var instance: RtccManager? = nil
static var token: Int = 0
}
_ = RtccManager.__once
return Singleton.instance!
}
I am getting error on line Singleton.instance = RtccManager() and line
if let instance = Singleton.instance
Error is: Use of unresolved identifier 'Singleton'
This error came after migrating to Swift 3.0
Any ideas on how can I resolve this error's?
If you're migrating your code It's should be a good idea to adapt your singleton creation mode to the new way suggested by Apple in their docs.
Here's an example
public class RtccManager
{
/*
Nested types
*/
public enum AppStatus
{
case sta_connected
case sta_notConnected
}
public static let sharedInstance: RtccManager = RtccManager()
public private(set) var connectionParameters: [String: Any]
public private(set) var currentStat: AppStatus
private init()
{
self.connectionParameters = [String: Any]()
// flattenConnectionParametersOverride
self.currentStat = .sta_notConnected
}
}

Swift 2.2 singleton

I am new in Swift. I am trying to parse some JSON data from web service and want a singleton class of user.But I got stuck to create the singleton. Here is my code:
import Foundation
class User {
private var success: String
private var userId: String
private var name: String
private var gender: String
private var email: String
private var userObject = [User]()
class var sharedInstane:User {
struct Singleton {
static var onceToken: dispatch_once_t = 0
static var instance:User? = nil
}
dispatch_once(&Singleton.onceToken){
Singleton.instance = User()
}
return Singleton.instance!
}
private init(success: String, userId: String, name: String, gender: String, email: String)
{
self.success = success
self.userId = userId
self.name = name
self.gender = gender
self.email = email
}
convenience init(dictionary: [String:AnyObject]) {
let success = dictionary["success"] as? String
let userId = dictionary["userId"] as? String
let name = dictionary["name"] as? String
let gender = dictionary["gender"] as? String
let email = dictionary["email"] as? String
self.init(success: success!, userId: userId!, name: name!, gender: gender!, email: email!, )
}
func callWebserviceToLoadUserInfo (url:String, param:[String:AnyObject],completeHandler:(Bool?,String) -> ())
{
let connection = ServerConnection()
connection.getJSONDataForWebService(url, params: param) { (response, error) in
// code goes here
var responseDict = response as! [String : AnyObject]
responseDict = responseDict["responseDict"] as! [String : AnyObject]
if responseDict["success"] as! String == "1" {
for dict in responseDict {
let user = User(dictionary: (dict as! [String:AnyObject]))
self.userObject.append(user)
}
print("user : \(self.userObject[0].name)")
}else{
// error goes here
}
}
}
}
Can any one please help me how should I do this code?
The singleton in the single line sample code.
class TheOneAndOnlyKraken {
static let sharedInstance = TheOneAndOnlyKraken()
private init() {} //This prevents others from using the default '()' initializer for this class.
}
For more details.
Using Krakendev's single-line singleton code, cited by Maheshwar, and turning your convenience init into an instance function to be called with User.sharedInstance.initialize(dictionary):
import Foundation
class User {
// Here you declare all your properties
// "private var" and all that jazz
static let sharedInstance = User()
private init() {
// If you have something to do at the initialization stage
// you can add it here, as long as it does not involve
// arbitrary values that you would pass as parameters.
}
func initialize(dictionary: [String:AnyObject]) {
// Transfer the values of the dictionary to each `self.property`.
// Be careful while using `as?` as you may have to deal with
// optionals. No need to call `self.init` at the end, because
// this is now a regular `func`.
}
// Add the rest of your stuff here
}
One note about how you were working inside of that convenience initializer: if you do property = SomeClass.someMethod().someProperty as? SomeType, then property will be of type SomeType?, or Optional(SomeType). According to The Swift Programming Language,
The conditional form, as?, returns an optional value of the type you are trying to downcast to.
While User was not instantiated at least one time sharedInstance will return nil. After the first successful instantiation of the User, sharedInstance starts return it and that's became impossible to instantiate another one User as singleton pattern requires it. Consider this:
class User {
private static var sharedUser: User?
class var sharedInstance: User? {
return sharedUser
}
private init(success: String, userId: String, name: String, gender: String, email: String)
{
//User initialization code here
User.sharedUser = self
}
convenience init?(dictionary: [String:AnyObject]) {
guard User.sharedUser == nil else {
return nil
}
//dictionary parsing code is here
self.init(success: success!, userId: userId!, name: name!, gender: gender!, email: email!)
}
}
Client's code:
User.sharedUser
//return nil
let dict: [String:AnyObject] = ["success": "success", "userId":"userId", "name":"name", "gender":"gender","email":"email"]
User(dictionary: dict)
//creates User
User.sharedUser
//returns just created user
User(dictionary: dict)
//return nil
You should think about making this two classes, so that User is your model class and then create a manager to handle all the users (which seems to be your goal).
So in User remove the sharedInstane part and create a second singleton class, e.g. called UserManager, with the standard way to create a singleton in Swift. Then you can keep the way you're creating your user and in the end just assign it to the singleton:
class UserManager {
static let sharedInstance = UserManager()
var users = [User]()
}
// in your code:
...
for dict in responseDict {
let user = User(dictionary: (dict as! [String:AnyObject]))
UserManager.sharedInstance.users.append(user)
}
...