I am working with NSCache in iOS i have following code in .h file for cache:
NSCache *_cache;
I am adding the image which are downloading from the url to the cache in .m:
-(void)cacheFromURL:(NSURL*)url
{
[_cache setCountLimit:50];
UIImage* newImage = [_cache objectForKey:url.description];
if( !newImage )
{
NSError *err = nil;
if(url!=Nil)
{
newImage=[UIImage imageWithData:[NSData dataWithContentsOfURL:url options:0 error:&err]];
}
if( newImage )
{
[_cache setValue:newImage forKey:url.description];
}
else
{
NSLog( #"UIImageView:LoadImage Failed: %#", err );
}
if(_cache==Nil)
{
NSLog(#"nil");
}
else
{
NSLog(#"cache is not nil");
}
}
}
But i am getting the nil cache every time . where i can see the downloading process in log .
why am i getting null cache?
You need to create/initialize the NSCache object before you can add and object to it. Also you need to add object with the setObject:forKey:
You have to initialize your NSCache object first:
_cache = [[NSCache alloc] init];
Related
Hi I have integrated google Dive with my app using Dr. Edit sample code from google drive. But i am not able to view all the files, which are stored in my Google Drive account.
// I have tried this
-(void)getFileListFromSpecifiedParentFolder
{
GTLQueryDrive *query2 = [GTLQueryDrive queryForChildrenListWithFolderId:#"root"];
query2.maxResults = 1000;
[self.driveService executeQuery:query2
completionHandler:^(GTLServiceTicket *ticket,
GTLDriveChildList *children, NSError *error)
{
NSLog(#"\nGoogle Drive: file count in the folder: %d", children.items.count);
if (!children.items.count)
{
return ;
}
if (error == nil)
{
for (GTLDriveChildReference *child in children)
{
GTLQuery *query = [GTLQueryDrive queryForFilesGetWithFileId:child.identifier];
[self.driveService executeQuery:query completionHandler:^(GTLServiceTicket *ticket,
GTLDriveFile *file,
NSError *error)
{
NSLog(#"\nfile name = %#", file.originalFilename);}];
}
}
}];
}
//I want to Display All content in NSLog...
1. How to get all files from Google Drive.
First in viewDidLoad: method check for authentication
-(void)viewDidLoad
{
[self checkForAuthorization];
}
And here is the definition of all methods:
// This method will check the user authentication
// If he is not logged in then it will go in else condition and will present a login viewController
-(void)checkForAuthorization
{
// Check for authorization.
GTMOAuth2Authentication *auth =
[GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
clientID:kClientId
clientSecret:kClientSecret];
if ([auth canAuthorize])
{
[self isAuthorizedWithAuthentication:auth];
}
else
{
SEL finishedSelector = #selector(viewController:finishedWithAuth:error:);
GTMOAuth2ViewControllerTouch *authViewController =
[[GTMOAuth2ViewControllerTouch alloc] initWithScope:kGTLAuthScopeDrive
clientID:kClientId
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:finishedSelector];
[self presentViewController:authViewController animated:YES completion:nil];
}
}
// This method will be call after logged in
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController finishedWithAuth: (GTMOAuth2Authentication *)auth error:(NSError *)error
{
[self dismissViewControllerAnimated:YES completion:nil];
if (error == nil)
{
[self isAuthorizedWithAuthentication:auth];
}
}
// If everthing is fine then initialize driveServices with auth
- (void)isAuthorizedWithAuthentication:(GTMOAuth2Authentication *)auth
{
[[self driveService] setAuthorizer:auth];
// and finally here you can load all files
[self loadDriveFiles];
}
- (GTLServiceDrive *)driveService
{
static GTLServiceDrive *service = nil;
if (!service)
{
service = [[GTLServiceDrive alloc] init];
// Have the service object set tickets to fetch consecutive pages
// of the feed so we do not need to manually fetch them.
service.shouldFetchNextPages = YES;
// Have the service object set tickets to retry temporary error conditions
// automatically.
service.retryEnabled = YES;
}
return service;
}
// Method for loading all files from Google Drive
-(void)loadDriveFiles
{
GTLQueryDrive *query = [GTLQueryDrive queryForFilesList];
query.q = [NSString stringWithFormat:#"'%#' IN parents", #"root"];
// root is for root folder replace it with folder identifier in case to fetch any specific folder
[self.driveService executeQuery:query completionHandler:^(GTLServiceTicket *ticket,
GTLDriveFileList *files,
NSError *error) {
if (error == nil)
{
driveFiles = [[NSMutableArray alloc] init];
[driveFiles addObjectsFromArray:files.items];
// Now you have all files of root folder
for (GTLDriveFile *file in driveFiles)
NSLog(#"File is %#", file.title);
}
else
{
NSLog(#"An error occurred: %#", error);
}
}];
}
Note: For get full drive access your scope should be kGTLAuthScopeDrive.
[[GTMOAuth2ViewControllerTouch alloc] initWithScope:kGTLAuthScopeDrive
clientID:kClientId
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:finishedSelector];
2. How to download a specific file.
So for this you will have to use GTMHTTPFetcher. First get the download URL for that file.
NSString *downloadedString = file.downloadUrl; // file is GTLDriveFile
GTMHTTPFetcher *fetcher = [self.driveService.fetcherService fetcherWithURLString:downloadedString];
[fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error)
{
if (error == nil)
{
if(data != nil){
// You have successfully downloaded the file write it with its name
// NSString *name = file.title;
}
}
else
{
NSLog(#"Error - %#", error.description)
}
}];
Note: If you found "downloadedString" null Or empty just have look at file.JSON there are array of "exportsLinks" then you can get the file with one of them.
3. How to upload a file in specific folder: This is an example of uploading image.
-(void)uploadImage:(UIImage *)image
{
// We need data to upload it so convert it into data
// If you are getting your file from any path then use "dataWithContentsOfFile:" method
NSData *data = UIImagePNGRepresentation(image);
// define the mimeType
NSString *mimeType = #"image/png";
// This is just because of unique name you can give it whatever you want
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"dd-MMM-yyyy-hh-mm-ss"];
NSString *fileName = [df stringFromDate:[NSDate date]];
fileName = [fileName stringByAppendingPathExtension:#"png"];
// Initialize newFile like this
GTLDriveFile *newFile = [[GTLDriveFile alloc] init];
newFile.mimeType = mimeType;
newFile.originalFilename = fileName;
newFile.title = fileName;
// Query and UploadParameters
GTLUploadParameters *uploadParameters = [GTLUploadParameters uploadParametersWithData:data MIMEType:mimeType];
GTLQueryDrive *query = [GTLQueryDrive queryForFilesInsertWithObject:newFile uploadParameters:uploadParameters];
// This is for uploading into specific folder, I set it "root" for root folder.
// You can give any "folderIdentifier" to upload in that folder
GTLDriveParentReference *parentReference = [GTLDriveParentReference object];
parentReference.identifier = #"root";
newFile.parents = #[parentReference];
// And at last this is the method to upload the file
[[self driveService] executeQuery:query completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (error){
NSLog(#"Error: %#", error.description);
}
else{
NSLog(#"File has been uploaded successfully in root folder.");
}
}];
}
I'm using SDWebImage library and I have this code:
[cell.imgLogo setImageWithURL:[NSURL URLWithString:[item objectForKey:#"s_logo"]] placeholderImage:[UIImage imageNamed:#"default.png"]];
I have tweak the library SDWebImage a little bit to ignore empty string or a NSUrl with empty string in method downloadWithURL: delegate: options: userInfo::
if ([url isKindOfClass:NSString.class])
{
if ([(NSString *)url length] > 0) {
url = [NSURL URLWithString:(NSString *)url];
} else {
return;
}
}
else if (![url isKindOfClass:NSURL.class])
{
url = nil; // Prevent some common crashes due to common wrong values passed like NSNull.null for instance
}
else if ([url isKindOfClass:NSURL.class]) {
if ([[url absoluteString] length] > 0) {
//valid url
} else {
return;
}
}
So now it works with empty string and just to display its default image but the problem is when it comes to a string that is not an image url like:
http://beta.xxxxxxx.com/gangnamwe?to=boko
It displays nothing, it removes the placeholder image and displays nothing.
So how will I identify a valid image url? or is there any better work around for this?
Your help are much appreaciated.
you can check after getting NSData from NSURL . You can use GCD to download data
here is an example i created which save your image in photo library.
dispatch_async(dispatch_queue_create("com.getImage", NULL), ^(void) {
NSData *data=[NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRKII9COB-hvMef4Zvb9XYVbXKDFZHJAHwwzzGyMiy_b-q65GD43Chd37jH"]];
UIImage *image=[UIImage imageWithData:data];
if (image==nil) {
//yourImageURL is not valid
image=[UIImage imageNamed:#"placeholder.png"];
}
else{
//yourImageURL is valid
dispatch_async(dispatch_get_main_queue(), ^{
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
//show your image
});
}
});
There is method in NSURL to check if the file exsists.
- (BOOL)checkResourceIsReachableAndReturnError:(NSError **)error
Example
NSURL *theURL = [NSURL URLWithString:string];
NSError *err;
if ([theURL checkResourceIsReachableAndReturnError:&err] == NO)
{
NSLog(#"resource not reachable");
}
Here's a category on NSURL for you :
// nsurl+documentTypes.h
#interface NSURL (documentTypes)
- (BOOL)isImageType;
#end
// nsurl+documentTypes.m
#implementation NSURL (documentTypes)
- (BOOL)isImageType
{
NSString * UTI = (__bridge NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,(__bridge CFStringRef)[self pathExtension],NULL);
return UTTypeConformsTo((__bridge CFStringRef)UTI, kUTTypeImage);
}
#end
You can check if the url ends with some image name or not.You can get the parts of the url in following way:
NSURL* url = [NSURL URLWithString:#"http://digg.com/news/business/24hr"];
NSString* reducedUrl = [NSString stringWithFormat:
#"%#://%#/%#",
url.scheme,
url.host,
[url.pathComponents objectAtIndex:1]];
Now, take the last object of the pathComponents and check if it contains .png or .jpg etc.
In Swift:
import Foundation
public extension NSURL {
public var isImage: Bool {
return UTI.map{ UTTypeConformsTo($0, kUTTypeImage) } ?? false
}
public var UTI: String? {
var value: AnyObject?
let _ = try? getResourceValue(&value, forKey: NSURLTypeIdentifierKey)
return value as? String
}
}
e.g:
let url = NSURL(fileURLWithPath: "/Users/i/Desktop/image.png")
url.isImage //--> true
In my app I am using the camera and photo library to get an UIImage...After picking the image I need to convert it to NSData and wants to pass this data to a method called addBlobToContainer:....but it gives the EXC_BAD_ACCESS....
How can I resolve this?
Here is my code for photo library...
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
image.image = [info objectForKey:#"UIImagePickerControllerEditedImage"];
imageData = [NSData dataWithData:UIImageJPEGRepresentation(image.image,1)];
guid = [Guid randomGuid];
NSLog(#"%#", guid.description);
GUID = guid.description;
NSLog(#"GUID===%#",GUID);
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)viewWillAppear:(BOOL)animated
{
NSLog(#"STRIMAGEDATA===%#",imageData);
if ([imageData length] != 0)
{
NSLog(#"%#",imageData);
[client addBlobToContainer:newcontainer blobName:GUID contentData:imageData contentType:#"application/octet-stream" withBlock:^(NSError *error)
{
if (error)
{
NSLog(#"%#",[error localizedDescription]);
}
else
{
NSLog(#"blob inserted suuccessfully…");
imageURL = [serviceURL stringByAppendingString:[NSString stringWithFormat:#"%#.jpg",GUID]];
NSLog(#"IMAGEURL=%#",imageURL);
}
}];-->EXC_BAD_ACCESS
}
}
You are not accessing a property, you are accessing the variable behind the property. If you want the data to be automatically retained by the property, use property setters, e.g. self.imageData = ... instead of imageData = ....
Try
self.imageData = UIImageJPEGRepresentation(image.image,1);
I'm facing an issue with the ALAsset library: I have an UIView with 100 image views. When the view is loading, i'm calling a function for generating the images from the file name.
This is my class:
#interface myClass
{
NSString *fileName;
int pathId;
}
viewDidLoad
-(void)viewDidLoad
{
NSMutableArray *imageCollectionArray = [self createImage:arrayOfmyClassObject];
//Here I'm binding the 100 images in UIView using the images in imageCollectionArray
}
This is my method in which I found the issue:
- (NSMutableArray *)createImage:(NSMutableArray *)imageFileNamesArray
{
imageArray = [[NSMutableArray alloc] init];
for (int imageNameKey = 0; imageNameKey<100; imageNameKey++)
{
myClass *obj= [imageFileNamesArray objectAtIndex:imageNameKey];
if(obj.pathId == 0)
{
//Here adding the bundle image into the imageArray
[imageArray addObject:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:obj.fileName ofType:#"png" inDirectory:#"Images"]]];
}
else
{
typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset); typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) {
};
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *images;
if (iref)
{
//Here adding the photo library image into the imageArray
images = [UIImage imageWithCGImage:iref];
[imageArray addObject:images];
}
else
{
//Here adding the Nofile.png image into the imageArray if didn't find a photo library image
images = [UIImage imageNamed:#"Nofile.png"];
[imageArray addObject:images];
}
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror) {
//Here adding the Nofile.png image into the imageArray if any failure occurs
[imageArray addObject:[UIImage imageNamed:#"Nofile.png"]];
NSLog(#"booya, cant get image - %#",[myerror localizedDescription]);
};
NSURL *asseturl = [NSURL URLWithString:obj.fileName];
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:asseturl
resultBlock:resultblock failureBlock:failureblock];
}
}
return imageArray;
}
The problem was when I loads the view at first time the asset library images are not generating, only bundle images were displayed, if i go to any of the another view and return back to 100 image view then the asset images are generated.And works fine. The problem is the same function is not generating asset images at the first load. How can i fix this? Thanks in advance.
All methods related to ALAssetLibrary are asynchronous, so your view may complete its loading life cycle before the desired data is returned. You have to take this into account and redraw your view (or a portion of it) as needed.
My app has a user select a photo from the picker. I apply the selected image to a view and then I save it to a file and reference that file in user defaults so when the UserProfile is created that avatar is loaded in.
When I close the app and then start it again, the app loads the image from the file. After loading the image from the file, my app is crashing when I apply it to an Image view because it is seen as an __NSCFArray. There is no method scale on __NSCFArray. Why is it being cast to this class?
-[__NSCFArray scale]: unrecognized selector sent to instance 0x145260
* Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '[__NSCFArray scale]:
unrecognized selector sent to instance 0x145260'
Here is my code where the UIImage is created from a file:
#implementation UserProfile
- (id) init {
self = [super init];
if(self) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
_username = (NSString *)[userDefaults objectForKey:USERNAME_KEY];
NSString *filename = (NSString *) [userDefaults objectForKey:#"AvatarFilename"];
NSLog(#"filename from user defaults: %#",filename);
if (filename) {
_avatar = [UIImage imageWithContentsOfFile:filename];
if (!_avatar) NSLog(#"LOGERROR: avatar was not created from file");
_customAvatar = TRUE;
} else {
_customAvatar = FALSE;
_avatar = [UIImage imageNamed:DEFAULT_AVATAR_FILENAME];
if (!_avatar) NSLog(#"LOGERROR: avatar was not created from default");
}
[self createThumbnail];
}
return self;
}
Note: in my createThumbnail code I call this [_avatar isKindOfClass:[UIImage class] and it says it is a UIImage. But then when I set the view, it thinks it is an __NSCFArray. I don't even understand how this is possible since the property is a UIImage *.
This is how the image is persisted
- (void) setAvatar:(UIImage *)image
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *filename = nil;
if (image) {
if (_avatar) [_avatar release];
_avatar = [image retain];
_customAvatar = TRUE;
filename = [NSString stringWithFormat:#"%#/%#",[MyUtilities applicationDocumentsDirectory],AVATAR_FILENAME];
if (![UIImagePNGRepresentation(image) writeToFile:filename atomically:YES])
NSLog(#"LOGERROR: Failure to write avatar file");
else NSLog(#"saved avatar to PNG file");
} else {
NSLog(#"setting default avatar");
_avatar = [UIImage imageNamed:DEFAULT_AVATAR_FILENAME];
_customAvatar = FALSE;
}
[userDefaults setObject:filename forKey:AVATAR_KEY];
// TODO If performance is crucial, consider creating a default thumbnail as well
[self createThumbnail];
if(![userDefaults synchronize])
{
NSLog(#"LOGERROR: Failure to synchronize userDefaults");
}
}
To my knowledge I am not receiving a memory warning.
You are assigning autoreleased objects to your ivars. Most likely they are being deallocated and then when you try to access one of those UIImage it happens to be an NSArray at the same memory address.
_username = (NSString *)[userDefaults objectForKey:USERNAME_KEY];
...
_avatar = [UIImage imageWithContentsOfFile:filename];
...
_avatar = [UIImage imageNamed:DEFAULT_AVATAR_FILENAME];
You need to retain them.
You are saving autoreleased object (return by imageNamed: and imageWithContentsOfFile:) without retaining in init. You can replace that method with next one:
- (id) init {
self = [super init];
if(self) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
_username = (NSString *)[userDefaults objectForKey:USERNAME_KEY];
NSString *filename = (NSString *) [userDefaults objectForKey:#"AvatarFilename"];
NSLog(#"filename from user defaults: %#",filename);
if (filename) {
_avatar = [[UIImage imageWithContentsOfFile:filename] retain];
if (!_avatar) NSLog(#"LOGERROR: avatar was not created from file");
_customAvatar = TRUE;
} else {
_customAvatar = FALSE;
_avatar = [[UIImage imageNamed:DEFAULT_AVATAR_FILENAME] retain];
if (!_avatar) NSLog(#"LOGERROR: avatar was not created from default");
}
[self createThumbnail];
}
return self;
}
You don't need to retain this object anymore.
Just release it in dealloc.
In setAvatar: you should also retain returned by imageNamed: value : _avatar = [[UIImage imageNamed:DEFAULT_AVATAR_FILENAME] retain];