When I run my app in from Xcode to my iPhone7, it crashes at this line:
audioNSURL NSURL "file:///private/var/containers/Bundle/Application/016483D7-D06F-4639-A026-CCD0B1F4B31F/Solo%20Mission.app/spaceMusic.mp3" 0x000000028217eac0
Something to do with the AVAudioPlayer inside of the GameViewController which gives me this error message:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x48)
Please help with this issue I have already checked the build phases and the file is there. Below is all of the code.
import UIKit
import SpriteKit
import AVFoundation
class GameViewController: UIViewController {
var backingAudio = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
let filePath = Bundle.main.path(forResource: "spaceMusic", ofType: "mp3")
let audioNSURL = NSURL(fileURLWithPath: filePath!)
do { backingAudio = try AVAudioPlayer(contentsOf: audioNSURL as URL) }
catch { return print("Cannot Find The Audio") }
backingAudio.numberOfLoops = -1
backingAudio.volume = 1
backingAudio.play()
SOLVED!
I tried so many different ways, but finally I solved the problem. I just needed to write the code differently. Here's what I wrote instead:
var player: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
func initializePlayer() -> AVAudioPlayer? {
guard let path = Bundle.main.path(forResource: "spaceMusic", ofType: "mp3") else {
return nil
}
return try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
}
player = initializePlayer()
player?.play()
To be honest, I don't quite understand why this works. But, I would appreciate any explanation given.
Related
I have a Swift app, with some Objective-C code mixed in. It was working yesterday, but this morning I updated XCode and now everything had gone to hell!
Firstly, after updating, I clicked the XCode popup box to allow it to upgrade my app to Swift4. This is where the problems started.
I have a Swift class called RestClient with the following 4 functions (among others):
class func getInstance() -> RestClient {
if (gRestClient == nil) {
let prefs:UserDefaults = UserDefaults.standard
return RestClient(username: prefs.string(forKey: "username")!, password: prefs.string(forKey: "password")!)
}
return gRestClient!
}
class func getUsername() -> String {
if (gUsername == nil) {
let prefs:UserDefaults = UserDefaults.standard
gUsername = prefs.string(forKey: "username")!
}
return gUsername!
}
class func getPassword() -> String {
if (gPassword == nil) {
let prefs:UserDefaults = UserDefaults.standard
gPassword = prefs.string(forKey: "password")!
}
return gPassword!
}
public func getServer() -> String {
return MAIN_SERVER;
}
Then in my /Objective-C/ folder, I have some more files, once of which is called RestClientObj.m. In here, I have this lines of code:
NSString* url = [NSString stringWithFormat:#"%#/receipt/email/%#/%#/", [[RestClient getInstance] getServer], rrn, emailAddress];
NSString *authStr = [NSString stringWithFormat:#"%#:%#", [RestClient getUsername], [RestClient getPassword]];
So as you can see, I'm calling the RestClient.swift from here. The RestClientObj.h is as follows:
#ifndef RestClientObj_h
#define RestClientObj_h
#endif /* ResClientObj_h */
#interface RestClientObj : NSObject {
}
+(BOOL) sendSMS:(NSString *)rrn mn:(NSString *)mobileNumber;
+(BOOL) sendEmail:(NSString *)rrn mn:(NSString *)emailAddress;
#end
This whole upgrade is causing other problems too. I have another class with the following error:
No visible #interface for 'AppDelegate' declares the selector 'managedObjectContext'
on the line:
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext]; <-ERROR
Can anyone shed any light on this?
EDIT: Here's some code from the AppDelegate class:
lazy var managedObjectContext: NSManagedObjectContext? = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil {
return nil
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
Just to close this off.
The issue was missing the #obj identifier before the variable declaration to make it visible to my objective-c code, in combination with the XCode Swift Upgrade wizard renaming some functions.
I am trying to call a Swift function that contains a completion handler in an objective C class, but I am not sure how to implement it.
This is my Swift Code
#objc class textToSpeech:NSObject{
func toSpeech(word: NSString, sucess:()->Void) -> NSURL {
let tempDirectory = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let tempFile = tempDirectory.URLByAppendingPathComponent((word as String) + ".wav")
let tts = TextToSpeech(username: "xxxxxx", password: "xxxxxx")
tts.synthesize(word as String,
voice: SynthesisVoice.GB_Kate,
audioFormat: AudioFormat.WAV,
failure: { error in
print("error was generated \(error)")
}) { data in
data.writeToURL(tempFile, atomically: true)
print("createdURL")
print(tempFile)
sucess();
}
return tempFile
}
How would I write the function call in objective c. I have already completed setting up the project so that I can call swift functions from objective c.
For example you have this code:
#objc class PDTextToSpeech: NSObject{
func toSpeech(word: NSString, success: () -> Void) -> NSURL {
// ...
return NSURL()
}
}
So you could easily bridge you Swift code in obj-c with #import "<ModuleName>-Swift.h"
where you project name.
Then you can call:
[[PDTextToSpeech new] toSpeech:#"String" success:^{
NSLog(#"Success");
}];
I was using PDTextToSpeech as class name, because it's preferable to call classes in obj-c with uniq prefix. If you project called TestProject - you can use TP prefix.
I guess it should look like this:
textToSpeech* text = [[textToSpeech alloc] init];
[text word:#"some text" sucess:^{
NSLog(#"success");
}];
1) I've managed to communicate from apple watch to iOS app using a complete swift project (iOS app in swift and Apple Watch target in swift)
Code:
In InterfaceController.swift
#IBAction func buttonPressed() {
let watchMessage = ["SiteName" : "Tech-recipes"]
WKInterfaceController.openParentApplication(watchMessage, reply: { (reply:[NSObject : AnyObject]!, error: NSError!) -> Void in
if let message = reply["Message"] as? String{
println(message)
}
})
}
in AppDelegate.swift
func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) {
if let siteName = userInfo["SiteName"] as? String{
reply(["Message":"Hello from \(siteName)"])
}
}
Output: "Hello from Tech-recipes"
2) However, when I want to integrate the apple watch target in swift to the exiting project in obj-c, it will crash and give me this error: "fatal error: unexpectedly found nil while unwrapping an Optional value"
In InterfaceController.swift
#IBAction func buttonPressed() {
let watchMessage = ["SiteName" : "Tech-recipes"]
WKInterfaceController.openParentApplication(watchMessage, reply: { (reply:[NSObject : AnyObject]!, error: NSError!) -> Void in
if let message = reply["Message"] as? String{ //CRASH HERE!!!!
println(message)
}
})
}
iPhoneAppDelegate.m
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply{
// Performs task on phone here
// Sending back a reply
if ([[userInfo valueForKey:#"SiteName"] length]>0) {
//NSMutableDictionary *reply = [[NSMutableDictionary alloc] init];
[reply setValue:[NSString stringWithFormat:#"Hello from %#", [userInfo valueForKey:#"SiteName"]] forKey:#"Message"];
}
}
Update:
--> Noticed that in handleWatchKitExtensionRequest: method the type for userInfo is different in swift and obj-c, in [NSObject : AnyObject]! and NSDictionary respectively. How to solve this?
--> Got an error in error: NSError!: [0] (null) #"NSLocalizedDescription" : #"The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]"
Solved it!
Missed out the reply(aNsdictionary) in the app delegate
WebKit 1 exposed the WebFrameView where I could call a print operation.
- (void)webView:(WebView *)sender printFrameView:(WebFrameView *)frameView {
NSPrintOperation *printOperation = [frameView printOperationWithPrintInfo:[NSPrintInfo sharedPrintInfo]];
[printOperation runOperation];
}
With WKWebKit API I can't seem to figure out how to perform a similar action or which view to grab for printing. All my efforts have come up with blank pages.
macOS 11 update: the printOperation(with:) method is no longer private and is now the solution that I use in my apps.
Amazingly, WKWebView still doesn't support printing on macOS, despite the legacy WebView being deprecated.
Looking at https://github.com/WebKit/webkit/commit/0dfc67a174b79a8a401cf6f60c02150ba27334e5 , printing was implemented years ago as a private API, but for some reason, has not been exposed. If you don't mind using a private API, you can print a WKWebView with:
public extension WKWebView {
// standard printing doesn't work for WKWebView; see http://www.openradar.me/23649229 and https://bugs.webkit.org/show_bug.cgi?id=151276
#available(OSX, deprecated: 10.16, message: "WKWebView printing will hopefully get fixed someday – maybe in 10.16?")
private static let webViewPrintSelector = Selector(("_printOperationWithPrintInfo:")) // https://github.com/WebKit/webkit/commit/0dfc67a174b79a8a401cf6f60c02150ba27334e5
func webViewPrintOperation(withSettings printSettings: [NSPrintInfo.AttributeKey : Any]) -> NSPrintOperation? {
guard self.responds(to: Self.webViewPrintSelector) else {
return nil
}
guard let po: NSPrintOperation = self.perform(Self.webViewPrintSelector, with: NSPrintInfo(dictionary: printSettings))?.takeUnretainedValue() as? NSPrintOperation else {
return nil
}
// without initializing the print view's frame we get the breakpoint at AppKitBreakInDebugger:
// ERROR: The NSPrintOperation view's frame was not initialized properly before knowsPageRange: returned. This will fail in a future release! (WKPrintingView)
po.view?.frame = self.bounds
return po
}
}
You can have this be the default print operation for your NSDocument subclass by adding:
override func printOperation(withSettings printSettings: [NSPrintInfo.AttributeKey : Any]) throws -> NSPrintOperation {
return myWebView.webViewPrintOperation(withSettings: printSettings) ?? try super.printOperation(withSettings: printSettings)
}
Here is marcprux swift solution in Objective C:
SEL printSelector = NSSelectorFromString(#"_printOperationWithPrintInfo:");
if ([self.webView respondsToSelector:printSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSPrintOperation *printOperation = (NSPrintOperation*) [self.webView performSelector:printSelector withObject:[NSPrintInfo sharedPrintInfo]];
#pragma clang diagnostic pop
return printOperation;
}
Starting on macOS 11 this is not private anymore: printOperation(with:)
Usage:
let info = NSPrintInfo.shared
// configure info...
let operation = webView.printOperation(with: info)
Im looking to migrate a game over to swift, the only trouble I am having is with blocks/closures. It's the syntax I just don't understand, whereas in Objective C I would use:
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (viewController != nil) {
[self presentViewController:viewController animated:YES completion:nil];
}
}
etc.etc. but I'm not sure how to go about doing the same in Swift. I know it's simple but I just can't get it to work, even after reading the Swift book and googling answers myself. I'm only a hobbyist programmer so I'm far from perfect at all this.
Any Help would be appreciated.
This is how you would do it in Swift:
var localPlayer = CGLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController : UIViewController!, error : NSError!) -> Void in
//handle authentication
}
The documentation for closures can be found here.
This is how you authenticate in Swift using Xcode 6.1 +:
var localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController : UIViewController!, error : NSError!) -> Void in
if ((viewController) != nil) {
self.presentViewController(viewController, animated: true, completion: nil)
}else{
println((GKLocalPlayer.localPlayer().authenticated))
}
}