I have recently been updating a library to swift 3.0 as a relatively new swift dev. I get a error where I cannot cast __NSCFDictionary to a NSArray format. How could I convert __NSCFDictionary to NSArray format in swift with a example please?
This is the current function I am converting
method(name: "login", params: NSArray(arrayLiteral: params)) //fails casting here
guard let e = error , (e.isValid == true) else {
if let user = params["user"] as? AnyObject {
if let email = user["email"] {
self.userData.set(email, forKey: DDP_EMAIL)
}
if let username = user["username"] {
self.userData.set(username, forKey: DDP_USERNAME)
}
}
if let data = result as? NSDictionary,
let id = data["id"] as? String,
let token = data["token"] as? String,
let tokenExpires = data["tokenExpires"] as? NSDictionary {
let expiration = dateFromTimestamp(containedIn: tokenExpires)
self.userData.set(id, forKey: DDP_ID)
self.userData.set(token, forKey: DDP_TOKEN)
self.userData.set(expiration, forKey: DDP_TOKEN_EXPIRES)
}
self.userMainQueue.addOperation() {
if let c = callback { c(result, error) }
self.userData.set(true, forKey: DDP_LOGGED_IN)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: DDP_USER_DID_LOGIN), object: nil)
if let _ = self.delegate {
self.delegate!.ddpUserDidLogin(user: self.user()!)
}
}
return
}
log.debug("Login error: \(e)")
if let c = callback { c(result, error) }
}
Related
I am trying to get data from firestore collection and assign it to an array of dictionaries. for this part of the code below... i get the error "Cast from 'QuerySnapshot?' to unrelated type '[[String : Any]]' always fails" and the console prints "is not working".
guard let snap = snapshot as? [[String:Any]] else {
print("is not working")
completion(.failure(DatabaseError.failedToFetch))
return
}
Here is the full code.
// fetches and returns all conversations for the user with passed in uid
public func getAllConversations(for uid: String, completion: #escaping(Result<[Conversation], Error>) -> Void) {
print("fetching all convos")
//NEW
let db = Firestore.firestore()
let CurrentUser = Auth.auth().currentUser?.uid
let ListRef = db.collection("users").document(CurrentUser!).collection("conversations")
// fetch the current users convo list
ListRef.getDocuments { snapshot, error in
if let err = error {
debugPrint("Error fetching documents: \(err)")
} else {
guard let snap = snapshot as? [[String:Any]] else {
print("is not working")
completion(.failure(DatabaseError.failedToFetch))
return
}
print("is working")
let conversations: [Conversation] = snap.compactMap({ dictionary in
guard let id = dictionary["id"] as? String,
let name = dictionary["name"] as? String,
let otherUserUID = dictionary["other_user-uid"] as? String,
let latestMessage = dictionary["latest-message"] as? [String:Any],
let date = latestMessage["date"] as? String,
let message = latestMessage["message"] as? String,
let isRead = latestMessage["is-read"] as? Bool else {
return nil
}
//save other user ID to a global var
self.test = otherUserUID
//assign data into an array of dictionaries
let latestConvoObject = LatestMessage(date: date, text: message, isRead: isRead)
return Conversation(id: id, name: name, otherUserUid: otherUserUID, latestMessage: latestConvoObject)
})
completion(.success(conversations))
}
}
}
There are a numbers of way to read that data, and the process can be simplified by conforming objects to the codable protocol but let me provide a straight forward example. I don't know what your Conversation object looks like so here's mine
class ConversationClass {
var from = ""
var to = ""
var msg = ""
var timestamp = 0
convenience init(withDoc: DocumentSnapshot) {
self.init()
self.from = withDoc.get("from") as? String ?? "no from"
self.to = withDoc.get("to") as? String ?? "no to"
self.msg = withDoc.get("msg") as? String ?? "no msg"
self.timestamp = withDoc.get("timestamp") as? Int ?? 0
}
}
and then here's the the code that reads in all the conversation documents from a Collection, stores each in a ConversationClass object, puts those in an array and returns it through an escaping completion handler
func getConversations(completion: #escaping( [ConversationClass] ) -> Void) {
let conversationCollection = self.db.collection("conversations")
conversationCollection.getDocuments(completion: { snapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let docs = snapshot?.documents else { return }
var convoArray = [ConversationClass]()
for doc in docs {
let convo = ConversationClass(withDoc: doc)
convoArray.append(convo)
}
completion(convoArray)
})
}
until version 10.7.6 of Realm I could convert to dictionary and then to json with this code below, but the ListBase class no longer exists.
extension Object {
func toDictionary() -> NSDictionary {
let properties = self.objectSchema.properties.map { $0.name }
let dictionary = self.dictionaryWithValues(forKeys: properties)
let mutabledic = NSMutableDictionary()
mutabledic.setValuesForKeys(dictionary)
for prop in self.objectSchema.properties as [Property] {
// find lists
if let nestedObject = self[prop.name] as? Object {
mutabledic.setValue(nestedObject.toDictionary(), forKey: prop.name)
} else if let nestedListObject = self[prop.name] as? ListBase { /*Cannot find type 'ListBase' in scope*/
var objects = [AnyObject]()
for index in 0..<nestedListObject._rlmArray.count {
let object = nestedListObject._rlmArray[index] as! Object
objects.append(object.toDictionary())
}
mutabledic.setObject(objects, forKey: prop.name as NSCopying)
}
}
return mutabledic
}
}
let parameterDictionary = myRealmData.toDictionary()
guard let postData = try? JSONSerialization.data(withJSONObject: parameterDictionary, options: []) else {
return
}
List now inherits from RLMSwiftCollectionBase apparently, so you can check for that instead. Also, this is Swift. Use [String: Any] instead of NSDictionary.
extension Object {
func toDictionary() -> [String: Any] {
let properties = self.objectSchema.properties.map { $0.name }
var mutabledic = self.dictionaryWithValues(forKeys: properties)
for prop in self.objectSchema.properties as [Property] {
// find lists
if let nestedObject = self[prop.name] as? Object {
mutabledic[prop.name] = nestedObject.toDictionary()
} else if let nestedListObject = self[prop.name] as? RLMSwiftCollectionBase {
var objects = [[String: Any]]()
for index in 0..<nestedListObject._rlmCollection.count {
let object = nestedListObject._rlmCollection[index] as! Object
objects.append(object.toDictionary())
}
mutabledic[prop.name] = objects
}
}
return mutabledic
}
}
Thanks to #Eduardo Dos Santos. Just do the following steps. You will be good to go.
Change ListBase to RLMSwiftCollectionBase
Change _rlmArray to _rlmCollection
Import Realm
I am trying to parse the emergency data in into emergency struct but it never statifies the condition and get into else case.Here is my code and structure.Some thing i have written woring in first line.
if let emergencyDict = snapshotValue["emergency"] as? [String:[String:Any]]{
for (emerId, emerData) in emergencyDict {
let emer = Emergency.init(emergency: emerData as NSDictionary)
emergency.append(emer)
}
}
else{
let emer = Emergency.init(emerg: "" as AnyObject)
emergency.append(emer)
}
struct Emergency{
var emer_id: String
var emer_name: String
var emer_phoneNo: String
init(emergency: NSDictionary) {
if emergency.object(forKey: "id") != nil {
emer_id = emergency.object(forKey: "id") as! String
}
else{
emer_id = ""
}
}
}
The problem you are having emergency as Array with type [Any] and if you remove the first object then you get Array of type [[String:Any]]. So try like this way.
if let array = snapshotValue["emergency"] as? [Any],
let emergencyArrar = Array(array.dropFirst()) as? [[String:Any]] {
print(emergencyArray)
for emergency in emergencyArray {
print(emergency)
}
}
You have written wrong in this line:
if let emergencyDict = snapshotValue["emergency"] as? [String:[String:Any]]{
It should be:
if let emergencyDict = snapshotValue["emergency"] as? [[String:Any]]{
This question should belong to query from firebase database.
// you have to get the children in emergency,
// then get the value(dictionary) of each child
ref.child("emergency").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let keys = value?.allKeys // [1, 2, 3 ....]
for key in keys {
ref.child("emergency").child(key)..observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
// Here is your dictionary
}
}
}) { (error) in
print(error.localizedDescription)
}
I am not an iOS dev and have to make a few changes to a Swift / AlamoFire project (not mine) and am a bit lost.
I have the following JSON:
{"metro_locations":
[
{
"name":"Ruby Red"
},
{
"name":"Blue Ocean"
}
]
}
class (I know that there are issues here):
class Location{
var name=""
init(obj:tmp){
self.name=tmp["name"]
}
}
and need to make an AlamoFire call
Alamofire.request(.GET, "https://www.domain.com/arc/v1/api/metro_areas/1", parameters: nil)
.responseJSON { response in
if let dataFromNetworking = response.result.value {
let metroLocations = dataFromNetworking["metro_locations"]
var locations: [Location]=[]
for tmp in metroLocations as! [Dictionary] { // <- not working, Generic Paramter 'Key' could not be inferred
let location=Location.init(obj: tmp)
locations.append(location)
}
}
}
I have included the error msg, the "not working" but feel that there are issues in other parts too (like expecting a dictionary in the initialization). What does the 'Key' could not be inferred mean and are there other changes I need to make?
edit #1
I have updated my Location to this to reflect your suggestion:
init?(dictionary: [String: AnyObject]) {
guard let id = dictionary["id"] else { return nil }
guard let name = dictionary["name"] else { return nil }
guard let latitude = dictionary["latitude"] else { return nil }
guard let longitude = dictionary["longitude"] else { return nil }
self.name = name as! String
self.id = id as! Int
self.latitude = latitude as! Double
self.longitude = longitude as! Double
}
but I get the error:
Could not cast value of type 'NSNull' (0x10f387600) to 'NSNumber' (0x10f77f2a0).
like this:
I would think that the guard statement would prevent this. What am I missing?
You can cast metroLocations as an array of dictionaries, namely:
Array<Dictionary<String, String>>
Or, more concisely:
[[String: String]]
Thus:
if let dataFromNetworking = response.result.value {
guard let metroLocations = dataFromNetworking["metro_locations"] as? [[String: String]] else {
print("this was not an array of dictionaries where the values were all strings")
return
}
var locations = [Location]()
for dictionary in metroLocations {
if let location = Location(dictionary: dictionary) {
locations.append(location)
}
}
}
Where
class Location {
let name: String
init?(dictionary: [String: String]) {
guard let name = dictionary["name"] else { return nil }
self.name = name
}
}
Clearly, I used [[String: String]] to represent an array of dictionaries where the values were all strings, as in your example. If the values included objects other than strings (numbers, booleans, etc.), then you might use [[String: AnyObject]].
In your revision, you show us a more complete Location implementation. You should avoid as! forced casting, and instead us as? in the guard statements:
class Location {
let id: Int
let name: String
let latitude: Double
let longitude: Double
init?(dictionary: [String: AnyObject]) {
guard let id = dictionary["id"] as? Int,
let name = dictionary["name"] as? String,
let latitude = dictionary["latitude"] as? Double,
let longitude = dictionary["longitude"] as? Double else {
return nil
}
self.name = name
self.id = id
self.latitude = latitude
self.longitude = longitude
}
}
Sigh, I have been working on this the whole afternoon... here is my nightmare:
I am trying to use the KeychainItemWrapper made by Apple. But I "translated" its Objective-C codes to Swift:
import Foundation
import Security
class MyKeychainItemWrapper: NSObject {
var keychainItemData: NSMutableDictionary?
var genericPasswordQuery: NSMutableDictionary = NSMutableDictionary()
init(identifier: String, accessGroup: String?) {
super.init()
// Begin Keychain search setup. The genericPasswordQuery leverages the special user
// defined attribute kSecAttrGeneric to distinguish itself between other generic Keychain
// items which may be included by the same application.
genericPasswordQuery.setObject(kSecClassGenericPassword, forKey: kSecClass)
genericPasswordQuery.setObject(identifier, forKey: kSecAttrGeneric)
// The keychain access group attribute determines if this item can be shared
// amongst multiple apps whose code signing entitlements contain the same keychain access group.
println(accessGroup)
if (!(accessGroup == nil)) {
genericPasswordQuery.setObject(accessGroup!, forKey: kSecAttrAccessGroup)
}
// Use the proper search constants, return only the attributes of the first match.
genericPasswordQuery.setObject(kSecMatchLimitOne, forKey: kSecMatchLimit)
genericPasswordQuery.setObject(kCFBooleanTrue, forKey: kSecReturnAttributes)
var tempQuery: NSDictionary = NSDictionary(dictionary: genericPasswordQuery)
var outDictionary: Unmanaged<AnyObject>? = nil
var status: OSStatus = SecItemCopyMatching(tempQuery as CFDictionaryRef, &outDictionary)
println(status == noErr)
if (status == noErr) {
// Stick these default values into keychain item if nothing found.
resetKeychainItem()
// Add the generic attribute and the keychain access group.
keychainItemData!.setObject(identifier, forKey: kSecAttrGeneric)
if (!(accessGroup == nil)) {
keychainItemData!.setObject(accessGroup!, forKey: kSecAttrAccessGroup)
}
} else {
// load the saved data from Keychain.
keychainItemData = secItemFormatToDictionary(outDictionary?.takeRetainedValue() as NSDictionary)
}
}
Then in my app's AppDelegate.swift, I am trying to use it by:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var passwordItem: MyKeychainItemWrapper = MyKeychainItemWrapper(identifier: "Password", accessGroup: nil)
...
So, the initializer is called, but but but somehow, I ALWAYS, ALWAYS get
Thread 1: EXC_BREAKPOINT (code=EXC_ARM_BREAKPOINT, subcode=0xe7ffdefe)
I have tried commenting out the problem lines and then I get this error at another if():
I even tried:
var mmm: Bool = (accessGroup == nil)
if (!mmm) {
genericPasswordQuery.setObject(accessGroup!, forKey: kSecAttrAccessGroup)
}
But same error at the same place, i.e. if(..)
I am now so confused. Did I miss something here or?
Environment: Xcode6-beta6, iOS 8 beta 5 on a non-jailbroken iPhone 5.
Swift 3
import UIKit
import Security
let kSecClassGenericPasswordValue = String(format: kSecClassGenericPassword as String)
let kSecClassValue = String(format: kSecClass as String)
let kSecAttrServiceValue = String(format: kSecAttrService as String)
let kSecValueDataValue = String(format: kSecValueData as String)
let kSecMatchLimitValue = String(format: kSecMatchLimit as String)
let kSecReturnDataValue = String(format: kSecReturnData as String)
let kSecMatchLimitOneValue = String(format: kSecMatchLimitOne as String)
let kSecAttrAccountValue = String(format: kSecAttrAccount as String)
struct KeychainAccess {
func setPasscode(identifier: String, passcode: String) {
if let dataFromString = passcode.data(using: String.Encoding.utf8) {
let keychainQuery = [
kSecClassValue: kSecClassGenericPasswordValue,
kSecAttrServiceValue: identifier,
kSecValueDataValue: dataFromString
] as CFDictionary
SecItemDelete(keychainQuery)
print(SecItemAdd(keychainQuery, nil))
}
}
func getPasscode(identifier: String) -> String? {
let keychainQuery = [
kSecClassValue: kSecClassGenericPasswordValue,
kSecAttrServiceValue: identifier,
kSecReturnDataValue: kCFBooleanTrue,
kSecMatchLimitValue: kSecMatchLimitOneValue
] as CFDictionary
var dataTypeRef: AnyObject?
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var passcode: String?
if (status == errSecSuccess) {
if let retrievedData = dataTypeRef as? Data,
let result = String(data: retrievedData, encoding: String.Encoding.utf8) {
passcode = result as String
}
}
else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return passcode
}
}
Swift 2
import UIKit;
import Security;
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword);
let kSecClassValue = NSString(format: kSecClass);
let kSecAttrServiceValue = NSString(format: kSecAttrService);
let kSecValueDataValue = NSString(format: kSecValueData);
let kSecMatchLimitValue = NSString(format: kSecMatchLimit);
let kSecReturnDataValue = NSString(format: kSecReturnData);
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne);
let kSecAttrAccountValue = NSString(format: kSecAttrAccount);
class KeychainAccess: NSObject {
func setPasscode(identifier: String, passcode: String) {
let dataFromString: NSData = passcode.dataUsingEncoding(NSUTF8StringEncoding)!;
let keychainQuery = NSDictionary(
objects: [kSecClassGenericPasswordValue, identifier, dataFromString],
forKeys: [kSecClassValue, kSecAttrServiceValue, kSecValueDataValue]);
SecItemDelete(keychainQuery as CFDictionaryRef);
let status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil);
}
func getPasscode(identifier: String) -> NSString? {
let keychainQuery = NSDictionary(
objects: [kSecClassGenericPasswordValue, identifier, kCFBooleanTrue, kSecMatchLimitOneValue],
forKeys: [kSecClassValue, kSecAttrServiceValue, kSecReturnDataValue, kSecMatchLimitValue]);
var dataTypeRef: AnyObject?
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var passcode: NSString?;
if (status == errSecSuccess) {
let retrievedData: NSData? = dataTypeRef as? NSData
if let result = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding) {
passcode = result as String
}
}
else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return passcode;
}
}
Then from anywhere simply call:
func setPasscode(passcode: String) {
let keychainAccess = KeychainAccess();
keychainAccess.setPasscode("YourAppIdentifier", passcode:passcode);
}
func getPasscode() -> NSString {
let keychainAccess = KeychainAccess();
return keychainAccess.getPasscode("YourAppIdentifier")!;
}
func deletePasscode() {
let keychainAccess = KeychainAccess();
keychainAccess.setPasscode("YourAppIdentifier", passcode:"");
}
official is GenericKeychain
existing several swift version, the best one is:
jrendel/SwiftKeychainWrapper ยท GitHub
how to use it:
download file: KeychainWrapper.swift
write code to set/get/delete:
let StrUsernameKey:String = "username"
let StrPasswordKey:String = "password"
let saveSuccessful: Bool = KeychainWrapper.setString(usernameTextField.text!, forKey: StrUsernameKey)
print("saveSuccessful=\(saveSuccessful)") //saveSuccessful=true
let retrievedString: String? = KeychainWrapper.stringForKey(StrUsernameKey)
print("retrievedString=\(retrievedString)") //retrievedString=Optional("yourLastStoredUsernameString")
let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey(StrUsernameKey)
print("removeSuccessful=\(removeSuccessful)") //removeSuccessful=true
let retrievedStringAfterDelete: String? = KeychainWrapper.stringForKey(StrUsernameKey)
print("retrievedStringAfterDelete=\(retrievedStringAfterDelete)") //retrievedStringAfterDelete=nil
Updates for Swift 2.
Here is an example implementation that may help.:
import Security
class ZLKeychainService: NSObject {
var service = "Service"
var keychainQuery :[NSString: AnyObject]! = nil
func save(name name: NSString, value: NSString) -> OSStatus? {
let statusAdd :OSStatus?
guard let dataFromString: NSData = value.dataUsingEncoding(NSUTF8StringEncoding) else {
return nil
}
keychainQuery = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : service,
kSecAttrAccount : name,
kSecValueData : dataFromString]
if keychainQuery == nil {
return nil
}
SecItemDelete(keychainQuery as CFDictionaryRef)
statusAdd = SecItemAdd(keychainQuery! as CFDictionaryRef, nil)
return statusAdd;
}
func load(name name: NSString) -> String? {
var contentsOfKeychain :String?
keychainQuery = [
kSecClass : kSecClassGenericPassword,
kSecAttrService : service,
kSecAttrAccount : name,
kSecReturnData : kCFBooleanTrue,
kSecMatchLimit : kSecMatchLimitOne]
if keychainQuery == nil {
return nil
}
var dataTypeRef: AnyObject?
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
if (status == errSecSuccess) {
let retrievedData: NSData? = dataTypeRef as? NSData
if let result = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding) {
contentsOfKeychain = result as String
}
}
else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return contentsOfKeychain
}
}
//Test:
let userName = "TestUser"
let userValue: NSString = "TestValue"
print("userName: '\(userName)'")
print("userValue: '\(userValue)'")
let kcs = ZLKeychainService()
kcs.save(name:userName, value: userValue)
print("Keychain Query \(kcs.keychainQuery)")
if let recoveredToken = kcs.load(name:userName) {
print("Recovered Value: '\(recoveredToken)'")
}
Output:
userName: 'TestUser'
userValue: 'TestValue'
Keychain Query [acct: TestUser, v_Data: <54657374 56616c75 65>, svce: Service, class: genp]
Recovered Value: 'TestValue'
My solution seems working:
init(identifier: String) {
super.init()
genericPasswordQuery.setObject(kSecClassGenericPassword, forKey: kSecClass as String)
genericPasswordQuery.setObject(identifier, forKey: kSecAttrGeneric as String)
// Use the proper search constants, return only the attributes of the first match.
genericPasswordQuery.setObject(kSecMatchLimitOne, forKey: kSecMatchLimit as String)
genericPasswordQuery.setObject(kCFBooleanTrue, forKey: kSecReturnAttributes as String)
var tempQuery: NSDictionary = NSDictionary(dictionary: genericPasswordQuery)
var outDictionary: Unmanaged<AnyObject>? = nil
let status: OSStatus = SecItemCopyMatching(tempQuery as CFDictionaryRef, &outDictionary)
var result: NSDictionary? = outDictionary?.takeRetainedValue() as NSDictionary?
if (result == nil) {
// Stick these default values into keychain item if nothing found.
resetKeychainItem()
// Add the generic attribute and the keychain access group.
keychainItemData!.setObject(identifier, forKey: kSecAttrGeneric as String)
} else {
// load the saved data from Keychain.
keychainItemData = secItemFormatToDictionary(result!)
}
}
The only thing I did is to unwrap the outDictionary immediately after getting it.