I have a swift project that uses an array of pictures. The array of pictures is kept in storage in the filmanager.default.urls path. The URLs are then kept in a SQLite database. I am able to add to the array without a problem, however, I run into an issue whenever I try to recall it from memory. I do not know if I am saving it wrong or loading it wrong. I will post the code for how I did that here.
func save(images: Array<UIImage>, identifier: String) -> Array<String> {
var picCounter = 0
var URLs: Array<String> = []
for pic in images {
let id = identifier + "makeSureThisCantAccidentallyBeAnID" + String(picCounter)
let jpgImageData = pic.jpegData(compressionQuality: 0.5)
let documentURL = fileManager.urls(for: .documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first
let path = documentURL!.appendingPathComponent(id + ".png")
do {
try jpgImageData!.write(to: path)
} catch {
print(error)
}
picCounter += 1
URLs.append(path.absoluteString)
}
return URLs
}
That is my save function. It should store the URLs and return an array of URLs in string format.
Here is my loading function, it should take an array of URLs and return the array of images.
func recover(URLarray: Array<String>, identifier: String) -> Array<UIImage> {
if URLarray[0] == "fake url" {
let pic = #imageLiteral(resourceName: "LaunchScreen")
let fakeArray = [pic]
return fakeArray
}
var picCounter = 0
var imageArray: Array<UIImage> = []
for url in URLarray {
//let fileData = FileManager.default.contents(atPath: url)
guard let image = UIImage(contentsOfFile: url) else { return imageArray }
picCounter += 1
imageArray.append(image)
}
return imageArray
}
The "#imageliteral" is a literal image that is used instead of an array if there is no array to load. This should theoretically recall every image without returning nil. Lastly, I will post my SQLite saving function
func saveNote(note: Note) {
connect()
var statement: OpaquePointer? = nil
if sqlite3_prepare_v2(
database,
"UPDATE remember SET person = ?, memories = ?, imageurl = ? WHERE rowid = ?",
-1,
&statement,
nil
) == SQLITE_OK {
sqlite3_bind_text(statement, 1, NSString(string: note.person).utf8String, -1, nil)
sqlite3_bind_text(statement, 2, NSString(string: note.memories).utf8String, -1, nil)
var imageURL: String = ""
for url in note.imageURLs {
imageURL.append(url)
imageURL.append("#")
}
sqlite3_bind_text(statement, 3, NSString(string: imageURL).utf8String, -1, nil)
sqlite3_bind_int(statement, 4, note.id)
if sqlite3_step(statement) != SQLITE_DONE {
print("Error saving note")
}
}
else {
print("Error creating note update statement")
}
sqlite3_finalize(statement)
}
This is how I tried to save it. I had to store a URL array as one string so I decided to do so by making it a string I could split with the character "#" if you think that is the problem then please tell me what character or method I could use to do this, as I have tried changing the character. If anyone helps me this far then I am extremely grateful! Thank you!
Related
I trying to make a SwiftUI app where after entering one letter in a TextField the cursor automatically moves to the next TextField. The UI is pretty much like this.
In Swift/IB, it looks like this was done with delegates and adding a target like in this post:
How to move to the next UITextField automatically in Swift
But can't find any documentation for using delegates/targets in SwiftUI.
I tried following this post:
SwiftUI TextField max length
But this has not worked for me. Setting the .prefix(1) does not seem to make a difference. The TextField still accepts any amount of characters and when moved to the next TextField does not reduce the characters entered to only the first character.
In SwiftUI's current state, is it possible to automatically move to the next TextField after 1 character is entered?
Thanks for any help!
It can be done in iOS 15 with FocusState
import SwiftUI
///Sample usage
#available(iOS 15.0, *)
struct PinParentView: View {
#State var pin: Int = 12356
var body: some View {
VStack{
Text(pin.description)
PinView(pin: $pin)
}
}
}
#available(iOS 15.0, *)
struct PinView: View {
#Binding var pin: Int
#State var pinDict: [UniqueCharacter] = []
#FocusState private var focusedField: UniqueCharacter?
var body: some View{
HStack{
ForEach($pinDict, id: \.id, content: { $char in
TextField("pin digit", text:
Binding(get: {
char.char.description
}, set: { newValue in
let newest: Character = newValue.last ?? "0"
//This check is only needed if you only want numbers
if Int(newest.description) != nil{
char.char = newest
}
//Set the new focus
DispatchQueue.main.async {
setFocus()
}
})
).textFieldStyle(.roundedBorder)
.focused($focusedField, equals: char)
})
}.onAppear(perform: {
//Set the initial value of the text fields
//By using unique characters you can keep the order
pinDict = pin.description.uniqueCharacters()
})
}
func setFocus(){
//Default to the first box when focus is not set or the user reaches the last box
if focusedField == nil || focusedField == pinDict.last{
focusedField = pinDict.first
}else{
//find the index of the current character
let idx = pinDict.firstIndex(of: focusedField!)
//Another safety check for the index
if idx == nil || pinDict.last == pinDict[idx!]{
focusedField = pinDict.first
}else{
focusedField = pinDict[idx! + 1]
}
}
//Update the Binding that came from the parent
setPinBinding()
}
///Updates the binding from the parent
func setPinBinding(){
var newPinInt = 0
for n in pinDict{
if n == pinDict.first{
newPinInt = Int(n.char.description) ?? 0
}else{
newPinInt = Int(String(newPinInt) + n.char.description) ?? 0
}
}
pin = newPinInt
}
}
//Convert String to Unique characers
extension String{
func uniqueCharacters() -> [UniqueCharacter]{
let array: [Character] = Array(self)
return array.uniqueCharacters()
}
func numberOnly() -> String {
self.trimmingCharacters(in: CharacterSet(charactersIn: "-0123456789.").inverted)
}
}
extension Array where Element == Character {
func uniqueCharacters() -> [UniqueCharacter]{
var array: [UniqueCharacter] = []
for char in self{
array.append(UniqueCharacter(char: char))
}
return array
}
}
//String/Characters can be repeating so yu have to make them a unique value
struct UniqueCharacter: Identifiable, Equatable, Hashable{
var char: Character
var id: UUID = UUID()
}
#available(iOS 15.0, *)
struct PinView_Previews: PreviewProvider {
static var previews: some View {
PinParentView()
}
}
I am using Swift(2.2) Realm Framework as doing with document. Here is my codes.
class SwipedAsset: Object{
dynamic var identifier = ""
dynamic var createdAt = ""
}
// save data
let realm = try! Realm()
let fileName = asset.originalFilename
if fileName != nil {
let swipedAssets = realm.objects(SwipedAsset.self).filter("identifier == '\(fileName!)'")
let assetCount = swipedAssets.count
if assetCount == 0 {
let swipedAsset = SwipedAsset()
if asset.originalFilename != nil {
swipedAsset.identifier = asset.originalFilename!
}
if asset.creationDate != nil {
let year = String(asset.creationDate!.year)[2...3]
let key = "\(asset.creationDate!.monthName) \(year)"
swipedAsset.createdAt = key
}
let realm = try! Realm()
try! realm.write{
realm.add(swipedAsset)
}
}
}
// load data
let realm = try! Realm()
let swipedAssets = realm.objects(SwipedAsset.self).filter("createdAt == '\(key)'")
let lastObject = swipedAssets.last
print(lastObject.identifier)
print(lastObject.createdAt)
Here, values are all "", "" Nothing, But swipedAssets.count = 3 I thought it means realm's query is working properly.
What's wrong with me ? Thanks for any help.
Please do not try to debug with breakpoint.
I just downloaded XCode Beta 7 and received the error "Type 'String' does not conform to protocol 'CollectionType'". This is my first attempt at coding, so I'm not sure how to fix this. Thank you!!!
//the Pasteboard is nil if full access is not granted
let pbWrapped: UIPasteboard? = UIPasteboard.generalPasteboard()
if let pb = pbWrapped {
var type = UIPasteboardTypeListImage[0] as! String
if (count(type) > 0) && (image != nil) {
pb.setData(UIImagePNGRepresentation(image!)!, forPasteboardType: type)
var readDataWrapped: NSData? = pb.dataForPasteboardType(type)
if let readData = readDataWrapped {
var readImage = UIImage(data: readData, scale: 2)
print("\(image) == \(pb.image) == \(readImage)")
}
}
}
Change it to:
type.characters.count
Your code should then read:
//the Pasteboard is nil if full access is not granted
let pbWrapped: UIPasteboard? = UIPasteboard.generalPasteboard()
if let pb = pbWrapped {
var type = UIPasteboardTypeListImage[0] as! String
if (type.characters.count > 0) && (image != nil) {
pb.setData(UIImagePNGRepresentation(image!)!, forPasteboardType: type)
var readDataWrapped: NSData? = pb.dataForPasteboardType(type)
if let readData = readDataWrapped {
var readImage = UIImage(data: readData, scale: 2)
print("\(image) == \(pb.image) == \(readImage)")
}
}
}
Just a suggestion for where to start learning about this issue... https://developer.apple.com/swift/blog/?id=30
Strings in Swift 2
I have a question about saving Arrays in Apple's new programming language Swift. In Objective-C I saved data with NSFileManager... but this doesn't work anymore in Swift. So I wanted to ask how I should save an array WITHOUT using NSUserDefaults which isn't really suited for storing a big amount of data. I would really much appreciate any help :]
First (if your array is not of string type) change it to String:
var notStringArray = [1, 2, 3, 4]
var array: [String] = []
for value in notStringArray{
array.append(String(value))
}
Then reduce the array to one string:
var array = ["1", "2", "3", "4", "5"] //Ignore this line if your array wasn't of type String and you did the step above
var stringFromArray = reduce(array, "") { $0.isEmpty ? $1 : "\($0)\n\($1)" }
This create an string that looks like this:
"1
2
3
4
5"
And then to write and read a file add this class at the top of your file:
class File {
class func open (path: String, utf8: NSStringEncoding = NSUTF8StringEncoding) -> String? {
var error: NSError? //
return NSFileManager().fileExistsAtPath(path) ? String(contentsOfFile: path, encoding: utf8, error: &error)! : nil
}
class func save (path: String, fileContent: String, utf8: NSStringEncoding = NSUTF8StringEncoding) -> Bool {
var error: NSError? //
return fileContent.writeToFile(path, atomically: true, encoding: utf8, error: &error)
}
}
(Don't forget to import UIKit)
To save to a file:
let didSave = File.save("DirectoryOfFile", content: stringFromArray)
if didSave {
println("file saved")
} else {
println("error saving file")
}
To get it back:
var stringFromFile = ""
if let loadData = File.open("DirectoryOfFile") {
stringFromFile = loadData
} else {
println("error reading file")
}
To put it back in an array:
var newArray: [String] = [] //Creates empty string array
newArray = stringFromFile.componentsSeparatedByString("\n")
And there you have it
I was trying to access temp directory in Swift. In Objective-C, I could use the following code to do so:
- (NSString *)tempDirectory {
NSString *tempDirectoryTemplate =
[NSTemporaryDirectory() stringByAppendingPathComponent:#"XXXXX"];
const char *tempDirectoryTemplateCString = [tempDirectoryTemplate fileSystemRepresentation];
char *tempDirectoryNameCString = (char *)malloc(strlen(tempDirectoryTemplateCString) + 1);
strcpy(tempDirectoryNameCString, tempDirectoryTemplateCString);
char *result = mkdtemp(tempDirectoryNameCString);
if (!result) {
return nil;
}
NSString *tempDirectoryPath = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempDirectoryNameCString length:strlen(result)];
free(tempDirectoryNameCString);
return tempDirectoryPath;
}
However, I'm a bit confuse about the type conversion and casting from Objective-C to Swift, such as const char * or CMutablePointer<CChar>. Is there any documents that I should look into?
Thanks.
How about something like :
public extension FileManager {
func createTempDirectory() throws -> String {
let tempDirectory = (NSTemporaryDirectory() as NSString).appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(atPath: tempDirectory,
withIntermediateDirectories: true,
attributes: nil)
return tempDirectory
}
}
It doesn't answer your question about char* but it's cleaner...
NSFileManager reference here.
Also check out this SO question regarding unique names.
According to Apple, use of NSTemporaryDirectory is discouraged:
See the FileManager method url(for:in:appropriateFor:create:) for the
preferred means of finding the correct temporary directory. For more
information about temporary files, see File System Programming Guide.
So instead, you should use FileManager.default.temporaryDirectory
or if you want an unique path:
let extractionPath = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
Swift 2.1 version:
func createTempDirectory() -> String? {
let tempDirURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("XXXXXX")
do {
try NSFileManager.defaultManager().createDirectoryAtURL(tempDirURL, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
return tempDirURL.absoluteString
}
Swift 3 and up
I think a good way to do this in swift is with an extension on FileManager. This should create a unique temporary folder and return the URL to you.
extension FileManager{
func createTemporaryDirectory() throws -> URL {
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString)
try createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
return url
}
}
Swift 3 version
func createTempDirectory() -> String? {
guard let tempDirURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("myTempFile.xxx") else {
return nil
}
do {
try FileManager.default.createDirectory(at: tempDirURL, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
return tempDirURL.absoluteString
}
A direct translation of your Objective-C code to Swift would be:
func tempDirectory()->String! {
let tempDirectoryTemplate = NSTemporaryDirectory() + "XXXXX"
var tempDirectoryTemplateCString = tempDirectoryTemplate.fileSystemRepresentation().copy()
let result : CString = reinterpretCast(mkdtemp(&tempDirectoryTemplateCString))
if !result {
return nil
}
let fm = NSFileManager.defaultManager()
let tempDirectoryPath = fm.stringWithFileSystemRepresentation(result, length: Int(strlen(result)))
return tempDirectoryPath
}
It uses the same mkdtemp() BSD method as your original code. This method creates
a directory name from the template which is guaranteed not to exist at the time where
the method is called.
Thanks to Nate Cook who figured out that reinterpretCast() can be used to treat the UnsafePointer<CChar> returned by mkdtemp() as a CString, so that it can be passed to stringWithFileSystemRepresentation(), see Working with C strings in Swift, or: How to convert UnsafePointer<CChar> to CString.
As of Xcode 6 beta 6, the reinterpretCast() is not necessary anymore and the
above code can be simplified to
func tempDirectory()->String! {
let tempDirectoryTemplate = NSTemporaryDirectory() + "XXXXX"
var tempDirectoryTemplateCString = tempDirectoryTemplate.fileSystemRepresentation()
let result = mkdtemp(&tempDirectoryTemplateCString)
if result == nil {
return nil
}
let fm = NSFileManager.defaultManager()
let tempDirectoryPath = fm.stringWithFileSystemRepresentation(result, length: Int(strlen(result)))
return tempDirectoryPath
}