Checking if NSData exist or not in swift - ios

I have an NSData object stored in my core data. I want to be able to check if it exist or not using an if statement. I can't seem to work it out. I have the variable set up as:
var coreImage : NSData?
and I have tried using:
if (coreImage != nil) {
println("Use core image")
}else {
println("Do something else")
}
I know I have NSData stored in core data but it never runs the if statement as I want it too so I must be doing something wrong?
Can anyone help?
Thanks.

Check if NSData's length is > 0, that's it.
Example:
if (coreImage.length > 0) {
//we're cool
}

I am newer to Swift than you, but I think you need to do some special work to access and utilize items from Core Data. Try something like this:
let fetchRequest = NSFetchRequest(entityName: "coreImage")
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [NSData] {
println("Use core image")
} else {
println("Do something else")
}

Related

Storing OpaquePointer type in UserDefaults

I am working on Xcode 12 and Swift 5 environment to build an iOS application.
I need to store an OpaquePointer type variable("self.loggers" in the code below) before the view disappears(or before the app closes) and retrieve it when the view appears( when the app runs).
I tried to use UserDefault as below,
// Storing(In viewWillDisappear)
do {
encodedData = try NSKeyedArchiver.archivedData(withRootObject: self.loggers, requiringSecureCoding: false)
UserDefaults.standard.set(encodedData, forKey: "accelLoggerID")
} catch {
print("archiving error")
}
...
// Retrieving(In viewDidLoad)
if let decodedData = UserDefaults.standard.object(forKey: "acceleration") as? Data {
do {
self.loggers = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decodedData) as? [String:OpaquePointer] ?? [:]} catch {
print("unarchiving error")
}
} else {
self.loggers = [:]
print("No value in Userdefault. [viewDidLoad]")
}
However, NSKeyedArchiver failed to encode such type. After that, I made a class that wraps the variable.
class LoggerWrapper {
var loggers: [String : OpaquePointer]
init(loggerDic: [String : OpaquePointer]) {
loggers = loggerDic
}
}
And changed my code like
self.loggers = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decodedData) as? LoggerWrapper ?? LoggerWrapper(loggerDic: [:])} catch {
print("unarchiving error")
}
However, this gives SIGABRT when NSKeyedArchiver.archivedData is called.
Is there any way to store Opaquepointer type in UserDefaults? If not, can using core data solve this problem?
Thank you.
You probably need to convert the pointers to Int bitPatterns. There is an initializer on Int for that: let intBitPattern = Int(bitPattern: opaquePointer).
There is also an initializer on OpaquePointer that takes an Int bitPattern: let opaquePointer = OpaquePointer(bitPattern: intBitPattern).
So you would need to convert the values in your dictionary (e.g. using compactMapValues) before storing and after reading it.
With a Dictionary<String, Int>, you should be able to store it directly in UserDefaults, without the need for NSKeyedArchiver and NSKeyedUnarchiver.
Sidenote:
Pointers aren't "long-lived". So storing it in UserDefaults is only valid during the lifetime of your app. Once your app restarts, the pointers might no longer be valid.

aws dynamodb how to use object mapper with batch get in ios swift

Thanks in advance for any help. I am trying to get Batch items (Load multiple) items from one DynamoDb table using the AWS iOS SDK (Swift). I can load one item using the Block syntax, but I need to load 10 or more than that. I don't want to use 10 Block calls to load them individually. I tried to follow the attach stackoverflow Link (where the similar solution is given) but I am getting the following compiler error message. I come from Java background, hence could also be a syntax issue. Is it the right way to load multiple items? I don't want to use low level API. Any help, where I am going wrong. Thanks.
aws dynamodb how to use object mapper with batch get in ios
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
var tasksList = Array<AWSTask<AnyObject>>()
for i in 1...10 {
tasksList.append(dynamoDBObjectMapper.load(AWSCards.self, hashKey: "SH_"+String(i), rangeKey: nil))
}
AWSTask.init(forCompletionOfAllTasksWithResults: tasksList).continueWithBlock { (task) -> AnyObject? in
if let cards = task.result as? [AWSCards] {
print(cards.count)
}
else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
Have a try with the following codes (Swift 4.1, Feb 9th, 2018):
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
var tasksList = Array<AWSTask<AnyObject>>()
for i in 1...10 {
tasksList.append(dynamoDBObjectMapper.load(AWSCards.self, hashKey: "SH_"+String(i), rangeKey: nil))
}
AWSTask<AnyObject>.init(forCompletionOfAllTasksWithResults: tasksList).continueWith { (task) -> Any? in
if let cards = task.result as? [AWSCards] {
print(cards.count)
}
else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
Your question is "how to use the object mapper" but it might be more efficient for you to not use it.
However, there is a way to use it. See Niklas's answer here and here (he copy & pasted), but something about it strikes me as fishy. I want to make the assertion that it is not as fast as the built-in batch-get function, but I am unsure. I suspect that this does not complete the items in parallel, or at least not as efficiently as in BatchGetItem.
See the docs: "In order to minimize response latency, BatchGetItem retrieves items in parallel."
According to Yosuke, "Currently, AWSDynamoDBObjectMapper does not support the batch get item. You need to load one item at a time if you want to use the object mapper" as of 2016. This still seems to be the case. I am using a version a couple versions behind, but not too far behind. Someone check.
In conclusion, if you are loading one item at a time, you are likely missing out on the whole purpose of BatchGetItem (low latency).
Pulling from various sources, including John Davis's question here, I have tested and ran this BatchGetItem result. Here ya go.
import AWSDynamoDB
let primaryKeyToSortKeyDict : [String : String] = .... // Your stuff
var keys = [Any]()
for key in primaryKeyToSortKeyDict.keys {
let partitionKeyValue = AWSDynamoDBAttributeValue()
partitionKeyValue?.s = String(key)
let sortValue = AWSDynamoDBAttributeValue()
sortValue?.s = String(primaryKeyToSortKeyDict[key]!)
keys.append(["partitionKeyAttributeName": partitionKeyValue, "sortKeyAttributeName": sortValue])
}
let keysAndAttributesMap = AWSDynamoDBKeysAndAttributes()
keysAndAttributesMap?.keys = keys as? [[String : AWSDynamoDBAttributeValue]]
keysAndAttributesMap?.consistentRead = true
let tableMap = [table : keysAndAttributesMap]
let request = AWSDynamoDBBatchGetItemInput()
request?.requestItems = tableMap as? [String : AWSDynamoDBKeysAndAttributes]
request?.returnConsumedCapacity = AWSDynamoDBReturnConsumedCapacity.total
guard request != nil else {
print("Handle some error")
return
}
AWSDynamoDB.default().batchGetItem(request!) { (output, error) in
print("Here is the batchgetitem output")
if error == nil {
// do output stuff
} else {
// handle error
}
}

Swift - Parse Check if PFFile is cached

Considering I'm using PFImageViews, with caching enabled, I would like to know if there's a way to determine whether an image has already been downloaded or not.
All in all, I want to say:
if imageAlreadyDownloaded {
...
}
else {
...
}
Is it possible?
So, I finally found the solution to my own problem! Every PFFile, has a boolean property called "isDataAvailable".
With a bit of code we can have the following solution:
let imageFile = file as? PFFile
if imageFile.isDataAvailable {
...
}
else {
...
}
And done! ;-)
I think you will have to roll your own solution using the PFImageView and the loadInBackground method that has a completion handler.
Something like:
// Instance property on your UIViewController
private var imageAlreadyDownloaded = false
// Somewhere else in your UIViewController...
imageView.loadInBackground() {
[unowned self] (image, error) in
guard error == nil else {
return
}
self.imageAlreadyDownloaded = true
}

Realm best practices: How to handle asynchronous HTTP object update?

I have a model which is a swift object.
I retrieve data from the web and then I need to update my object but there are different cases to handle:
I create an object, fetch the data, update the properties, save it in realm
I create an object, save it in realm, fetch the data, update the properties, save it again
I create an object, save it in realm, start to fetch the data, delete it from realm, receive the data, do nothing.
And this is how I handle it:
If self.invalidated == false & self.realm == nil -> update the properties on self
If self.invalidated == false & self.realm != nil -> Fetch the object from Realm in a background thread, set the properties, Refresh Realm on main thread before completion
If self.invalidated == true -> Stop (object has been deleted so it's not needed anymore)
One solution to simplify this code is to save the object in realm, but I don't want to save an object that could be dirtier than a potential one in realm. Or I could fetch whatever I have in realm before fetching the data online, so that I'm sure I save something at least as dirty as one in realm (but performance is not as optimal as it could be)
Could you give me some insight about what is the cleanest way to handle such a case?
Here is my code at the moment:
func fetchDataOnline(completion:(success : Bool)->()){
let params = ["tmdb_id":self.tmdbId,"lang":kLang]
let tmdbId = self.tmdbId
let invoker = AWSLambdaInvoker.defaultLambdaInvoker()
invoker.invokeFunction("getMovie", JSONObject: params).continueWithBlock { (task) -> AnyObject? in
guard self.invalidated == false else{
DDLogWarn("Movie has been invalidated while fecthing data")
completion(success: false)
return nil
}
if let dic = task.result as? NSDictionary{
var objectToUpdate = self
if self.realm != nil{ //Use new realm instance
guard let newRealmInstance = try! Realm().objectForPrimaryKey(MovieNew.self, key: tmdbId) else{
DDLogError("self.realm not nil but can't find movie in realm")
completion(success: false)
return nil
}
objectToUpdate = newRealmInstance
}
try! Realm().write{
objectToUpdate.setProperties(dic: dic)
objectToUpdate.lastUpdate = NSDate()
}
}
else{ //No dictionary found from result
if let error = task.error{
DDLogError(error.description)
}
DDLogError("Error getting movie")
}
Async.main{
try! Realm().refresh()
completion(success : task.error == nil)
}
return nil
}
}
Given your specific use-case scenarios, I think this looks like the best way to go about doing it. While you could do state tracking within the Realm objects themselves, it's much better to simply track their status against their parent Realm objects and respond to that accordingly.
The main best practice for Realm is mainly to try and minimize the number of write transactions as much as possible., so the only thing I could potentially question here is if it's absolutely necessary to add an object to a Realm before performing the request, only to potentially delete it again before the download is complete. If that's necessary because you're using those objects as placeholders in your UI, then that's perfectly fine.
Either way, all of this is really a matter of opinion. This solution you've put forward is competent and fills all of your requirements, so I'm not sure if it's worth trying to find something better. :)

Delete duplicated object in core data (swift)

I'm saving objects to core data from a JSON, which I get using a for loop (let's say I called this setup function.
Because the user might stop this loop, the objects saved in core data will be partial. The user can restart this setup function, restarting the parsing and the procedure to save object to core data.
Now, I'm getting duplicated objects in core data if I restart the setup().
The object has an attribute which is id.
I've thought I could fetch first objects that could eventually already exist in core data, save them to an array (a custom type one), and test for each new object to add to core data if already exist one with the same id.
The code used is the following:
if !existingCards.isEmpty {
for existingCard in existingCards {
if id == existingCard.id {
moc.deleteObject(existingCard)
println("DELETED \(existingCard.name)")
}
}
}
...
// "existingCards is the array of object fetched previously.
// Code to save the object to core data.
Actually, the app return
EXC_BAD_ACCESS(code=1, address Ox0)
Is there an easier way to achieve my purpose or what should I fix to make my code work? I'm quite new to swift and I can't figure other solution.
The main purpose is to delete duplicated core data, BTW.
Swift 4 code to delete duplicate object:
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Card")
var resultsArr:[Card] = []
do {
resultsArr = try (mainManagedObjectContext!.fetch(fetchRequest) as! [Card])
} catch {
let fetchError = error as NSError
print(fetchError)
}
if resultsArr.count > 0 {
for x in resultsArr {
if x.id == id {
print("already exist")
mainManagedObjectContext.deleteObject(x)
}
}
}
At the end, I managed to make it work.
I had to rewrite my code, because I realized moc.deleteObject() works with a fetch before, which in my previous code wasn't in the same function, but it was in viewDidLoad().
// DO: - Fetch existing cards
var error: NSError?
var fetchRequest = NSFetchRequest(entityName: "Card")
if let results = moc.executeFetchRequest(fetchRequest, error: &error) as? [Card] {
if !results.isEmpty {
for x in results {
if x.id == id {
println("already exist")
moc.deleteObject(x)
}
}
}
} else {
println(error)
}
No more existingCards, the result of the the fetch is now processed as soon as possible. Something isn't clear to me yet, but now my code works. If you have any improvements/better ways, they're welcome.
P.S.: I actually found Apple reference useful but hard to understand because I don't know Obj-C. Often I can figure what the code do, but in swift functions and properties are a bit different.

Resources