I want to detect when a user changes the SIM card.
I tried using subscriberCellularProviderDidUpdate but after removing and reinserting the SIM card the closure/block never gets called. Also the instance property is deprecated. Is there a replacement?
subscriberCellularProviderDidUpdateNotifier appears to have been replaced with serviceSubscriberCellularProvidersDidUpdateNotifier as of iOS 12.
If you need to support iOS 11 or earlier in addition to iOS 12 you can something like:
let ct = CTTelephonyNetworkInfo()
if #available(iOS 12.0, *) {
ct.serviceSubscriberCellularProvidersDidUpdateNotifier = { (carrier) in
// carrier is a String
}
} else {
ct.subscriberCellularProviderDidUpdateNotifier = { (carrier) in
// carrier is a CTCarrier
}
}
Related
In my application, I was implemented pull-to-refresh feature and custom loading icon. In IPhone which has dynamic island, It was overlapsed my loading icon.
I want to detect device which has dynamic island or not. If it has, I will add some top space to it.
Currently, as far as I know, dynamic island is will included in ActivityKit on late of 2022. You can check from this link for ActivityKit and Apple's thread about it. And Apple doesn't provide way to check dynamic island is on device or not.
But there is a workaround for you to get the thing you want. Currently dynamic island only available on iPhone 14 Pro and iPhone 14 Pro Max. So just need to check this both device.
Update: Thanks to this link for type model, name model type of iPhone 14 Pro and iPhone 14 Pro Max is iPhone15,2 and iPhone15,3 so we just need to check these case.
Code will be like this
extension UIDevice {
func checkIfHasDynamicIsland() -> Bool {
if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
let nameSimulator = simulatorModelIdentifier
return nameSimulator == "iPhone15,2" || nameSimulator == "iPhone15,3" ? true : false
}
var sysinfo = utsname()
uname(&sysinfo) // ignore return value
let name = String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)!.trimmingCharacters(in: .controlCharacters)
return name == "iPhone15,2" || name == "iPhone15,3" ? true : false
}
}
Usage
let value = UIDevice().checkIfHasDynamicIsland()
print("value: ", value)
According to the live activity documentation, we can only detect whether the device supports Live activity, but we don't know if the device has dynamic island
I use the window safeAreaInsets value to detect dynamic island. when the device orientation is portrait, safeAreaInsets.top is equal to 59(Display Zoom Default),
or 51(Display Zoom Large Text).
This is likely to support the iPhone15 Pro/iPhone15 Pro Max and later models.
usage: print(UIDevice.current.hasDynamicIsland)
extension UIDevice {
// Get this value after sceneDidBecomeActive
var hasDynamicIsland: Bool {
// 1. dynamicIsland only support iPhone
guard userInterfaceIdiom == .phone else {
return false
}
// 2. Get key window, working after sceneDidBecomeActive
guard let window = (UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.flatMap { $0.windows }.first { $0.isKeyWindow}) else {
print("Do not found key window")
return false
}
// 3.It works properly when the device orientation is portrait
return window.safeAreaInsets.top >= 51
}
}
I want to be able to only allow a api to be called if the version is 10.*. I know usually we use #available(10.0, *), but this means 10 and above.
How do i restrict 10 to <11?
Here is the persudo code:
if device is ios10 but less than 11 {
//Do this only for iOS10.*
}
You can use #available instead of #available, just tested this and it seems to do what you need:
if #available(iOS 11.0, *) {
// leave blank if you don't need to do anything here
} else if #available(iOS 10.0, *) {
print("You're on iOS 10!")
}
You can use code:
let os = ProcessInfo().operatingSystemVersion
switch (os.majorVersion, os.minorVersion) {
case (10, 0): // iOS 10.0
// Do your code
default:
break // Some other version
}
Or if you want to use for all 10.* versions of OS, then simply skip minor version:
let os = ProcessInfo().operatingSystemVersion
if os.majorVersion == 10 {
// Do your code
}
I am trying to test a UiImagePicker I have used the record feature on Xcode to get most of my test but it fails to capture an element for confirming the picture that I want to select. Same thing happens with the other option "Which is cancel" I recall some way of getting a list of all the elements on a view or something to that effect. So my question how do I get a reference to an option in a ImagePicker object in a view.
I am building for iOS9 and running Xcode7.2
my current test looks like this
`
func testMenu(){
loginInApp(app) //Gets you past the login
app.buttons["LogoButton"].tap() //entry point for menuView
XCTAssert(app.buttons["BR"].exists)
let brButton = app.buttons["BR"]
brButton.tap()
let photosFromSheet = app.sheets["Where would you like to get photos from?"]
XCTAssert(photosFromSheet.exists)
photosFromSheet.staticTexts["Where would you like to get photos from?"].tap()
XCTAssert(photosFromSheet.collectionViews.buttons["Chose from Library"].exists && photosFromSheet.buttons["Cancel"].exists)
photosFromSheet.collectionViews.buttons["Chose from Library"].tap()
XCTAssert(app.tables/*#START_MENU_TOKEN#*/.buttons["Moments"]/*[[".cells[\"Moments\"].buttons[\"Moments\"]",".buttons[\"Moments\"]"],[[[-1,1],[-1,0]]],[0]]#END_MENU_TOKEN#*/.exists)
app.tables/*#START_MENU_TOKEN#*/.buttons["Moments"]/*[[".cells[\"Moments\"].buttons[\"Moments\"]",".buttons[\"Moments\"]"],[[[-1,1],[-1,0]]],[0]]#END_MENU_TOKEN#*/.tap()
XCTAssert(app.collectionViews.cells["Photo, Landscape, March 12, 2011, 4:17 PM"].exists)
app.collectionViews.cells["Photo, Landscape, March 12, 2011, 4:17 PM"].tap()
XCTAssert(app.childrenMatchingType(.Window).elementBoundByIndex(0).childrenMatchingType(.Other).elementBoundByIndex(2).childrenMatchingType(.Other).element.exists)
// Here is where things get ambiguous and do not actually select anything when the tests run.
app.childrenMatchingType(.Window).elementBoundByIndex(0).childrenMatchingType(.Other).elementBoundByIndex(2).childrenMatchingType(.Other).element.tap()
brButton.tap()
photosFromSheet.buttons["Cancel"].tap()
app.buttons["LogoButton"].tap()
`
The UIImagePicker is apparently not hittable
so this is the workaround
If you see tap() in documentation it says
/*!
* Sends a tap event to a hittable point computed for the element.
*/
- (void)tap;
/*Sends a tap event to a hittable/unhittable element.*/
extension XCUIElement {
func forceTapElement() {
if self.hittable {
self.tap()
}
else {
let coordinate: XCUICoordinate = self.coordinateWithNormalizedOffset(CGVectorMake(0.0, 0.0))
coordinate.tap()
}
}
}
Now you can call as
button.forceTapElement()
This does not work on the 6S+ tho. Hopefully that will be resolved soon.
/*Sends a tap event to a hittable/unhittable element.*/
extension XCUIElement {
func forceTapElement() {
if self.isHittable {
self.tap()
}
else {
let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset: CGVector(dx: 0.0, dy: 0.0))
coordinate.tap()
}
}
}
Swift 4 Version.
I registered UIMutableUserNotificationAction:
let responseTextAction = UIMutableUserNotificationAction()
responseTextAction.identifier = "text"
responseTextAction.title = "New text"
if #available(iOS 9.0, *) {
responseTextAction.behavior = UIUserNotificationActionBehavior.TextInput
} else {
// Fallback on earlier versions
}
This is screenshot from iOS 9:
and from iOS 8:
How can I implement text input for iOS 8 also.
Text Input for Notifications is only available in iOS 9+. Previous versions will default to a standard notification as you have seen.
I have this very strange problem running my application on my iPhone 6 running iOS 8.1.1 and Xcode 6.1.
My app runs fine on multiple devices in the simulator but when I run on a physical device (iPhone 6 or iPad 4) I get the problem.
This code was working fine in Objective C and have recently converted it to Swift which is when the problems started.
Please see an extract of code below which is causing the problem.
// ======================================================================
// SAVE CHANGES
// ======================================================================
func saveDeviceChanges () {
//var deviceData : NSMutableDictionary
var deviceFound : Bool = false
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
// Update friendly names array
for var i = 0; i < _deviceFriendlyNames.count; i++ {
var deviceData: Dictionary = _deviceFriendlyNames.objectAtIndex(i).mutableCopy() as NSDictionary
let usn: String = deviceData["USN"] as NSString
let availDevice : String = delegate._selectedSerialNumber
if usn == availDevice {
// Update value in dictionary
**// THIS LINE 2**
**deviceData.updateValue(txtFriendlyName.text, forKey: "FriendlyName")**
// Update array
_deviceFriendlyNames.replaceObjectAtIndex(i, withObject: deviceData)
deviceFound = true
}
}
// Add new device to friendly names array
if deviceFound == false {
let newDevice: Dictionary = ["USN": lblUSN.text, "FriendlyName": txtFriendlyName.text]
_deviceFriendlyNames.addObject(newDevice)
}
// Save Changes back to plist
let documentDirectoryPath : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]
let filePath:NSString = documentDirectoryPath.stringByAppendingString("NowRemote.plist")
// Create dictionary to hold the plist data
**// THIS LINE 1**
**var pListData : NSMutableDictionary = NSDictionary(contentsOfFile: filePath)?.mutableCopy() as NSMutableDictionary**
// Update Devices list back into pList root
//pListData?.updateValue(_deviceFriendlyNames, forKey: "Devices")
pListData.setObject(_deviceFriendlyNames, forKey: "Devices")
// Write Data
pListData.writeToFile(filePath, atomically: true)
delegate._selectedDeviceName = ""
delegate._selectedDeviceAddress = ""
delegate._selectedSerialNumber = ""
delegate._reloadData = true
}
The problem starts when it gets to the line which I've put a comment (THIS LINE #1), I have put a breakpoint on this line and then click the step into button to see which line gets executed next. Strangely it jumps to the line (THIS LINE #2) which is inside an IF statement which is inside a FOR loop, then an error gets thrown of "Thread 1: EXC_Breakpoint (Code =1, subcode = 0x1000767d4).
I have no idea why when running on my device it's jumping back in the code to this statement? If I run the code in the simulators it works fine and goes through the statements correctly.
I've been scratching my head with this for days so would be extremely grateful if anyone has any idea why this is happening?
Problem resolved by "holex" (thank you!) - replaced "stringByAppendingString" with "stringByAppendingPathComponent"