How to Open Request in Browser in cellphone - ios

I have a problem with an open request in my app. My apps sends a request to a web view and I need to open this request in a browser on a cellphone. I can not get it to open or perform the request in the web browser.
when I use the command code only can string format of URL.
How I can do request?
I use the command code string format of URL. How I can issue this request?
import UIKit
class PaymentViewController: UIViewController, UIWebViewDelegate {
var delegate: BackProtocol? = nil
var firstCall = true
var providerId: Int!
var transactionId: String!
#IBOutlet weak var webView: UIWebView!
func configureView() {
self.navigationItem.title = LocalizationSystem.sharedInstance.localizedStringForKey(key: "pay" , comment:"")
// back button
self.navigationItem.addCustomButton(style: .action, position: .left, customView: UIView())
if LocalizationSystem.sharedInstance.getLanguage() == "fa" {
self.navigationItem.addBackButtonEnglishLanguage(target: self, selector: #selector(onBackPressed))
} else {
self.navigationItem.addBackButton(target: self, selector: #selector(onBackPressed))
}
self.webView.delegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
self.configureView()
var request = URLRequest(url: URL(string: String(format: "%#%#",
Downloader.API_BASE_URL,
String(format: Downloader.API_PAYMENT_URL, transactionId,
providerId, Statics.CALLBACK_URL)))!)
request.httpMethod = "GET"
request.setValue(UserInfo.acessToken!, forHTTPHeaderField: "Authorization")
self.webView.loadRequest(request)
// guard let url = URL(string: "http://www.google.com") else {
// return //be safe
// }
//
// if #available(iOS 10.0, *) {
// UIApplication.shared.open(url, options: [:], completionHandler: nil)
// } else {
// UIApplication.shared.openURL(url)
// }
}
func webViewDidFinishLoad(_ webView: UIWebView) {
if let requestedUrl = webView.request?.mainDocumentURL?.absoluteString {
let parts = requestedUrl.components(separatedBy: "/")
// return to previous page
if parts[3] == "verify_payment" {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) { // 2 seconds later
self.onBackPressed()
}
}
}
}
}

Related

How to add close or back button in Xcode using swift language when a file such as pdf, ppt .. etc. is opened?

First of all I'm new in swift and objective c languages. My problem is that I have a WebView app for iOS device using swift language , the app when opens a document file such as pdf, ppt,...etc. there is no option to close the file or back to previous. Just googled the problem and found the solution with objective c in the link below, but my problem is that I'm using swift not objective c.
xCode add close/done button when pdf file is opened
and my code is:
import UIKit
import WebKit
import QuickLook
import AVFoundation
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, QLPreviewControllerDataSource, WKScriptMessageHandler {
var documentPreviewController = QLPreviewController()
var documentUrl = URL(fileURLWithPath: "")
let webViewConfiguration = WKWebViewConfiguration()
let userContentController = WKUserContentController()
var webViewCookieStore: WKHTTPCookieStore!
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// initial configuration of custom JavaScripts
webViewConfiguration.userContentController = userContentController
webViewConfiguration.websiteDataStore = WKWebsiteDataStore.default()
// init this view controller to receive JavaScript callbacks
userContentController.add(self, name: "openDocument")
userContentController.add(self, name: "jsError")
// QuickLook document preview
documentPreviewController.dataSource = self
// link the appDelegate to be able to receive the deviceToken
//------------
// Add script message handlers that, when run, will make the function
// window.webkit.messageHandlers.test.postMessage() available in all frames.
// controller.add(self, name: "test")
guard let scriptPath = Bundle.main.path(forResource: "script", ofType: "js"),
let scriptSource1 = try? String(contentsOfFile: scriptPath) else { return }
let userScript = WKUserScript(source: scriptSource1, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
userContentController.addUserScript(userScript)
//----- end
webView = WKWebView(frame: CGRect.zero, configuration: webViewConfiguration)
let myURL = URL(string:"My URL")
let myRequest = URLRequest(url: myURL!)
// see "The 2 delegates": // https://samwize.com/2016/06/08/complete-guide-to-implementing-wkwebview/
webView.uiDelegate = self
webView.navigationDelegate = self
view.addSubview(webView)
let layoutGuide = view.safeAreaLayoutGuide
webView.translatesAutoresizingMaskIntoConstraints = false
webView.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor).isActive = true
webView.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor).isActive = true
webView.topAnchor.constraint(equalTo: layoutGuide.topAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor).isActive = true
self.load(myRequest)
webViewCookieStore = webView.configuration.websiteDataStore.httpCookieStore
webView.allowsBackForwardNavigationGestures = true
if(webView.canGoBack) {
//Go back in webview history
webView.goBack()
} else {
//Pop view controller to preview view controller
self.navigationController?.popViewController(animated: true)
}
let cameraMediaType = AVMediaType.video
let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: cameraMediaType)
switch cameraAuthorizationStatus {
case .denied: break
case .authorized: break
case .restricted: break
case .notDetermined:
// Prompting user for the permission to use the camera.
AVCaptureDevice.requestAccess(for: cameraMediaType) { granted in
if granted {
print("Granted access to \(cameraMediaType)")
} else {
print("Denied access to \(cameraMediaType)")
}
}
#unknown default:
fatalError()
}
}
private func load(_ url: URL) {
load(URLRequest(url:url))
}
private func load(_ req: URLRequest) {
let request = req
// request.setValue(self.deviceToken, forHTTPHeaderField: "iosDeviceToken")
//request.setValue(self.myVersion as? String, forHTTPHeaderField: "iosVersion")
//request.setValue(self.myBuild as? String, forHTTPHeaderField: "iosBuild")
//request.setValue(UIDevice.current.modelName, forHTTPHeaderField: "iosModelName")
//debugPrintHeaderFields(of: request, withMessage: "Loading request")
webView.load(request)
debugPrint("Loaded request=\(request.url?.absoluteString ?? "n/a")")
}
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
let url = navigationAction.request.url
if openInDocumentPreview(url!) {
decisionHandler(.cancel)
executeDocumentDownloadScript(forAbsoluteUrl: url!.absoluteString)
} else {
decisionHandler(.allow)
}
}
/*
Handler method for JavaScript calls.
Receive JavaScript message with downloaded document
*/
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
debugPrint("did receive message \(message.name)")
if (message.name == "openDocument") {
previewDocument(messageBody: message.body as! String)
} else if (message.name == "jsError") {
debugPrint(message.body as! String)
}
}
/*
Open downloaded document in QuickLook preview
*/
private func previewDocument(messageBody: String) {
// messageBody is in the format ;data:;base64,
// split on the first ";", to reveal the filename
let filenameSplits = messageBody.split(separator: ";", maxSplits: 1, omittingEmptySubsequences: false)
let filename = String(filenameSplits[0])
// split the remaining part on the first ",", to reveal the base64 data
let dataSplits = filenameSplits[1].split(separator: ",", maxSplits: 1, omittingEmptySubsequences: false)
let data = Data(base64Encoded: String(dataSplits[1]))
if (data == nil) {
debugPrint("Could not construct data from base64")
return
}
// store the file on disk (.removingPercentEncoding removes possible URL encoded characters like "%20" for blank)
let localFileURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename.removingPercentEncoding ?? filename)
do {
try data!.write(to: localFileURL);
} catch {
debugPrint(error)
return
}
// and display it in QL
DispatchQueue.main.async {
self.documentUrl = localFileURL
self.documentPreviewController.refreshCurrentPreviewItem()
self.present(self.documentPreviewController, animated: true, completion: nil)
}
}
/*
Implementation for QLPreviewControllerDataSource
*/
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return documentUrl as QLPreviewItem
}
/*
Implementation for QLPreviewControllerDataSource
We always have just one preview item
*/
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
/*
Checks if the given url points to a document download url
*/
private func openInDocumentPreview(_ url : URL) -> Bool {
// this is specific for our application - can be everything in your application
return url.absoluteString.contains("/APP/connector")
}
/*
Intercept the download of documents in webView, trigger the download in JavaScript and pass the binary file to JavaScript handler in Swift code
*/
private func executeDocumentDownloadScript(forAbsoluteUrl absoluteUrl : String) {
// TODO: Add more supported mime-types for missing content-disposition headers
webView.evaluateJavaScript("""
(async function download() {
const url = '\(absoluteUrl)';
try {
// we use a second try block here to have more detailed error information
// because of the nature of JS the outer try-catch doesn't know anything where the error happended
let res;
try {
res = await fetch(url, {
credentials: 'include'
});
} catch (err) {
window.webkit.messageHandlers.jsError.postMessage(`fetch threw, error: ${err}, url: ${url}`);
return;
}
if (!res.ok) {
window.webkit.messageHandlers.jsError.postMessage(`Response status was not ok, status: ${res.status}, url: ${url}`);
return;
}
const contentDisp = res.headers.get('content-disposition');
if (contentDisp) {
const match = contentDisp.match(/(^;|)\\s*filename=\\s*(\"([^\"]*)\"|([^;\\s]*))\\s*(;|$)/i);
if (match) {
filename = match[3] || match[4];
} else {
// TODO: we could here guess the filename from the mime-type (e.g. unnamed.pdf for pdfs, or unnamed.tiff for tiffs)
window.webkit.messageHandlers.jsError.postMessage(`content-disposition header could not be matched against regex, content-disposition: ${contentDisp} url: ${url}`);
}
} else {
window.webkit.messageHandlers.jsError.postMessage(`content-disposition header missing, url: ${url}`);
return;
}
if (!filename) {
const contentType = res.headers.get('content-type');
if (contentType) {
if (contentType.indexOf('application/json') === 0) {
filename = 'unnamed.pdf';
} else if (contentType.indexOf('image/tiff') === 0) {
filename = 'unnamed.tiff';
}
}
}
if (!filename) {
window.webkit.messageHandlers.jsError.postMessage(`Could not determine filename from content-disposition nor content-type, content-dispositon: ${contentDispositon}, content-type: ${contentType}, url: ${url}`);
}
let data;
try {
data = await res.blob();
} catch (err) {
window.webkit.messageHandlers.jsError.postMessage(`res.blob() threw, error: ${err}, url: ${url}`);
return;
}
const fr = new FileReader();
fr.onload = () => {
window.webkit.messageHandlers.openDocument.postMessage(`${filename};${fr.result}`)
};
fr.addEventListener('error', (err) => {
window.webkit.messageHandlers.jsError.postMessage(`FileReader threw, error: ${err}`)
})
fr.readAsDataURL(data);
} catch (err) {
// TODO: better log the error, currently only TypeError: Type error
window.webkit.messageHandlers.jsError.postMessage(`JSError while downloading document, url: ${url}, err: ${err}`)
}
})();
// null is needed here as this eval returns the last statement and we can't return a promise
null;
""") { (result, err) in
if (err != nil) {
debugPrint("JS ERR: \(String(describing: err))")
}
}
}
var toolbars: [UIView] = []
var observations : [NSKeyValueObservation] = []
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.toolbar.isHidden = false
if let navigationToobar = navigationController?.toolbar {
let observation = navigationToobar.observe(\.isHidden) {[weak self] (changedToolBar, change) in
if self?.navigationController?.toolbar.isHidden == true {
self?.navigationController?.toolbar.isHidden = false
}
}
observations.append(observation)
}
toolbars = toolbarsInSubviews(forView: view)
for toolbar in toolbars {
toolbar.isHidden = false
let observation = toolbar.observe(\.isHidden) { (changedToolBar, change) in
if let isHidden = change.newValue,
isHidden == true {
changedToolBar.isHidden = false
}
}
observations.append(observation)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(false)
//This hides the share item
if let add = self.children.first as? UINavigationController {
if let layoutContainerView = add.view.subviews[1] as? UINavigationBar {
layoutContainerView.subviews[2].subviews[1].isHidden = false
}
}
}
private func toolbarsInSubviews(forView view: UIView) -> [UIView] {
var toolbars: [UIView] = []
for subview in view.subviews {
if subview is UIToolbar {
toolbars.append(subview)
}
toolbars.append(contentsOf: toolbarsInSubviews(forView: subview))
}
return toolbars
}
}
You've done an excellent job getting this far. A small adjustment will fix your code. The callback needs to be inside a method matching the actual callback
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
Move the canGoBack code as follows:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if webView.canGoBack {
let backButton = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(backButtonPressed))
navigationItem.rightBarButtonItem = backButton
} else {
self.navigationItem.rightBarButtonItem = nil
}
}
And the method it calls can look like this (based on the link you provided):
#objc
func backButtonPressed() {
if webView.canGoBack {
webView.goBack()
} else {
self.navigationController?.popViewController(animated: true)
}
}
Edit: (this is how to embed your controller inside a UINavigationController)
I've made an assumption that you're using Interface Builder. (This isn't how I would do this, but it should work for you).
Click on LaunchScreen.storyboard (in the left side column)
Click on and expand the View Controller Scene (the next column to the right at the top)
Click on the ViewController
Move to the top of the screen and select from the menu:
/Editor/Embed In/Navigation Controller
That should be all you need to do. But look up navigation controllers and learn how they work/why and when you need them.
They're very powerful, and as you've learned, they're essential!
🍀

(Swift) API call is not going through first time, and on second attempt the call is made for the first tap

I an application that suppose to get weather data in certain cities and I have a dropdown and based on the dropdown selection, an api should be called (based on city name), when I tap on a city for the first time the app runs it returns nil and if I tap on another city it returns data based on the first selection, I have debugged and traced the code line by line however the URL is correct and the everything seems to be in order, any help? here is my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tempraturevalue: UILabel!
#IBOutlet weak var cityname: UILabel!
#IBOutlet weak var temprature: UIImageView!
#IBOutlet var cityButtons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func handleSelection(_ sender: UIButton) {
cityButtons.forEach{ (button) in
UIView.animate(withDuration: 0.3) {
button.isHidden = !button.isHidden
self.view.layoutIfNeeded()
}
}
}
enum Cities:String {
case amman = "Amman"
case azzerqa = "Az zerqa"
case irbid = "Irbid"
case aqaba = "Aqaba"
}
var tempraturevalueData:Double = 0.0
var cityNameData:String = ""
#IBAction func cityTapped(_ sender: UIButton) {
guard let title = sender.currentTitle, let City = Cities(rawValue: title)
else {
return
}
var city:String
switch City {
case .amman:
city = "Amman"
case .azzerqa:
city = "zerqa"
case .irbid:
city = "Irbid"
case .aqaba:
city = "Aqaba"
}
let url = URL(string: "https://api.weatherapi.com/v1/current.json?key={key}&q=\(city)")
guard url != nil else {
print("error creating URL Object")
return
}
var request = URLRequest(url: url!, cachePolicy: .useProtocolCachePolicy , timeoutInterval: 10)
let headers = ["Content-Type" : "application/json"]
request.allHTTPHeaderFields = headers
request.httpMethod = "GET"
let session = URLSession.shared
let dataTask = session.dataTask(with: request, completionHandler: {(data, response, error) in
if error == nil && data != nil {
do {
let decoder = JSONDecoder()
do {
let weatherdatadecoded = try decoder.decode(WeatherData.self, from: data!)
self.tempraturevalueData = weatherdatadecoded.current?.temp_c ?? 0.1
self.cityNameData = weatherdatadecoded.location?.name ?? "no city found"
}
catch {
print(error)
}
}
catch {
print(error.localizedDescription)
}
}
})
dataTask.resume()
print(self.cityNameData)
print(self.tempraturevalueData)
}
}

Swift Instagram API username isn't displayed on label

I just wanna make an iOS app with Instagram API then I can see the user profile.
I just wrote this Code on my Xcode project to make iOS app.
here is the code :--
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
signInRequest()
}
func signInRequest() {
let getURL = String(format: "%#?client_id=%#&redirect_uri=%#&response_type=token&scope=%#&DEBUG=True", arguments: [API.INSTAGRAM_AUTHURL,API.INSTAGRAM_CLIENT_ID,API.INSTAGRAM_REDIRECT_URI,API.INSTAGRAM_SCOPE])
let request = URLRequest.init(url: URL.init(string: getURL)!)
webView.loadRequest(request)
}
func checkRequestForCallbackURL(request: URLRequest) -> Bool {
let requestURLString = (request.url?.absoluteString)! as String
if requestURLString.hasPrefix(API.INSTAGRAM_REDIRECT_URI) {
let range: Range<String.Index> = requestURLString.range(of: "#access_token=")!
handleAuth(authToken: requestURLString.substring(from: range.upperBound))
return false
}
return true
}
func handleAuth(authToken: String) {
let url = String(format: "https://api.instagram.com/v1/users/self/?access_token=%#", authToken)
let request: NSMutableURLRequest = NSMutableURLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let session = URLSession(configuration: .default)
session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
if let data = data {
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary
let strFullName = (json?.value(forKey: "data") as AnyObject).value(forKey: "full_name") as! String
let secondVC: SecondViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "secondSeg") as! SecondViewController
secondVC.text = strFullName
self.present(secondVC
, animated: true, completion: nil)
}
}.resume()
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
return checkRequestForCallbackURL(request: request)
}
}
my question is when I run my app and want to see the instagram user username I can't see on my label.
I can't see the user (full_name, username, followers, etc...)
here is secondVC Code.
var text: String = ""
#IBOutlet weak var userNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
userNameLabel?.text = text
}
I believe your viewdidload function is being called before the update of your text variable
Try replacing:
var text: String = ""
With:
var text: String = "" {
didSet {
userNameLabel?.text = text
}
}

How to record an audio stream for save it in file / swift 4.2

I am creating a radio application for iPhone (coded in Swift 4.2) and I want to add a function allowing me to record and save in a file, the sound produced by my radio (read from an AVPlayer) when I push a button. Which code should I use?
The code is in Swift 4.2, with Xcode 10.1.
I search on the web : "How to record an audio stream swift 4.2", "How to record audio from AVPlayer swift 4.2", but I can't find an answer.
My code:
import UIKit
import AVFoundation
import MediaPlayer
class ViewControllerPlayer: UIViewController {
var URl = "http://link_of_audio_stream"
var player:AVPlayer?
var playerItem:AVPlayerItem?
var playerLayer:AVPlayerLayer?
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: URl)
let playerItem1:AVPlayerItem = AVPlayerItem(url: url!)
player = AVPlayer(playerItem: playerItem1)
}
#IBAction func Play(_ sender: Any) {
player?.play()
}
#IBAction func Pause(_ sender: Any) {
player?.pause()
}
private var audioRecorder: AVAudioRecorder!
func startRecording() throws {
guard let newFileURL = createURLForNewRecord() else {
throw RecordingServiceError.canNotCreatePath
}
do {
var urlString = URL(string: URl)
urlString = newFileURL
audioRecorder = try AVAudioRecorder(url: newFileURL,
settings: [AVFormatIDKey:Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 8000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.min.rawValue])
audioRecorder.delegate = self as? AVAudioRecorderDelegate
audioRecorder.prepareToRecord()
audioRecorder.record(forDuration: TimeConstants.recordDuration)
//error: Use of unresolved identifier 'TimeConstants'
} catch let error {
print(error)
}
}
func STOPREC1() throws {
audioRecorder.stop()
audioRecorder = nil
print("Recording finished successfully.")
}
enum RecordingServiceError: String, Error {
case canNotCreatePath = "Can not create path for new recording"
}
private func createURLForNewRecord() -> URL? {
guard let appGroupFolderUrl = FileManager.getAppFolderURL() else {
return nil
}
let date = String(describing: Date())
let fullFileName = "Enregistrement radio " + date + ".m4a"
let newRecordFileName = appGroupFolderUrl.appendingPathComponent(fullFileName)
return newRecordFileName
}
}
extension FileManager {
class func getAppFolderURL() -> URL? {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
}
After multiple internet search, I found the solution.
I found this Swift Class somewhere on internet named « CachingPlayerItem.swift », it will allow the to record an online audio stream.
import Foundation
import AVFoundation
fileprivate extension URL {
func withScheme(_ scheme: String) -> URL? {
var components = URLComponents(url: self, resolvingAgainstBaseURL: false)
components?.scheme = scheme
return components?.url
}
}
#objc protocol CachingPlayerItemDelegate {
/// Is called when the media file is fully downloaded.
#objc optional func playerItem(_ playerItem: CachingPlayerItem, didFinishDownloadingData data: Data)
/// Is called every time a new portion of data is received.
#objc optional func playerItem(_ playerItem: CachingPlayerItem, didDownloadBytesSoFar bytesDownloaded: Int, outOf bytesExpected: Int)
/// Is called after initial prebuffering is finished, means
/// we are ready to play.
#objc optional func playerItemReadyToPlay(_ playerItem: CachingPlayerItem)
/// Is called when the data being downloaded did not arrive in time to
/// continue playback.
#objc optional func playerItemPlaybackStalled(_ playerItem: CachingPlayerItem)
/// Is called on downloading error.
#objc optional func playerItem(_ playerItem: CachingPlayerItem, downloadingFailedWith error: Error)
}
open class CachingPlayerItem: AVPlayerItem {
class ResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate, URLSessionDataDelegate, URLSessionTaskDelegate {
var playingFromData = false
var mimeType: String? // is required when playing from Data
var session: URLSession?
var mediaData: Data?
var response: URLResponse?
var pendingRequests = Set<AVAssetResourceLoadingRequest>()
weak var owner: CachingPlayerItem?
var fileURL: URL!
var outputStream: OutputStream?
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
if playingFromData {
// Nothing to load.
} else if session == nil {
// If we're playing from a url, we need to download the file.
// We start loading the file on first request only.
guard let initialUrl = owner?.url else {
fatalError("internal inconsistency")
}
startDataRequest(with: initialUrl)
}
pendingRequests.insert(loadingRequest)
processPendingRequests()
return true
}
func startDataRequest(with url: URL) {
var recordingName = "record.mp3"
if let recording = owner?.recordingName{
recordingName = recording
}
fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent(recordingName)
let configuration = URLSessionConfiguration.default
configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
session?.dataTask(with: url).resume()
outputStream = OutputStream(url: fileURL, append: true)
outputStream?.schedule(in: RunLoop.current, forMode: RunLoop.Mode.default)
outputStream?.open()
}
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, didCancel loadingRequest: AVAssetResourceLoadingRequest) {
pendingRequests.remove(loadingRequest)
}
// MARK: URLSession delegate
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
let bytesWritten = data.withUnsafeBytes{outputStream?.write($0, maxLength: data.count)}
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
completionHandler(Foundation.URLSession.ResponseDisposition.allow)
mediaData = Data()
self.response = response
processPendingRequests()
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let errorUnwrapped = error {
owner?.delegate?.playerItem?(owner!, downloadingFailedWith: errorUnwrapped)
return
}
processPendingRequests()
owner?.delegate?.playerItem?(owner!, didFinishDownloadingData: mediaData!)
}
// MARK: -
func processPendingRequests() {
// get all fullfilled requests
let requestsFulfilled = Set<AVAssetResourceLoadingRequest>(pendingRequests.compactMap {
self.fillInContentInformationRequest($0.contentInformationRequest)
if self.haveEnoughDataToFulfillRequest($0.dataRequest!) {
$0.finishLoading()
return $0
}
return nil
})
// remove fulfilled requests from pending requests
_ = requestsFulfilled.map { self.pendingRequests.remove($0) }
}
func fillInContentInformationRequest(_ contentInformationRequest: AVAssetResourceLoadingContentInformationRequest?) {
if playingFromData {
contentInformationRequest?.contentType = self.mimeType
contentInformationRequest?.contentLength = Int64(mediaData!.count)
contentInformationRequest?.isByteRangeAccessSupported = true
return
}
guard let responseUnwrapped = response else {
// have no response from the server yet
return
}
contentInformationRequest?.contentType = responseUnwrapped.mimeType
contentInformationRequest?.contentLength = responseUnwrapped.expectedContentLength
contentInformationRequest?.isByteRangeAccessSupported = true
}
func haveEnoughDataToFulfillRequest(_ dataRequest: AVAssetResourceLoadingDataRequest) -> Bool {
let requestedOffset = Int(dataRequest.requestedOffset)
let requestedLength = dataRequest.requestedLength
let currentOffset = Int(dataRequest.currentOffset)
guard let songDataUnwrapped = mediaData,
songDataUnwrapped.count > currentOffset else {
return false
}
let bytesToRespond = min(songDataUnwrapped.count - currentOffset, requestedLength)
let dataToRespond = songDataUnwrapped.subdata(in: Range(uncheckedBounds: (currentOffset, currentOffset + bytesToRespond)))
dataRequest.respond(with: dataToRespond)
return songDataUnwrapped.count >= requestedLength + requestedOffset
}
deinit {
session?.invalidateAndCancel()
}
}
fileprivate let resourceLoaderDelegate = ResourceLoaderDelegate()
fileprivate let url: URL
fileprivate let initialScheme: String?
fileprivate var customFileExtension: String?
weak var delegate: CachingPlayerItemDelegate?
func stopDownloading(){
resourceLoaderDelegate.session?.invalidateAndCancel()
}
open func download() {
if resourceLoaderDelegate.session == nil {
resourceLoaderDelegate.startDataRequest(with: url)
}
}
private let cachingPlayerItemScheme = "cachingPlayerItemScheme"
var recordingName = "record.mp3"
/// Is used for playing remote files.
convenience init(url: URL, recordingName: String) {
self.init(url: url, customFileExtension: nil, recordingName: recordingName)
}
/// Override/append custom file extension to URL path.
/// This is required for the player to work correctly with the intended file type.
init(url: URL, customFileExtension: String?, recordingName: String) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let scheme = components.scheme,
var urlWithCustomScheme = url.withScheme(cachingPlayerItemScheme) else {
fatalError("Urls without a scheme are not supported")
}
self.recordingName = recordingName
self.url = url
self.initialScheme = scheme
if let ext = customFileExtension {
urlWithCustomScheme.deletePathExtension()
urlWithCustomScheme.appendPathExtension(ext)
self.customFileExtension = ext
}
let asset = AVURLAsset(url: urlWithCustomScheme)
asset.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main)
super.init(asset: asset, automaticallyLoadedAssetKeys: nil)
resourceLoaderDelegate.owner = self
addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)
NotificationCenter.default.addObserver(self, selector: #selector(playbackStalledHandler), name:NSNotification.Name.AVPlayerItemPlaybackStalled, object: self)
}
/// Is used for playing from Data.
init(data: Data, mimeType: String, fileExtension: String) {
guard let fakeUrl = URL(string: cachingPlayerItemScheme + "://whatever/file.\(fileExtension)") else {
fatalError("internal inconsistency")
}
self.url = fakeUrl
self.initialScheme = nil
resourceLoaderDelegate.mediaData = data
resourceLoaderDelegate.playingFromData = true
resourceLoaderDelegate.mimeType = mimeType
let asset = AVURLAsset(url: fakeUrl)
asset.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main)
super.init(asset: asset, automaticallyLoadedAssetKeys: nil)
resourceLoaderDelegate.owner = self
addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)
NotificationCenter.default.addObserver(self, selector: #selector(playbackStalledHandler), name:NSNotification.Name.AVPlayerItemPlaybackStalled, object: self)
}
// MARK: KVO
override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
delegate?.playerItemReadyToPlay?(self)
}
// MARK: Notification hanlers
#objc func playbackStalledHandler() {
delegate?.playerItemPlaybackStalled?(self)
}
// MARK: -
override init(asset: AVAsset, automaticallyLoadedAssetKeys: [String]?) {
fatalError("not implemented")
}
deinit {
NotificationCenter.default.removeObserver(self)
removeObserver(self, forKeyPath: "status")
resourceLoaderDelegate.session?.invalidateAndCancel()
}
}
After, in you main swift file, you put this code to record:
let recordingName = "my_rec_name.mp3"
var playerItem: CachingPlayerItem!
let url_stream = URL(string: "http://my_url_stream_link")
playerItem = CachingPlayerItem(url: url_stream!, recordingName: recordingName ?? "record.mp3")
var player1 = AVPlayer(playerItem: playerItem)
player1.automaticallyWaitsToMinimizeStalling = false
And to stop the record, you use this code:
playerItem.stopDownloading()
recordingName = nil
playerItem = nil
Recordings will be saved on the directory of your app.
I had a really hard time with this one so I am posting an answer.
Remember to add these lines to your info.plist:
Here is my controller that records the voice input and returns it to a previous controller:
import Foundation
import UIKit
import Speech
class SpeechToTextViewController: UIViewController {
#IBOutlet weak var animationView: UIView!
#IBOutlet weak var circleView: UIView!
#IBOutlet weak var micImage: UIImageView!
#IBOutlet weak var listeningLabel: UILabel!
#IBOutlet weak var buttonStartView: UIView!
#IBOutlet weak var cancelRecordingButton: UIButton!
#IBOutlet weak var stopRecordingButton: UIButton!
#IBOutlet weak var startRecordingButton: UIButton!
private let audioEngine = AVAudioEngine()
private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier:"en-US"))
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest!
private var recognitionTask: SFSpeechRecognitionTask?
private var isRecording: Bool = false
var delegate: SpeechToTextViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(white: 1.0, alpha: 0.25)
self.stopRecordingButton.isHidden = true
self.listeningLabel.isHidden = true
}
#IBAction func startStopRecording(_ sender: Any) {
isRecording = !isRecording
if isRecording && !audioEngine.isRunning {
self.cancelRecordingButton.isHidden = true
self.startRecordingButton.isHidden = true
self.stopRecordingButton.isHidden = false
self.listeningLabel.isHidden = false
UIView.animate(withDuration: 1, animations: {}) { _ in
UIView.animate(withDuration: 1, delay: 0.25, options: [.autoreverse, .repeat], animations: {
self.circleView.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
})
}
do {
try recordSpeech()
} catch {
print(error)
}
} else {
self.listeningLabel.isHidden = true
stopRecording()
}
}
func recordSpeech() throws {
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
let node = audioEngine.inputNode
let recordingFormat = node.outputFormat(forBus: 0)
node.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) {buffer, _ in
self.recognitionRequest.append(buffer)
}
audioEngine.prepare()
try audioEngine.start()
guard let myRecognizer = SFSpeechRecognizer() else {
print("myRecognizer is unable to be created")
return
}
if !myRecognizer.isAvailable
{
print("myRecognizer is not available")
return
}
recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { result, error in
var isFinal = false
if let result = result
{
isFinal = result.isFinal
self.delegate?.appendMessage(result.bestTranscription.formattedString)
}
if error != nil || isFinal {
if error != nil {
print("error trying to capture speech to text")
print(error!)
}
self.stopRecording()
}
})
}
func stopRecording() {
if audioEngine.isRunning {
self.audioEngine.stop()
self.recognitionRequest.endAudio()
// Cancel the previous task if it's running
if let recognitionTask = recognitionTask {
recognitionTask.cancel()
self.recognitionTask = nil
}
}
delegate?.doneTalking()
self.dismiss(animated: true, completion: nil)
}
#IBAction func cancelRecording(_ sender: Any) {
delegate?.doneTalking()
self.dismiss(animated: true, completion: nil)
}
}
Use AVAudioRecorder for recording:
private var audioRecorder: AVAudioRecorder!
After you declared an audio recorder you can write a recording method:
func startRecording() throws {
guard let newFileURL = createURLForNewRecord() else {
throw RecordingServiceError.canNotCreatePath
}
do {
currentFileURL = newFileURL
audioRecorder = try AVAudioRecorder(url: newFileURL,
settings: [AVFormatIDKey:Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 8000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.min.rawValue])
audioRecorder.delegate = self
audioRecorder.prepareToRecord()
audioRecorder.record(forDuration: TimeConstants.recordDuration)
} catch let error {
print(error)
}
}
And use some helper methods and structs:
enum RecordingServiceError: String, Error {
case canNotCreatePath = "Can not create path for new recording"
}
private func createURLForNewRecord() -> URL? {
guard let appGroupFolderUrl = FileManager.getAppFolderURL() else {
return nil
}
let fileNamePrefix = DateFormatter.stringFromDate(Date())
let fullFileName = "Record_" + fileNamePrefix + ".m4a"
let newRecordFileName = appGroupFolderUrl.appendingPathComponent(fullFileName)
return newRecordFileName
}
extension FileManager {
class func getAppFolderURL() -> URL? {
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "you app bundle")
}
}

sign in on instagram from mobile device

I'm currently trying to sign into instagram from my application. I've been following this tutorial. However it's written in objective-c so I rewrote it to swift 3.0 (I hope lol).
However it doesn't seem to work like it's supposed to. It seems like it doesn't get the appropriate url request. Maybe I've done something wrong, maybe the tutorial I've used is outdated. I couldn't seem to find any others tho.
This is how my rewritten code looks.
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet var loginWebView: UIWebView!
#IBOutlet var loginactivity: UIActivityIndicatorView!
#IBOutlet var loadedlabel: UILabel!
var consumer: OAConsumer!;
var requestToken: OAToken!;
var accessToken: OAToken!;
var receivedData: Data!
var isLogin: String!;
var typeOfAuthentication: String!;
var client_id:String = "";
var secret:String = "";
var callback:String = "";
var authURL:String = "";
var scope: String = "";
override func viewDidLoad() {
super.viewDidLoad();
super.viewDidAppear(true);
client_id = "myid";
secret = "mysecret";
callback = "http://whattodo.com/";
typeOfAuthentication = "UNSIGNED";
scope = "likes+comments+relationships"
var authURL: String = "";
if(typeOfAuthentication == "UNSIGNED"){
authURL = String(format: "client_id=%#&redirect_uri=%#&response_type=token&scope=%#&DEBUG=true",
client_id, callback, scope);
}
else{
authURL = String(format: "client_id=%#&redirect_uri=%#&response_type=code&scope=%#&DEBUG=true", client_id, callback, scope);
}
loginWebView.loadRequest(URLRequest(url: URL(string: authURL)!));
loginWebView.delegate = self;
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool{
return checkRequestForCallbackURL(request: request);
}
func webViewDidStartLoad(_ webView: UIWebView) {
loginactivity.startAnimating();
loadedlabel.isHidden = false;
loginWebView.layer.removeAllAnimations();
loginWebView.isUserInteractionEnabled = false;
UIView.animate(withDuration: 0.1, animations: {
self.loginWebView.alpha = 0.2;
})
}
func webViewDidFinishLoad(_ webView: UIWebView) {
loginactivity.stopAnimating();
loadedlabel.isHidden = true;
loginWebView.layer.removeAllAnimations();
loginWebView.isUserInteractionEnabled = true;
UIView.animate(withDuration: 0.1, animations: {
self.loginWebView.alpha = 1;
})
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
self.webViewDidFinishLoad(webView);
}
func checkRequestForCallbackURL(request: URLRequest) -> Bool{
let urlString: String = (request.url?.absoluteString)!
if(typeOfAuthentication == "UNSIGNED"){
if(urlString.hasPrefix(callback)){
let range: Range = urlString.range(of: "access_token=")!;
let test: String = String(describing: range);
handleAuth(authToken: String(describing: range));
return false;
}
}
else{
if(urlString.hasPrefix(callback)){
let range: Range = urlString.range(of: "code")!;
let test: String = String(describing: range);
makePostRequest(code: String(describing: range))
return false;
}
}
return true;
}
func makePostRequest(code: String){
let post: String = String(format: "client_id=%#&client_secret=%#&grant_type=authorization_code&redirect_uri=%#&code=%#", client_id, secret, callback, code)
let postDate: Data = post.data(using: String.Encoding.ascii, allowLossyConversion: true)!;
let postLength: String = "\(UInt(postDate.count))"
let requestData: NSMutableURLRequest = NSMutableURLRequest(url: URL.init(string: "https://api.instagram.com/oauth/access_token")!);
requestData.httpMethod = "POST";
requestData.setValue(postLength, forHTTPHeaderField: "Content-Length");
requestData.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type");
requestData.httpBody = postDate;
var response: URLResponse? = nil;
let requestError: NSError? = nil;
var responseData: Data = try! NSURLConnection.sendSynchronousRequest(requestData as URLRequest, returning: &response);
let dict: NSDictionary = try! JSONSerialization.jsonObject(with: responseData, options: JSONSerialization.ReadingOptions.allowFragments) as! NSDictionary
handleAuth(authToken: dict["access_token"]! as! String)
}
func handleAuth(authToken: String){
NSLog("Successfully logged in with token: " + authToken);
}
}
I've made sure that disable implicit OAuth and enforce signed requests are both deselected. I also don't see anything strange showing up in my logs.
Does anyone know where I'm going wrong?

Resources