In my code I am using this function
func checkIsError(someDate: Date) -> Bool {
var someError: SomeError?
guard SecTrustEvaluateWithError(trust, &someError),
someError == nil else {
return false
}
return true
}
When I run application and open file in which is that function I am getting
This method should not be called on the main thread as it may lead to UI unresponsiveness.
I tried using DispatchQueue.global.async() but when I am using that I can't return value from Void function (in my case false). Is there any option I don't run SecTrustEvaluateWithError on main thread without and return false when it fail?
You can call the method itself on the background thread. Try this:
DispatchQueue.global(qos: .background).async {
if !self.checkValidation(yourDate) {
DispatchQueue.main.async {
self.showPopupOrSmth()
}
}
}
Related
I'm building an app with SwiftUI with a stop watch functionality.
The TimedSession class has a var timer: Timer computed property like this:
Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { timer in
guard let time = self.currentTime else {
print("no current time")
return
}
if self.status != .running {
timer.invalidate()
return
}
time.duration += 1
self.objectWillChange.send()
}
this works well until I start to interact with other parts of the UI, like a scrollview. It's not just that the timer UI is locked, but the block: callbck on scheduledTimer isn't being executed.
What's the best way to put the callback block into the background? I've tried with GCD but no good so far.
Scheduled Timer works by default in .default run loop mode, but during user interaction, like scrolling, a run loop works in different tracking mode.
The solution is set up Timer for all standard modes, named .common, like below
// just create non attached timer
let timer = Timer(timeInterval: 0.01, repeats: true) { timer in
guard let time = self.currentTime else {
print("no current time")
return
}
if self.status != .running {
timer.invalidate()
return
}
time.duration += 1
self.objectWillChange.send()
}
...
init() {
// Start timer in run-loop on common modes
RunLoop.main.add(timer, forMode: .common)
}
I have a problem and I have to find a solution. I must immediately stop the "Starting" method if the user presses the Cancel button.
I have not fixed the problem yet because I can not block the method while it is running but only at the end or at the beginning of the execution.
ContentView:
Button(action: {
self.Starting()
DispatchQueue.main.asyncAfter(deadline: .init(uptimeNanoseconds: 0), execute: self.workItem)
}) {
Text("START")
}
Button(action: {
self.workItem.cancel()
}) {
Text("CANCEL")
}
Starting method:
func Starting() {
self.workItem = DispatchWorkItem {
DispatchQueue.main.async {
self.impostazioniChek=true
}
DispatchQueue.global().async {
if (self.pm.MarksSwitch)
{
sleep(UInt32(self.MarksT))
self.Marks()
}
if (self.pm.ReadySwitch)
{
sleep(UInt32(self.ReadyT))
self.Ready()
}
self.Start()
sleep(3)
DispatchQueue.main.async {
self.tm.start()
}
}
}
}
You cannot cancel because
1) almost all the time work items runs on main (UI) queue, so block it
2) inside work item there is no check for isCancelled. DispatchWorkItem.cancel() only marks it cancelled, but does not stops execution you have to do this.
So to make this work it needs to redirect everything not-UI related in your work item into .background queue explicitly.
Eg. instead of
DispatchQueue.main.asyncAfter(deadline: .init(uptimeNanoseconds: 0), execute: self.workItem)
use
DispatchQueue.global(qos: .background).async { self.workItem }
Instead of
DispatchQueue.global().async {
if (self.pm.MarksSwitch)
Just use (because it is already in background)
if (self.pm.MarksSwitch)
and I'm not sure what is (probably it also should be run on background queue)
self.tm.start()
And on second add inside block interruption on cancellation by regular check, like below
...
}
if self.workItem.isCancelled { // add as many such as needed
return
}
if (self.pm.ReadySwitch)
...
I'm making a new Swift (3.0) framenwork which involves the use of a NSSearchField programmatically created, like the rest of the views.
This framenwork has the deployment target set to 10.10 and I found strange being not able to set the delegate metods for it. In fact NSSearchFieldDelegate only appear availabe in 10.11 on.
class PlistEditor: NSObject, NSOutlineViewDelegate, NSOutlineViewDataSource, NSTextFieldDelegate, NSSearchFieldDelegate {
var searchField : NSSearchField?
// more code..
init(tabItem: NSTabViewItem,
plistPath: String?) {
// more code..
let sf = NSSearchField(frame: NSMakeRect(80, 0, 80, 22))
self.searchField? = sf
// more code..
super.init()
// more code..
if #available(OSX 10.11, *) {
self.searchField?.delegate = self
} else {
// Fallback on earlier versions
}
// more code
}
}
Ok, I thought it was inherent from NStextField, and maybe I can access the cell and set up using the superclass delegate, but unfurtunately I cannot found a way to do that.
What I need is to be able to receive NSText/NSTextField notification in 10.10. How can I do this?
EDITED:
added more info on how is made plus some picts
Without providing more info, i am guessing you forgot to declare that your class is conforming to the NSSearchFieldDelegate.
See the example below, how to set it up for example a viewController. You just create an extension for your vc, and declare it to conform to the delegate.
class SearchViewController: NSViewController {
let searchField: NSSearchField? = NSSearchField(frame: .zero)
override func viewWillAppear() {
super.viewWillAppear()
if #available(OSX 10.11, *) {
self.searchField?.delegate = self
} else {
// Fallback on earlier versions
}
}
}
extension SearchViewController: NSSearchFieldDelegate {
func searchFieldDidStartSearching(_ sender: NSSearchField) {
print("didStart")
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
print("didEnd")
}
}
EDIT:
To capture the textDidChange event in earlier versions of Mac OS, you need to subclass NSSearchField and override textDidChange function. Every time a change happens in the searchField, it will call the function for you.
class DLSearchField: NSSearchField {
override func textDidChange(_ notification: Notification) {
Swift.print("textDidChange")
}
}
Hi I am creating a photo album app as a learning project and I am having some issues with the PHPhotoLibrary.shared().performChangesAndWait; the main issue is that when the method saveImage which has the PHPhotoLibrary.shared().performChangesAndWait code; the method exits before PHPhotoLibrary.shared().performChangesAndWait finishes saving the image. It seems that code gets execute asynchronously even when I am using the performChangesAndWait method which according to the documentation says it should wait until it finishes.
Here is the code of the saveImage method in my class:
func saveImage(_ image: UIImage) ->Bool {
print("Executing saveImage")
var result:Bool = false
if assetCollection == nil {
print("the album is nil")
return result
}
print("saveImage: we will start saving the image")
//TODO: the return false is happening before the photo is save.
do
{
try PHPhotoLibrary.shared().performChangesAndWait({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
print("saveImage: image was save without issues")
result = true
print("saveImage: result value after saving the image: \(result)")
})
}
catch let error
{
result = false
print("saveImage: there was a problem: \(error.localizedDescription)")
}
print("saveImage: result value before exiting the method: \(result)")
return result
}
Basically again when the app runs for the first time the return result gets execute before the saving the image block finishes.
Is there any way that I can force the saveImage method to actually wait until the performChangesAndWait method actually finishes?
Thanks in advance for your help.
performChangesAndWait is an async callback method. The changes made on result inside performChangesAndWait will not be reflected on return , as those two are in different threads.
One way to wait for saveImage method to finish is, implementing callback. Something like:
func saveImage(_ image: UIImage, callback: #escaping(_ result: Bool)->Void) {
print("Executing saveImage")
if assetCollection == nil {
print("the album is nil")
callback(false)
}
print("saveImage: we will start saving the image")
//TODO: the return false is happening before the photo is save.
do {
try PHPhotoLibrary.shared().performChangesAndWait({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
print("saveImage: image was save without issues")
callback(true)
})
}
catch let error {
print("saveImage: there was a problem: \(error.localizedDescription)")
callback(false)
}
}
Let us know what you got.
Here is the solution (Swift 5):
#objc private func handleSavePhoto() {
guard let previewImage = self.photoImageView.image else { return }
PHPhotoLibrary.requestAuthorization { (status) in
if status == .authorized {
do {
try PHPhotoLibrary.shared().performChangesAndWait {
PHAssetChangeRequest.creationRequestForAsset(from: previewImage)
}
} catch let error {
print("failed to save photo in library: ", error)
}
} else {
print("Something went wrong with permission...")
}
}
}
i have started updating my application to swift 3.0, and i got a bunch of errors. Now i am down to one, and i simply can't figure out how to solve it. I have recreated the error with some basic code.
First a model
struct Model {
var num: Int?
enum TestError : Error {
case logout
case showalert
case bluebackground
}
func testNumber() throws {
guard let num = num, num > 0 else {
throw TestError.logout
}
}
}
and then a controller
class ViewController: UIViewController {
var modelError : Model.TestError?
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global(qos: .default).async {
do {
var test = Model()
test.num = 0
try test.testNumber()
} catch let error as Model.TestError {
self.modelError = error
} catch let error {
print(error)
}
DispatchQueue.main.async(execute: {
switch(self.modelError) {
case .logout: //logout
case .showalert: //show alert
case .bluebackground: //set background blue
}
})
}
}
}
It should be pretty clear to see what is happening. My model contains an enum of type error, which i wonna save in a variable in my viewcontroller to later tackle the error on the main que. This code works perfectly in swift 2.2, but in 3.0 and xcode 8b4 i simply get:
How can i tackle this problem, so i can work with the error on the main que?
UPDATE
I forgot to mention, when i remove
var modelError : Model.TestError?
and the associated code, it works perfectly, but then i can tackle the error later