I have a Channel Method in my Flutter project which takes a String (url), downloads an image and presents it as part of a new contact.
However, the image won't be displayed for most urls.
This is the code I use to get the image and add to the new contact, before displaying it in a CNContactViewController.
let urlString : String = (arguments["imgUrl"] as? String)!
let url = URL(string: urlString)
let data = try? Data(contentsOf: url!)
newContact.imageData = data
I have tried encoding the url and casting it as an NSString with no luck.
Interesting, if I hard code the url in like below, it works. So what is happening between sending it from my Dart source code, over to Swift?
(URL modified to conceal contents, real URL returns image)
let url = URL(string: "https://23ee7ca4ch9430946b76-4f3bba1a032272305d39a357e474f3b2.ssl.cf1.rackcdn.com/talent/81843293_1556369106.jpeg")
let data = try? Data(contentsOf: url!)
newContact.imageData = data
Update:
Here is the full code...
Dart:
static const platform = const MethodChannel('contacts');
static Future<String> addSystemContact({
String firstName,
String lastName,
String jobTitle,
String company,
String phone,
String email,
String city,
String country,
String profile,
String imgUrl,
}) async {
try {
var result = await platform.invokeMethod(
'addSystemContact',
{
'firstName' : firstName != null && firstName.isNotEmpty ? firstName : "",
'lastName' : lastName != null && lastName.isNotEmpty ? lastName : "",
'jobTitle' : jobTitle,
'company' : company,
'phone' : phone,
'email' : email,
'city' : city,
'country' : country,
'profile' : profile,
'imgUrl': imgUrl,
},
);
return result;
} on PlatformException catch (e) {
return null;
}
}
Swift:
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let contactChannel = FlutterMethodChannel(name: "contacts", binaryMessenger: controller)
contactChannel.setMethodCallHandler({(call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
//ADD SYSTEM CONTACT
//
if (call.method == "addSystemContact") {
let arguments : Dictionary = call.arguments as! Dictionary<String,Any>
guard let firstName : String = arguments["firstName"] as? String else { return result(false); }
guard let lastName : String = arguments["lastName"] as? String else { return result(false); }
//create contact
let newContact = CNMutableContact()
newContact.contactType = CNContactType.person
newContact.givenName = firstName
newContact.familyName = lastName
if let jobTitle : String = arguments["jobTitle"] as? String {
newContact.jobTitle = jobTitle
}
if let company : String = arguments["company"] as? String {
newContact.organizationName = company
}
if let phone : String = arguments["phone"] as? String {
newContact.phoneNumbers = [CNLabeledValue(
label:CNLabelPhoneNumberMain,
value:CNPhoneNumber(stringValue:phone))]
}
if let email : NSString = arguments["email"] as? NSString {
newContact.emailAddresses = [CNLabeledValue(label:CNLabelWork, value:email)]
}
let address = CNMutablePostalAddress()
if let city : String = arguments["city"] as? String {
address.city = city
}
if let country : String = arguments["country"] as? String {
address.country = country
}
newContact.postalAddresses = [CNLabeledValue(label:CNLabelWork, value:address)]
if let profile : String = arguments["profile"] as? String {
newContact.socialProfiles = [CNLabeledValue(label:
CNSocialProfileServiceLinkedIn, value: CNSocialProfile.init(urlString: profile, username: nil, userIdentifier: nil, service: nil))]
}
// Offending code...
//
let urlString : String = (arguments["imgUrl"] as? String)!
let url = URL(string: urlString)
let data = try? Data(contentsOf: url!)
newContact.imageData = data
//
//
//presents nav controller with CNContactViewController
self.displayConactViewController(newContact: newContact, result: result)
}
})
You seem to have a mix of methodologies. Are you passing a single string as the argument, or a map? Unfortunately you don't show the Dart end.
Here are some working examples, adapted from a working plugin.
Single String
Dart end
static void ping(String pong) {
_channel.invokeMethod('ping', pong);
}
Swift end - call.arguments is a String
case "ping":
NSLog("ping")
if let pong = call.arguments as! String {
// do something
} else {
NSLog("ping - Invalid Args")
}
break
String as map member
Dart end
static void foo(String bar, String baz) {
_channel.invokeMethod('foo', <String, dynamic>{
'bar': bar,
'baz': baz,
});
}
Swift end - call.arguments is a Dictionary
case "foo":
NSLog("foo")
if let args = call.arguments as? Dictionary<String, Any>, let bar = args["bar"] as! String {
// do something with bar and/or args["baz"] as! String
} else {
NSLog("foo - Args is not a Dictionary")
}
break
Related
Please help! I am experiencing an app crash.
public enum memberships {
case noMembership
case paid
case freeTrial
case trialExpired
}
public class DataManager {
private static let uuid = UIDevice.current.identifierForVendor!.uuidString
private static let user = Auth.auth().currentUser
private static let userRef = Database.database().reference().child("Users").child(user?.uid ?? "")
static var currentStatus: memberships? = nil
/**
Get's the current user's info from Firebase
and returns the info as a User object
*/
class func getUser(completion: #escaping (User?) -> ()) {
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
// UIPasteboard.general.string = uuid
let uuid = snapshot.key
let name = value["name"] as? String ?? ""
let email = value["email"] as? String ?? ""
let dateJoined = value["dateJoined"] as? String ?? ""
let membershipString = value["membership"] as? String
let alertsStartTime = value["alertsStartTime"] as? String ?? ""
let alertsEndTime = value["alertsEndTime"] as? String ?? ""
let alertsFrequency = value["alertsFrequency"] as? Int ?? 1
let alertsType = value["alertsType"] as? String ?? ""
let isEnable = value["isEnable"] as? Bool ?? true
//Gets users current membership
var membershipStatus: memberships!
if membershipString == "Paid" {
membershipStatus = .paid
}else if membershipString == "NoMembership" {
membershipStatus = .noMembership
}else{
membershipStatus = Utils.getUserMembershipStatus(dateJoined: dateJoined)
}
let user = User(uuid: uuid, name: name, email: email, dateJoined: dateJoined, membership: membershipStatus, alertsStartTime: alertsStartTime, alertsEndTime: alertsEndTime, alertsType: alertsType, alertsFrequency: alertsFrequency, isEnable: isEnable)
completion(user)
}else{
completion(nil)
}
}) { (error) in
print(error.localizedDescription)
completion(nil)
}
}
Your user object is empty. So user?.uid is nil. Which means child(user?.uid ?? "") -> child("").
Firebase does not accept empty strings as key values (It also does not accepts strings which includes '.' '#' '$' '[' or ']'' as keys).
So in your case make sure user is logged or use different key value.
I'm using FB Login for my app in Swift and when I make a graph request, it returns the following result:
Optional({
email = "arjun.ramjams#gmail.com";
id = 10218497873670001;
name = "Arjun Ram";
})
Now, how should I read, each individual value(i.e: email, id and name)?
func fetchProfile() {
let parameters = ["fields": "id,email, first_name, last_name"]
GraphRequest(graphPath: "me",parameters: parameters).start{(connection, user, Err) in
if Err != nil {
print(Err!)
return
}
let dic = user as! NSDictionary
let userID = dic["id"] as! String
let Email = dic["email"] as! String
let fname = dic["first_name"] as! String
let lname = dic["last_name"] as! String
}
}
The result is a Dictionary of type [String:Any]?.
let result:[String:Any]? = [
"email":"arjun.ramjams#gmail.com",
"id" : 10218497873670001,
"name":"Arjun Ram"
]
You can fetch the fields from result like,
let email = result?["email"] as? String
let id = result?["id"] as? Int
let name = result?["name"] as? String
You need to use if let or guard let for unwraping an object or variable
e.g.:
if let result = result as? [String: Any] {
print(result[“email”])
}
or
guard let result = result as? [String: Any] else {
// return something
}
Here is my code and it fails to execute as noted below.
I am trying to cast an object to my custom data type called UserData.
First problem I have is don't understand how to get the value out of the array correctly
Second I cannot seem to cast the object as the type I need, UserData. What I am doing wrong?
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
var users = NSMutableArray();
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
for i in 0 ..< jsonResult.count {
print("loop count :", i );
jsonElement = jsonResult[i] as! NSDictionary
let user = UserData()
//the following insures none of the JsonElement values are nil through optional binding
if let UserID = jsonElement["Userid"] as? String,
let firstName = jsonElement["First_Name"] as? String,
let lastName = jsonElement["Last_Name"] as? String,
let userSessionID = jsonElement["Session_ID"] as? String
{
user.UserID = UserID
user.FirstName = firstName
user.LastName = lastName
user.UserSessionID = userSessionID
print("users firstName:", user.FirstName ?? "blank");
}
users.add(user)
}
print("users size:", users.count); // this shows 2
// So i know I have data loaded... BUT when i try and
// get it then it all goes to heck. See below
// NOT SURE what I am doing here...
// Thought it was java like where I could just get a
// item from the NSMutableArray using an index value
// then cast it as my UserData object
// and print the output... but this does not work
// Why is this so hard??
let userDataVal = users.index(of: 0) as! UserData;
print("firstName:", userDataVal.FirstName);
}
Below is how I would write this function. Some comments
No NS... classes used, instead I use native arrays and dictionaries
If JSONSerialization.jsonObject generates an error the function is exited
Local variables are define as close as possible to as where they are used
Create an init method for your UserData struct/class that takes the values as parameters so you can do let user = UserData(userId: UserId, firstName:... instead.
Name local variables and properties with a first lowercase character, it makes it easier to read the code
func parseJSON(_ data:Data) {
var jsonResult: [[String: Any]]?
do {
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as? [[String: Any]]
} catch let error as NSError {
print(error)
return
}
guard let result = jsonResult else {
return
}
var users = [UserData]()
for jsonElement in result {
if let UserID = jsonElement["Userid"] as? String,
let firstName = jsonElement["First_Name"] as? String,
let lastName = jsonElement["Last_Name"] as? String,
let userSessionID = jsonElement["Session_ID"] as? String
{
var user = UserData()
user.UserID = UserID
user.FirstName = firstName
user.LastName = lastName
user.UserSessionID = userSessionID
users.append(user)
}
}
for user in users {
print("firstName:", user.FirstName);
}
}
Better approach would be to use JSONDecoder() instead of a JSONSerailizer. Try using the following code.
struct User: Codable {
var userId, firstName, lastName, userSessionId: String
enum CodingKeys: String, CodingKey {
case userId = "Userid"
case firstName = "First_Name"
case lastName = "Last_Name"
case userSessionId = "Session_ID"
}
}
func parseJSON(_ data: Data) {
do {
let users = try JSONDecoder().decode([User].self, from: data)
users.forEach { user in
print("users first name:", user.firstName)
}
} catch {
print(error.localizedDescription)
}
}
i fetched all contacts information exactly like below . At the moment i just fetched a Phone Number from contact . the question is how i can fetch all of Phone Numbers of a contact in String array to use it?
Store Data :
class ContactStruct : NSObject {
let identifier : String
let thumbnailImageData : UIImage?
let givenName : String
let familyName : String
let phoneNumbers : String
let emailAddresses : String
init(identi:String,img:UIImage?,name:String,family:String,phone:String,email:String) {
self.identifier = identi
self.thumbnailImageData = img
self.givenName = name
self.familyName = family
self.phoneNumbers = phone
self.emailAddresses = email
}
fetch method :
class func generateModelArray() -> [ContactStruct]{
let contactStore = CNContactStore()
var contactsData = [ContactStruct]()
let key = [CNContactGivenNameKey,CNContactFamilyNameKey,CNContactImageDataKey,CNContactThumbnailImageDataKey,CNContactPhoneNumbersKey,CNContactEmailAddressesKey,CNLabelPhoneNumberMobile] as [CNKeyDescriptor]
let request = CNContactFetchRequest(keysToFetch: key)
try? contactStore.enumerateContacts(with: request, usingBlock: { (contact, stoppingPointer) in
let givenName = contact.givenName
let familyName = contact.familyName
let emailAddress = contact.emailAddresses.first?.value ?? ""
let phoneNumber = contact.phoneNumbers.first?.value.stringValue ?? ""
let identifier = contact.identifier
var image : UIImage?
if contact.thumbnailImageData != nil{
image = UIImage(data: contact.thumbnailImageData!)!
}
contactsData.append(ContactStruct(identi: identifier, img: image, name: givenName, family: familyName, phone: phoneNumber, email: emailAddress as String))
})
return contactsData
}
you can use this
func extractNumber(data: [ContactStruct]) ->[String]
{
var arrNumbers = [String]()
for number in data
{
arrNumbers.append(number.phoneNumber)
}
return arrNumbers
}
and use like this:
let arrayNumbers = extractNumber(data: contactsData)
dump(arrayNumbers) //<- this line prints on console
Well first of all your phoneNumbers and emailAddresses variables should be an array of String and not String since you can have multiple of those.
More over i would make them a Dictionary like so [String:String] - more on that later.
class ContactStruct: NSObject {
let identifier: String
let thumbnailImageData: UIImage?
let givenName: String
let familyName: String
let phoneNumbers: [String:String] // <----- the new dictionaries
let emailAddresses: [String:String] // <--
init(identifier: String, img: UIImage?, name: String, family: String, phones: [String:String], emails: [String:String]) {
self.identifier = identifier
self.thumbnailImageData = img
self.givenName = name
self.familyName = family
self.phoneNumbers = phones
self.emailAddresses = emails
}
Secondly, when it comes to fetching phone numbers and emails you need to loop through them because they arrive of Type [CNLabeledValue<CNPhoneNumber>] which is an Array of phoneNumbers encased in CNLabledValue, which means that they have a value (phone number) and label which in this instance is type of phone number (i.e home, mobile, fax, esc.. more on that here - from Apple
Documentation.)
This is also why I would use a Dictionary [String:String] so you can keep them in a key-value relationship where the key will be phone type and the value is the phone number.
["mobile": "(494) 232 1134"] for example.
If I were you I would build a simple function to iterate through them and return your Dictionary ready, for example:
private func getPhoneNumbersFromContact(_ contact: CNContact) -> [String:String] {
var numbers: [String:String] = [:]
var i = 1
for number in contact.phoneNumbers {
if number.label != nil {
numbers[number.label!] = number.value.stringValue
} else {
numbers["unknown #\(i)"] = number.value.stringValue
i += 1
}
}
return numbers
}
You can always add your own flavour and style to it.
I would leave the job of writing the getEmailAddressesFromContact function for you :)
So your final generateModelArray Class might look something like this -
class func generateModelArray() -> [ContactStruct]{
let contactStore = CNContactStore()
var contactsData = [ContactStruct]()
let key = [CNContactGivenNameKey,CNContactFamilyNameKey,CNContactImageDataKey,CNContactThumbnailImageDataKey,CNContactPhoneNumbersKey,CNContactEmailAddressesKey,CNLabelPhoneNumberMobile] as [CNKeyDescriptor]
let request = CNContactFetchRequest(keysToFetch: key)
try? contactStore.enumerateContacts(with: request, usingBlock: { (contact, stoppingPointer) in
let givenName = contact.givenName
let familyName = contact.familyName
let emailAddress = getEmailAddressesFromContact(contact) // <--- only changes
let phoneNumber = getPhoneNumbersFromContact(contact) // <--- are here
let identifier = contact.identifier
var image : UIImage?
if contact.thumbnailImageData != nil{
image = UIImage(data: contact.thumbnailImageData!)!
}
contactsData.append(ContactStruct(identifier: identifier, img: image, name: givenName, family: familyName, phone: phoneNumber, email: emailAddress))
})
return contactsData
}
I am using the linkedIn SDK in my app to create a linkedIn login.
I am attempting to get the company name from the API but keep getting nil on the line:
print("Company: (companyString!)")
The dict is as follows:
["publicProfileUrl": https://www.linkedin.com/in/joebloggs, "formattedName": Joe Bloggs, "id": Zazobgtf1Q, "pictureUrls": {
"_total" = 1;
values = (
"https://media.licdn.com/mpr/mprx/0_xBXVf6v56zJf42DuUQJy70N69gfspmi8VYJMYZq_Q6X8SCm_a-4jUmz6FF4wOai_xjJMpPN_qiNQ7xaiUpvv4jq5_iN67xx8apv4S6HL9JLf7HadaZ5JIuNzlJ"
);
}, "pictureUrl": https://media.licdn.com/mpr/mprx/0_OzOPtLSS9GH8gFDYUA6-xtfSKPkgY5YmNkEPBAGSc5ypRvS_AzXjVcuSNssssXO_qkE1Mi_DxXcyZT2mBQR7sAa3VXcjZTdGlQRKlrR2ALGltB-YjcSOAkzjslDDATY14Lxx9mjGA2G, "lastName": Bloggs, "emailAddress": joe#gmail.com, "positions": {
"_total" = 1;
values = (
{
company = {
id = 9433004;
industry = "Marketing & Advertising";
name = "Company Name";
size = "2-10";
type = "Privately Held";
};
id = 865817330;
isCurrent = 1;
location = {
country = {
code = gb;
name = "United Kingdom";
};
name = "Leeds, United Kingdom";
};
startDate = {
month = 9;
year = 2016;
};
title = "Staff";
}
);
}, "firstName": Joe]
I am using the code below:
func linkedInLogin(){
LISDKSessionManager.createSession(withAuth: [LISDK_BASIC_PROFILE_PERMISSION, LISDK_EMAILADDRESS_PERMISSION], state: nil, showGoToAppStoreDialog: true, successBlock: { (returnState) -> Void in
print("success called!")
let session = LISDKSessionManager.sharedInstance().session
//let url = "https://api.linkedin.com/v1/people/~"
let url = "https://api.linkedin.com/v1/people/~:(id,summary,positions,email-address,first-name,last-name,public-profile-url,formatted-name,picture-url,picture-urls::(original))?format=json"
if LISDKSessionManager.hasValidSession() {
LISDKAPIHelper.sharedInstance().getRequest(url, success: { (response) -> Void in
// print(response!.data!)
let str = response!.data!
let dict = self.convertToDictionary(text: str)
print(dict!)
let firstName : String? = dict!["firstName"] as! String?
let lastName : NSString? = dict?["lastName"] as? NSString
let email : NSString? = dict?["emailAddress"] as? NSString
let userName : NSString? = dict?["formattedName"] as? NSString
let linkedInID : NSString? = dict?["id"] as? NSString
let link : NSString? = dict?["publicProfileUrl"] as? NSString
let liid : NSString? = dict?["id"] as? NSString
let picurl : NSString? = dict?["pictureUrl"] as? NSString
// let summary : NSString? = dict?["summary"] as? NSString
let positions : NSString? = dict?["positions"] as? NSString
var companyString:String!
// let type = (self.data[indexPath.row] as? [String : String])?["Type"]
if let company = (dict?["company"] as? [String : String])?["name"]{
companyString = company
}
print("FIRSTNAME: \(firstName!)")
print("LASTNAME: \(lastName!)")
if email != nil {print("email: \(email!)")}
print("userName: \(userName!)")
print("linkedinid: \(linkedInID!)")
print("Link: \(link!)")
print("Liid: \(liid!)")
print("pic url: \(picurl!)")
// print("summary: \(summary!)")
print("positions: \(positions!)")
print("Company: \(companyString!)")
}, error: { (error) -> Void in
print(error!)
})
}
}) { (error) -> Void in
print("Error: \(error)")
}
}
func convertToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
The value for company is [String:Any] because id is Int
if let dict = self.convertToDictionary(text: str) {
...
if let company = dict["company"] as? [String : Any],
let companyName = company["name"] as? String {
print(companyName)
}
...
}
Side notes:
Don't annotate types the compiler can infer.
Don't use NSString in Swift.
There are too many exclamation and question marks. Use optional bindings to get safely unwrapped non-optional types.