function signature specialization with ABPeoplePickerNavigationController - ios

My crashlog is here.
This crash occurs on some of the testers. One tester with iPhone 6 (iOS 8.4.1) crashes but another tester with the same device (also iOS 8.4.1) doesn't crash. It also works on simulator.
I know this has been asked before but I think the problem is about ABPeoplePickerNavigationController. Or maybe this is a problem about Crashlytics.
What I wonder is: why do the same devices with the same operating system works differently? Also, I appreciate any solution to this problem?
Here is my code:
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson person: ABRecord!, property: ABPropertyID, identifier: ABMultiValueIdentifier) {
if property != kABPersonPhoneProperty {
return
}
let phoneNumbers: ABMultiValueRef = ABRecordCopyValue(person, kABPersonPhoneProperty).takeRetainedValue()
if (ABMultiValueGetCount(phoneNumbers) > 0) {
let index = ABMultiValueGetIndexForIdentifier(phoneNumbers, identifier)
let selectedPhoneNumber = ABMultiValueCopyValueAtIndex(phoneNumbers, index).takeRetainedValue() as! String
let newNumber = "tel:\(phoneNumber)"
println(newNumber)
let url = NSURL(string: newNumber)
if UIApplication.sharedApplication().canOpenURL(url!) {
//test
//UIApplication.sharedApplication().openURL(url!)
} else {
return
}
} else {
return
}
}

Related

How to read SIM Carrier name programmatically in iOS using Swift4

Before going in-depth, let me tell you all that - Yes, I've gone through all possible solutions provided on Stack-Overflow.
Problem Statement : I'm not able to read 'CarrierName' of my available SIM using iPhone
What did I tried : I've tried two different solutions, but I'm unable to read CarrierName.
Solution 1 : When I tried this solution I've received only "Carrier" as output, instead of CarrierName.
Solution 1 :
//--------------- CodeBase : Solution1 -----------------
let networkInfo = CTTelephonyNetworkInfo()
let carrier = networkInfo.serviceSubscriberCellularProviders?.first?.value
if let carrierName = carrier?.carrierName {
cell.textLabel?.text = carrierName
}
else{
cell.textLabel?.text = "No Data"
}
//-------------------------------------------------------
Output : Carrier
Solution 2 : When I tried this solution I've received "iPhone X" as output, instead of CarrierName.
Solution 2 :
//--------------- CodeBase : Solution2 -----------------
let networkInfo = CTTelephonyNetworkInfo()
let carrier = networkInfo.serviceSubscriberCellularProviders?.first?.value
if var carrierName = carrier?.carrierName {
if carrierName.contains("Carrier"){
carrierName = self.getCarrierName() ?? "No Data"
}
else{
cell.textLabel?.text = carrierName
}
}
else{
cell.textLabel?.text = "No Data"
}
//-------------------------------------------------------
Func : getCarrierName()
//--------------- CodeBase : Part of Solution2 -----------------
func getCarrierName() -> String? {
var carrierName: String?
let typeName: (Any) -> String = { String(describing: type(of: $0)) }
let statusBar = UIApplication.shared.value(forKey: "_statusBar") as! UIView
for statusBarForegroundView in statusBar.subviews {
if typeName(statusBarForegroundView) == "UIStatusBarForegroundView" {
for statusBarItem in statusBarForegroundView.subviews {
if typeName(statusBarItem) == "UIStatusBarServiceItemView" {
carrierName = (statusBarItem.value(forKey: "_serviceString") as! String)
}
}
}
}
return carrierName
}
//-------------------------------------------------------
Output : iPhone X i.e. It returns ModelName instead of CarrierName
Can someone, please help me to get - CarrierName.
In my case (Xcode 11.4, Swift 5.2, iPhone 8, iOS 13.3.1), I can get the proper carrier name.
Code snippet:
import CoreTelephony
if #available(iOS 12.0, *) {
if let providers = CTTelephonyNetworkInfo().serviceSubscriberCellularProviders {
providers.forEach { (key, value) in
print("key: \(key), carrier: \(value.carrierName ?? "nil")")
}
}
} else {
let provider = CTTelephonyNetworkInfo().subscriberCellularProvider
print("carrier: \(provider?.carrierName ?? "nil")")
}
The result in console is:
key: 0000000100000001, carrier: 中国电信

priceLocale throw an EXC_BREAKPOINT when accessed

We are using Introductory Prices on our app. And we have an issue only reproducible on one of our two QA devices which is an iPhone 6S (11.4.1) on the French App Store. The other is an iPhone 7 (12.0 with French App Store) and the app is not crashing.
We are using this extension based on the SKProduct extension provided by SwiftyStoreKit :
#available(iOS 11.2, *)
public extension SKProductDiscount {
public var localizedPrice: String? {
return priceFormatter(locale: priceLocale).string(from: price)
}
private func priceFormatter(locale: Locale) -> NumberFormatter {
let formatter = NumberFormatter()
formatter.locale = locale
formatter.numberStyle = .currency
return formatter
}
}
Used like this :
func updateWith(storeProducts: Set<SKProduct>) {
guard
let selfStoreInfo = storeProducts.filter({ $0.productIdentifier == self.id }).first else {
Logger.warn(message: "Subscription \(self.id) not found on store", .inAppPurchase)
return
}
if #available(iOS 11.2, *) {
if let promo = selfStoreInfo.introductoryPrice {
promotionId = selfStoreInfo.productIdentifier
price = promo.localizedPrice
originalPrice = selfStoreInfo.localizedPrice
} else {
price = selfStoreInfo.localizedPrice
}
} else {
price = selfStoreInfo.localizedPrice
}
}
When debugging we found that priceLocale is responsible for throwing the EXC_BREAKPOINT.
EDIT Could be linked to this : https://bugs.swift.org/browse/SR-7922?attachmentOrder=desc but it's strange that it would work on our iPhone 7 and not on the iPhone 6s
Try this:
DispatchQueue.global(qos: .default).async {
while true {
if !SKProduct().productIdentifier.isEmpty {
if let productPriceString: String = SC.storeProduct.localizedPrice {
DispatchQueue.main.async {
print(productPriceString)
}
}
break
}
// Some wait process like using "semaphore"
}
Replace SKProduct() with your product.
In my case, this occurs when the function accessed before the product initialized.

How to check if WiFi is on or off in iOS Swift 2?

I want to check if the wifi is off then show alert to the user to check his/her connectivity.
I find code like this but it checks if there is an internet connection, not checking if the wifi is on or off:
func isConnectionAvailble()->Bool{
var rechability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, "www.apple.com").takeRetainedValue()
var flags : SCNetworkReachabilityFlags = 0
if SCNetworkReachabilityGetFlags(rechability, &flags) == 0
{
return false
}
let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}
You can't.
With Apple's reachability class, you can distinguish three things according to the NetworkStatus struct:
typedef enum : NSInteger {
NotReachable = 0, // 1
ReachableViaWiFi, // 2
ReachableViaWWAN // 3
} NetworkStatus;
You have neither WiFi nor mobile data connection.
You have a WiFi connection, but you may or may not have a mobile data connection.
You have a mobile data connection, but no WiFi connection.
You can't check whether WiFi is turned off, or whether WiFi is turned on but there is no WiFi network nearby, or whether Airplane mode has been turned on.
For mobile data, you can use the telephony class to find whether your device is capable of mobile data connections (iPhone and not iPad, and SIM card plugged in), and you can detect whether mobile data is disabled in the preferences of your application.
Found the following, which was really helpful for me (found on the Apple Developer Forums). The below code works with Swift 4.
func fetchSSIDInfo() -> String {
var currentSSID = ""
if let interfaces:CFArray = CNCopySupportedInterfaces() {
for i in 0..<CFArrayGetCount(interfaces){
let interfaceName: UnsafeRawPointer = CFArrayGetValueAtIndex(interfaces, i)
let rec = unsafeBitCast(interfaceName, to: AnyObject.self)
let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)" as CFString)
if unsafeInterfaceData != nil {
let interfaceData = unsafeInterfaceData! as Dictionary!
for dictData in interfaceData! {
if dictData.key as! String == "SSID" {
currentSSID = dictData.value as! String
}
}
}
}
}
return currentSSID
}
You can then check if a device is connected to Wi-Fi by the following:
if fetchSSIDInfo() != nil {
/* Wi-Fi is Connected */
}
Not perfect, but if the device is not connected to a Wi-Fi Network, you could then ask the user to connect to a Wi-Fi Network:
let wifiNotifcation = UIAlertController(title: "Please Connect to Wi-Fi", message: "Please connect to your standard Wi-Fi Network", preferredStyle: .alert)
wifiNotifcation.addAction(UIAlertAction(title: "Open Wi-Fi", style: .default, handler: { (nil) in
let url = URL(string: "App-Prefs:root=WIFI")
if UIApplication.shared.canOpenURL(url!){
UIApplication.shared.openURL(url!)
self.navigationController?.popViewController(animated: false)
}
}))
self.present(wifiNotifcation, animated: true, completion: nil)
Tested with swift 4 and swift 5
let nwPathMonitor = NWPathMonitor()
nwPathMonitor.pathUpdateHandler = { path in
if path.usesInterfaceType(.wifi) {
print("Path is Wi-Fi")
} else if path.usesInterfaceType(.cellular) {
print("Path is Cellular")
} else if path.usesInterfaceType(.wiredEthernet) {
print("Path is Wired Ethernet")
} else if path.usesInterfaceType(.loopback) {
print("Path is Loopback")
} else if path.usesInterfaceType(.other) {
print("Path is other")
}
}
nwPathMonitor.start(queue: .main)
As already #abba_de_bo mentioned: you could fetch the current SSID and check if it's set or nil.
This is the answer Apple's Eskimo gave to this question:
The trick with using CF-based APIs from Swift is to get the data into ‘Swift space’ as quickly as possible.
func currentSSIDs() -> [String] {
guard let interfaceNames = CNCopySupportedInterfaces() as? [String] else {
return []
}
return interfaceNames.flatMap { name in
guard let info = CNCopyCurrentNetworkInfo(name as CFString) as? [String:AnyObject] else {
return nil
}
guard let ssid = info[kCNNetworkInfoKeySSID as String] as? String else {
return nil
}
return ssid
}
}
Note that this returns an array of names; how you handle the non-standard cases (no elements, more than one element) is up to you.
Make sure you import SystemConfiguration.CaptiveNetwork. Otherwise the build will fail with on of those error messages:
Use of unresolved identifier 'CNCopySupportedInterfaces'
Use of unresolved identifier 'CNCopyCurrentNetworkInfo'
Use of unresolved identifier 'kCNNetworkInfoKeySSID'
You can take a look at the official Apple sample for Reachability:
https://developer.apple.com/library/content/samplecode/Reachability/Introduction/Intro.html
var netStatus = reachability.currentReachabilityStatus()
var connectionRequired = reachability.connectionRequired()
var statusString = ""
switch netStatus {
case NotReachable:
break
case ReachableViaWWAN:
//DATA
break
case ReachableViaWiFi:
//WIFI
break
}
You can use this method to check:
First you import this framework:
import SystemConfiguration.CaptiveNetwork
func isWifiEnabled() -> Bool {
var hasWiFiNetwork: Bool = false
let interfaces: NSArray = CFBridgingRetain(CNCopySupportedInterfaces()) as! NSArray
for interface in interfaces {
// let networkInfo = (CFBridgingRetain(CNCopyCurrentNetworkInfo(((interface) as! CFString))) as! NSDictionary)
let networkInfo: [AnyHashable: Any]? = CFBridgingRetain(CNCopyCurrentNetworkInfo(((interface) as! CFString))) as? [AnyHashable : Any]
if (networkInfo != nil) {
hasWiFiNetwork = true
break
}
}
return hasWiFiNetwork;
}

userInterfaceIdiom returning wrong values on real devices

My app was rejected because there is a problem on the iPad UI of my app. I have fixed it before this getting rejected by checking if the device was an iPad to make some minor adjustments to the UI measures. The problem is that when I call:
UIDevice.currentDevice().userInterfaceIdiom //returns .Unspecified
traitCollection.userInterfaceIdiom //returns -1
On a real iPad I get .Unspecified. Why is this?
In order to fix this I have created a custom method which actually works on a real iPad
public extension UIDevice {
func isIpadDevice() -> Bool {
let identifier = deviceIdentifier()
return identifier.lowercaseString.containsString("ipad")
}
private func deviceIdentifier() -> String {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8 where value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
return identifier
}
}
In order to use it call
UIDevice.currentDevice().isIpadDevice() //wohoo

Unique identifiers on iOS Phonebook are not as expected

I am using the new version of retrieving contacts from the Phonebook on iOS. There is a problem with the unique identifier, which is returned by the first query. Normally the unique identifier is a UUID, but sometimes it is appended by “: ABPerson”.
Querying the Phonebook API with this identifier works sometimes, but not always. Does anybody know, if there is a way to avoid this behaviour?
Currently, I try with a safeguard of two queries. The first is using the identifier as is, the second (if the first fails) is to use the identifier stripping the “: ABPerson” extension.
The first query used is:
class func getAllPhoneBookContacts() -> [PhonebookContact] {
let contactStore = CNContactStore()
var contacts = [PhonebookContact]()
PhoneBookContactsHelper.requestForAccess { (accessGranted) -> Void in
if accessGranted {
let keys = [CNContactIdentifierKey, CNContactPhoneNumbersKey]
do {
let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
try contactStore.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: { (contact: CNContact, _) -> Void in
for phoneNoLab in contact.phoneNumbers {
if let phoneNo = phoneNoLab.value as? CNPhoneNumber,
normalizedPhoneNumber = PhoneNumberNormalizer.normalizePhoneNumber(phoneNo.stringValue) {
let pbc = PhonebookContact(contactID: contact.identifier, phoneNumber: normalizedPhoneNumber)
contacts.append(pbc)
}
}
})
}
catch {
NSLog("Unable to fetch contacts.")
}
}
}
return contacts
}
Accessing a certain contact again later by the identifier is done by:
class func getContactNameByUUID(identifier: String) -> String?{
var name : String?
PhoneBookContactsHelper.requestForAccess()
{ (accessGranted) -> Void in
if accessGranted {
let contactStore = CNContactStore()
let keys = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName)]
do {
let cnc = try contactStore.unifiedContactWithIdentifier(identifier, keysToFetch: keys)
name = CNContactFormatter.stringFromContact(cnc, style: .FullName)!
}
catch _ {
NSLog("Could not fetch contact with id \(identifier))")
}
}
}
return name
}
I am using iOS 9 and tested on simulator and various iPhones and the unexpected behaviour is present everywhere.

Resources