Setting the Desktop background on OSX using Swift 2 - macos

After an introduction to Javascript, I'm trying to tackle OSX/iOS programming by creating some simple tools to scratch my own itches.
However, right from the jump I hit a roadblock.
I found two examples that should work.
https://github.com/hinderberg/ios-swift-kurs/blob/master/swift-intro/wallpaper.swift
https://www.snip2code.com/Snippet/196825/Swift-shell-script-to-randomize-wallpape
Here's the second:
#!/usr/bin/env xcrun swift
import Foundation
import AppKit
let imagesDir = "/Users/david/Dropbox/Graphics/Wallpaper-HD/"
var err: NSError?
let fs = NSFileManager.defaultManager()
let filenames = fs.contentsOfDirectoryAtPath(imagesDir, error: &err) as [String]?
if let error = err {
NSLog(error.localizedDescription)
} else {
let imagenames = filenames!.filter { $0.hasSuffix(".jpg") || $0.hasSuffix("png") }
let ir = Int(arc4random_uniform(UInt32(imagenames.count)))
let imgurl = NSURL.fileURLWithPath(imagesDir + imagenames[ir])
let workspace = NSWorkspace.sharedWorkspace()
let screen = NSScreen.mainScreen()
let ok : Bool = workspace.setDesktopImageURL( imgurl!, forScreen: screen!, options: nil, error: nil )
if ok {
println( "New wallpaper: " + imagenames[ir] )
} else {
println("Oops!")
}
}
This didn't work in XCode 7 beta 3.
Hoping to reduce to the essentials, I arrived at:
#!/usr/bin/env xcrun swift
import Foundation
import AppKit
let imagesDir = "/Users/josh/Downloads/"
let singleImage = "/Users/josh/Downloads/xlarge.png"
let imgurl = NSURL.fileURLWithPath(singleImage)
let workspace = NSWorkspace.sharedWorkspace()
let screen = NSScreen.mainScreen()
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen!, options: nil, error: nil )
if ok {
print( "New wallpaper set!" )
} else {
print("Oops!")
}
And saved as the file wallpaper.swift.
On execution, the error is:
./wallpaper.swift:17:49: error: extra argument 'error' in call
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen!, options: nil, error: nil )
And now I'm completely stuck...
I've tried referring to NSWorkspace and NSScreen documentation as well as running through playground, but it's beyond my current skills.
Removing the extra argument it complains about (error: nil) simply gives a different error:
./wallpaper.swift:13:31: error: cannot invoke 'setDesktopImageURL' with an argument list of type '(NSURL, forScreen: NSScreen?, options: nil)'
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen, options: nil )
Where is the code failing, and how can I understand how to make it work properly?

In your example you're passing nil as options to the method.
I guess it worked before but now in the comments you showed the current method signature:
(url: NSURL, forScreen screen: NSScreen, options: [String : AnyObject]) throws
We see that options should be a non-Optional Dictionary.
It means you can't use nil anymore for the options parameter: if you don't have options, just pass an empty Dictionary.
Also, now in Swift 2, this method doesn't return a Bool anymore, it throws.
Meaning you have to use it with do try catch:
do {
let imgurl = NSURL.fileURLWithPath(singleImage)
let workspace = NSWorkspace.sharedWorkspace()
if let screen = NSScreen.mainScreen() {
try workspace.setDesktopImageURL(imgurl, forScreen: screen, options: [:])
}
} catch {
print(error)
}

Updated example for Swift 3:
do {
let imgurl = NSURL.fileURL(withPath: singleImage)
let workspace = NSWorkspace.shared()
if let screen = NSScreen.main() {
try workspace.setDesktopImageURL(imgurl, for: screen, options: [:])
}
} catch {
print(error)
}

For those of us using Xamarin Mac, this can be achieved like so:
string filepath = "/Users/you/Desktop/sweet-wallpaper.jpg";
var workspace = NSWorkspace.SharedWorkspace;
var screen = NSScreen.MainScreen;
NSUrl url = NSUrl.FromFilename(filepath);
NSDictionary options = new NSDictionary();
NSError errorContainer = new NSError();
workspace.SetDesktopImageUrl(url, NSScreen.MainScreen, options, errorContainer);

Updated version for Swift 5.x:
do {
let imageURL = URL(fileURLWithPath: "/path/to/image")
if let screen = NSScreen.main {
try NSWorkspace.shared.setDesktopImageURL(imageURL, for: screen, options: [:])
}
} catch {
print(error)
}

Related

Extra argument error in call [duplicate]

This question already has answers here:
Swift: Extra argument 'error' in call
(3 answers)
Closed 5 years ago.
I have been trying to login for a while using php but I have a final bug that stops me from finishing. It turns out to give a bug in NSJSONSerialization where the error tells me: error extra argument in call. Then I will provide a screenshot so that the error is clearer and the code so you can help me since I am in a blocking moment and I do not know how to solve it. Thanks in advance
Photo error: [
Code:
#IBAction func loginButtonTapped(sender: AnyObject) {
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
if(userEmail!.isEmpty || userPassword!.isEmpty) { return; }
let myUrl = NSURL(string: "http://localhost/billapp/userSignIn.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
let postString = "userEmail=\(userEmail)&userPassword=\(userPassword)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if error != nil{
print("error=\(error)")
return
}
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error:NSError) as? NSDictionary
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
if(resultValue=="Success")
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion: nil);
}
}
}
task.resume()
}
last error
You can follow the quick fix sugestions. Doing so will let you end up like this:
do {
var json = try JSONSerialization.jsonObject(with: Data(), options: .mutableContainers)
if let parseJSON = json {
//your code
}
} catch {
//handle error
}
Unlike Objective C, as per the new methodolgy in Swift, errors are handled using try/catch block instead of passing the error object in the method.
Use the following code for Swift 3.
do {
var json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
if(resultValue=="Success")
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion: nil);
}
}
} catch let error {
//Perform the error handling here
}
Following for Swift 2
do {
var json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
if(resultValue=="Success")
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion: nil);
}
}
} catch let error {
//Perform the error handling here
}

Updated from Swift 1 to Swift 2

I wrote this code in the previous version of Swift and Xcode and now when I updated xcode to 7.0.1 and updated swift to another version I got a lot of errors. This is something I can't solve and it would be so nice if you could help me. The fat text is the error. The part from >>> to <<< is the part that I need help with so the > and < is not in the real code.
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
>>> var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)! <<<
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as! GameScene
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}
Here, I made your code nicer and safe. This code (in theory) cannot crash whatever happens:
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
guard let
path = NSBundle.mainBundle().pathForResource(file, ofType: "sks"),
sceneData = try? NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe) else {
return nil
}
let archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
guard let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as? GameScene else {
return nil
}
archiver.finishDecoding()
return scene
}
}
If you don't know how the new error handling and guard works, you should check online.
Also if you really want to be fancy, you can use the new defer:
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
guard let
path = NSBundle.mainBundle().pathForResource(file, ofType: "sks"),
sceneData = try? NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe) else {
return nil
}
let archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
defer { archiver.finishDecoding() }
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
return archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as? GameScene
}
}
In swift 2.0 you have to manage errors thrown:
do {
var sceneData = try NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
} catch let error as NSError {
// manage error case
}
There is no longer an NSData.init(contentsOfFile:options:error:) constructor. Most Cocoa calls that returned an error this way are now throwing. The new way to do what you've done here is:
var sceneData = try! NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
Rather than getting back an optional and then force-unwrapping it, this uses a force-try to do the same thing. In both cases, this will crash if the read fails. Generally a normal try inside of a do/catch is preferred (just like you generally should not force-unwrap optionals).
You need to download the new version of the Swift language book. Enums that consist of bits that should be combined are now represented as sets, not as integer values. Everything is nicely explained in Apple's documentation.
Reading release notes is also very, very useful. At this point in time, Apple doesn't have the slightest scruples to modify the language in a way that breaks existing code. You need to keep up with it yourself.

Completion handler giving: Type of expression is ambiguous without more context, after updating to swift 2

I'm using the SODA Client for swift (Created by Socrata), I just updated to XCode 7 and swift 2 and found some troubles. The one I haven't been able to solve is the completion handler case when it finds an error, it's not accepting the line "syncCompletion(.Error (reqError))" that supposedly should get the error and return to main thread.
I've seen many errors with the same description here "Type of expression is ambiguous without more context", but not in completion handlers, I saw one using do - catch that is different. I'm don't know enough of swift to find out the way to change this.
Some answers suppose you should rewrite the code because some types could have change in swift 2, but I wouldn't know where to start rewriting.
Thanks in advance for your help.
var task = session.dataTaskWithRequest(request, completionHandler: { data, response, reqError in
// We sync the callback with the main thread to make UI programming easier
let syncCompletion = { res in NSOperationQueue.mainQueue().addOperationWithBlock { completionHandler (res) } }
// Give up if there was a net error
if reqError != nil {
syncCompletion(.Error (reqError))
return
}
// Try to parse the JSON
// println(NSString (data: data, encoding: NSUTF8StringEncoding))
var jsonError: NSError?
var jsonResult: AnyObject!
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
} catch var error as NSError {
jsonError = error
jsonResult = nil
} catch {
fatalError()
}
if let error = jsonError {
syncCompletion(.Error (error))
return
}
// Interpret the JSON
if let a = jsonResult as? [[String: AnyObject]] {
syncCompletion(.Dataset (a))
}
else if let d = jsonResult as? [String: AnyObject] {
if let e : AnyObject = d["error"] {
if let m : AnyObject = d["message"] {
syncCompletion(.Error (NSError(domain: "SODA", code: 0, userInfo: ["Error": m])))
return
}
}
syncCompletion(.Dataset ([d]))
}
else {
syncCompletion(.Error (NSError(domain: "SODA", code: 0, userInfo: nil)))
}
})
Solved, I replaced:
if reqError != nil
With
if let error = reqError
and:
syncCompletion(.Error (reqError))
with
syncCompletion(.Error (error))

Swift 2 error handling of audio recording code

can someone please assist me? I have some code (which I know runs properly in Xcode 6) that is having difficulty executing in Xcode 7 (due to the changes in error handling for Swift 2). I tried to convert the code using the latest Swift syntax in Xcode 7, but no recommendations were offered. Any suggestions?
//Setup audio session
var session = AVAudioSession.sharedInstance()
session.setCategory(AVAudioSessionCategoryPlayAndRecord,error: nil)
//Initialize and prepare the recorder
audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error:nil)
audioRecorder.meteringEnabled = true;
audioRecorder.prepareToRecord()
audioRecorder.record()
}
#IBAction func stopAudio(sender: UIButton) {
recordingInProgress.hidden = true
//TODO: Stop recording the user's voice
audioRecorder.stop()
var audioSession = AVAudioSession.sharedInstance();
audioSession.setActive(false, error:nil)
}
The Error messages are for the following code:
session.setCategory(AVAudioSessionCategoryPlayAndRecord,error: nil)
Extra argument 'error' in call
audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error:nil)
Cannot find an initializer for type 'AVAudioRecorder' that accepts an argument list of type '(URL: NSURL?, settings: nil, error: nil)'
audioSession.setActive(false, error:nil)
****Extra argument 'error' in call****
try this class ive tested this and it works in the latest version of swift and Xcode
import UIKit
import AVFoundation
class RecordViewController: UIViewController {
required init(coder aDecoder: NSCoder) {
var baseString : String = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
var pathComponents = [baseString, NSUUID().UUIDString + ".m4a"]
self.audioURL = NSURL.fileURLWithPathComponents(pathComponents)!
var session = AVAudioSession.sharedInstance()
session.setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)
var recordSettings: [NSObject : AnyObject] = Dictionary()
recordSettings[AVFormatIDKey] = kAudioFormatMPEG4AAC
recordSettings[AVSampleRateKey] = 44100.0
recordSettings[AVNumberOfChannelsKey] = 2
self.audioRecorder = AVAudioRecorder(URL: self.audioURL, settings: recordSettings, error: nil)
self.audioRecorder.meteringEnabled = true
self.audioRecorder.prepareToRecord()
// Super init is below
super.init(coder: aDecoder)
}
you will then have to implement your buttons of course
something like this
#IBAction func recordPressed(sender: AnyObject) {
if self.audioRecorder.recording {
self.audioRecorder.stop()
self.recordButton.setTitle("RECORD", forState: UIControlState.Normal)
self.playButton.enabled = true
} else {
var session = AVAudioSession.sharedInstance()
session.setActive(true, error: nil)
self.audioRecorder.record()
self.recordButton.setTitle("PLAY", forState: UIControlState.Normal)
self.playButton.enabled = false
}
}
#IBAction func playPressed(sender: AnyObject) {
self.audioPlayer = AVAudioPlayer(contentsOfURL: self.audioURL, error: nil)
audioPlayer.play()
}
}

Continuously update a UILabel in Swift

I have a function, shown below, that I would like to continuously update. It is taking data from a webpage, and every so often that webpage is updated to reflect current information. Is there a way that I can catch this update and reflect that in my application? I'm pretty new to Swift and iOS programming. Some of the code made seem very bizarre, but it currently works for whatever song is playing when you first open the app (that is, it updates the text to show that song playing but doesn't update later).
let url = NSURL(string: "http://api.vicradio.org/songs/current")!
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) in
if error != nil {
return
}
let name = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
var songName = ""
var artistName = "by "
var quoteNumber = 0
for character in name.characters {
if character == "\"" {
quoteNumber++
}
if quoteNumber == 3 && character != "\"" {
songName += String(character)
} else if quoteNumber == 7 && character != "\"" {
artistName += String(character)
}
}
if (songName != "no song metadata provided") {
self.SongNowText.text = songName
self.ArtistNowText.text = artistName
self.SongNowText.setNeedsDisplay()
self.ArtistNowText.setNeedsDisplay()
} else if (songName == "no song metadata provided") {
self.SongNowText.text = "The Best of What's Next!"
self.ArtistNowText.text = "only on VIC Radio"
}
}
task!.resume()
It looks like the URL you're accessing there is an API endpoint putting out JSON. I highly recommend using NSJSONSerialization.JSONObjectWithData to parse the response body into a dictionary and use that instead of rolling your own solution by counting quote marks.
The callback to dataTaskWithURL is executed on a background thread. Avoid updating the UI on anything besides the main thread because it can cause problems. Use dispatch_async to execute your UI update function on the main thread as in the example.
All you can do with this API is send it requests and read the responses. You can poll the endpoint at a regular interval while the app is open and get decent results from that. NSTimer is one way to do that, and it requires you put the method you want to execute repeatedly in a class inheriting from NSObject because it depends on Objective-C style message sending.
Throw this in a playground and try it:
import Cocoa
import XCPlayground
XCPSetExecutionShouldContinueIndefinitely()
class RadioDataAccessor : NSObject {
private let callback: [String : AnyObject] -> Void
init(callback: [String : AnyObject] -> Void) {
self.callback = callback
super.init()
NSTimer.scheduledTimerWithTimeInterval(5.0, target: self,
selector: "updateData", userInfo: nil, repeats: true)
// just so it happens quickly the first time
updateData()
}
func updateData() {
let session = NSURLSession.sharedSession()
let url = NSURL(string: "http://api.vicradio.org/songs/current")!
session.dataTaskWithURL(url) { data, response, error in
if error != nil {
return
}
var jsonError = NSErrorPointer()
let json = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.allZeros,
error: jsonError) as? [String : AnyObject]
if jsonError != nil {
return
}
dispatch_async(dispatch_get_main_queue()) { self.callback(json!) }
}.resume()
}
}
RadioDataAccessor() { data in
println(data)
}
You may want to save the timer to a variable and expose a function that lets you invalidate it.

Resources