Removing url fragment from NSURL - cocoa

I'm writing a Cocoa application, which uses NSURLs -- I need to remove the fragment portion of the URL (the #BLAH part).
example: http://example.com/#blah should end up as http://example.com/
I found some code in WebCore that seems to do it by using CFURL functionality, but it never finds the fragment portion in the URL. I've encapsulated it in a extension category:
-(NSURL *)urlByRemovingComponent:(CFURLComponentType)component {
CFRange fragRg = CFURLGetByteRangeForComponent((CFURLRef)self, component, NULL);
// Check to see if a fragment exists before decomposing the URL.
if (fragRg.location == kCFNotFound)
return self;
UInt8 *urlBytes, buffer[2048];
CFIndex numBytes = CFURLGetBytes((CFURLRef)self, buffer, 2048);
if (numBytes == -1) {
numBytes = CFURLGetBytes((CFURLRef)self, NULL, 0);
urlBytes = (UInt8 *)(malloc(numBytes));
CFURLGetBytes((CFURLRef)self, urlBytes, numBytes);
} else
urlBytes = buffer;
NSURL *result = (NSURL *)CFMakeCollectable(CFURLCreateWithBytes(NULL, urlBytes, fragRg.location - 1, kCFStringEncodingUTF8, NULL));
if (!result)
result = (NSURL *)CFMakeCollectable(CFURLCreateWithBytes(NULL, urlBytes, fragRg.location - 1, kCFStringEncodingISOLatin1, NULL));
if (urlBytes != buffer) free(urlBytes);
return result ? [result autorelease] : self;
}
-(NSURL *)urlByRemovingFragment {
return [self urlByRemovingComponent:kCFURLComponentFragment];
}
This is used as such:
NSURL *newUrl = [[NSURL URLWithString:#"http://example.com/#blah"] urlByRemovingFragment];
unfortunately, newUrl ends up being "http://example.com/#blah" because the first line in urlByRemovingComponent always returns kCFNotFound
I'm stumped. Is there a better way of going about this?
Working Code, thanks to nall
-(NSURL *)urlByRemovingFragment {
NSString *urlString = [self absoluteString];
// Find that last component in the string from the end to make sure to get the last one
NSRange fragmentRange = [urlString rangeOfString:#"#" options:NSBackwardsSearch];
if (fragmentRange.location != NSNotFound) {
// Chop the fragment.
NSString* newURLString = [urlString substringToIndex:fragmentRange.location];
return [NSURL URLWithString:newURLString];
} else {
return self;
}
}

How about this:
NSString* s = #"http://www.somewhere.org/foo/bar.html/#label";
NSURL* u = [NSURL URLWithString:s];
// Get the last path component from the URL. This doesn't include
// any fragment.
NSString* lastComponent = [u lastPathComponent];
// Find that last component in the string from the end to make sure
// to get the last one
NSRange fragmentRange = [s rangeOfString:lastComponent
options:NSBackwardsSearch];
// Chop the fragment.
NSString* newURLString = [s substringToIndex:fragmentRange.location + fragmentRange.length];
NSLog(#"%#", s);
NSLog(#"%#", newURLString);

This is quite an old question, and it has already been answered, but for another simple option this is how I did it:
NSString* urlAsString = [myURL absoluteString];
NSArray* components = [urlAsString componentsSeparatedByString:#"#"];
NSURL* myURLminusFragment = [NSURL URLWithString: components[0]];
if there is no fragment, urlMinusFragment will be the same as myURL

Swift 3.0
this will remove the fragment
if let fragment = url.fragment{
url = URL(string: url.absoluteString.replacingOccurrences(of: "#\(fragment)", with: "")!
}

Related

macOS SearchKit CoreService found nothing

im working on an that search in the content of allot of files, i planed to use SearchKit but i can't figure out to make Apple's sample code to work, and i can't find any other ressources (NSHipster code didn't work either), here's my code:
#define kSearchMax 1000
#interface ViewController()
#property(nonatomic) SKIndexRef mySKIndex;
#end
#implementation ViewController
#synthesize mySKIndex;
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self openIndex];
[self addDoc];
SKIndexFlush(self.mySKIndex);
// i thought that the indexation may need some time ..
sleep(2);
dispatch_async(dispatch_get_main_queue(), ^{
[self searchterm:#"var"];
});
});
}
- (void) openIndex {
NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:#"index"] stringByAppendingPathExtension:#"txt"]; // 1
NSURL *url = [NSURL fileURLWithPath:path];
NSString *name = #"extension_index";
if ([name length] == 0) name = nil;
SKIndexType type = kSKIndexInverted;
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
mySKIndex = SKIndexOpenWithURL ((__bridge CFURLRef) url,
(__bridge CFStringRef) name,
true
);
}else{
self.mySKIndex = SKIndexCreateWithURL((__bridge CFURLRef) url,
(__bridge CFStringRef) name,
(SKIndexType) type,
(CFDictionaryRef) NULL);
}
}
- (void) addDoc {
SKLoadDefaultExtractorPlugIns ();
NSString *path = [NSBundle.mainBundle pathForResource:#"Products" ofType:#"rtf"]; // 1
NSURL *url = [NSURL fileURLWithPath: path]; // 2
SKDocumentRef doc = SKDocumentCreateWithURL ((__bridge CFURLRef) url);
NSString *mimeTypeHint = #"text/rtf";
BOOL added = SKIndexAddDocument ((SKIndexRef) mySKIndex,
(SKDocumentRef) doc,
(__bridge CFStringRef)mimeTypeHint,
(Boolean) true
);
NSLog(added ? #"added" : #"not added");
}
- (void) searchterm:(NSString*)query{
SKSearchOptions options = kSKSearchOptionDefault;
BOOL more = YES;
UInt32 totalCount = 0;
SKSearchRef search = SKSearchCreate (mySKIndex,
(__bridge CFStringRef) query,
options);
while (more) {
SKDocumentID foundDocIDs [kSearchMax];
float foundScores [kSearchMax];
float *scores;
Boolean unranked =
options & kSKSearchOptionNoRelevanceScores;
if (unranked) {
scores = NULL;
} else {
scores = foundScores;
}
CFIndex foundCount = 0;
more = SKSearchFindMatches (
search,
kSearchMax,
foundDocIDs,
scores,
100,
&foundCount
);
NSLog(#"%#", [NSString stringWithFormat:#"current count = %i", totalCount]);
totalCount += foundCount;
}
}
#end
it always print "current count = 0" and the loop is executed only one time.

How do you get the image data from NSAttributedString

I have an NSTextView. I paste an image into it and see it. When I get the NSTextAttachment for the NSAttributedString of the text view, it's file wrapper is nil. How do I get the image data that was pasted into the text view?
I'm using a category on NSAttributedString to get the text attachments. I would prefer not to write to disk if it's possible.
- (NSArray *)allAttachments
{
NSError *error = NULL;
NSMutableArray *theAttachments = [NSMutableArray array];
NSRange theStringRange = NSMakeRange(0, [self length]);
if (theStringRange.length > 0)
{
NSUInteger N = 0;
do
{
NSRange theEffectiveRange;
NSDictionary *theAttributes = [self attributesAtIndex:N longestEffectiveRange:&theEffectiveRange inRange:theStringRange];
NSTextAttachment *theAttachment = [theAttributes objectForKey:NSAttachmentAttributeName];
if (theAttachment != NULL){
NSLog(#"filewrapper: %#", theAttachment.fileWrapper);
[theAttachments addObject:theAttachment];
}
N = theEffectiveRange.location + theEffectiveRange.length;
}
while (N < theStringRange.length);
}
return(theAttachments);
}
Enumerate the attachments. [NSTextStorage enumerateAttribute:...]
Get the attachment's filewrapper.
Write to a URL.
[textStorage enumerateAttribute:NSAttachmentAttributeName
inRange:NSMakeRange(0, textStorage.length)
options:0
usingBlock:^(id value, NSRange range, BOOL *stop)
{
NSTextAttachment* attachment = (NSTextAttachment*)value;
NSFileWrapper* attachmentWrapper = attachment.fileWrapper;
[attachmentWrapper writeToURL:outputURL options:NSFileWrapperWritingAtomic originalContentsURL:nil error:nil];
(*stop) = YES; // stop so we only write the first attachment
}];
This sample code will only write the first attachment to outputURL.
You can get the contained NSImage from the attachment cell.
Minimalistic example:
// assuming we have a NSTextStorage* textStorage object ready to go,
// and that we know it contains an attachment at some_index
// (in real code we would probably enumerate attachments).
NSRange range;
NSDictionary* textStorageAttrDict = [textStorage attributesAtIndex:some_index
longestEffectiveRange:&range
inRange:NSMakeRange(0,textStorage.length)];
NSTextAttachment* textAttachment = [textStorageAttributesDictionary objectForKey:#"NSAttachment"];
NSTextAttachmentCell* textAttachmentCell = textAttachment.attachmentCell;
NSImage* attachmentImage = textAttachmentCell.image;
EDITING:
OS X only (AppKit version)
#EmeraldWeapon's answer is good for Objective-C, but falls down in Swift, as in Swift the attachmentCell is not an NSTextAttachmentCell, but rather an NSTextAttachmentCellProtocol? (which does not provide .image) - so you need to cast it to a concrete instance before accessing the .image:
func firstImage(textStorage: NSTextStorage) -> NSImage? {
for idx in 0 ..< textStorage.string.count {
if
let attr = textStorage.attribute(NSAttributedString.Key.attachment, at: idx, effectiveRange: nil),
let attachment = attr as? NSTextAttachment,
let cell = attachment.attachmentCell as? NSTextAttachmentCell,
let image = cell.image {
return image
}
}
return nil
}

Reading .txt File into NSInputstream returns filestream has no bytes available

I used NSInputstream to read from a file. After reading, the NSInputstream contents are empty.I used the code(For transferring a .txt file to ftp server)
- (void)startSend
{
AppDelegate *mainDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
BOOL success;
NSURL * url;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *testsessionid=[defaults stringForKey:#"testsessionid"];
NSString *writeFileName=[NSString stringWithFormat:#"%#%#.txt",testsessionid,mainDelegate.studentID];
NSLog(#"Write file name %#",writeFileName);
NSArray *searchPaths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolderPath = [searchPaths objectAtIndex: 0];
NSString *filePath= [documentFolderPath stringByAppendingPathComponent: writeFileName];
NSLog(#"Write folder name %#",filePath);
filePath=#"/Users/sree/Desktop/ARATHY/BCLSTestApp/BCLSTest/Question2.txt";
assert(filePath != nil);
assert([[NSFileManager defaultManager] fileExistsAtPath:filePath]);
assert( [filePath.pathExtension isEqual:#"txt"] );
assert(self.networkStream == nil); // don't tap send twice in a row!
assert(self.fileStream == nil);
// First get and check the URL.
url = [NSURL URLWithString:#"ftp://edugame#www.edugame.co/bclstest/243"];
success = (url != nil);
if (success) {
// Add the last part of the file name to the end of the URL to form the final
// URL that we're going to put to.
url = CFBridgingRelease(
CFURLCreateCopyAppendingPathComponent(NULL, ( CFURLRef) url, (CFStringRef) [filePath lastPathComponent], false)
);
success = (url != nil);
}
// If the URL is bogus, let the user know. Otherwise kick off the connection
if ( ! success) {
NSLog(#"Invalid URL");
} else {
// Open a stream for the file we're going to send. We do not open this stream;
// NSURLConnection will do it for us.
self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath];
// self.fileStream=[[NSInputStream alloc] initWithFileAtPath:filePath];
if(self.fileStream==nil)
NSLog(#"FILE DOESN'T EXIST");
else
NSLog(#"FILE EXISTS");
assert(self.fileStream != nil);
BOOL hasbyte=[self.fileStream hasBytesAvailable];
if (hasbyte==YES)
NSLog(#"Has contents");
else
NSLog(#"no contents");
[self.fileStream open];
// NSLog(#"SIZE OF STREAM IS >> %d",fi);
// Open a CFFTPStream for the URL.
self.networkStream = CFBridgingRelease(
CFWriteStreamCreateWithFTPURL(NULL, ( CFURLRef) url)
);
assert(self.networkStream != nil);
success = [self.networkStream setProperty:#"edugame" forKey:(id)kCFStreamPropertyFTPUserName];
assert(success);
success = [self.networkStream setProperty:#"edu1#Game" forKey:(id)kCFStreamPropertyFTPPassword];
assert(success);
self.networkStream.delegate = self;
[self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.networkStream open];
// Tell the UI we're sending.
//[self sendDidStart];
}
}
It prints, "FILE EXISTS"
But in the next line "no contents"
The file is not empty.
I just had the same issue, and the answer is simple and silly.
After you create your stream:
self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath];
you need to call open on it:
[self.fileStream open];
It makes total sense now that I know what is missing, but I didn't see the call to open in the examples I found of using NSInputStream.

CGImageSourceRef memoryleak

NSDictionary* result = nil;
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)[self TIFFRepresentation], NULL);
if ( NULL == source )
{
}
else
{
CFDictionaryRef metadataRef = CGImageSourceCopyPropertiesAtIndex (source, 0, NULL);
if (metadataRef)
{
NSDictionary* immutableMetadata = (__bridge NSDictionary *)metadataRef;
if (immutableMetadata)
{
result = [NSDictionary dictionaryWithDictionary : (__bridge NSDictionary *)metadataRef];
}
CFRelease(metadataRef);
metadataRef = nil;
}
CFRelease(source);
source = nil;
}
return result;
I am using XCode with ARC.
This code causes my app to leak memory when i run it on many images in a loop.
Does anybody know what i did wrong?
wrapping #autoreleasepool around code solved the problem. Images were about 1.2MB

Caching URL - Creating appropriate NSString representation

I want to cache an image I download from a URL in the local filesystem.
What I don't know, is how can I create from the URL a NSString that is compatible with the characters the iOS file system supports. For instance, if I try to create a file with a : in the name, it will fail.
What steps should I follow to create this NSString? Is a simple hash the best way to go? If so, what hash routine is available in iOS that I can use?
I'd use hashing:
it'll make for much more readable file names
you avoid problems with file name length
md5 should be perfectly fine for your purposes. Unfortunately, the ios5-sdk contains only a C-String function for this:
CC_MD5(in, in_len, out);
The function is contained in <CommonCrypto/CommonDigest.h>, there are other hash functions there, too.
You can find infos on how to wrap this up in a function that takes/returns an NSString here.
The following code belongs to ZDS_Shared. resolveLocalURLForRemoteURL accepts a remote URL and returns a URL pointing to the file on the iOS filesystem. The filename will be an alphanumeric string that doesn't resemble the original URL, but that shouldn't be a problem.
https://github.com/ZarraStudios/ZDS_Shared/blob/master/ZSAssetManager.m#L185
- (NSURL*)resolveLocalURLForRemoteURL:(NSURL*)url
{
if (!url) return nil;
NSString *filename = [[url absoluteString] zs_digest];
NSString *filePath = [[self cachePath] stringByAppendingPathComponent:filename];
return [NSURL fileURLWithPath:filePath];
}
https://github.com/ZarraStudios/ZDS_Shared/blob/master/NSString%2BZSAdditions.m#L38
// NSString category
- (NSString*)zs_digest
{
const char *cstr = [self cStringUsingEncoding:NSASCIIStringEncoding];
return [[NSData dataWithBytes:cstr length:strlen(cstr)] zs_digest];
}
https://github.com/ZarraStudios/ZDS_Shared/blob/master/NSData%2BZSAdditions.m#L38
// NSData category
- (NSString*)zs_digest
{
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1([self bytes], [self length], digest);
NSMutableString* outputHolder = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
[outputHolder appendFormat:#"%02x", digest[i]];
}
NSString *output = [outputHolder copy];
MCRelease(outputHolder);
return [output autorelease];
}

Resources