I'm trying to get contacts (swift 3) in ios 9
func getInnerContacts()-> Observable<[CNContact]>{
return Observable<[CNContact]>.create({ (observer) -> Disposable in
var contacts = [CNContact]()
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactPhoneNumbersKey] as [Any]
self.contactStore.requestAccess(for: .contacts, completionHandler: { (granted, error) -> Void in
if granted {
let predicate = CNContact.predicateForContactsInContainer(withIdentifier: self.contactStore.defaultContainerIdentifier())
do {
contacts = try self.contactStore.unifiedContacts(matching: predicate, keysToFetch: keysToFetch as! [CNKeyDescriptor])
contacts = newContacts.filter({ (contact) -> Bool in
return !contact.phoneNumbers.isEmpty && contact.givenName != "" || !contact.phoneNumbers.isEmpty && contact.familyName != ""
})
observer.onNext(contacts)
observer.onCompleted()
}catch {
observer.onError(error)
}
}
})
return Disposables.create()
})
}
but it doesn't work, moreover, I can't see permission alert.
Before executing i first check the iOS version, like this:
if #available(iOS 10 , *){
}else if #available(iOS 9, *){
}
then i added permission tag Privacy - Contacts Usage Description into Info.plist file. This works fine in ios 10, any ideas how can I achieve this in ios 9
It will work fine in Swift 3.
var store = CNContactStore()
var contacts = [CNContact]()
Try this in viewDidLoad():
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey, CNContactOrganizationNameKey, CNContactImageDataKey]
let request = CNContactFetchRequest(keysToFetch: keysToFetch as [CNKeyDescriptor])
do {
try self.store.enumerateContacts(with: request) { contact, stop in
self.contacts.append(contact)
}
DispatchQueue.main.async(execute: {
for contact in self.contacts {
print("contact:\(contact)")
let firstName=String(format:"%#",contact.givenName)
// print("first:\(firstName)")
self.givenNameArray.append(firstName)
let lastName=String(format:"%#",contact.familyName)
// print("last:\(lastName)")
self.familyNameArray.append(lastName)
let comapny=String(format:"%#",contact.organizationName)
// print("company:\(comapny)")
self.organizationNameArray.append(comapny)
//get all phone numbers
// for ContctNumVar: CNLabeledValue in contact.phoneNumbers
// {
// let MobNumVar = (ContctNumVar.value ).value(forKey: "digits") as? String
// print("ph no:\(MobNumVar!)")
// get one phone number
let MobNumVar = (contact.phoneNumbers[0].value ).value(forKey: "digits") as! String
// print("mob no:\(MobNumVar)")
self.phonenosArray.append(MobNumVar)
// get all emails
// for ContctNumVar1: CNLabeledValue in contact.emailAddresses
// {
// print("email \(ContctNumVar1.value(forKey: "value") as! String)")
// }
// get one email
let email = (contact.emailAddresses[0]).value(forKey: "value") as! String
// print("email:\(email)")
self.emailsArray.append(email)
let imagedat=contact.imageData
// print("image data:\(imagedat)")
let image=UIImage(data: imagedat!)
// print("image:\(image)")
self.imagesArray.append(image!)
}
})
} catch {
print(error)
}
Related
I am having initialization trouble with an exchange rate structure. In the method getRates I have been trying to implement dictionary key / value logic to copy exchange rates into an ordered array. I am getting the error "Variable 'moneyRates' used before being initialized". I tried adding a memberwise initializer but was unsure how to initialize the rate array. I have also been wondering if I should move the instance of MoneyRates to the top of the class instead of in the getRates method.
var currCode: [String] = ["usd", "afn", "all", "dzd", "amd", "ang", "aoa", "ars", "aud", "awg", "azn",
/* b */ "bsd", "bhd", "bdt", "bbd", "byr", "bzd", "bmd", "btn", "bob", "bam", "bwp", "brl", "bnd", "bgn", "bif",
/* c */ "cad", "khr", "cve", "xcd", "kyd", "xaf", "xof", "xpf", "clf", "clp", "cnh", "cny", "cop", "kmf", "cdf", "crc", "hrk", "cup", "czk"]
struct MoneyRates {
let date: String
let base: String
let rates: [String: Double]
}
class CurrencyRates: ObservableObject {
#Published var rateArray = [Double] ()
init() {
if UserDefaults.standard.array(forKey: "rates") != nil {
rateArray = UserDefaults.standard.array(forKey: "rates") as! [Double]
} else {
rateArray = [Double] (repeating: 0.0, count: 170)
UserDefaults.standard.set(self.rateArray, forKey: "rates")
}
}
// retrieve exchange rates for all 150+ countries from internet and save to rateArray
func updateRates(baseCur: String) {
print("doing update")
let baseUrl = "https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api#1/latest/currencies/"
let requestType = ".json"
guard let url = URL(string: baseUrl + baseCur + requestType) else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
do {
let result = try JSONSerialization.jsonObject(with: data) as! [String:Any]
var keys = Array(result.keys)
if let dateIndex = keys.firstIndex(of: "date"),
let date = result[keys[dateIndex]] as? String, keys.count == 2 {
keys.remove(at: dateIndex)
let base = keys.first!
print("base = \(base)")
print("date = \(date)")
let rates = MoneyRates(date: date, base: base, rates: result[base] as! [String:Double])
print(rates)
self.getRates(rates: rates, baseCur: baseCur)
}
} catch {
print(error)
}
}
}.resume()
}
// copy rates from MoneyRates to rateArray
func getRates (rates: MoneyRates, baseCur: String) -> () {
var moneyRates: MoneyRates
var currencyCode: String = ""
// loop through all available currencies
// the rates are stored in the same order as currCode
for index in 0..<currCode.count {
currencyCode = currCode[index]
// special handling for base currency
if currencyCode == baseCur {
rateArray[index] = 1.0000
} else {
print(currencyCode)
if moneyRates.rates[currencyCode] != nil { // error here
let unwrapped = (moneyRates.rates[currencyCode]!) // and here (same)
print( unwrapped)
rateArray[index] = 1.0 / unwrapped // want inverse exchange rate
}
}
UserDefaults.standard.set(self.rateArray, forKey: "rates")
}
}
}
The error you are getting is because you declare the variable "moneyRates"
but you do not instantiate it to something.
var moneyRates: MoneyRates
In other words there is nothing in "moneyRates",
but you are trying to get something from it in:
if moneyRates.rates[currencyCode] != nil {
...
}
So populate "moneyRates" with some data.
Here is my requirement: I want to display a login screen, perform authentication via a network request, and if successful, display the content page.
I have implemented the network request part as per the suggestion by jpdnx here.
I have a UserAuth class and the corresponding code to display the Login View if not logged in, and the Content View if logged in, as per M Reza here.
However, I am not able to combine the two - when the network request finishes in LoginView, I am not able to get the flow back to the LoginControllerView to navigate to ContentView. Any help will be appreciate here.
Here is my code:
Network.swift
class ViewModel<T: Codable> : ObservableObject {
//#Published var calendar : IDCCalendar?
#Published var modelData : T?
func getData(url: URL, encoded: String, completion: (#escaping ()->()) ) {
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
print(encoded)
let encoder = JSONEncoder()
if let data = try? encoder.encode(encoded) {
request.httpBody = data
}
print("Request: \(request)")
URLSession.shared.dataTask(with: request) { data1, response, error in
if let error = error {
print("Request error: ", error)
return
}
guard let data1 = data1 else { return }
DispatchQueue.main.async {
let decoder = JSONDecoder()
print(String(decoding: data1, as: UTF8.self))
if let value = try? decoder.decode(T.self, from: data1)
{
self.modelData = value
completion()
print("Success!")
}
else {
print("Does not decode correctly")
}
}
}.resume()
}
LoginControllerView.swift
var body: some View {
Group {
if !userAuth.isLoggedin {
LoginView(userAuth: userAuth)
} else {
ContentView()
}
}
LoginView.swift
struct LoginView: View {
#EnvironmentObject var userAuth: UserAuth
#StateObject var viewModel = ViewModel<Login>()
var body: some View {
// Login Screen UI here
Button("Login") {
// Commenting out network request code out for now
/*
let url = URL(<URL goes here>)!
let encoded = <JSON String goes here>
let completion = {
if viewModel.modelData?.Status == 0 {
userAuth.isLoggedin = true
let login = viewModel.modelData!
let encoder = JSONEncoder()
if let data = try? encoder.encode(login) {
UserDefaults.standard.set(data, forKey: "LoginData")
}
}
viewModel.getData(url: url, encoded: encoded, completion: completion)
*/
// For test purposes, setting it to true, I want this to percolate back to
// LoginControllerView
userAuth.isLoggedin = true
}
UserAuth.swift
import Combine
class UserAuth: ObservableObject {
let didChange = PassthroughSubject<UserAuth,Never>()
// required to conform to protocol 'ObservableObject'
let willChange = PassthroughSubject<UserAuth,Never>()
func login() {
// login request... on success:
self.isLoggedin = true
}
var isLoggedin = false {
didSet {
didChange.send(self)
}
}
}
I need help with currency exchange rate lookup given a key (3 digit currency code). The JSON object is rather unusual with no lablels such as date, timestamp, success, or rate. The first string value is the base or home currency. In the example below it is "usd" (US dollars).
I would like to cycle through all the currencies to get each exchange rate by giving its 3 digit currency code and storing it in an ordered array.
{
"usd": {
"aed": 4.420217,
"afn": 93.3213,
"all": 123.104693,
"amd": 628.026474,
"ang": 2.159569,
"aoa": 791.552347,
"ars": 111.887966,
"aud": 1.558363,
"awg": 2.164862,
"azn": 2.045728,
"bam": 1.9541,
"bbd": 2.429065,
"bch": 0.001278
}
}
In a slightly different formatted JSON object I used the following loop to copy exchange rates to an ordered array.
for index in 0..<userData.rateArray.count {
currencyCode = currCode[index]
if let unwrapped = results.rates[currencyCode] {
userData.rateArray[index] = 1.0 / unwrapped
}
}
The follow code is the API used to get the 3 digit currency codes and the exchange rates (called via UpdateRates).
class GetCurrency: Codable {
let id = UUID()
var getCurrencies: [String : [String: Double]] = [:]
required public init(from decoder: Decoder) throws {
do{
print(#function)
let baseContainer = try decoder.singleValueContainer()
let base = try baseContainer.decode([String : [String: Double]].self)
for key in base.keys{
getCurrencies[key] = base[key]
}
}catch{
print(error)
throw error
}
}
}
class CurrencyViewModel: ObservableObject{
#Published var results: GetCurrency?
#Published var selectedBaseCurrency: String = "usd"
func UpdateRates() {
let baseUrl = "https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api#1/latest/currencies/"
let baseCur = selectedBaseCurrency // usd, eur, cad, etc
let requestType = ".json"
guard let url = URL(string: baseUrl + baseCur + requestType) else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
do{
let decodedResponse = try JSONDecoder().decode(GetCurrency.self, from: data)
DispatchQueue.main.async {
self.results = decodedResponse
// this prints out the complete table of currency code and exchange rates
print(self.results?.getCurrencies["usd"] ?? 0.0)
}
} catch {
//Error thrown by a try
print(error)//much more informative than error?.localizedDescription
}
}
if error != nil {
//data task error
print(error!)
}
}.resume()
}
}
Thanks lorem ipsum for your help. Below is the updated ASI logic that copies the exchange rates to the rateArray using key/value lookups.
class CurrencyViewModel: ObservableObject{
#Published var results: GetCurrency?
#Published var rateArray = [Double] ()
init() {
if UserDefaults.standard.array(forKey: "rates") != nil {
rateArray = UserDefaults.standard.array(forKey: "rates") as! [Double]
}else {
rateArray = [Double] (repeating: 0.0, count: 160)
UserDefaults.standard.set(self.rateArray, forKey: "rates")
}
}
func updateRates(baseCur: String) {
...
DispatchQueue.main.async {
self.results = decodedResponse
// loop through all available currencies
for index in 0..<currCode.count {
currencyCode = currCode[index]
// spacial handling for base currency
if currencyCode == baseCur {
self.rateArray[index] = 1.0000
} else {
let homeRate = self.results?.getCurrencies[baseCur]
// complement and save the exchange rate
if let unwrapped = homeRate?[currencyCode] {
self.rateArray[index] = 1.0 / unwrapped
}
}
}
}
} catch {
//Error thrown by a try
print(error)//much more informative than error?.localizedDescription
}
}
if error != nil {
//data task error
print(error!)
}
}.resume()
}
}
I am fetching user's information like his name,phone number and email id from contacts.But it is only showing first contact number.IF a person has more than one contact number,it didnt show that second number.Can someone help?I am using this function
where EVContactProtocol is part of Library
func didChooseContacts(_ contacts: [EVContactProtocol]?) {
var conlist : String = ""
if let cons = contacts {
for con in cons {
if let fullname = con.fullname(),let email1 = con.email , let phoneNumber = con.phone {
conlist += fullname + "\n"
print("Full Name: ",fullname)
print("Email: ",email1)
print("Phone Number: ",phoneNumber)
}
}
self.textView?.text = conlist
} else {
print("I got nothing")
}
let _ = self.navigationController?.popViewController(animated: true)
}
You should try this:
import Contacts
class ViewController: UIViewController
{
lazy var contacts: [CNContact] =
{
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey] as [Any]
// Get all the containers
var allContainers: [CNContainer] = []
do
{
allContainers = try contactStore.containers(matching: nil)
}
catch
{
print("Error fetching containers")
}
var results: [CNContact] = []
// Iterate all containers and append their contacts to our results array
for container in allContainers
{
let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)
do
{
let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keysToFetch as! [CNKeyDescriptor])
results.append(contentsOf: containerResults)
}
catch
{
print("Error fetching results for container")
}
}
return results
}()
override func viewDidLoad()
{
super.viewDidLoad()
print(contacts[0].givenName)
print(contacts[0].phoneNumbers)
print(contacts[0].emailAddresses)
print(contacts)
}
}
I want to show notification when app close on swift 3. is it possible,
how to do it? could you advise me?
//get Data
func getData(){
let url=URL(string:"http://MyGPS/service.php")
do {
let allContactsData = try Data(contentsOf: url!)
let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : NSArray]
if let arrJSON = allContacts["mygps"] {
for index in 0...arrJSON.count-1 {
vibration.append(aObject["vibration"] as! String)
}
}
}
catch {
}
}
//show notification
func notification(){
let content = UNMutableNotificationContent()
content.title = "GPS alert message"
// content.subtitle = "Do you know?"
content.body = "Your GPS detect vibration!!"
content.badge = 1
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "timerDone", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
// call function
func someBackgroundTask(timer:Timer) {
DispatchQueue.global(qos: DispatchQoS.background.qosClass).async {
//print("do some background task")
self.getData()
DispatchQueue.main.async {
//self.updatMarker()
}
}
}
// Use Timer
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) {
timer in
self.someBackgroundTask(timer: timer)
}