Type NSArray cannot be downcast implicitly - ios

I'm working on converting my app over to swift but I'm running into a swift error I don't quite understand yet.
In Obj-c I have a for in loop here:
for (MYVisitor *visitor in visitors) {
if (visitor.boxState == MYVisitorBoxStateChatting) {
[self.chattingVisitors addObject:visitor];
}
else {
[self.unknownVisitors addObject:visitor];
}
}
In swift I have:
for visitor: MYVisitor in visitors {
if visitor.boxState == MYVisitorBoxStateChatting {
self.chattingVisitors.addObject(visitor)
}
else {
self.unknownVisitors.addObject(visitor)
}
}
I'm getting an error in swift that says: NSArray cannot be implicitly downcast to MYVisitor, did you mean to use as to force downcast?
Xcode recommends this change: for visitor: FHVisitor in visitors as MYVisitor {...
which then gives me this error: MYVisitor does not conform to protocol SequenceType

If visitors is an NSArray and it is guaranteed that all its elements are of
the MYVisitor type then you can cast the array to [MYVisitor]:
for visitor in visitors as [MYVisitor] {
// ...
}
Note that this will crash at runtime if any array element is not a MYVisitor.
Alternatively, you can check the array with
if let theVisitors = visitors as? [MYVisitor] {
for visitor in theVisitors {
println(visitor.name)
}
} else {
// This is not an array of MYVisitor objects.
}
Or you can check each array element:
for item in visitors {
if let visitor = item as? MYVisitor {
// ...
} else {
// This is not a MYVisitor.
}
}

Related

Cannot convert value of type 'inout NSNumber?' to expected argument type 'AutoreleasingUnsafeMutablePointer<AnyObject?>' error

I have this script use to check whether the *downloaded file from iCloud is available or not. But unfortunately I encountered error Cannot convert value of type 'inout NSNumber?' to expected argument type 'AutoreleasingUnsafeMutablePointer<AnyObject?>' in some lines of code. Please help me to solve this issue because it is my first time to create a code to check whether the downloaded file is available in the icloud or not.
Please refer to the image below as sample of the error and also codes are available below for your reference. Hope you could help me. Thank you.
Sample screenshot of error
//-------------------------------------------------------------------
// ダウンロードできるか判定 Judgment or can be downloaded
//-------------------------------------------------------------------
func downloadFileIfNotAvailable(_ file: URL?) -> Bool {
var isIniCloud: NSNumber? = nil
do {
try (file as NSURL?)?.getResourceValue(&isIniCloud, forKey: .isUbiquitousItemKey)
if try (file as NSURL?)?.getResourceValue(&isIniCloud, forKey: .isUbiquitousItemKey) != nil {
if isIniCloud?.boolValue ?? false {
var isDownloaded: NSNumber? = nil
if try (file as NSURL?)?.getResourceValue(&isDownloaded, forKey: .ubiquitousItemIsDownloadedKey) != nil {
if isDownloaded?.boolValue ?? false {
return true
}
performSelector(inBackground: #selector(startDownLoad(_:)), with: file)
return false
}
}
}
} catch {
}
return true
}
It looks like you copied and pasted some really old code. Besides, this is Swift, not Objective-C. Do not use NSURL or getResourceValue. Your code should look more like this:
if let rv = try file?.resourceValues(forKeys: [.isUbiquitousItemKey]) {
if let isInCloud = rv.isUbiquitousItem {
// and so on
}
}
And so on; the same pattern applied to other keys. Note that there is no .ubiquitousItemIsDownloadKey either. You can condense things like this:
if let rv = try file?.resourceValues(
forKeys: [.isUbiquitousItemKey, .ubiquitousItemDownloadingStatusKey]) {
if let isInCloud = rv.isUbiquitousItem {
if let status = rv.ubiquitousItemDownloadingStatus {
if status == .downloaded {
}
}
}
}

CloudKit CKError extension not available in Objective-C?

I read somewhere here that CKError is not available in Objective-C, and I concur. For instance, this extension is available in Swift.
#available(OSX 10.10, iOS 8.0, watchOS 3.0, *)
extension CKError {
/// Retrieve partial error results associated by item ID.
public var partialErrorsByItemID: [AnyHashable : Error]? { get }
/// The original CKRecord object that you used as the basis for
/// making your changes.
public var ancestorRecord: CKRecord? { get }
/// The CKRecord object that was found on the server. Use this
/// record as the basis for merging your changes.
public var serverRecord: CKRecord? { get }
/// The CKRecord object that you tried to save. This record is based
/// on the record in the CKRecordChangedErrorAncestorRecordKey key
/// but contains the additional changes you made.
public var clientRecord: CKRecord? { get }
/// The number of seconds after which you may retry a request. This
/// key may be included in an error of type
/// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
public var retryAfterSeconds: Double? { get }
}
The problem is that I need these objects in my Objective-C project.
I've somehow (I believe) managed to get the partialErrorsByItemID in Objective-C by making a category for NSError and a little comprehension of the documentation of CKError.h, like so:
CKErrorCode ckErrorCode = (CKErrorCode) _code;
if (ckErrorCode == CKErrorPartialFailure) {
// When a CKErrorPartialFailure happens this key will be set in the error's userInfo dictionary.
// The value of this key will be a dictionary, and the values will be errors for individual items with the keys being the item IDs that failed.
NSDictionary *dicError = _userInfo;
if ([dicError objectForKey:CKPartialErrorsByItemIDKey] != nil) {
NSDictionary *dic = (NSDictionary *)[dicError objectForKey:CKPartialErrorsByItemIDKey];
for (NSString* key in dic) {
NSError *newError = dic[key];
if (code == newError.code) {
match = YES;
}
}
} else {
return NO;
}
}
But again, my problem is how to get the objects serverRecord and the clientRecord. Any idea?
Here's an Objective-C category that replicates most of the CKError structure of Swift. I didn't add errorCode, localizedDescription or errorUserInfo since NSError already provides those as code, localizedDescription, and userInfo.
CloudKitExtensions.h
#import <CloudKit/CloudKit.h>
NS_ASSUME_NONNULL_BEGIN
extern const double UnknownRetrySeconds;
#interface NSError (CKError)
- (NSDictionary<id, NSError *> * _Nullable)partialErrorsByItemID;
- (CKRecord * _Nullable)ancestorRecord;
- (CKRecord * _Nullable)clientRecord;
- (CKRecord * _Nullable)serverRecord;
- (double)retryAfterSeconds; // returns UnknownRetrySeconds if not available
#end
NS_ASSUME_NONNULL_END
CloudKitExtensions.m
#import "CloudKitExtensions.h"
const double UnknownRetrySeconds = -1;
#implementation NSError (CKError)
- (NSDictionary<id, NSError *> * _Nullable)partialErrorsByItemID {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorPartialFailure) {
return self.userInfo[CKPartialErrorsByItemIDKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)ancestorRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorAncestorRecordKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)clientRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorClientRecordKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)serverRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorServerRecordKey];
} else {
return nil;
}
}
- (double)retryAfterSeconds {
if ([self.domain isEqualToString:CKErrorDomain]) {
NSNumber *delayVal = self.userInfo[CKErrorRetryAfterKey];
return delayVal ? [delayVal doubleValue] : UnknownRetrySeconds;
} else {
return UnknownRetrySeconds;
}
}
#end

How to remove duplicate object in array?

I have a VC where I fetch data for only the 1st object in an array of objects. so I only fetch arrayOfObjects[0] then when I enter a second VC I need to fetch all the other data associated with the other objects in that same array. But then I have a problem where I end up fetching that first bit of data I already had again. So my array would look like, data1, data1, data2, data3 ... which is not what I want of course.
Currently what I had tried to fix this issue, was to do the following: MainObject?.arrayOfSubObjects.remove(at: 0), this however means that on the first go it works well, but every time I go back to the preceding VC and then back I subtract one of the objects that I want to be there. So I end up with: data2, data3 ...
So my question is how can I remove that extra object from the beginning, but not delete anything after its been deleted?
Some things i have tried:
if selectedPost?.media[0].videoURL != nil {
if selectedPost?.media[0].videoURL == selectedPost?.media[1].videoURL {
selectedPost?.media.remove(at: 0)
} else {
print("NO!!!!!! they are not the same ")
}
} else if selectedPost?.media[0].image != nil {
if selectedPost?.media[0].image == selectedPost?.media[1].image {
selectedPost?.media.remove(at: 0)
} else {
print("NO!!! they are not the same ")
}
}
This however does not do anything, it always ends up going into the else. I have also tried stuff like setting number schemes, but this failed because the VC kept reloading
You can try to do it like this:
guard let selectedPost = selectedPost else { return }
if selectedPost.media.contains(where: {$0.image == image}) { return } // Here the $0.image should be replaced with $0.mediaURL == mediaURL for the videos or however you videoURL is called in Media
selectedPost.media.append(Media(image: image, timeStamp: Double(timeStamp)))
self.tableView.reloadData()
Try conforming Hashable in you Media class:
class Media : Hashable {
var hashValue : Int {
return image.hashValue
}
var image = UIImage()
// .....
static func == (lhs: Media, rhs: Media) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
Then you can compare two media objects with ==, So you can do something similar to the top part of this answer. Good luck

Cannot access more than one value from function using PromiseKit Swift

TemplateClass.m
+ (AnyPromise *) promisefunctionReturnThreeValus:(NSString *)sampleName {
return [self anotherPromiseFunction:sampleName].then(^(NSMutableDictionary *sampleDict) {
DataArray *data = [DataArray dataArrayFromDict:sampleDict];
PropertyArray *property = [PropertyArray PropertyArrayFromDict:sampleDict];
if ([sampleDict objectForKey:NAME])
{
NameModel *name = [[NameModel alloc]initWithDictionary:[responseDict objectForKey:NAME]];
return (PMKManifold(data,property,name));
}
else
{
return (PMKManifold(data,property,nil));
}
});
}
well i can able to access this from objc using the below code
[TemplateClass promisefunctionReturnThreeValus:#"hello"].then(^(DataArray *data,PropertyArray *property,NameModel *name) {
//Here i can able to access the three values data,property and name
}
But when i try to access this from swift
TemplateClass.promisefunctionReturnThreeValus(sampleName: "hello").then{ data,property,name in
// it show me error " Contextual closure type '(Any?) -> AnyPromise' expects 1 argument, but 3 were used in closure body "
}
i can able to access only data but not the other two
i also tried debug it and print through log it show only the data of DataArray Object
lldb output
<DataArray : 0x1c0631340 count:1 value:{
"id" = 3631;
}
>

Backendless - How To Get Objects From 'Data'

How do I get all the objects from Backendless's database into a UITableView in my iOS app?
Looking at their Documentation, it doesn't clearly state how to get all objects. (I'm new to the platform)
Any help would be appreciated!
Here's how I do it in Swift (for my table of Blurb objects):
func retrieveBlurbs() {
let query = BackendlessDataQuery()
// Use backendless.persistenceService to obtain a ref to a data store for the class
let dataStore = self.backendless.persistenceService.of(Blurb.ofClass()) as IDataStore
dataStore.find(query, response: { (retrievedCollection) -> Void in
print("Successfully retrieved: \(retrievedCollection)")
self.blurbs = retrievedCollection.data as! [Blurb]
self.tableView.reloadData()
}) { (fault) -> Void in
print("Server reported an error: \(fault)")
}
}
I am also new to Backendless and really enjoying it! It's a lot like Parse, but better in a bunch of ways.
Start with this:
https://backendless.com/feature-16-data-retrieval-api-how-to-load-objects-from-an-mbaas-storage/
Then move on to this: https://backendless.com/feature-17-data-paging-or-how-to-efficiently-load-large-data-sets-in-a-mobile-app/
Both articles include concrete examples in Swift.
Try this:
- (void)viewDidLoad {
[super viewDidLoad];
[self getDataFromBackendless];
}
-(void)getDataFromBackendless {
#try {
BackendlessCollection *documents = [backendless.persistenceService of:[YOUR_TABLE_NAME class]];
currentPage =[documents getCurrentPage];
}
#catch (Fault *fault) {
NSLog(#"Server reported an error: %#", fault);
}
}
Then perform UITableView methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [currentPage count];
}

Resources