Difference between Data.write(to: ) and filemanager.default.createFile? [duplicate] - swift3

This question already has answers here:
UIImage(contentsOfFile:) returning nil despite file existing in caches directory [duplicate]
(1 answer)
NSURL path vs absoluteString
(3 answers)
Closed 6 years ago.
EDIT: The questions linked to in the duplicate section answer this question quite well (I just hadn't found them when I posted this). Please refer to those.
I have just started looking into Swift (as of about three or four days ago), and have encountered a problem that I really do not understand, and for which I could not find anything already written about. I had already seen Read and write data from text file, and borrowed some of the code from the accepted answer there, but I'm still left with a question.
I was having some file IO trouble elsewhere, so in order to experiment and make sure I understood everything, I created a brand new single view app for iOS 10, to experiment with. All I have added is the following to the viewDidLoad() function in the view controller (sorry it is a bit messy):
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let mydata = "I like to eat food!".data(using: .utf8)
let mydata2 = "Tigers are a man's best friend".data(using: .utf8)
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! as URL
let fURL = dir.appendingPathComponent("hello.txt", isDirectory: false)
NSLog(fURL.absoluteString)
do {
if !FileManager.default.fileExists(atPath: fURL.absoluteString) {
let createdFile = FileManager.default.createFile(atPath: fURL.absoluteString, contents: mydata2!, attributes: nil)
NSLog("File created? \(createdFile)")
}
try "The other string!".write(to: fURL, atomically: true, encoding: .utf8)
if !FileManager.default.fileExists(atPath: fURL.absoluteString) {
let createdFile = FileManager.default.createFile(atPath: fURL.absoluteString, contents: mydata2, attributes: nil)
NSLog("File created? \(createdFile)")
}
try mydata2?.write(to: fURL)
let filehandle = try FileHandle.init(forWritingTo: fURL)
filehandle.truncateFile(atOffset: 0)
filehandle.write(mydata!)
} catch {
NSLog(error.localizedDescription)
}
}
I then run that through Xcode 8's simulator, and watch the documents folder for the app. The lines which use a write() function appear to work fine. A .txt file named hello.txt is created, and filled then re-filled with the data that is written out. The createFile lines however appear to fail, with createdFile always coming out as false, and no file appearing in the directory. The obvious difference between them is that the createFile lines use fURL.absoluteString, rather than the URL - unfortunately there doesn't seem to be a function in FileManager to use the URL as of right now.
When I tried using the absoluteString with one of the write functions, I got an error informing me that it failed because the folder "hello.txt" doesn't exist, which leaves me thoroughly confused because I don't quite follow how the code thinks that is supposed to be a directory, especially when use the isDirectory
So, clearly I am not understanding something and doing something wrong, but I can't see what. Can someone please explain the correct approach if I want to simply create a file ready for future write(s)? I should be able to use the fileHandle approach, but it seems strange to me that the other shouldn't work.

Related

SwiftUI - open link from my website in the app

I have one SwiftUI - iOS app running, and I need to open the link from email/whatsapp/etc in my app.
So lets say the link is like this (from my website):
https://www.mapplebook.com/welcome;id=123
I have tried the custom Scheme, which works fine, but for that i need to do something like: myScheme://www.mapplebook.com/etc....
Which is bad because Website and Android only understand things without the scheme.
The universal link looks like the best approach, I read a couple of things about Apple docs and some other questions, but could not make it work. :(
Is there any suggestion? Maybe some tutorial that I am missing.
Any help is much appreciated.
Editing the question as requested:
This is my info.plist - In this case I am using the custom scheme mapplebook
In SceneDelegate.swift, I have the followng:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else {
return
}
if let range = url.absoluteString.range(of:"https//www.mapplebook.com/id=") {
let sharedtoken = url.absoluteString[range.upperBound...]
//with sharedtoken i can redirect internally and open the correct view - working okay
}
}
But, as mentioned, that uses scheme mapplebook right?
Is it possible to have something like in android, where we say like:
android:host="mapplebook.com"
android:pathPattern="/something"
android:scheme="https" />
So it ask to open the https://mapplebook.com/something url in my app.
Thank you Koen for helping.

In-repo addon writing public files on build causes endless build loop on serve

I'm having difficulty with my in-repo addon writing to appDir/public. What I'd like to do is write out a JSON file on each build to be included in the app /dist. The problem I'm running into is when running "ember serve", the file watcher detects the new file and rebuilds again, causing an endless loop.
I've tried writing the JSON file using preBuild() and postBuild() hooks, saving to /public, but after build, the watcher detects it and rebuild over and over, writing a new file again each time. I also tried using my-addon/public folder and writing to that, same thing.
The only thing that partially works is writing on init(), which is fine, except I don't see the changes using ember serve.
I did try using the treeForPublic() method, but did not get any further. I can write the file and use treeForPublic(). This only runs once though, on initial build. It partially solves my problem, because I get the files into app dist folder. But I don't think ember serve will re-run treeForPublic on subsequent file change in the app.
Is there a way to ignore specific files from file watch? Yet still allow files to include into the build? Maybe there's an exclude watch property in ember-cli-build?
Here's my treeForPublic() , but I'm guessing my problems aren't here:
treeForPublic: function() {
const publicTree = this._super.treeForPublic.apply(this, arguments);
const trees = [];
if (publicTree) {
trees.push(publicTree);
}
// this writes out the json
this.saveSettingsFile(this.pubSettingsFile, this.settings);
trees.push(new Funnel(this.addonPubDataPath, {
include: [this.pubSettingsFileName],
destDir: '/data'
}));
return mergeTrees(trees);
},
UPDATE 05/20/2019
I should probably make a new question at this point...
My goal here is to create an auto-increment build number that updates both on ember build and ember serve. My comments under #real_ates's answer below help explain why. In the end, if I can only use this on build, that's totally ok.
The answer from #real_ate was very helpful and solved the endless loop problem, but it doesn't run on ember serve. Maybe this just can't be done, but I'd really like to know either way. I'm currently trying to change environment variables instead of using treeforPublic(). I've asked that as a separate question about addon config() updates to Ember environment:
Updating Ember.js environment variables do not take effect using in-repo addon config() method on ember serve
I don't know if can mark #real_ate's answer as the accepted solution because it doesn't work on ember serve. It was extremely helpful and educational!
This is a great question, and it's often something that people can be a bit confused about when working with broccoli (I know for sure that I've been stung by this in the past)
The issue that you have is that your treeForPublic() is actually writing a file to the source directory and then you're using broccoli-funnel to select that new custom file and include it in the build. The correct method to do this is instead to use broccoli-file-creator to create an output tree that includes your new file. I'll go into more detail with an example below:
treeForPublic: function() {
const publicTree = this._super.treeForPublic.apply(this, arguments);
const trees = [];
if (publicTree) {
trees.push(publicTree);
}
let data = getSettingsData(this.settings);
trees.push(writeFile('/data/the-settings-file.json', JSON.stringify(data)));
return mergeTrees(trees);
}
As you will see the most of the code is exactly the same as your example. The two main differences are that instead of having a function this.saveSettingsFile() that writes out a settings file on disk we now have a function this.getSettingsData() that returns the content that we would like to see in the newly created file. Here is the simple example that we came up with when we were testing this out:
function getSettingsData() {
return {
setting1: 'face',
setting2: 'my',
}
}
you can edit this function to take whatever parameters you need it to and have whatever functionality you would like.
The next major difference is that we are using the writeFile() function which is actually just the broccoli-file-creator plugin. Here is the import that you would put at the top of the file:
let writeFile = require('broccoli-file-creator');
Now when you run your application it won't be writing to the source directory any more which means it will stop constantly reloading 🎉
This question was answered as part of "May I Ask a Question" Season 2 Episode 2. If you would like to see us discuss this answer in full you can check out the video here: https://youtu.be/9kMGMK9Ur4E

VSIX how to get current snapshot document name?

I have been trying to to create an extension that highlights specific line numbers for me in Visual Studio in the margins.
I manged to get my marking in the margins using predefined line number but for it to work properly I need to know what the current document FullName is (Path and filename)
After much googling I figured out how to do it with the sample code (which is not ideal)
DTE2 dte = (DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.15.0");
var activeDocument = dte.ActiveDocument;
var docName = activeDocument.Name;
var docFullName = activeDocument.FullName;
Now I know the problems here
is that is for specific version bases on the text
there is no way to select which instance (when running more than one VS)
It seems to be very slow
I have a feeling I should be doing this with MEF Attributes but the MS docs examples are so simple that they do not work for me. I scanned a few SO questions too and I just cannot get them to work. They mostly talk about Services.. which I do not have and have no idea how to get.
The rest of my code uses SnapshotSpans as in the example Extension of Todo_Classification examples which is great if you do NOT need to know the file name.
I have never done any extensions development. Please can somebody help me do this correctly.
You can use following code to get a file from a snapshot without any dependencies.
public string GetDocumentPath(Microsoft.VisualStudio.Text.ITextSnapshot ts)
{
Microsoft.VisualStudio.Text.ITextDocument textDoc;
bool rc = ts.TextBuffer.Properties.TryGetProperty(
typeof(Microsoft.VisualStudio.Text.ITextDocument), out textDoc);
if (rc && textDoc != null)
return textDoc.FilePath;
return null;
}
If you don't mind adding Microsoft.CodeAnalysis.EditorFeatures.Text to your project it will provide you with an extension method Document GetOpenDocumentInCurrentContextWithChanges() on the Microsoft.VisualStudio.Text.Snapshot class. (Plus many other Rosyln based helpers)
using Microsoft.CodeAnalysis.Text;
Document doc = span.Snapshot.GetOpenDocumentInCurrentContextWithChanges();

Create a file in terminal using swift script

I'm now playing with swift scripts and have bumped into something I cannot seem to find an answer to. Cutting to the chase, I'm trying to run an image comparison script that compares two images and outputs a file showing the differences. However, the problem is, I cannot seem to be able to force Xcode to create any sort of files, even a simple text ones as it prompts me an error:
touch: /Users/CurrentUser/Desktop/TestExport/somethingelse.txt: Operation not permitted
I've tried giving this folder a full permission access using "chmod 777" terminal line, sadly, with no avail.
The initial code I'm trying to execute is this:
#discardableResult
func shell(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
shell("touch", "/Users/CurrentUser/Desktop/TestExport/somethingelse.txt")
Do you have any ideas what can be done towards solving this? Thanks in advance!

Blueimp Server Side UploadHandler.php -> Where to put custom code

Just tried out the blueimp "out of the box" files.
With a few hurdles, I got the plugin to work on my site.
In my application, I want to store the uploaded files in specific directories based on the file name.
The PHP code to do this is pretty straight forward:
function StoreAudioFiles()
{
$TempFileName = $_FILES['file']['tmp_name'];
$OriginalFileName= $_FILES['file']['name'];
$TheFolderName=MyCustomFunction($OriginalFileName);
move_uploaded_file($TempFileName,$TheFolderName.$OriginalFileName);
}
I have no idea where to modify the 'out-of-the-box' file "UploadHandler.php" to insert my code.
Given the fact that the file is 55 pages long when opened in Word, any help would be appreciated.
David
I worked out a solution and am posting it here for others to use.
In the index.php file that comes with blueimp, add functions after the object is created. Here's what I did:
require('UploadHandler.php');
$upload_handler = new UploadHandler();
//Now Add whatever custom functionality you want from here on.
MoveFiles();
function MoveFiles()
{
$UploadDir="files/";
$TheHandle=opendir($UploadDir);
while (False !== ($FileName = readdir($TheHandle))) MoveThisFile($FileName);
}
function MoveThisFile($TheFileName)
{
if(strlen($TheFileName)<4) return;
$UploadFilePath='mysite/server/php/files/';
$TheFolderName=MyCustomFolderName($TheFileName);
$OriginalFileName=$UploadFilePath.$TheFileName;
$TargetFileName=$TheFolderName.$TheFileName;
rename($OriginalFileName,$TargetFileName);
}