I'm working on my first app for OSX 10.8 using Swift. I want to be able to have the state of the battery dictate the text in Today pulldown menu. That aspect of the code works, but I am very frustrated as my class never gets called. It is in a separate supporting script called 'DeviceMonitor.swift'. Thanks for the help!
Code:
import Foundation
import UIKit
class BatteryState {
var device: UIDevice
init() {
self.device = UIDevice.currentDevice()
println("Device Initialized")
}
func isPluggedIn(value: Bool) {
let sharedDefaults = NSUserDefaults(suiteName: "group.WidgetExtension")
let batteryState = self.device.batteryState
if (batteryState == UIDeviceBatteryState.Charging || batteryState == UIDeviceBatteryState.Full){
let isPluggedIn = true
println("Plugged In")
sharedDefaults?.setObject("Plugged In", forKey: "stringKey")
}
else {
let isPluggedIn = false
println("Not Plugged In")
sharedDefaults?.setObject("Not Plugged In", forKey: "stringKey")
}
sharedDefaults?.synchronize()
}
}
Unless you have done it somewhere else in your code, it looks like you haven't registered to receive UIDeviceBatteryLevelDidChangeNotification events. You should also ensure that the current UIDevice has batteryMonitoringEnabled as YES.
Related
Recently I started testing an iOS app using XCTest but I found some difficulties, the main difficulty was deleting or resetting the app content in each test class.
I'm currently using XCode 11 and trying to delete/reset an app from iOS 13 for each test class, I've already tried:
Delete app through springboard
Delete app by going to the app settings
This step is really important in my tests because in each test I need to create a profile and log in, so in the next test I need to have the app just installed from scratch
iOS 14
The working solution for iOS 14
import XCTest
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
func deleteMyApp() {
XCUIApplication().terminate()
let bundleDisplayName = "MyApp"
let icon = springboard.icons[bundleDisplayName]
if icon.exists {
icon.press(forDuration: 1)
let buttonRemoveApp = springboard.buttons["Remove App"]
if buttonRemoveApp.waitForExistence(timeout: 5) {
buttonRemoveApp.tap()
} else {
XCTFail("Button \"Remove App\" not found")
}
let buttonDeleteApp = springboard.alerts.buttons["Delete App"]
if buttonDeleteApp.waitForExistence(timeout: 5) {
buttonDeleteApp.tap()
}
else {
XCTFail("Button \"Delete App\" not found")
}
let buttonDelete = springboard.alerts.buttons["Delete"]
if buttonDelete.waitForExistence(timeout: 5) {
buttonDelete.tap()
}
else {
XCTFail("Button \"Delete\" not found")
}
}
}
class HomeUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
deleteMyApp()
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}
Try to press the app icon a little longer than in previous iOS versions.
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
func deleteMyApp() {
XCUIApplication().terminate()
let icon = springboard.icons["YourAppName"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.press(forDuration: 5)
// Tap the little "X" button at approximately where it is. The X is not exposed directly
springboard.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + 3) / springboardFrame.maxX, dy: (iconFrame.minY + 3) / springboardFrame.maxY)).tap()
springboard.alerts.buttons["Delete"].tap()
}
}
Updated Roman's answer to support iOS 13 on iPad.
By inserting otherElements["Home screen icons"], the code works both of iPhone and iPad.
class Springboard {
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
class func deleteMyApp() {
XCUIApplication().terminate()
// Insert otherElements["Home screen icons"] here
let icon = springboard.otherElements["Home screen icons"].icons["Workoutimer"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.press(forDuration: 5)
// Tap the little "X" button at approximately where it is. The X is not exposed directly
springboard.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + 3) / springboardFrame.maxX, dy: (iconFrame.minY + 3) / springboardFrame.maxY)).tap()
springboard.alerts.buttons["Delete"].tap()
XCUIDevice.shared.press(XCUIDevice.Button.home)
}
}
}
Misc.
On iPad, "recent launched app" will be added to the right side of the Dock. This iPad specific behavior leads XCUITest finds two elements if you search icons only by its identifier.
Your app will be added here, too.
To handle that, specify otherElements["Home screen icons"] and it excludes elements of the "Dock".
enum Springboard {
static let springboardApp = XCUIApplication(bundleIdentifier: "com.apple.springboard")
static let appName = "appName"
static func deleteApp() {
XCUIApplication().terminate()
let icon = springboardApp.icons[appName]
if icon.exists {
icon.press(forDuration: 3)
icon.buttons["DeleteButton"].tap()
springboardApp.alerts.buttons["Delete"].tap()
}
}
}
This is a quick and dirty solution for iOS 14:
let appName = "You app name as it appears on the home screen"
// Put the app in the background
XCUIDevice.shared.press(XCUIDevice.Button.home)
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
if springboard.icons[appName].waitForExistence(timeout: 5) {
springboard.icons[appName].press(forDuration: 1.5);
}
if springboard.collectionViews.buttons["Remove App"].waitForExistence(timeout: 5) {
springboard.collectionViews.buttons["Remove App"].tap()
}
if springboard.alerts["Remove “\(appName)”?"].scrollViews.otherElements.buttons["Delete App"].waitForExistence(timeout: 5) {
springboard.alerts["Remove “\(appName)”?"].scrollViews.otherElements.buttons["Delete App"].tap()
}
if springboard.alerts["Delete “\(appName)”?"].scrollViews.otherElements.buttons["Delete"].waitForExistence(timeout: 5) {
springboard.alerts["Delete “\(appName)”?"].scrollViews.otherElements.buttons["Delete"].tap()
}
you can simply do this via springboard same as you uninstall build by yourself.
Here is the class with deleteApp function which will uninstall build at anytime you need.
class Springboard {
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
class func deleteApp () {
//terminate app and activate Springboard
XCUIApplication().terminate()
springboard.activate()
//tap on app icon
let appIcon = springboard.icons.matching(identifier: "App Display Name").firstMatch
if appIcon.exists {
appIcon.press(forDuration: 2.0)
//Access first alert button (Remove App)
let _ = springboard.alerts.buttons["Remove App"].waitForExistence(timeout: 1.0)
springboard.buttons["Remove App"].tap()
//Access second alert button (Delete App)
let _ = springboard.alerts.buttons["Delete App"].waitForExistence(timeout: 1.0)
springboard.buttons["Delete App"].tap()
//Access second alert button (Delete)
let _ = springboard.alerts.buttons["Delete"].waitForExistence(timeout: 1.0)
springboard.buttons["Delete"].tap()
}
}
}
Usgae:
func test_YourTestName() {
Springboard.deleteApp()
}
iOS 14 and below
func deleteMyApp() {
XCUIApplication().terminate()
let bundleDisplayName = "App Name"
XCUIDevice.shared.press(.home)
let icon = springboard.icons[bundleDisplayName]
if icon.waitForExistence(timeout: 5) {
XCUIDevice.shared.press(.home)
let value = springboard.pageIndicators.element(boundBy: 0).value as? String
let f = value!.last?.wholeNumberValue
for _ in (1...f!){
if(icon.isHittable){
break
}
else{
springboard.swipeLeft()
}
}
let systemVersion = UIDevice.current.systemVersion.prefix(2)
switch systemVersion {
case "13":
icon.press(forDuration: 1)
let rrd = springboard.buttons["Delete App"]
XCTAssert(rrd.waitForExistence(timeout: 5))
rrd.tap()
let rrd2 = springboard.buttons["Delete"]
XCTAssert(rrd2.waitForExistence(timeout: 5))
rrd2.tap()
case "14":
icon.press(forDuration: 1)
let rrd = springboard.buttons["Remove App"]
XCTAssert(rrd.waitForExistence(timeout: 5))
rrd.tap()
let rrd2 = springboard.buttons["Delete App"]
XCTAssert(rrd2.waitForExistence(timeout: 5))
rrd2.tap()
let rrd3 = springboard.buttons["Delete"]
XCTAssert(rrd3.waitForExistence(timeout: 5))
rrd3.tap()
default:
XCTFail("Did not handle")
}
}}
My app reports and records location, altitude, rotation and accelerometer data (DeviceMotion) while in the background. This works fine on ios 10.3.3. On IOS 11, I no longer have access motion data while the device is locked. Altitude data and location data is still streaming to the console, though.
Has something changed in IOS 11 that prevents me from accessing motion data or am I doing trying to access it in a way that Apple now blocks like OperationQueue.main
Here is how I'm starting motion updates. If the phone is unlocked, all works fine. If I locking the phone, no more updates.:
let motionManager = self.motionManager
if motionManager.isDeviceMotionAvailable {
motionUpdateInterval = 0.15
motionManager.deviceMotionUpdateInterval = motionUpdateInterval
motionManager.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: OperationQueue.main) {deviceMotion, error in
guard let deviceMotion = deviceMotion else { return }
I can't find anything about Motion background modes changing but it seems there must be a way otherwise RunKeeper, Strava will break. Can someone help me get this working again before IOS11 launch?
Thanks!
Also came across this problem.
Our solution was to ensure we have another background mode enabled and running (in our case location updates + audio) and restart core motion updates when switching background/foreground.
Code sample:
import UIKit
import CoreMotion
final class MotionDetector {
private let motionManager = CMMotionManager()
private let opQueue: OperationQueue = {
let o = OperationQueue()
o.name = "core-motion-updates"
return o
}()
private var shouldRestartMotionUpdates = false
init() {
NotificationCenter.default.addObserver(self,
selector: #selector(appDidEnterBackground),
name: .UIApplicationDidEnterBackground,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(appDidBecomeActive),
name: .UIApplicationDidBecomeActive,
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self,
name: .UIApplicationDidEnterBackground,
object: nil)
NotificationCenter.default.removeObserver(self,
name: .UIApplicationDidBecomeActive,
object: nil)
}
func start() {
self.shouldRestartMotionUpdates = true
self.restartMotionUpdates()
}
func stop() {
self.shouldRestartMotionUpdates = false
self.motionManager.stopDeviceMotionUpdates()
}
#objc private func appDidEnterBackground() {
self.restartMotionUpdates()
}
#objc private func appDidBecomeActive() {
self.restartMotionUpdates()
}
private func restartMotionUpdates() {
guard self.shouldRestartMotionUpdates else { return }
self.motionManager.stopDeviceMotionUpdates()
self.motionManager.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: self.opQueue) { deviceMotion, error in
guard let deviceMotion = deviceMotion else { return }
print(deviceMotion)
}
}
}
The official 11.1 release fixed the issue and I've heard from iPhone 8 users that the original implementation is working for them.
The 11.2 beta has not broken anything.
I'm new to swift coding, so most of my code is "this is how I found it on the internet" status.
I'm trying to send a HTTP GET request and then work with it. I'm using NSURLConnection for this. I have 2 swift files in my Xcode project (it is a swift console project, not playground), one is main one and 2nd contains my class I would like to use for delegation:
import Foundation
class Remote: NSObject, NSURLConnectionDelegate {
var data = NSMutableData()
func connect() {
var url = NSURL(string: "https://www.google.com")
var request = NSURLRequest(URL: url!)
NSLog("Is%# main thread", NSThread.isMainThread() ? "" : " NOT");
var conn = NSURLConnection(request: request, delegate: self, startImmediately: false)
conn?.scheduleInRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)
conn?.start()
sleep(2)
}
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
println("didReceiveResponse")
}
func connection(connection: NSURLConnection!, didReceiveData conData: NSData!) {
println("didReceiveData")
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
println("DidFinishLoading")
}
deinit {
println("deiniting")
}
}
Here is my main swift code:
import Foundation
var xxx = Remote()
xxx.connect()
sleep(10)
sleep(2)
When I set breakpoints on each println in delegate functions, they are never hit. Execution is done from main thread according to NSLog output.
In debugger I can see that data are sent and received over the network, but I never get any output. I've seen many similar questions here but nothing helped me.
What am I doing wrong?
As Martin R suggested I added correct NSRunLoop into my code right after starting NSURLConnection:
self.shouldKeepRunning = true
let theRL = NSRunLoop.currentRunLoop()
while self.shouldKeepRunning && theRL.runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeInterval: 1.0, sinceDate: NSDate())) { }
shouldKeepRunning is bool variable defined in my class and it set to false in delegate function connectionDidFinishLoading, so
I'm making a game in swift and I've implemented iAd successfully using Swift but it's just a small banner at the bottom of the screen now which is fine, but I also want a full-screen advertisement to pop-up when the user is game over. How do I do this? The 'Game Over' view has it's own class by the way. This is what I've got so far:
func bannerViewAdLoad(banner: ADBannerView!) {
if !_bannerIsVisible {
if _adBanner?.superview == nil {
self.view.addSubview(_adBanner!)
}
}
UIView.beginAnimations("animateAdBannerOn", context: nil)
banner.alpha = 1.0
UIView.commitAnimations()
_bannerIsVisible = true
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
println("Failed to retrieve Ad")
if _bannerIsVisible {
UIView.beginAnimations("animateAdBannerOff", context: nil)
banner.alpha = 0.0
UIView.commitAnimations()
_bannerIsVisible = false
}
}
Hi Please go through the this document i am not sure but it might help
https://developer.apple.com/library/prerelease/ios/documentation/iAd/Reference/ADInterstitialAd_Ref/index.html
I am trying to implement window toggling (something I've done many times in Objective-C), but now in Swift. It seams that I am getting the use of NSWindowOcclusionState.Visible incorrectly, but I really cannot see my problem. Only the line w.makeKeyAndOrderFront(self) is called after the initial window creation.
Any suggestions?
var fileArchiveListWindow: NSWindow? = nil
#IBAction func tougleFileArchiveList(sender: NSMenuItem) {
if let w = fileArchiveListWindow {
if w.occlusionState == NSWindowOcclusionState.Visible {
w.orderOut(self)
}
else {
w.makeKeyAndOrderFront(self)
}
}
else {
let sb = NSStoryboard(name: "FileArchiveOverview",bundle: nil)
let controller: FileArchiveOverviewWindowController = sb?.instantiateControllerWithIdentifier("FileArchiveOverviewController") as FileArchiveOverviewWindowController
fileArchiveListWindow = controller.window
fileArchiveListWindow?.makeKeyAndOrderFront(self)
}
}
Old question, but I just run into the same problem. Checking the occlusionState is done a bit differently in Swift using the AND binary operator:
if (window.occlusionState & NSWindowOcclusionState.Visible != nil) {
// visible
}
else {
// not visible
}
In recent SDKs, the NSWindowOcclusionState bitmask is imported into Swift as an OptionSet. You can use window.occlusionState.contains(.visible) to check if a window is visible or not (fully occluded).
Example:
observerToken = NotificationCenter.default.addObserver(forName: NSWindow.didChangeOcclusionStateNotification, object: window, queue: nil) { note in
let window = note.object as! NSWindow
if window.occlusionState.contains(.visible) {
// window at least partially visible, resume power-hungry calculations
} else {
// window completely occluded, throttle down timers, CPU, etc.
}
}